diff options
author | Daniel P. Berrange <berrange@redhat.com> | 2008-11-26 13:03:50 -0500 |
---|---|---|
committer | Daniel P. Berrange <berrange@redhat.com> | 2008-11-26 13:03:50 -0500 |
commit | 61a05530b104c7cab018f691b4678824d04de9fa (patch) | |
tree | 85e73151b99a3a74f35a9353fb1a7bd77b0864fe | |
parent | 9951d5e831915ab3b90f4d2190739000a8cd88e9 (diff) | |
download | virt-viewer-61a05530b104c7cab018f691b4678824d04de9fa.tar.gz virt-viewer-61a05530b104c7cab018f691b4678824d04de9fa.tar.xz virt-viewer-61a05530b104c7cab018f691b4678824d04de9fa.zip |
Re-write completely to use Glade, libvirt events, and libvirt-glib integration
-rw-r--r-- | .hgignore | 7 | ||||
-rwxr-xr-x | autogen.sh | 4 | ||||
-rw-r--r-- | configure.ac | 6 | ||||
-rw-r--r-- | man/virt-viewer.pod | 12 | ||||
-rw-r--r-- | plugin/Makefile.am | 15 | ||||
-rw-r--r-- | plugin/npshell.c | 8 | ||||
-rw-r--r-- | plugin/virt-viewer-plugin.c | 12 | ||||
-rw-r--r-- | plugin/virt-viewer-plugin.h | 2 | ||||
-rw-r--r-- | src/Makefile.am | 24 | ||||
-rw-r--r-- | src/about.glade | 56 | ||||
-rw-r--r-- | src/auth.glade | 103 | ||||
-rw-r--r-- | src/main.c | 1128 | ||||
-rw-r--r-- | src/viewer.glade | 311 | ||||
-rw-r--r-- | src/viewer.h | 8 | ||||
-rw-r--r-- | virt-viewer.spec.in | 5 |
15 files changed, 1221 insertions, 480 deletions
@@ -25,3 +25,10 @@ Makefile\.in$ ^config.h.in$ ^man/Makefile$ ^man/virt-viewer\.1$ +^plugin/\.libs/ +^plugin/\.deps/ +^plugin/.*\.o +^plugin/.*\.la +^plugin/.*\.so +^plugin/.*\.lo +^plugin/Makefile$ @@ -31,7 +31,7 @@ if test "$DIE" -eq 1; then fi if test -z "$*"; then - echo "I am going to run ./configure with --enable-warnings - if you " + echo "I am going to run ./configure with not arguments - if you " echo "wish to pass any extra arguments to it, please specify them on " echo "the $0 command line." fi @@ -44,7 +44,7 @@ autoconf cd $THEDIR -$srcdir/configure --enable-warnings "$@" && { +$srcdir/configure "$@" && { echo echo "Now type 'make' to compile virt-viewer." } diff --git a/configure.ac b/configure.ac index 7030e2f..c8a7b06 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ -AC_INIT(virt-viewer, 0.0.3) +AC_INIT(virt-viewer, 0.0.4) AC_CONFIG_SRCDIR(src/main.c) AM_CONFIG_HEADER(config.h) dnl Make automake keep quiet about wildcards & other GNUmake-isms @@ -17,8 +17,10 @@ AC_DEFINE([_GNU_SOURCE], [], [Enable GNU extensions]) VIRT_VIEWER_COMPILE_WARNINGS(maximum) PKG_CHECK_MODULES(LIBXML2, libxml-2.0 >= 2.6.0) -PKG_CHECK_MODULES(LIBVIRT, libvirt >= 0.2.0) +PKG_CHECK_MODULES(LIBVIRT, libvirt >= 0.5.0) +PKG_CHECK_MODULES(LIBVIRT_GLIB, libvirt-glib >= 0.0.1) PKG_CHECK_MODULES(GTK2, gtk+-2.0 >= 2.10.0) +PKG_CHECK_MODULES(LIBGLADE2, libglade-2.0 >= 2.6.0) PKG_CHECK_MODULES(GTKVNC, gtk-vnc-1.0 >= 0.3.5) dnl Decide if this platform can support the SSH tunnel feature. diff --git a/man/virt-viewer.pod b/man/virt-viewer.pod index 3e8e41e..b545087 100644 --- a/man/virt-viewer.pod +++ b/man/virt-viewer.pod @@ -43,6 +43,10 @@ Specify the hypervisor connection URI Wait for the domain to start up before attempting to connect to the console +=item -w, --reconnect + +Automatically reconnect to the domain if it shuts down and restarts + =item -d, --direct Do not attempt to tunnel the console over SSH, even if the main connection URI @@ -61,9 +65,9 @@ To connect to the guest with ID 7 running under QEMU virt-viewer --connect qemu:///system 7 To wait for the guest with UUID 66ab33c0-6919-a3f7-e659-16c82d248521 to -startup and then connect +startup and then connect, also reconnecting upon restart of VM - virt-viewer --wait 66ab33c0-6919-a3f7-e659-16c82d248521 + virt-viewer --reconnect --wait 66ab33c0-6919-a3f7-e659-16c82d248521 To connect to a remote console using TLS @@ -72,7 +76,7 @@ To connect to a remote console using TLS To connect to a remote host using SSH, lookup the guest config and then make a direct non-tunnelled connection of the console - virt-viewer --connect xen+ssh://root@example.org/ demo + virt-viewer --direct --connect xen+ssh://root@example.org/ demo =head1 AUTHOR @@ -84,7 +88,7 @@ Report bugs to the mailing list C<http://www.redhat.com/mailman/listinfo/et-mgmt =head1 COPYRIGHT -Copyright (C) 2007 Red Hat, Inc, and various contributors. +Copyright (C) 2007-2008 Red Hat, Inc, and various contributors. This is free software. You may redistribute copies of it under the terms of the GNU General Public License C<http://www.gnu.org/licenses/gpl.html>. There is NO WARRANTY, to the extent permitted by law. diff --git a/plugin/Makefile.am b/plugin/Makefile.am index 1735ba6..c64d80a 100644 --- a/plugin/Makefile.am +++ b/plugin/Makefile.am @@ -9,13 +9,24 @@ virt_viewer_plugin_la_SOURCES = \ npshell.c npunix.c virt_viewer_plugin_la_LIBADD = \ @FIREFOX_PLUGIN_LIBS@ \ - @GTKVNC_LIBS@ @GTK2_LIBS@ @LIBXML2_LIBS@ @LIBVIRT_LIBS@ + @GTKVNC_LIBS@ \ + @GTK2_LIBS@ \ + @LIBXML2_LIBS@ \ + @LIBGLADE2_LIBS@ \ + @LIBVIRT_LIBS@ \ + @LIBVIRT_GLIB_LIBS@ virt_viewer_plugin_la_LDFLAGS = \ -module -avoid-version virt_viewer_plugin_la_CFLAGS = \ -DPLUGIN=1 -DENABLE_DEBUG=1 -DDEBUG=1 \ @FIREFOX_PLUGIN_CFLAGS@ \ - @GTKVNC_CFLAGS@ @GTK2_CFLAGS@ @LIBXML2_CFLAGS@ @LIBVIRT_CFLAGS@ \ + @GTKVNC_CFLAGS@ \ + @GTK2_CFLAGS@ \ + @LIBXML2_CFLAGS@ \ + @LIBGLADE2_CFLAGS@ \ + @LIBVIRT_CFLAGS@ \ + @LIBVIRT_GLIB_CFLAGS@ \ + -DGLADE_DIR="\"$(pkgdatadir)/ui\"" \ @WARN_CFLAGS@ \ -I$(top_srcdir)/src diff --git a/plugin/npshell.c b/plugin/npshell.c index b2419a4..60010d6 100644 --- a/plugin/npshell.c +++ b/plugin/npshell.c @@ -198,7 +198,7 @@ NPP_New(NPMIMEType pluginType G_GNUC_UNUSED, This->mode = mode; This->instance = instance; This->uri = This->name = NULL; - This->direct = This->waitvnc = 0; + This->direct = This->waitvm = This->reconnect = 0; /* Read the parameters passed to the plugin. */ for (i = 0; i < argc; i++) @@ -209,8 +209,10 @@ NPP_New(NPMIMEType pluginType G_GNUC_UNUSED, This->name = strdup (argv[i]); else if (strcasecmp (argn[i], "direct") == 0) This->direct = strcmp (argv[i], "1") == 0; - else if (strcasecmp (argn[i], "waitvnc") == 0) - This->waitvnc = strcmp (argv[i], "1") == 0; + else if (strcasecmp (argn[i], "wait") == 0) + This->waitvm = strcmp (argv[i], "1") == 0; + else if (strcasecmp (argn[i], "reconnect") == 0) + This->reconnect = strcmp (argv[i], "1") == 0; } return NPERR_NO_ERROR; diff --git a/plugin/virt-viewer-plugin.c b/plugin/virt-viewer-plugin.c index 8d1bce3..ebd35f2 100644 --- a/plugin/virt-viewer-plugin.c +++ b/plugin/virt-viewer-plugin.c @@ -41,12 +41,6 @@ #include "virt-viewer-plugin.h" -static GtkWidget * -get_container (void *thisv) -{ - PluginInstance *This = (PluginInstance *) thisv; - return This->container; -} NPError VirtViewerXSetWindow (NPP instance, NPWindow *window) @@ -88,9 +82,9 @@ VirtViewerXSetWindow (NPP instance, NPWindow *window) /* Make the VNC widget. */ if (This->uri && This->name) { - debug ("calling viewer_start uri=%s name=%s direct=%d waitvnc=%d container=%p", This->uri, This->name, This->direct, This->waitvnc, This->container); - r = viewer_start (This->uri, This->name, This->direct, This->waitvnc, 1, - get_container, This, 0); + debug ("calling viewer_start uri=%s name=%s direct=%d waitvm=%d reconnect=%d container=%p", + This->uri, This->name, This->direct, This->waitvm, This->reconnect, This->container); + r = viewer_start (This->uri, This->name, This->direct, This->waitvm, This->reconnect, 1, This->container); if (r != 0) fprintf (stderr, "viewer_start returned %d != 0\n", r); } diff --git a/plugin/virt-viewer-plugin.h b/plugin/virt-viewer-plugin.h index 60b980a..d1d134b 100644 --- a/plugin/virt-viewer-plugin.h +++ b/plugin/virt-viewer-plugin.h @@ -57,7 +57,7 @@ typedef struct { GtkWidget *container; char *uri, *name; - int direct, waitvnc; + gboolean direct, waitvm, reconnect; } PluginInstance; extern NPError VirtViewerXSetWindow (NPP instance, NPWindow* window); diff --git a/src/Makefile.am b/src/Makefile.am index c6315b2..74ca6bc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,26 @@ bin_PROGRAMS = virt-viewer +gladedir = $(pkgdatadir)/ui +glade_DATA = viewer.glade about.glade auth.glade + +EXTRA_DIST = $(glade_DATA) + virt_viewer_SOURCES = main.c viewer.h -virt_viewer_LDADD = @GTKVNC_LIBS@ @GTK2_LIBS@ @LIBXML2_LIBS@ @LIBVIRT_LIBS@ @LIBOBJS@ -virt_viewer_CFLAGS = @GTKVNC_CFLAGS@ @GTK2_CFLAGS@ @LIBXML2_CFLAGS@ @LIBVIRT_CFLAGS@ @WARN_CFLAGS@ +virt_viewer_LDADD = \ + @GTKVNC_LIBS@ \ + @GTK2_LIBS@ \ + @LIBXML2_LIBS@ \ + @LIBGLADE2_LIBS@ \ + @LIBVIRT_LIBS@ \ + @LIBVIRT_GLIB_LIBS@ + +virt_viewer_CFLAGS = \ + @GTKVNC_CFLAGS@ \ + @GTK2_CFLAGS@ \ + @LIBXML2_CFLAGS@ \ + @LIBGLADE2_CFLAGS@ \ + @LIBVIRT_CFLAGS@ \ + @LIBVIRT_GLIB_CFLAGS@ \ + @WARN_CFLAGS@ \ + -DGLADE_DIR="\"$(gladedir)\"" diff --git a/src/about.glade b/src/about.glade new file mode 100644 index 0000000..958ab35 --- /dev/null +++ b/src/about.glade @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--Generated with glade3 3.4.5 on Wed Nov 26 12:15:46 2008 --> +<glade-interface> + <widget class="GtkAboutDialog" id="about"> + <property name="border_width">5</property> + <property name="title" translatable="yes">About Glade</property> + <property name="resizable">False</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="has_separator">False</property> + <property name="program_name">Virtual Machine Viewer</property> + <property name="copyright" translatable="yes">Copyright 2007-2008 Daniel P. Berrange +Copyright 2007-2008 Red Hat, Inc.</property> + <property name="comments" translatable="yes">A remote desktop client built with GTK-VNC and libvirt</property> + <property name="website">http://virt-manager.org/</property> + <property name="website_label" translatable="yes">virt-vmanager.org</property> + <property name="license" translatable="yes">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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</property> + <property name="authors">Daniel P. Berrange</property> + <property name="translator_credits" translatable="yes">The Fedora Translation Team</property> + <signal name="delete_event" handler="about_delete"/> + <signal name="response" handler="about_close"/> + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox1"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <placeholder/> + </child> + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/src/auth.glade b/src/auth.glade new file mode 100644 index 0000000..2e443fc --- /dev/null +++ b/src/auth.glade @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--Generated with glade3 3.4.5 on Wed Nov 26 18:01:48 2008 --> +<glade-interface> + <widget class="GtkDialog" id="auth"> + <property name="border_width">5</property> + <property name="title" translatable="yes">Authentication required</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="has_separator">False</property> + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox1"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <widget class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="column_spacing">6</property> + <property name="row_spacing">6</property> + <child> + <widget class="GtkLabel" id="prompt-password"> + <property name="visible">True</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Password:</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="prompt-username"> + <property name="visible">True</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Username:</property> + </widget> + </child> + <child> + <widget class="GtkEntry" id="cred-username"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="cred-password"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <child> + <widget class="GtkButton" id="button-cancel"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="label" translatable="yes">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="response_id">-6</property> + </widget> + </child> + <child> + <widget class="GtkButton" id="button-ok"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="label" translatable="yes">gtk-ok</property> + <property name="use_stock">True</property> + <property name="response_id">-5</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> @@ -25,11 +25,15 @@ #include <vncdisplay.h> #include <gtk/gtk.h> #include <gdk/gdkkeysyms.h> +#include <sys/types.h> +#include <sys/stat.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <getopt.h> +#include <glade/glade.h> #include <libvirt/libvirt.h> +#include <libvirt-glib/libvirt-glib.h> #include <libxml/xpath.h> #include <libxml/uri.h> @@ -53,13 +57,24 @@ int usleep (unsigned int usecs); // #define DEBUG 1 #ifdef DEBUG -#define DEBUG_LOG(s, ...) fprintf(stderr, (s), ## __VA_ARGS__) +#define DEBUG_LOG(s, ...) g_debug((s), ## __VA_ARGS__) #else #define DEBUG_LOG(s, ...) do {} while (0) #endif -static char *domname = NULL; -static int verbose = 0; +enum menuNums { + FILE_MENU, + VIEW_MENU, + SEND_KEY_MENU, + HELP_MENU, + LAST_MENU // sentinel +}; + +static const char * const menuNames[LAST_MENU] = { + "menu-file", "menu-view", "menu-send", "menu-help" +}; + + #define MAX_KEY_COMBO 3 struct keyComboDef { guint keys[MAX_KEY_COMBO]; @@ -68,143 +83,342 @@ struct keyComboDef { }; static const struct keyComboDef keyCombos[] = { - { { GDK_Control_L, GDK_Alt_L, GDK_Delete }, 3, "Ctrl+Alt+_Del"}, - { { GDK_Control_L, GDK_Alt_L, GDK_BackSpace }, 3, "Ctrl+Alt+_Backspace"}, - { {}, 0, "" }, - { { GDK_Control_L, GDK_Alt_L, GDK_F1 }, 3, "Ctrl+Alt+F_1"}, - { { GDK_Control_L, GDK_Alt_L, GDK_F2 }, 3, "Ctrl+Alt+F_2"}, - { { GDK_Control_L, GDK_Alt_L, GDK_F3 }, 3, "Ctrl+Alt+F_3"}, - { { GDK_Control_L, GDK_Alt_L, GDK_F4 }, 3, "Ctrl+Alt+F_4"}, - { { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F_5"}, - { { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F_6"}, - { { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F_7"}, - { { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F_8"}, - { {}, 0, "" }, - { { GDK_Print }, 1, "_PrintScreen"}, + { { GDK_Control_L, GDK_Alt_L, GDK_Delete }, 3, "Ctrl+Alt+_Del"}, + { { GDK_Control_L, GDK_Alt_L, GDK_BackSpace }, 3, "Ctrl+Alt+_Backspace"}, + { {}, 0, "" }, + { { GDK_Control_L, GDK_Alt_L, GDK_F1 }, 3, "Ctrl+Alt+F_1"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F2 }, 3, "Ctrl+Alt+F_2"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F3 }, 3, "Ctrl+Alt+F_3"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F4 }, 3, "Ctrl+Alt+F_4"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F_5"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F_6"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F_7"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F_8"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F_9"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F1_0"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F11"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F12"}, + { {}, 0, "" }, + { { GDK_Print }, 1, "_PrintScreen"}, }; -enum menuNums { - FILE_MENU, - VIEW_MENU, - SEND_KEY_MENU, - HELP_MENU, - LAST_MENU // sentinel -}; -struct menuItem { - guint menu; - GtkWidget *label; - const char *ungrabbed_text; - const char *grabbed_text; -}; -static struct menuItem menuItems[] = { - { FILE_MENU, NULL, "_File", "File" }, - { VIEW_MENU, NULL, "_View", "View" }, - { SEND_KEY_MENU, NULL, "_Send Key", "Send Key" }, - { HELP_MENU, NULL, "_Help", "Help" } -}; +typedef struct VirtViewer { + char *uri; + virConnectPtr conn; + char *domkey; + char *domtitle; + + GladeXML *glade; + GtkWidget *window; + GtkWidget *vnc; + int active; + + gboolean accelEnabled; + GValue accelSetting; + GSList *accelList; + int accelMenuSig[LAST_MENU]; + + int waitvm; + int reconnect; + int direct; + int verbose; +} VirtViewer; + +typedef struct VirtViewerSize { + gint width, height; + gulong sig_id; +} VirtViewerSize; + + +static GladeXML * +viewer_load_glade(const char *name, const char *widget) +{ + char *path; + struct stat sb; + GladeXML *xml; + + if (stat(name, &sb) >= 0) + return glade_xml_new(name, widget, NULL); + + if (asprintf(&path, "%s/%s", GLADE_DIR, name) < 0) + abort(); + + xml = glade_xml_new(path, widget, NULL); + free(path); + return xml; +} + +/* Now that the size is set to our preferred sizing, this + * triggers another resize calculation but without our + * scrolled window callback active. This is the key that + * allows us to set the fixed size, but then allow the user + * to later resize it smaller again + */ +static gboolean +viewer_unset_scroll_size (gpointer data) +{ + GtkWidget *widget = data; + + gtk_widget_queue_resize_no_redraw (widget); + + return FALSE; +} + +/* + * This sets the actual size of the scrolled window, and then + * sets an idle callback to resize again, without constraints + * activated + */ +static void +viewer_set_preferred_scroll_size (GtkWidget *widget, + GtkRequisition *req, + gpointer data) +{ + VirtViewerSize *size = data; + DEBUG_LOG("Scroll resize to preferred %d %d\n", size->width, size->height); + + req->width = size->width; + req->height = size->height; + + g_signal_handler_disconnect (widget, size->sig_id); + g_free (size); + g_idle_add (viewer_unset_scroll_size, widget); +} + + +/* + * Called when the scroll widgets actual size has been set. + * We now update the VNC widget's size + */ +static void viewer_resize_vnc(GtkWidget *scroll G_GNUC_UNUSED, + GtkAllocation *alloc, + VirtViewer *viewer) +{ + int vncw = vnc_display_get_width(VNC_DISPLAY(viewer->vnc)); + int vnch = vnc_display_get_height(VNC_DISPLAY(viewer->vnc)); + VirtViewerSize *size = g_new (VirtViewerSize, 1); + + if (vnc_display_get_scaling(VNC_DISPLAY(viewer->vnc))) { + double vncaspect = (double)vncw / (double)vnch; + double scrollaspect = (double)alloc->width / (double)alloc->height; + + /* When scaling, we set VNC widget size to maximum possible + * scaled which fits inside teh scrolled window (no scrollbarS) + * while maintaining the aspect ratio */ + if (scrollaspect > vncaspect) { + size->width = alloc->height * vncaspect; + size->height = alloc->height; + } else { + size->width = alloc->width; + size->height = alloc->width / vncaspect; + } + } else { + /* When scrollling, the VNC widget is always at its native size */ + size->width = vncw; + size->height = vnch; + } + + DEBUG_LOG("Scroll resize is %d %d, desktop is %d %d, VNC is %d %d\n", + alloc->width, alloc->height, + vncw, vnch, size->width, size->height); + + size->sig_id = g_signal_connect + (viewer->vnc, "size-request", + G_CALLBACK (viewer_set_preferred_scroll_size), + size); + + gtk_widget_queue_resize (viewer->vnc); +} + + +/* + * Called when VNC desktop size changes. + * We figure out 'best' size for the containing scrolled window + */ +static void viewer_resize_desktop(GtkWidget *vnc G_GNUC_UNUSED, gint width, gint height, VirtViewer *viewer) +{ + GtkWidget *scroll; + VirtViewerSize *size = g_new (VirtViewerSize, 1); + + DEBUG_LOG("Resized VNC event %d %d\n", width, height); + + scroll = glade_xml_get_widget(viewer->glade, "vnc-scroll"); + + if (viewer->window) + gtk_window_resize(GTK_WINDOW (viewer->window), 1, 1); -static void viewer_set_title(VncDisplay *vnc G_GNUC_UNUSED, GtkWidget *window, gboolean grabbed) + size->width = width; + size->height = height; + size->sig_id = g_signal_connect + (scroll, "size-request", + G_CALLBACK (viewer_set_preferred_scroll_size), + size); + + gtk_widget_queue_resize (scroll); +} + + + +static void viewer_set_title(VirtViewer *viewer, gboolean grabbed) { char title[1024]; const char *subtitle; + if (!viewer->window) + return; + if (grabbed) subtitle = "(Press Ctrl+Alt to release pointer) "; else subtitle = ""; snprintf(title, sizeof(title), "%s%s - Virt Viewer", - subtitle, domname); + subtitle, viewer->domtitle); - gtk_window_set_title(GTK_WINDOW(window), title); + gtk_window_set_title(GTK_WINDOW(viewer->window), title); } -static void viewer_grab(GtkWidget *vnc, GtkWidget *window) +static void viewer_ignore_accel(GtkWidget *menu G_GNUC_UNUSED, + VirtViewer *viewer G_GNUC_UNUSED) +{ + /* ignore accelerator */ +} + + +static void viewer_disable_modifiers(VirtViewer *viewer) { + GtkSettings *settings = gtk_settings_get_default(); + GValue empty; + GSList *accels; int i; - viewer_set_title(VNC_DISPLAY(vnc), window, TRUE); + if (!viewer->window) + return; - for (i = 0 ; i < LAST_MENU; i++) { - gtk_label_set_text_with_mnemonic(GTK_LABEL(menuItems[i].label), menuItems[i].grabbed_text); + if (!viewer->accelEnabled) + return; + + /* This stops F10 activating menu bar */ + memset(&empty, 0, sizeof empty); + g_value_init(&empty, G_TYPE_STRING); + g_object_get_property(G_OBJECT(settings), "gtk-menu-bar-accel", &viewer->accelSetting); + g_object_set_property(G_OBJECT(settings), "gtk-menu-bar-accel", &empty); + + /* This stops global accelerators like Ctrl+Q == Quit */ + for (accels = viewer->accelList ; accels ; accels = accels->next) { + gtk_window_remove_accel_group(GTK_WINDOW(viewer->window), accels->data); } + + /* This stops menu bar shortcuts like Alt+F == File */ + for (i = 0 ; i < LAST_MENU ; i++) { + GtkWidget *menu = glade_xml_get_widget(viewer->glade, menuNames[i]); + viewer->accelMenuSig[i] = + g_signal_connect(GTK_OBJECT(menu), "mnemonic-activate", + GTK_SIGNAL_FUNC(viewer_ignore_accel), viewer); + } + + viewer->accelEnabled = FALSE; } -static void viewer_ungrab(GtkWidget *vnc, GtkWidget *window) + +static void viewer_enable_modifiers(VirtViewer *viewer) { + GtkSettings *settings = gtk_settings_get_default(); + GSList *accels; int i; - viewer_set_title(VNC_DISPLAY(vnc), window, FALSE); + if (!viewer->window) + return; + + if (viewer->accelEnabled) + return; - for (i = 0 ; i < LAST_MENU; i++) { - gtk_label_set_text_with_mnemonic(GTK_LABEL(menuItems[i].label), menuItems[i].ungrabbed_text); + /* This allows F10 activating menu bar */ + g_object_set_property(G_OBJECT(settings), "gtk-menu-bar-accel", &viewer->accelSetting); + + /* This allows global accelerators like Ctrl+Q == Quit */ + for (accels = viewer->accelList ; accels ; accels = accels->next) { + gtk_window_add_accel_group(GTK_WINDOW(viewer->window), accels->data); } -} -static void viewer_shutdown(GtkWidget *src G_GNUC_UNUSED, void *dummy G_GNUC_UNUSED, GtkWidget *vnc) -{ - vnc_display_close(VNC_DISPLAY(vnc)); - gtk_main_quit(); + /* This allows menu bar shortcuts like Alt+F == File */ + for (i = 0 ; i < LAST_MENU ; i++) { + GtkWidget *menu = glade_xml_get_widget(viewer->glade, menuNames[i]); + g_signal_handler_disconnect(GTK_OBJECT(menu), + viewer->accelMenuSig[i]); + } + + viewer->accelEnabled = TRUE; } -static void viewer_quit(GtkWidget *src G_GNUC_UNUSED, GtkWidget *vnc) + + +static void viewer_grab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer) { - viewer_shutdown(src, NULL, vnc); + viewer_set_title(viewer, TRUE); + viewer_disable_modifiers(viewer); } -static void viewer_connected(GtkWidget *vnc G_GNUC_UNUSED) +static void viewer_ungrab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer) { - DEBUG_LOG("Connected to server\n"); + viewer_set_title(viewer, FALSE); + viewer_enable_modifiers(viewer); } -static void viewer_initialized(GtkWidget *vnc, GtkWidget *window) + +static void viewer_shutdown(GtkWidget *src G_GNUC_UNUSED, void *dummy G_GNUC_UNUSED, VirtViewer *viewer) { - DEBUG_LOG("Connection initialized\n"); - gtk_widget_show_all(window); - viewer_set_title(VNC_DISPLAY(vnc), window, FALSE); + vnc_display_close(VNC_DISPLAY(viewer->vnc)); + gtk_main_quit(); } -static void viewer_disconnected(GtkWidget *vnc G_GNUC_UNUSED) +static void viewer_menu_file_quit(GtkWidget *src G_GNUC_UNUSED, VirtViewer *viewer) { - DEBUG_LOG("Disconnected from server\n"); - gtk_main_quit(); + viewer_shutdown(src, NULL, viewer); } -static void viewer_fullscreen(GtkWidget *menu, GtkWidget *window) +static void viewer_menu_view_fullscreen(GtkWidget *menu, VirtViewer *viewer) { + if (!viewer->window) + return; + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu))) { - gtk_window_fullscreen(GTK_WINDOW(window)); + gtk_window_fullscreen(GTK_WINDOW(viewer->window)); } else { - gtk_window_unfullscreen(GTK_WINDOW(window)); + gtk_window_unfullscreen(GTK_WINDOW(viewer->window)); } } -static void viewer_scalable(GtkWidget *menu, GtkWidget *vnc) +static void viewer_menu_view_scale(GtkWidget *menu, VirtViewer *viewer) { + GtkWidget *scroll = glade_xml_get_widget(viewer->glade, "vnc-scroll"); + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu))) { - vnc_display_set_scaling(VNC_DISPLAY(vnc), TRUE); + vnc_display_set_scaling(VNC_DISPLAY(viewer->vnc), TRUE); } else { - vnc_display_set_scaling(VNC_DISPLAY(vnc), FALSE); + vnc_display_set_scaling(VNC_DISPLAY(viewer->vnc), FALSE); } + + gtk_widget_queue_resize (scroll); } -static void viewer_send_key(GtkWidget *menu, GtkWidget *vnc) +static void viewer_menu_send(GtkWidget *menu G_GNUC_UNUSED, VirtViewer *viewer) { - int i; - GtkWidget *label = gtk_bin_get_child(GTK_BIN(menu)); - const char *text = gtk_label_get_label(GTK_LABEL(label)); - - for (i = 0 ; i < (sizeof(keyCombos)/sizeof(keyCombos[0])) ; i++) { - if (!strcmp(text, keyCombos[i].label)) { - DEBUG_LOG("Sending key combo %s\n", gtk_label_get_text(GTK_LABEL(label))); - vnc_display_send_keys(VNC_DISPLAY(vnc), - keyCombos[i].keys, - keyCombos[i].nkeys); - return; - } - } + int i; + GtkWidget *label = gtk_bin_get_child(GTK_BIN(menu)); + const char *text = gtk_label_get_label(GTK_LABEL(label)); + DEBUG_LOG("Woo\n"); + for (i = 0 ; i < (sizeof(keyCombos)/sizeof(keyCombos[0])) ; i++) { + if (!strcmp(text, keyCombos[i].label)) { + DEBUG_LOG("Sending key combo %s\n", gtk_label_get_text(GTK_LABEL(label))); + vnc_display_send_keys(VNC_DISPLAY(viewer->vnc), + keyCombos[i].keys, + keyCombos[i].nkeys); + return; + } + } DEBUG_LOG("Failed to find key combo %s\n", gtk_label_get_text(GTK_LABEL(label))); } @@ -217,7 +431,7 @@ static void viewer_save_screenshot(GtkWidget *vnc, const char *file) gdk_pixbuf_unref(pix); } -static void viewer_screenshot(GtkWidget *menu G_GNUC_UNUSED, GtkWidget *vnc) +static void viewer_menu_file_screenshot(GtkWidget *menu G_GNUC_UNUSED, VirtViewer *viewer) { GtkWidget *dialog; @@ -236,20 +450,51 @@ static void viewer_screenshot(GtkWidget *menu G_GNUC_UNUSED, GtkWidget *vnc) char *filename; filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); - viewer_save_screenshot(vnc, filename); + viewer_save_screenshot(viewer->vnc, filename); g_free (filename); } gtk_widget_destroy (dialog); } +static void viewer_about_close(GtkWidget *dialog, VirtViewer *viewer G_GNUC_UNUSED) +{ + gtk_widget_hide(dialog); + gtk_widget_destroy(dialog); +} + +static void viewer_about_delete(GtkWidget *dialog, void *dummy G_GNUC_UNUSED, VirtViewer *viewer G_GNUC_UNUSED) +{ + gtk_widget_hide(dialog); + gtk_widget_destroy(dialog); +} + +static void viewer_menu_help_about(GtkWidget *menu G_GNUC_UNUSED, VirtViewer *viewer) +{ + GladeXML *about; + GtkWidget *dialog; + + about = viewer_load_glade("about.glade", "about"); + + dialog = glade_xml_get_widget(about, "about"); + gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), VERSION); + glade_xml_signal_connect_data(about, "about_delete", + G_CALLBACK(viewer_about_delete), viewer); + glade_xml_signal_connect_data(about, "about_close", + G_CALLBACK(viewer_about_close), viewer); + + gtk_widget_show_all(dialog); + + g_object_unref(G_OBJECT(about)); +} + static void viewer_credential(GtkWidget *vnc, GValueArray *credList) { - GtkWidget *dialog = NULL; - int response; - unsigned int i, prompt = 0; + GtkWidget *dialog = NULL; const char **data; + gboolean wantPassword = FALSE, wantUsername = FALSE; + int i; DEBUG_LOG("Got credential request for %d credential(s)\n", credList->n_values); @@ -259,8 +504,10 @@ static void viewer_credential(GtkWidget *vnc, GValueArray *credList) GValue *cred = g_value_array_get_nth(credList, i); switch (g_value_get_enum(cred)) { case VNC_DISPLAY_CREDENTIAL_USERNAME: + wantUsername = TRUE; + break; case VNC_DISPLAY_CREDENTIAL_PASSWORD: - prompt++; + wantPassword = TRUE; break; case VNC_DISPLAY_CREDENTIAL_CLIENTNAME: data[i] = "libvirt"; @@ -269,58 +516,40 @@ static void viewer_credential(GtkWidget *vnc, GValueArray *credList) } } - 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); + if (wantUsername || wantPassword) { + GladeXML *creds = viewer_load_glade("auth.glade", "auth"); + GtkWidget *credUsername; + GtkWidget *credPassword; + GtkWidget *promptUsername; + GtkWidget *promptPassword; + int response; - 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); + dialog = glade_xml_get_widget(creds, "auth"); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); - 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++; - } + credUsername = glade_xml_get_widget(creds, "cred-username"); + promptUsername = glade_xml_get_widget(creds, "prompt-username"); + credPassword = glade_xml_get_widget(creds, "cred-password"); + promptPassword = glade_xml_get_widget(creds, "prompt-password"); - vbox = gtk_bin_get_child(GTK_BIN(dialog)); - gtk_container_add(GTK_CONTAINER(vbox), box); + gtk_widget_set_sensitive(credUsername, wantUsername); + gtk_widget_set_sensitive(promptUsername, wantUsername); + gtk_widget_set_sensitive(credPassword, wantPassword); + gtk_widget_set_sensitive(promptPassword, wantPassword); - gtk_widget_show_all(dialog); - response = gtk_dialog_run(GTK_DIALOG(dialog)); - gtk_widget_hide(GTK_WIDGET(dialog)); + gtk_widget_show_all(dialog); + response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_hide(dialog); if (response == GTK_RESPONSE_OK) { - for (i = 0, row = 0 ; i < credList->n_values ; i++) { + 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: + data[i] = gtk_entry_get_text(GTK_ENTRY(credUsername)); + break; case VNC_DISPLAY_CREDENTIAL_PASSWORD: - data[i] = gtk_entry_get_text(GTK_ENTRY(entry[row])); + data[i] = gtk_entry_get_text(GTK_ENTRY(credPassword)); break; } } @@ -347,229 +576,7 @@ static void viewer_credential(GtkWidget *vnc, GValueArray *credList) gtk_widget_destroy(GTK_WIDGET(dialog)); } -static void viewer_about(GtkWidget *menu G_GNUC_UNUSED) -{ - GtkWidget *about; - const char *authors[] = { - "Daniel P. Berrange <berrange@redhat.com>", - NULL - }; - - about = gtk_about_dialog_new(); - - gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), "Virtual Machine Viewer"); - gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION); - gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://virt-manager.org/"); - gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(about), "http://virt-manager.org/"); - 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 GtkWidget *menu_item_new(int which_menu) -{ - GtkWidget *widget; - GtkWidget *label; - const char *text; - - 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); - - 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; - - return widget; -} - -static GtkWidget *viewer_build_file_menu(VncDisplay *vnc) -{ - GtkWidget *file; - GtkWidget *filemenu; - GtkWidget *quit; - GtkWidget *screenshot; - - file = menu_item_new(FILE_MENU); - - filemenu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu); - - screenshot = gtk_menu_item_new_with_mnemonic("_Screenshot"); - gtk_menu_append(GTK_MENU(filemenu), screenshot); - g_signal_connect(screenshot, "activate", GTK_SIGNAL_FUNC(viewer_screenshot), vnc); - - gtk_menu_append(GTK_MENU(filemenu), gtk_separator_menu_item_new()); - - - quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL); - gtk_menu_append(GTK_MENU(filemenu), quit); - g_signal_connect(quit, "activate", GTK_SIGNAL_FUNC(viewer_quit), vnc); - - return file; -} - -static GtkWidget *viewer_build_view_menu(VncDisplay *vnc, GtkWidget *window, gboolean composited) -{ - GtkWidget *view; - GtkWidget *viewmenu; - GtkWidget *fullscreen; - GtkWidget *scalable; - - view = menu_item_new(VIEW_MENU); - - viewmenu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), viewmenu); - - fullscreen = gtk_check_menu_item_new_with_mnemonic("_Fullscreen"); - gtk_menu_append(GTK_MENU(viewmenu), fullscreen); - g_signal_connect(fullscreen, "toggled", GTK_SIGNAL_FUNC(viewer_fullscreen), window); - - scalable = gtk_check_menu_item_new_with_mnemonic("_Scale display"); - if (!composited) - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(scalable), TRUE); - gtk_menu_append(GTK_MENU(viewmenu), scalable); - g_signal_connect(scalable, "toggled", GTK_SIGNAL_FUNC(viewer_scalable), vnc); - - return view; -} - -static GtkWidget *viewer_build_sendkey_menu(VncDisplay *vnc) -{ - GtkWidget *sendkey; - GtkWidget *sendkeymenu; - int i; - - sendkey = menu_item_new(SEND_KEY_MENU); - - sendkeymenu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(sendkey), sendkeymenu); - for (i = 0 ; i < (sizeof(keyCombos)/sizeof(keyCombos[0])) ; i++) { - GtkWidget *key; - - if (keyCombos[i].nkeys) { - key = gtk_menu_item_new_with_mnemonic(keyCombos[i].label); - gtk_menu_append(GTK_MENU(sendkeymenu), key); - g_signal_connect(key, "activate", GTK_SIGNAL_FUNC(viewer_send_key), vnc); - } else { - gtk_menu_append(GTK_MENU(sendkeymenu), gtk_separator_menu_item_new()); - } - } - - return sendkey; -} - -static GtkWidget *viewer_build_help_menu(void) -{ - GtkWidget *help; - GtkWidget *helpmenu; - GtkWidget *about; - - help = menu_item_new(HELP_MENU); - - helpmenu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(help), helpmenu); - - 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(viewer_about), NULL); - - return help; -} - -static GtkWidget *viewer_build_menu(VncDisplay *vnc, GtkWidget *window, gboolean composited) -{ - GtkWidget *menubar; - GtkWidget *file; - GtkWidget *view; - GtkWidget *sendkey; - GtkWidget *help; - - menubar = gtk_menu_bar_new(); - - file = viewer_build_file_menu(vnc); - view = viewer_build_view_menu(vnc, window, composited); - sendkey = viewer_build_sendkey_menu(vnc); - help = viewer_build_help_menu(); - - gtk_menu_bar_append(GTK_MENU_BAR(menubar), file); - 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), help); - - - return menubar; -} - -static GtkWidget *viewer_build_window(VncDisplay *vnc, - GtkWidget *(*get_toplevel)(void *), - void *data, - int with_menubar) -{ - GtkWidget *window; - GtkWidget *menubar; - GtkWidget *layout; - - /* In the standalone program, calls viewer_get_toplevel above - * to make a window. In the browser plugin this calls a function - * in the plugin which returns the GtkPlug that we live inside. - * In both cases they are GTK_CONTAINERs and NOT resizable. - */ - window = get_toplevel (data); - gtk_window_set_resizable(GTK_WINDOW(window), TRUE); - - if (with_menubar) { - layout = gtk_vbox_new(FALSE, 3); - menubar = viewer_build_menu(vnc, window, gtk_widget_is_composited(window)); - gtk_container_add(GTK_CONTAINER(window), layout); - gtk_container_add_with_properties(GTK_CONTAINER(layout), menubar, "expand", FALSE, NULL); - gtk_container_add_with_properties(GTK_CONTAINER(layout), GTK_WIDGET(vnc), "expand", TRUE, NULL); - } else - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vnc)); - - gtk_signal_connect(GTK_OBJECT(vnc), "vnc-pointer-grab", - GTK_SIGNAL_FUNC(viewer_grab), window); - gtk_signal_connect(GTK_OBJECT(vnc), "vnc-pointer-ungrab", - GTK_SIGNAL_FUNC(viewer_ungrab), window); - - gtk_signal_connect(GTK_OBJECT(window), "delete-event", - GTK_SIGNAL_FUNC(viewer_shutdown), vnc); - - gtk_signal_connect(GTK_OBJECT(vnc), "vnc-connected", - GTK_SIGNAL_FUNC(viewer_connected), NULL); - gtk_signal_connect(GTK_OBJECT(vnc), "vnc-initialized", - GTK_SIGNAL_FUNC(viewer_initialized), window); - gtk_signal_connect(GTK_OBJECT(vnc), "vnc-disconnected", - GTK_SIGNAL_FUNC(viewer_disconnected), NULL); - - g_signal_connect(GTK_OBJECT(vnc), "vnc-auth-credential", - GTK_SIGNAL_FUNC(viewer_credential), NULL); - - return window; -} static int viewer_parse_uuid(const char *name, unsigned char *uuid) { @@ -612,33 +619,59 @@ static int viewer_parse_uuid(const char *name, unsigned char *uuid) } -static virDomainPtr viewer_lookup_domain(virConnectPtr conn, const char *name) +static virDomainPtr viewer_lookup_domain(VirtViewer *viewer) { char *end; - int id = strtol(name, &end, 10); + int id = strtol(viewer->domkey, &end, 10); virDomainPtr dom = NULL; unsigned char uuid[16]; if (id >= 0 && end && !*end) { - dom = virDomainLookupByID(conn, id); + dom = virDomainLookupByID(viewer->conn, id); } - if (!dom && viewer_parse_uuid(name, uuid) == 0) { - dom = virDomainLookupByUUID(conn, uuid); + if (!dom && viewer_parse_uuid(viewer->domkey, uuid) == 0) { + dom = virDomainLookupByUUID(viewer->conn, uuid); } if (!dom) { - dom = virDomainLookupByName(conn, name); + dom = virDomainLookupByName(viewer->conn, viewer->domkey); } return dom; } -static int viewer_extract_vnc_graphics(virDomainPtr dom, char **port) +static int viewer_matches_domain(VirtViewer *viewer, + virDomainPtr dom) +{ + char *end; + const char *name; + int id = strtol(viewer->domkey, &end, 10); + unsigned char wantuuid[16]; + unsigned char domuuid[16]; + + if (id >= 0 && end && !*end) { + if (virDomainGetID(dom) == id) + return 1; + } + if (!dom && viewer_parse_uuid(viewer->domkey, wantuuid) == 0) { + virDomainGetUUID(dom, domuuid); + if (memcmp(wantuuid, domuuid, VIR_UUID_BUFLEN) == 0) + return 1; + } + + name = virDomainGetName(dom); + if (strcmp(name, viewer->domkey) == 0) + return 1; + + return 0; +} + +static char * viewer_extract_vnc_port(virDomainPtr dom) { char *xmldesc = virDomainGetXMLDesc(dom, 0); xmlDocPtr xml = NULL; xmlParserCtxtPtr pctxt = NULL; xmlXPathContextPtr ctxt = NULL; xmlXPathObjectPtr obj = NULL; - int ret = -1; + char *port = NULL; pctxt = xmlNewParserCtxt(); if (!pctxt || !pctxt->sax) @@ -659,15 +692,12 @@ static int viewer_extract_vnc_graphics(virDomainPtr dom, char **port) if (!obj || obj->type != XPATH_STRING || !obj->stringval || !obj->stringval[0]) goto error; if (!strcmp((const char*)obj->stringval, "-1")) - goto missing; + goto error; - *port = strdup((const char*)obj->stringval); + port = strdup((const char*)obj->stringval); xmlXPathFreeObject(obj); obj = NULL; - missing: - ret = 0; - error: if (obj) xmlXPathFreeObject(obj); @@ -677,9 +707,10 @@ static int viewer_extract_vnc_graphics(virDomainPtr dom, char **port) xmlFreeDoc(xml); if (pctxt) xmlFreeParserCtxt(pctxt); - return ret; + return port; } + static int viewer_extract_host(const char *uristr, char **host, char **transport, char **user, int *port) { xmlURIPtr uri; @@ -793,100 +824,296 @@ static int viewer_open_tunnel_ssh(const char *sshhost, int sshport, const char * #endif /* defined(HAVE_SOCKETPAIR) && defined(HAVE_FORK) */ -int -viewer_start (const char *uri, const char *name, - int direct, int waitvnc, int set_verbose, - GtkWidget *(*get_toplevel)(void *), void *data, - int with_menubar) +static void viewer_set_status(VirtViewer *viewer, const char *text) +{ + GtkWidget *status, *notebook; + + notebook = glade_xml_get_widget(viewer->glade, "notebook"); + status = glade_xml_get_widget(viewer->glade, "status"); + + gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 0); + gtk_label_set_text(GTK_LABEL(status), text); +} + + +static void viewer_set_vnc(VirtViewer *viewer) +{ + GtkWidget *notebook; + + notebook = glade_xml_get_widget(viewer->glade, "notebook"); + gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 1); + + gtk_widget_show(viewer->vnc); +} + + +static int viewer_activate(VirtViewer *viewer, + virDomainPtr dom) { - GtkWidget *window; - GtkWidget *vnc; - virConnectPtr conn = NULL; - virDomainPtr dom = NULL; - virDomainInfo domInfo; - char *host = NULL; char *vncport = NULL; + char *host = NULL; char *transport = NULL; char *user = NULL; - const char *tmpname = NULL; - int port = 0; - int fd = -1; + int port, fd = -1; + int ret = -1; - verbose = set_verbose; + if (viewer->active) + goto cleanup; - conn = virConnectOpenReadOnly(uri); - if (!conn) { - fprintf(stderr, "unable to connect to libvirt %s\n", - uri ? uri : "xen"); - return 2; + if ((vncport = viewer_extract_vnc_port(dom)) == NULL) + goto cleanup; + + if (viewer_extract_host(viewer->uri, &host, &transport, &user, &port) < 0) + goto cleanup; + + DEBUG_LOG("Remote host is %s and transport %s user %s\n", + host, transport ? transport : "", user ? user : ""); + +#if defined(HAVE_SOCKETPAIR) && defined(HAVE_FORK) + if (transport && strcasecmp(transport, "ssh") == 0 && + !viewer->direct) + if ((fd = viewer_open_tunnel_ssh(host, port, user, vncport)) < 0) + return -1; +#endif + + if (fd >= 0) + vnc_display_open_fd(VNC_DISPLAY(viewer->vnc), fd); + else + vnc_display_open_host(VNC_DISPLAY(viewer->vnc), host, vncport); + + viewer_set_status(viewer, "Connecting to VNC server"); + + free(viewer->domtitle); + viewer->domtitle = strdup(virDomainGetName(dom)); + + viewer->active = 1; + viewer_set_title(viewer, FALSE); + + ret = 0; + cleanup: + free(host); + free(transport); + free(user); + free(vncport); + return ret; + +} + +static void viewer_deactivate(VirtViewer *viewer) +{ + if (!viewer->active) + return; + + vnc_display_close(VNC_DISPLAY(viewer->vnc)); + free(viewer->domtitle); + viewer->domtitle = NULL; + + if (viewer->reconnect) { + viewer_set_status(viewer, "Waiting for guest domain to re-start"); + } else { + viewer_set_status(viewer, "Guest domain has shutdown"); + gtk_main_quit(); } + viewer->active = 0; + viewer_set_title(viewer, FALSE); +} - do { - do { - dom = viewer_lookup_domain(conn, name); - if (!dom && !waitvnc) { - fprintf(stderr, "unable to lookup domain %s\n", name); - return 3; - } - if (!dom) - usleep(500*1000); - } while (!dom); +static void viewer_connected(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer) +{ + viewer_set_status(viewer, "Connected to VNC server"); +} - if (virDomainGetInfo(dom, &domInfo) != 0) { - fprintf(stderr, "unable to get information for %s\n", name); - return 6; - } +static void viewer_initialized(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer) +{ + viewer_set_vnc(viewer); + viewer_set_title(viewer, FALSE); +} - if (domInfo.state == VIR_DOMAIN_SHUTOFF && !waitvnc) { - fprintf(stderr, "%s is not running\n", name); - return 7; - } - if (domInfo.state != VIR_DOMAIN_SHUTOFF) { - viewer_extract_vnc_graphics(dom, &vncport); - if (!vncport) { - fprintf(stderr, "unable to find vnc graphics for %s\n", name); - return 4; +static void viewer_disconnected(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer) +{ + viewer_deactivate(viewer); +} + + +static int viewer_domain_event(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + int event, + int detail G_GNUC_UNUSED, + void *opaque) +{ + VirtViewer *viewer = opaque; + + if (!viewer_matches_domain(viewer, dom)) + return 0; + + switch (event) { + case VIR_DOMAIN_EVENT_STOPPED: + viewer_deactivate(viewer); + break; + + case VIR_DOMAIN_EVENT_STARTED: + viewer_activate(viewer, dom); + break; + } + + return 0; +} + + +static int viewer_initial_connect(VirtViewer *viewer) +{ + virDomainPtr dom = NULL; + virDomainInfo info; + int ret = -1; + + viewer_set_status(viewer, "Finding guest domain"); + dom = viewer_lookup_domain(viewer); + if (!dom) + goto cleanup; + + viewer_set_status(viewer, "Checking guest domain"); + if (virDomainGetInfo(dom, &info) < 0) + goto cleanup; + + if (info.state == VIR_DOMAIN_SHUTOFF) { + viewer_set_status(viewer, "Waiting for guest domain to start"); + } else { + if (viewer_activate(viewer, dom) < 0) { + if (viewer->waitvm) { + viewer_set_status(viewer, "Waiting for guest domain to start VNC"); + } else { + goto cleanup; } } + } - if (!vncport) { - virDomainFree(dom); - usleep(300*1000); - } - } while (!vncport); - tmpname = virDomainGetName(dom); - if (tmpname != NULL) { - domname = strdup(tmpname); + ret = 0; + cleanup: + if (dom) + virDomainFree(dom); + return ret; +} + +int +viewer_start (const char *uri, + const char *name, + gboolean direct, + gboolean waitvm, + gboolean reconnect, + gboolean verbose, + GtkWidget *container) +{ + VirtViewer *viewer; + GtkWidget *notebook; + GtkWidget *scroll; + GtkWidget *align; + + viewer = g_new0(VirtViewer, 1); + + viewer->active = 0; + viewer->direct = direct; + viewer->waitvm = waitvm; + viewer->reconnect = reconnect; + viewer->verbose = verbose; + viewer->domkey = g_strdup(name); + viewer->uri = g_strdup(uri); + + g_value_init(&viewer->accelSetting, G_TYPE_STRING); + + virEventRegisterGLib(); + + viewer->conn = virConnectOpenReadOnly(uri); + if (!viewer->conn) { + fprintf(stderr, "unable to connect to libvirt %s\n", + uri ? uri : "xen"); + return -1; } - virDomainFree(dom); - virConnectClose(conn); - if (viewer_extract_host(uri, &host, &transport, &user, &port) < 0) { - fprintf(stderr, "unable to determine hostname for URI %s\n", uri); - return 5; + if (!(viewer->glade = viewer_load_glade("viewer.glade", + container ? "notebook" : "viewer"))) + return -1; + + glade_xml_signal_connect_data(viewer->glade, "viewer_menu_file_quit", + G_CALLBACK(viewer_menu_file_quit), viewer); + glade_xml_signal_connect_data(viewer->glade, "viewer_menu_file_screenshot", + G_CALLBACK(viewer_menu_file_screenshot), viewer); + glade_xml_signal_connect_data(viewer->glade, "viewer_menu_view_fullscreen", + G_CALLBACK(viewer_menu_view_fullscreen), viewer); + glade_xml_signal_connect_data(viewer->glade, "viewer_menu_view_scale", + G_CALLBACK(viewer_menu_view_scale), viewer); + glade_xml_signal_connect_data(viewer->glade, "viewer_menu_send", + G_CALLBACK(viewer_menu_send), viewer); + glade_xml_signal_connect_data(viewer->glade, "viewer_menu_help_about", + G_CALLBACK(viewer_menu_help_about), viewer); + + + viewer->vnc = vnc_display_new(); + vnc_display_set_keyboard_grab(VNC_DISPLAY(viewer->vnc), TRUE); + vnc_display_set_pointer_grab(VNC_DISPLAY(viewer->vnc), TRUE); + vnc_display_set_force_size(VNC_DISPLAY(viewer->vnc), FALSE); + //vnc_display_set_scaling(VNC_DISPLAY(viewer->vnc), TRUE); + + g_signal_connect(GTK_OBJECT(viewer->vnc), "vnc-connected", + GTK_SIGNAL_FUNC(viewer_connected), viewer); + g_signal_connect(GTK_OBJECT(viewer->vnc), "vnc-initialized", + GTK_SIGNAL_FUNC(viewer_initialized), viewer); + g_signal_connect(GTK_OBJECT(viewer->vnc), "vnc-disconnected", + GTK_SIGNAL_FUNC(viewer_disconnected), viewer); + g_signal_connect(GTK_OBJECT(viewer->vnc), "vnc-desktop-resize", + GTK_SIGNAL_FUNC(viewer_resize_desktop), viewer); + g_signal_connect(GTK_OBJECT(viewer->vnc), "vnc-pointer-grab", + GTK_SIGNAL_FUNC(viewer_grab), viewer); + g_signal_connect(GTK_OBJECT(viewer->vnc), "vnc-pointer-ungrab", + GTK_SIGNAL_FUNC(viewer_ungrab), viewer); + g_signal_connect(GTK_OBJECT(viewer->vnc), "vnc-auth-credential", + GTK_SIGNAL_FUNC(viewer_credential), NULL); + + notebook = glade_xml_get_widget(viewer->glade, "notebook"); + + if (!notebook) + return -1; + + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE); + //gtk_notebook_append_page(GTK_NOTEBOOK(notebook), viewer->vnc, NULL); + scroll = glade_xml_get_widget(viewer->glade, "vnc-scroll"); + align = glade_xml_get_widget(viewer->glade, "vnc-align"); + gtk_container_add(GTK_CONTAINER(align), viewer->vnc); + + g_signal_connect(GTK_OBJECT(scroll), "size-allocate", + GTK_SIGNAL_FUNC(viewer_resize_vnc), viewer); + + if (container) { + gtk_container_add(GTK_CONTAINER(container), GTK_WIDGET(notebook)); + if (GTK_IS_WINDOW(container)) + gtk_window_set_resizable(GTK_WINDOW(container), TRUE); + gtk_widget_show_all(container); + } else { + GtkWidget *window = glade_xml_get_widget(viewer->glade, "viewer"); + GSList *accels; + viewer->window = window; + g_signal_connect(GTK_OBJECT(window), "delete-event", + GTK_SIGNAL_FUNC(viewer_shutdown), viewer); + gtk_window_set_resizable(GTK_WINDOW(window), TRUE); + viewer->accelEnabled = TRUE; + accels = gtk_accel_groups_from_object(G_OBJECT(window)); + for ( ; accels ; accels = accels->next) { + viewer->accelList = g_slist_append(viewer->accelList, accels->data); + g_object_ref(G_OBJECT(accels->data)); + } + gtk_widget_show_all(window); } - DEBUG_LOG("Remote host is %s and transport %s user %s\n", host, transport ? transport : "", user ? user : ""); -#if defined(HAVE_SOCKETPAIR) && defined(HAVE_FORK) - if (transport && strcasecmp(transport, "ssh") == 0 && !direct) - fd = viewer_open_tunnel_ssh(host, port, user, vncport); -#endif + gtk_widget_realize(viewer->vnc); - vnc = vnc_display_new(); - window = viewer_build_window (VNC_DISPLAY(vnc), - get_toplevel, data, with_menubar); - gtk_widget_realize(vnc); - vnc_display_set_keyboard_grab(VNC_DISPLAY(vnc), TRUE); - vnc_display_set_pointer_grab(VNC_DISPLAY(vnc), TRUE); - if (!gtk_widget_is_composited(window)) - vnc_display_set_scaling(VNC_DISPLAY(vnc), TRUE); + if (viewer_initial_connect(viewer) < 0) + return -1; - if (fd >= 0) - vnc_display_open_fd(VNC_DISPLAY(vnc), fd); - else - vnc_display_open_host(VNC_DISPLAY(vnc), host, vncport); + virConnectDomainEventRegister(viewer->conn, + viewer_domain_event, + viewer, + NULL); return 0; } @@ -894,15 +1121,6 @@ viewer_start (const char *uri, const char *name, #ifndef PLUGIN /* Standalone program. */ -static GtkWidget *viewer_get_toplevel (void *data G_GNUC_UNUSED) -{ - GtkWidget *window; - - window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_resizable(GTK_WINDOW(window), FALSE); - return window; -} - static void viewer_version(FILE *out) { fprintf(out, "%s version %s\n", PACKAGE, VERSION); @@ -917,21 +1135,24 @@ int main(int argc, char **argv) char *uri = NULL; gchar **args = NULL; gboolean print_version = FALSE; - gboolean set_verbose = FALSE; + gboolean verbose = FALSE; gboolean direct = FALSE; - gboolean waitvnc = FALSE; + gboolean waitvm = FALSE; + gboolean reconnect = FALSE; const char *help_msg = "Run '" PACKAGE " --help' to see a full list of available command line options"; const GOptionEntry options [] = { { "version", 'V', 0, G_OPTION_ARG_NONE, &print_version, "display version information", NULL }, - { "verbose", 'v', 0, G_OPTION_ARG_NONE, &set_verbose, + { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "display verbose information", NULL }, { "direct", 'd', 0, G_OPTION_ARG_NONE, &direct, "direct connection with no automatic tunnels", NULL }, { "connect", 'c', 0, G_OPTION_ARG_STRING, &uri, "connect to hypervisor", "URI"}, - { "wait", 'w', 0, G_OPTION_ARG_NONE, &waitvnc, + { "wait", 'w', 0, G_OPTION_ARG_NONE, &waitvm, "wait for domain to start", NULL }, + { "reconnect", 'r', 0, G_OPTION_ARG_NONE, &reconnect, + "reconnect to domain upon restart", NULL }, { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &args, NULL, "DOMAIN-NAME|ID|UUID" }, { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL } @@ -960,8 +1181,7 @@ int main(int argc, char **argv) return 1; } - ret = viewer_start (uri, args[0], direct, waitvnc, set_verbose, - viewer_get_toplevel, NULL, 1); + ret = viewer_start (uri, args[0], direct, waitvm, reconnect, verbose, NULL); if (ret != 0) return ret; diff --git a/src/viewer.glade b/src/viewer.glade new file mode 100644 index 0000000..8e35b67 --- /dev/null +++ b/src/viewer.glade @@ -0,0 +1,311 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--Generated with glade3 3.4.5 on Wed Nov 26 17:35:24 2008 --> +<glade-interface> + <widget class="GtkWindow" id="viewer"> + <property name="default_width">400</property> + <property name="default_height">400</property> + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuBar" id="top-menu"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="menu-file"> + <property name="visible">True</property> + <property name="label" translatable="yes">_File</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu1"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="menu-file-screenshot"> + <property name="visible">True</property> + <property name="label" translatable="yes">Screenshot</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_file_screenshot"/> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separatormenuitem1"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="imagemenuitem5"> + <property name="visible">True</property> + <property name="label" translatable="yes">gtk-quit</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="viewer_menu_file_quit"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-view"> + <property name="visible">True</property> + <property name="label" translatable="yes">_View</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu2"> + <property name="visible">True</property> + <child> + <widget class="GtkCheckMenuItem" id="menu-view-fullscreen"> + <property name="visible">True</property> + <property name="label" translatable="yes">Full screen</property> + <property name="use_underline">True</property> + <signal name="toggled" handler="viewer_menu_view_fullscreen"/> + </widget> + </child> + <child> + <widget class="GtkCheckMenuItem" id="menu-view-scale"> + <property name="visible">True</property> + <property name="label" translatable="yes">Scale display</property> + <property name="use_underline">True</property> + <signal name="toggled" handler="viewer_menu_view_scale"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Send key</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu5"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="menu-send-cad"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ctrl+Alt+_Del</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send" object="cad"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send-cab"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ctrl+Alt+_Backspace</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send" object="cab"/> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separatormenuitem2"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send-caf1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ctrl+Alt+F_1</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send" object="caf1"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send-caf2"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ctrl+Alt+F_2</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send" object="caf2"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send-caf3"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ctrl+Alt+F_3</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send" object="caf3"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send-caf4"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ctrl+Alt+F_4</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send" object="caf4"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send-caf5"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ctrl+Alt+F_5</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send" object="caf5"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send-caf6"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ctrl+Alt+F_6</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send-caf7"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ctrl+Alt+F_7</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send" object="caf7"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send-caf8"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ctrl+Alt+F_8</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send" object="caf8"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send-caf9"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ctrl+Alt+F_9</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send" object="caf9"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send-caf10"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ctrl+Alt+F1_0</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send" object="caf10"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send-caf11"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ctrl+Alt+F11</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send" object="caf11"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send-caf12"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ctrl+Alt+F12</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send" object="caf12"/> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separatormenuitem3"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-send-print"> + <property name="visible">True</property> + <property name="label" translatable="yes">_PrintScreen</property> + <property name="use_underline">True</property> + <signal name="activate" handler="viewer_menu_send" object="printscreen"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menu-help"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Help</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu3"> + <property name="visible">True</property> + <child> + <widget class="GtkImageMenuItem" id="imagemenuitem10"> + <property name="visible">True</property> + <property name="label" translatable="yes">gtk-about</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="viewer_menu_help_about"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + <child> + <widget class="GtkNotebook" id="notebook"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="show_border">False</property> + <child> + <widget class="GtkLabel" id="status"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes">page 1</property> + </widget> + <packing> + <property name="type">tab</property> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <widget class="GtkScrolledWindow" id="vnc-scroll"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <child> + <widget class="GtkViewport" id="vnc-port"> + <property name="visible">True</property> + <property name="resize_mode">GTK_RESIZE_QUEUE</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <child> + <widget class="GtkAlignment" id="vnc-align"> + <property name="visible">True</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <child> + <placeholder/> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes">page 2</property> + </widget> + <packing> + <property name="type">tab</property> + <property name="position">1</property> + <property name="tab_fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/src/viewer.h b/src/viewer.h index 59015cc..0d435f2 100644 --- a/src/viewer.h +++ b/src/viewer.h @@ -23,6 +23,12 @@ #ifndef VIEWER_H #define VIEWER_H -extern int viewer_start (const char *uri, const char *name, int direct, int waitvnc, int set_verbose, GtkWidget *(*get_toplevel)(void *), void *data, int with_menubar); +extern int viewer_start (const char *uri, + const char *name, + gboolean direct, + gboolean waitvm, + gboolean reconnect, + gboolean verbose, + GtkWidget *container); #endif /* VIEWER_H */ diff --git a/virt-viewer.spec.in b/virt-viewer.spec.in index 13cf335..2330169 100644 --- a/virt-viewer.spec.in +++ b/virt-viewer.spec.in @@ -75,6 +75,11 @@ rm -rf $RPM_BUILD_ROOT %defattr(-,root,root,-) %doc README COPYING AUTHORS ChangeLog NEWS %{_bindir}/%{name} +%dir %{_datadir}/%{name} +%dir %{_datadir}/%{name}/ui/ +%{_datadir}/%{name}/ui/auth.glade +%{_datadir}/%{name}/ui/about.glade +%{_datadir}/%{name}/ui/viewer.glade %{_mandir}/man1/%{name}* %if %{_with_plugin} |