X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;ds=sidebyside;f=main.c;h=390046a3a5ab45375e185b19fce5d11dab04a230;hb=0e605c0d34447f049f14dfd96ace73fb1c0cffaf;hp=b4a3607a54f70e90e8b97e9ab73b9e637741304e;hpb=432ef642869fbbac2d6f6d111d80137cc5e7b119;p=ovirt-viewer.git diff --git a/main.c b/main.c index b4a3607..390046a 100644 --- a/main.c +++ b/main.c @@ -27,6 +27,14 @@ #include #include +#ifdef HAVE_NETDB_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif + #ifdef HAVE_SYS_SOCKET_H #include #endif @@ -59,9 +67,25 @@ static void connect_to_wui (GtkWidget *, gpointer); static void login_to_wui (GtkWidget *, gpointer); static gboolean delete_event (GtkWidget *widget, GdkEvent *event, gpointer data); static void destroy (GtkWidget *widget, gpointer data); +static void clear_connectmenu (void); +static void help_about (GtkWidget *menu); +static void viewer_shutdown (GtkWidget *src, void *dummy, GtkWidget *vnc); +#if 0 +static void viewer_quit (GtkWidget *src, GtkWidget *vnc); +#endif +static void viewer_connected (GtkWidget *vnc); +static void viewer_initialized (GtkWidget *vnc, GtkWidget *data); +static void viewer_disconnected (GtkWidget *vnc); +static void viewer_credential (GtkWidget *vnc, GValueArray *credList); +static int viewer_open_vnc_socket (const char *vnchost, int vncport); /* For any widgets accessed from multiple functions. */ static GtkWidget *window; +static GtkWidget *connectitem; +static GtkWidget *connectmenu; +static GtkWidget *no_connections; +static GtkWidget *refresh_vmlist; +static GtkWidget *refresh_vmlist_separator; static GtkWidget *connection_area; static GtkWidget *ca_hostname; static GtkWidget *ca_button; @@ -70,11 +94,14 @@ static GtkWidget *login_area; 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. */ enum menuNums { - FILE_MENU, + CONNECT_MENU, VIEW_MENU, SEND_KEY_MENU, WINDOW_MENU, @@ -89,7 +116,7 @@ struct menuItem { }; static struct menuItem menuItems[] = { - { FILE_MENU, NULL, "_File", "File" }, + { CONNECT_MENU, NULL, "_Connect", "Connect" }, { VIEW_MENU, NULL, "_View", "View" }, { SEND_KEY_MENU, NULL, "_Send Key", "Send Key" }, { WINDOW_MENU, NULL, "_Window", "Window" }, @@ -203,8 +230,6 @@ start_ui (void) { GtkWidget *vbox; GtkWidget *menubar; - GtkWidget *file; - GtkWidget *filemenu; GtkWidget *view; GtkWidget *viewmenu; GtkWidget *sendkey; @@ -213,11 +238,11 @@ start_ui (void) GtkWidget *windmenu; GtkWidget *help; GtkWidget *helpmenu; + GtkWidget *about; GtkWidget *ca_vbox; GtkWidget *ca_hbox; GtkWidget *ca_label; GtkWidget *la_hbox; - GtkWidget *notebook; DEBUG ("creating viewer windows and menus"); @@ -245,9 +270,22 @@ start_ui (void) /* Menubar. */ menubar = gtk_menu_bar_new (); - file = menu_item_new (FILE_MENU); - filemenu = gtk_menu_new (); - gtk_menu_item_set_submenu (GTK_MENU_ITEM (file), filemenu); + connectitem = menu_item_new (CONNECT_MENU); + connectmenu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (connectitem), connectmenu); + + no_connections = gtk_menu_item_new_with_label ("Not connected"); + gtk_menu_append (GTK_MENU (connectmenu), no_connections); + gtk_widget_set_sensitive (no_connections, FALSE); + + /* This is not added to the menu yet, but will be when we are + * connected. + */ + refresh_vmlist = + gtk_menu_item_new_with_label ("Refresh list of virtual machines"); + g_object_ref (refresh_vmlist); + refresh_vmlist_separator = gtk_separator_menu_item_new (); + g_object_ref (refresh_vmlist_separator); #if 0 screenshot = gtk_menu_item_new_with_mnemonic ("_Screenshot"); @@ -272,7 +310,11 @@ start_ui (void) helpmenu = gtk_menu_new (); gtk_menu_item_set_submenu (GTK_MENU_ITEM (help), helpmenu); - gtk_menu_bar_append (GTK_MENU_BAR (menubar), file); + about = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL); + gtk_menu_append(GTK_MENU(helpmenu), about); + g_signal_connect(about, "activate", GTK_SIGNAL_FUNC (help_about), NULL); + + gtk_menu_bar_append (GTK_MENU_BAR (menubar), connectitem); gtk_menu_bar_append (GTK_MENU_BAR (menubar), view); gtk_menu_bar_append (GTK_MENU_BAR (menubar), sendkey); gtk_menu_bar_append (GTK_MENU_BAR (menubar), wind); @@ -319,10 +361,16 @@ start_ui (void) /* 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, @@ -333,6 +381,8 @@ start_ui (void) "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); @@ -344,7 +394,7 @@ start_ui (void) } static GtkWidget * -menu_item_new(int which_menu) +menu_item_new (int which_menu) { GtkWidget *widget; GtkWidget *label; @@ -352,14 +402,14 @@ menu_item_new(int which_menu) text = menuItems[which_menu].ungrabbed_text; - widget = gtk_menu_item_new(); - label = g_object_new(GTK_TYPE_ACCEL_LABEL, NULL); - gtk_label_set_text_with_mnemonic(GTK_LABEL(label), text); - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + widget = gtk_menu_item_new (); + label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL); + gtk_label_set_text_with_mnemonic (GTK_LABEL (label), text); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_container_add(GTK_CONTAINER(widget), label); - gtk_accel_label_set_accel_widget(GTK_ACCEL_LABEL(label), widget); - gtk_widget_show(label); + gtk_container_add (GTK_CONTAINER (widget), label); + gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), widget); + gtk_widget_show (label); menuItems[which_menu].label = label; @@ -381,6 +431,42 @@ destroy (GtkWidget *widget, gpointer data) } static void +help_about (GtkWidget *menu) +{ + GtkWidget *about; + const char *authors[] = { + "Richard W.M. Jones ", + "Daniel P. Berrange ", + NULL + }; + + about = gtk_about_dialog_new(); + + gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), "oVirt Viewer"); + gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION); + gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://ovirt.org/"); + gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(about), "oVirt website"); + gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors); + gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about), + "This program is free software; you can redistribute it and/or modify\n" \ + "it under the terms of the GNU General Public License as published by\n" \ + "the Free Software Foundation; either version 2 of the License, or\n" \ + "(at your option) any later version.\n" \ + "\n" \ + "This program is distributed in the hope that it will be useful,\n" \ + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \ + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" \ + "GNU General Public License for more details.\n" \ + "\n" \ + "You should have received a copy of the GNU General Public License\n" \ + "along with this program; if not, write to the Free Software\n" \ + "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"); + + gtk_dialog_run(GTK_DIALOG(about)); + gtk_widget_destroy(about); +} + +static void connect_to_wui (GtkWidget *widget, gpointer data) { const char *hostname; @@ -410,6 +496,303 @@ login_to_wui (GtkWidget *widget, gpointer data) 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, fd; + GtkWidget *child; + const char *label; + const char* hostname; + 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. */ + hostname = gtk_entry_get_text (GTK_ENTRY (ca_hostname)); + fd = viewer_open_vnc_socket(hostname, vm->forward_vnc_port); + if (fd == -1) return; /* We've already given an error. */ + + child = vnc_display_new (); + if (! vnc_display_open_fd (VNC_DISPLAY (child), fd)) { + main_status_error (g_strdup ("internal error in Gtk-VNC widget")); + return; + } + + /* + gtk_signal_connect(GTK_OBJECT(child), "vnc-pointer-grab", + GTK_SIGNAL_FUNC(viewer_grab), window); + gtk_signal_connect(GTK_OBJECT(child), "vnc-pointer-ungrab", + GTK_SIGNAL_FUNC(viewer_ungrab), window); + */ + + gtk_signal_connect(GTK_OBJECT(child), "delete-event", + GTK_SIGNAL_FUNC(viewer_shutdown), child); + + gtk_signal_connect(GTK_OBJECT(child), "vnc-connected", + GTK_SIGNAL_FUNC(viewer_connected), NULL); + gtk_signal_connect(GTK_OBJECT(child), "vnc-initialized", + GTK_SIGNAL_FUNC(viewer_initialized), NULL); + gtk_signal_connect(GTK_OBJECT(child), "vnc-disconnected", + GTK_SIGNAL_FUNC(viewer_disconnected), NULL); + + g_signal_connect(GTK_OBJECT(child), "vnc-auth-credential", + GTK_SIGNAL_FUNC(viewer_credential), NULL); + + /* NB. We have to do this before adding it to the notebook. */ + gtk_widget_show (child); + + /* Choose a tab label, which MUST end with the uuid string, since + * we use the tab label to store uuid. + */ + len = strlen (vm->description) + 1 + strlen (vm->uuid) + 1; + label2 = g_alloca (len); + snprintf (label2, len, "%s %s", vm->description, vm->uuid); + + 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"); +} + +/* +static void viewer_grab(GtkWidget *vnc, GtkWidget *window) +{ + int i; + + viewer_set_title(VNC_DISPLAY(vnc), window, TRUE); + + for (i = 0 ; i < LAST_MENU; i++) { + gtk_label_set_text_with_mnemonic(GTK_LABEL(menuItems[i].label), menuItems[i].grabbed_text); + } +} + +static void viewer_ungrab(GtkWidget *vnc, GtkWidget *window) +{ + int i; + + viewer_set_title(VNC_DISPLAY(vnc), window, FALSE); + + for (i = 0 ; i < LAST_MENU; i++) { + gtk_label_set_text_with_mnemonic(GTK_LABEL(menuItems[i].label), menuItems[i].ungrabbed_text); + } +} +*/ + +static void +viewer_shutdown (GtkWidget *src, void *dummy, GtkWidget *vnc) +{ + vnc_display_close (VNC_DISPLAY(vnc)); + + /* Just close the notebook tab for now. XXX */ + gtk_notebook_remove_page (GTK_NOTEBOOK (notebook), + gtk_notebook_page_num (GTK_NOTEBOOK (notebook), + vnc)); +} + +#if 0 +static void +viewer_quit (GtkWidget *src, GtkWidget *vnc) +{ + viewer_shutdown (src, NULL, vnc); +} +#endif + +static void +viewer_connected (GtkWidget *vnc) +{ + DEBUG ("Connected to server"); +} + +static void +viewer_initialized (GtkWidget *vnc, GtkWidget *data) +{ + DEBUG ("Connection initialized"); +} + +static void +viewer_disconnected (GtkWidget *vnc) +{ + DEBUG ("Disconnected from server"); +} + +static void +viewer_credential (GtkWidget *vnc, GValueArray *credList) +{ + GtkWidget *dialog = NULL; + int response; + unsigned int i, prompt = 0; + const char **data; + + DEBUG ("Got credential request for %d credential(s)", + credList->n_values); + + data = g_new0(const char *, credList->n_values); + + for (i = 0 ; i < credList->n_values ; i++) { + GValue *cred = g_value_array_get_nth(credList, i); + switch (g_value_get_enum(cred)) { + case VNC_DISPLAY_CREDENTIAL_USERNAME: + case VNC_DISPLAY_CREDENTIAL_PASSWORD: + prompt++; + break; + case VNC_DISPLAY_CREDENTIAL_CLIENTNAME: + data[i] = "libvirt"; + default: + break; + } + } + + if (prompt) { + GtkWidget **label, **entry, *box, *vbox; + int row; + dialog = gtk_dialog_new_with_buttons("Authentication required", + NULL, + 0, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, + GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); + + box = gtk_table_new(credList->n_values, 2, FALSE); + label = g_new(GtkWidget *, prompt); + entry = g_new(GtkWidget *, prompt); + + for (i = 0, row =0 ; i < credList->n_values ; i++) { + GValue *cred = g_value_array_get_nth(credList, i); + switch (g_value_get_enum(cred)) { + case VNC_DISPLAY_CREDENTIAL_USERNAME: + label[row] = gtk_label_new("Username:"); + break; + case VNC_DISPLAY_CREDENTIAL_PASSWORD: + label[row] = gtk_label_new("Password:"); + break; + default: + continue; + } + entry[row] = gtk_entry_new(); + if (g_value_get_enum(cred) == VNC_DISPLAY_CREDENTIAL_PASSWORD) + gtk_entry_set_visibility(GTK_ENTRY(entry[row]), FALSE); + + gtk_table_attach(GTK_TABLE(box), label[i], 0, 1, row, row+1, GTK_SHRINK, GTK_SHRINK, 3, 3); + gtk_table_attach(GTK_TABLE(box), entry[i], 1, 2, row, row+1, GTK_SHRINK, GTK_SHRINK, 3, 3); + row++; + } + + vbox = gtk_bin_get_child(GTK_BIN(dialog)); + gtk_container_add(GTK_CONTAINER(vbox), box); + + gtk_widget_show_all(dialog); + response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_hide(GTK_WIDGET(dialog)); + + if (response == GTK_RESPONSE_OK) { + for (i = 0, row = 0 ; i < credList->n_values ; i++) { + GValue *cred = g_value_array_get_nth(credList, i); + switch (g_value_get_enum(cred)) { + case VNC_DISPLAY_CREDENTIAL_USERNAME: + case VNC_DISPLAY_CREDENTIAL_PASSWORD: + data[i] = gtk_entry_get_text(GTK_ENTRY(entry[row])); + break; + } + } + } + } + + for (i = 0 ; i < credList->n_values ; i++) { + GValue *cred = g_value_array_get_nth(credList, i); + if (data[i]) { + if (vnc_display_set_credential(VNC_DISPLAY(vnc), + g_value_get_enum(cred), + data[i])) { + DEBUG("Failed to set credential type %d", + g_value_get_enum(cred)); + vnc_display_close(VNC_DISPLAY(vnc)); + } + } else { + DEBUG("Unsupported credential type %d", + g_value_get_enum(cred)); + vnc_display_close(VNC_DISPLAY(vnc)); + } + } + + g_free(data); + if (dialog) + gtk_widget_destroy(GTK_WIDGET(dialog)); +} + +#if defined(HAVE_SOCKET) && defined(HAVE_CONNECT) && defined(HAVE_HTONS) && defined(HAVE_GETHOSTBYNAME) + +static int +viewer_open_vnc_socket(const char* vnchost, int vncport) +{ + int socketfd; + struct hostent *serv; + struct sockaddr_in serv_addr; + + socketfd = socket(PF_INET, SOCK_STREAM, 0); + if(socketfd < 0){ + return -1; + } + + serv = gethostbyname(vnchost); + if(serv == NULL){ + return -1; + } + + serv_addr.sin_family = PF_INET; + serv_addr.sin_port = htons(vncport); + serv_addr.sin_addr.s_addr = ((struct in_addr *)(serv->h_addr))->s_addr; + + if (connect(socketfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0){ + return -1; + } + + return socketfd; +} + +#endif /* defined(HAVE_SOCKET) && defined(HAVE_CONNECT) && defined(HAVE_HTONS) && defined(HAVE_GETHOSTBYNAME) */ + +/* Remove all menu items from the Connect menu. */ +static void +remove_menu_item (GtkWidget *menu_item, gpointer data) +{ + gtk_container_remove (GTK_CONTAINER (connectmenu), menu_item); +} + +static void +clear_connectmenu (void) +{ + DEBUG ("clear Connect menu"); + gtk_container_foreach (GTK_CONTAINER (connectmenu), remove_menu_item, NULL); +} + /* The WUI thread has changed its state to connected. */ gboolean main_connected (gpointer data) @@ -434,6 +817,11 @@ main_disconnected (gpointer data) gtk_widget_show (connection_area); gtk_widget_hide (login_area); + + clear_connectmenu (); + gtk_menu_append (GTK_MENU (connectmenu), no_connections); + gtk_widget_show_all (connectmenu); + return FALSE; } @@ -525,7 +913,7 @@ main_login_error (gpointer _str) ASSERT_IS_MAIN_THREAD (); /* - gtk_label_set_text (GTK_LABEL (ca_error), str); + gtk_label_set_text (GTK_LABEL (la_error), str); */ g_free (str); @@ -543,20 +931,65 @@ main_status_error (gpointer _str) DEBUG ("status error: %s", str); ASSERT_IS_MAIN_THREAD (); - /* - gtk_label_set_text (GTK_LABEL (ca_error), 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. */ +/* 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; + +static void add_vm_to_connectmenu (gpointer _vm, gpointer data); + gboolean main_vmlist_updated (gpointer data) { + GSList *new_vmlist; + DEBUG ("vmlist updated"); ASSERT_IS_MAIN_THREAD (); + /* Get the new 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); + + if (vmlist != NULL) { + gtk_menu_append (GTK_MENU (connectmenu), refresh_vmlist_separator); + g_slist_foreach (vmlist, add_vm_to_connectmenu, NULL); + } + + /* 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); +}