--- /dev/null
+/* ovirt viewer console application
+ * Copyright (C) 2008 Red Hat Inc.
+ * Written by Richard W.M. Jones <rjones@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <glib.h>
+
+#include "internal.h"
+
+/* Private functions. */
+static gpointer wui_thread (gpointer data);
+static void wui_thread_send_quit (void);
+static gboolean do_connect (void);
+static gboolean do_login (void);
+static void refresh_vm_list (void);
+
+/* Messages (main thread -> WUI thread only).
+ *
+ * These are passed by reference. They are allocated by the sender
+ * (ie. the main thread) and freed by the receiver (ie. the WUI thread).
+ */
+enum message_type {
+ QUIT, /* Tell the WUI thread to quit cleanly. */
+ CONNECT, /* Tell to connect (just fetch the URI). */
+ DISCONNECT, /* Tell to disconnect, forget state. */
+ LOGIN, /* Tell to login, and pass credentials. */
+ REFRESH_VM_LIST, /* Tell to refresh the VM list right away. */
+};
+
+struct message {
+ enum message_type type;
+ char *str1;
+ char *str2;
+};
+
+/* Start the WUI thread. See main() for explanation of the threading model. */
+static GThread *wui_gthread = NULL;
+static GAsyncQueue *wui_thread_queue = NULL;
+
+void
+start_wui_thread (void)
+{
+ GError *error = NULL;
+
+ DEBUG ("starting the WUI thread");
+
+ /* Create the message queue for main -> WUI thread communications. */
+ wui_thread_queue = g_async_queue_new ();
+
+ wui_gthread = g_thread_create (wui_thread, wui_thread_queue, TRUE, &error);
+ if (error) {
+ g_print ("%s\n", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+}
+
+void
+stop_wui_thread (void)
+{
+ DEBUG ("stopping the WUI thread");
+ assert (wui_gthread);
+
+ /* Send a quit message then wait for the WUI thread to join.
+ *
+ * This "nice" shutdown could cause the UI to hang for an
+ * indefinite period (eg. if the WUI thread is engaged in some
+ * long connection or download from the remote server). But
+ * I want to keep it this way for the moment so that we can
+ * diagnose problems with the WUI thread.
+ *
+ * (This could be solved with some sort of interruptible
+ * join, but glib doesn't support that AFAICT).
+ */
+ wui_thread_send_quit ();
+ (void) g_thread_join (wui_gthread);
+ g_async_queue_unref (wui_thread_queue);
+ wui_gthread = NULL;
+}
+
+/* Send the quit message to the WUI thread. */
+static void
+wui_thread_send_quit (void)
+{
+ struct message *msg;
+
+ msg = g_new (struct message, 1);
+ msg->type = QUIT;
+ g_async_queue_push (wui_thread_queue, msg);
+}
+
+/* Send the connect message to the WUI thread. */
+void
+wui_thread_send_connect (const char *uri)
+{
+ struct message *msg;
+
+ msg = g_new (struct message, 1);
+ msg->type = CONNECT;
+ msg->str1 = g_strdup (uri);
+ g_async_queue_push (wui_thread_queue, msg);
+}
+
+/* Send the disconnect message to the WUI thread. */
+void
+wui_thread_send_disconnect (void)
+{
+ struct message *msg;
+
+ msg = g_new (struct message, 1);
+ msg->type = DISCONNECT;
+ g_async_queue_push (wui_thread_queue, msg);
+}
+
+/* Send the login message to the WUI thread. */
+void
+wui_thread_send_login (const char *username, const char *password)
+{
+ struct message *msg;
+
+ msg = g_new (struct message, 1);
+ msg->type = LOGIN;
+ msg->str1 = g_strdup (username);
+ msg->str2 = g_strdup (password);
+ g_async_queue_push (wui_thread_queue, msg);
+}
+
+/* Send the refresh VM list message to the WUI thread. */
+void
+wui_thread_send_refresh_vm_list (void)
+{
+ struct message *msg;
+
+ msg = g_new (struct message, 1);
+ msg->type = REFRESH_VM_LIST;
+ g_async_queue_push (wui_thread_queue, msg);
+}
+
+/* The current state.
+ *
+ * For safety, the main thread must lock this before reading, and the
+ * WUI thread must lock this before writing. However the WUI thread
+ * does not need to lock before reading, because no other thread
+ * can modify it.
+ */
+static gboolean connected = FALSE;
+static gboolean logged_in = FALSE;
+static gboolean busy = FALSE;
+static GStaticMutex state_mutex;
+
+static void set_connected (gboolean);
+static void set_logged_in (gboolean);
+static void set_busy (gboolean);
+
+/* The private state of the WUI thread. */
+static int secs_between_refresh = 60;
+static char *uri = NULL;
+static char *username = NULL;
+static char *password = NULL;
+
+static gboolean process_message (struct message *);
+
+/* The WUI thread. See main() above for explanation of
+ * the threading model.
+ */
+static gpointer
+wui_thread (gpointer _queue)
+{
+ GAsyncQueue *queue = (GAsyncQueue *) _queue;
+ gboolean quit = FALSE;
+ GTimeVal tv;
+ gpointer _msg;
+ struct message *msg;
+
+ DEBUG ("WUI thread starting up");
+
+ g_async_queue_ref (queue);
+
+ /* In the thread's loop we check for new instructions from the main
+ * thread and carry them out. Also, if we are connected and logged
+ * in then we periodically recheck the list of VMs.
+ */
+ while (!quit) {
+ set_busy (FALSE);
+
+ if (logged_in) {
+ g_get_current_time (&tv);
+ g_time_val_add (&tv, secs_between_refresh * 1000000);
+ _msg = g_async_queue_timed_pop (queue, &tv);
+ } else
+ _msg = g_async_queue_pop (queue);
+
+ set_busy (TRUE);
+
+ msg = (struct message *) _msg;
+
+ if (msg) {
+ DEBUG ("received message %d", msg->type);
+ quit = process_message (msg);
+ /* Don't free any strings in the message - we've saved them. */
+ g_free (msg);
+ } else {
+ /* No message, must have got a timeout instead, which means
+ * we are logged in and we should refresh the list of VMs.
+ * Note it's not an error if we temporarily lose contact
+ * with the WUI.
+ */
+ refresh_vm_list ();
+ }
+ }
+
+ DEBUG ("WUI thread shutting down cleanly");
+
+ g_async_queue_unref (queue);
+ g_thread_exit (NULL);
+ return NULL; /* not reached? */
+}
+
+/* The WUI thread calls this to safely update the state variables.
+ * This also updates elements in the UI by setting idle callbacks
+ * which are executed in the context of the main thread.
+ */
+static void
+set_connected (gboolean new_connected)
+{
+ g_static_mutex_lock (&state_mutex);
+ connected = new_connected;
+ g_static_mutex_unlock (&state_mutex);
+}
+
+static void
+set_logged_in (gboolean new_logged_in)
+{
+ g_static_mutex_lock (&state_mutex);
+ logged_in = new_logged_in;
+ g_static_mutex_unlock (&state_mutex);
+}
+
+static void
+set_busy (gboolean new_busy)
+{
+ g_static_mutex_lock (&state_mutex);
+ busy = new_busy;
+ g_static_mutex_unlock (&state_mutex);
+}
+
+/* The main thread should call these functions to get the WUI thread's
+ * current state.
+ */
+gboolean
+wui_thread_is_connected (void)
+{
+ gboolean ret;
+
+ g_static_mutex_lock (&state_mutex);
+ ret = connected;
+ g_static_mutex_unlock (&state_mutex);
+ return ret;
+}
+
+gboolean
+wui_thread_is_logged_in (void)
+{
+ gboolean ret;
+
+ g_static_mutex_lock (&state_mutex);
+ ret = logged_in;
+ g_static_mutex_unlock (&state_mutex);
+ return ret;
+}
+
+gboolean
+wui_thread_is_busy (void)
+{
+ gboolean ret;
+
+ g_static_mutex_lock (&state_mutex);
+ ret = busy;
+ g_static_mutex_unlock (&state_mutex);
+ return ret;
+}
+
+/* Process a message from the main thread. */
+static gboolean
+process_message (struct message *msg)
+{
+ switch (msg->type) {
+ case QUIT:
+ if (uri) g_free (uri);
+ if (username) g_free (username);
+ if (password) g_free (password);
+ set_connected (FALSE);
+ set_logged_in (FALSE);
+ return 1;
+
+ case CONNECT:
+ if (uri) g_free (uri);
+ uri = msg->str1;
+
+ if (do_connect ()) {
+ set_connected (TRUE);
+ } else {
+ set_connected (FALSE);
+ set_logged_in (FALSE);
+ }
+ break;
+
+ case DISCONNECT:
+ /* This just forgets the state. REST is connectionless. */
+ if (uri) g_free (uri);
+ if (username) g_free (username);
+ if (password) g_free (password);
+ set_connected (FALSE);
+ set_logged_in (FALSE);
+ break;
+
+ case LOGIN:
+ if (username) g_free (username);
+ username = msg->str1;
+ if (password) g_free (password);
+ password = msg->str2;
+
+ if (connected) {
+ if (do_login ()) {
+ set_logged_in (TRUE);
+ refresh_vm_list ();
+ } else {
+ set_logged_in (FALSE);
+ }
+ }
+ break;
+
+ case REFRESH_VM_LIST:
+ refresh_vm_list ();
+ secs_between_refresh = 60;
+ break;
+
+ default:
+ DEBUG ("unknown message type %d", msg->type);
+ abort ();
+ }
+
+ return 0;
+}
+
+/* Try to connect to the current URI. Returns true on success. */
+static gboolean
+do_connect (void)
+{
+ DEBUG ("connecting to uri %s", uri);
+ return FALSE;
+}
+
+/* Try to login to URI/login with the username and password given.
+ * Returns true on success.
+ */
+static gboolean
+do_login (void)
+{
+ DEBUG ("logging in with username %s, password *****", username);
+ return FALSE;
+}
+
+/* Refresh the list of VMs. */
+static void
+refresh_vm_list (void)
+{
+ DEBUG ("refreshing list of VMs");
+}