static GtkWidget *la_username;
static GtkWidget *la_password;
static GtkWidget *la_button;
+static GtkWidget *notebook;
+static GtkWidget *statusbar;
+static guint statusbar_ctx;
static GdkCursor *busy_cursor;
/* Menus. */
GtkWidget *ca_hbox;
GtkWidget *ca_label;
GtkWidget *la_hbox;
- GtkWidget *notebook;
DEBUG ("creating viewer windows and menus");
/* Tabbed notebook. */
notebook = gtk_notebook_new ();
- gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_LEFT);
+ /*gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_LEFT);*/
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), TRUE);
gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE);
+ /* Status bar. */
+ statusbar = gtk_statusbar_new ();
+ statusbar_ctx = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar),
+ "context");
+ gtk_statusbar_push (GTK_STATUSBAR (statusbar), statusbar_ctx, "");
+
/* Packing. */
gtk_container_add (GTK_CONTAINER (window), vbox);
gtk_container_add_with_properties (GTK_CONTAINER (vbox), menubar,
"expand", FALSE, "fill", TRUE, NULL);
gtk_container_add_with_properties (GTK_CONTAINER (vbox), notebook,
"expand", TRUE, NULL);
+ gtk_container_add_with_properties (GTK_CONTAINER (vbox), statusbar,
+ "expand", FALSE, NULL);
/* Show widgets. */
gtk_widget_show_all (window);
wui_thread_send_login (username, password);
}
+/* Connect to a virtual machine. This callback is called from the
+ * connect menu. It searches the notebook of gtk-vnc widgets to see
+ * if we have already connected to this machine, and if not it
+ * makes a new connection.
+ */
+static void
+connect_to_vm (GtkWidget *widget, gpointer _vm)
+{
+ struct vm *vm = (struct vm *) _vm;
+ int n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
+ int i, uuidlen, len;
+ GtkWidget *child;
+ const char *label;
+ char *label2;
+
+ DEBUG ("searching tabs for uuid %s", vm->uuid);
+
+ uuidlen = strlen (vm->uuid);
+
+ /* Search the tabs for this UUID, and if found, switch to it and return. */
+ for (i = 0; i < n; ++i) {
+ child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i);
+ label = gtk_notebook_get_tab_label_text (GTK_NOTEBOOK (notebook), child);
+ len = strlen (label);
+ if (len >= uuidlen &&
+ STREQ (label + len - uuidlen, vm->uuid)) {
+ DEBUG ("found on tab %d", i);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), i);
+ return;
+ }
+ }
+
+ DEBUG ("not found, creating new tab");
+
+ /* This VM isn't in the notebook already, so create a new console. */
+ len = strlen (vm->description) + 1 + strlen (vm->uuid) + 1;
+ label2 = g_alloca (len);
+ snprintf (label2, len, "%s %s", vm->description, vm->uuid);
+
+ child = gtk_label_new (label2); /* XXX */
+
+ /* NB. We have to do this before adding it to the notebook. */
+ gtk_widget_show (child);
+
+ i = gtk_notebook_append_page (GTK_NOTEBOOK (notebook), child, NULL);
+ gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (notebook), child, label2);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), i);
+
+ DEBUG ("finished creating new tab");
+}
+
/* Remove all menu items from the Connect menu. */
static void
remove_menu_item (GtkWidget *menu_item, gpointer data)
DEBUG ("status error: %s", str);
ASSERT_IS_MAIN_THREAD ();
- /*
- gtk_label_set_text (GTK_LABEL (...), str);
- */
+ gtk_statusbar_pop (GTK_STATUSBAR (statusbar), statusbar_ctx);
+ gtk_statusbar_push (GTK_STATUSBAR (statusbar), statusbar_ctx, str);
g_free (str);
return FALSE;
}
-/* The WUI thread has updated the vm list. */
-static void
-add_vm_to_connectmenu (gpointer _vm, gpointer data)
-{
- struct vm *vm = (struct vm *) _vm;
- GtkWidget *item;
-
- DEBUG ("adding %s to Connect menu", vm->description);
+/* The WUI thread has updated the vm list. Here in the main thread
+ * we keep our own copy of the vmlist.
+ */
+static GSList *vmlist = NULL;
- item = gtk_menu_item_new_with_label (vm->description);
- gtk_menu_append (GTK_MENU (connectmenu), item);
-}
+static void add_vm_to_connectmenu (gpointer _vm, gpointer data);
gboolean
main_vmlist_updated (gpointer data)
{
- GSList *vmlist;
+ GSList *new_vmlist;
DEBUG ("vmlist updated");
ASSERT_IS_MAIN_THREAD ();
/* Get the new vmlist. */
- if (wui_thread_get_vmlist (&vmlist)) {
+ if (wui_thread_get_vmlist (&new_vmlist)) {
+ /* Free the previous vmlist. This invalidates all the vm pointers
+ * contained in the Connect menu callbacks, but we're going to
+ * delete those callbacks and create news ones in a moment anyway ...
+ */
+ free_vmlist (vmlist);
+
+ vmlist = new_vmlist;
+
clear_connectmenu ();
gtk_menu_append (GTK_MENU (connectmenu), refresh_vmlist);
gtk_menu_append (GTK_MENU (connectmenu), refresh_vmlist_separator);
g_slist_foreach (vmlist, add_vm_to_connectmenu, NULL);
}
- free_vmlist (vmlist);
/* Grrrr Gtk is stupid. */
gtk_widget_show_all (connectmenu);
return FALSE;
}
+
+static void
+add_vm_to_connectmenu (gpointer _vm, gpointer data)
+{
+ struct vm *vm = (struct vm *) _vm;
+ GtkWidget *item;
+
+ DEBUG ("adding %s to Connect menu", vm->description);
+
+ item = gtk_menu_item_new_with_label (vm->description);
+ gtk_menu_append (GTK_MENU (connectmenu), item);
+
+ g_signal_connect (G_OBJECT (item), "activate",
+ G_CALLBACK (connect_to_vm), vm);
+}
/* This checks wui_gthread global which is actually set in the
* main thread. Of course, it might not be set if the WUI thread
- * runs first. Hence we yield for the main thread to run.
+ * runs first. Hence we sleep for the main thread to run. (XXX)
*/
- g_thread_yield ();
+ g_usleep (100000);
ASSERT_IS_WUI_THREAD ();
g_async_queue_ref (queue);
vm2 = g_memdup (vm, sizeof (*vm));
vm2->description = g_strdup (vm->description);
+ vm2->uuid = g_strdup (vm->uuid);
vm2->state = vm->state ? g_strdup (vm->state) : NULL;
- vm2->uuid = vm->uuid ? g_strdup (vm->uuid) : NULL;
vm2->mac_addr = vm->mac_addr ? g_strdup (vm->mac_addr) : NULL;
return vm2;
}
char *error_str;
GSList *new_vmlist = NULL;
struct vm *vm;
+ int len;
/*DEBUG ("XML =\n%s", xml);*/
ASSERT_IS_WUI_THREAD ();
/* We don't really expect that we won't be able to parse the XML ... */
- doc = xmlParseDoc ((const xmlChar *) xml);
+ len = strlen (xml);
+ doc = xmlReadMemory (xml, len, NULL, NULL, 0);
+
if (!doc) {
DEBUG ("error parsing XML document, xml =\n%s", xml);
error_str = g_strdup ("error parsing XML document from remote server");
DEBUG ("required field \"description\" missing from <vm> structure");
else if (vm.vnc_port == -1)
DEBUG ("required field \"vnc-port\" missing from <vm> structure");
+ else if (vm.uuid == NULL)
+ DEBUG ("required field \"uuid\" missing from <vm> structure");
else
ret = g_memdup (&vm, sizeof vm);