diff --git a/AUTHORS b/AUTHORS index 0d4a7c7..1adc118 100644 --- a/AUTHORS +++ b/AUTHORS @@ -13,6 +13,7 @@ With additional patches from: Guido G\374nther Hiroyuki Kaguchi Ronnie Sahlberg + Marc-André Lureau ...send patches to get your name here... diff --git a/configure.ac b/configure.ac index 014eb13..da8280a 100644 --- a/configure.ac +++ b/configure.ac @@ -33,6 +33,7 @@ PKG_CHECK_MODULES(LIBVIRT, libvirt >= 0.6.0) 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.8) +PKG_CHECK_MODULES(SPICEGTK, spice-client-gtk >= 0.1.0.14) dnl Decide if this platform can support the SSH tunnel feature. AC_CHECK_HEADERS([sys/socket.h sys/un.h windows.h]) diff --git a/man/Makefile.am b/man/Makefile.am index 0ff50be..9257ce2 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -2,6 +2,7 @@ man_MANS = virt-viewer.1 EXTRA_DIST = virt-viewer.pod +DISTCLEANFILES = $(man_MANS) %.1: %.pod pod2man $< > $@ diff --git a/man/virt-viewer.pod b/man/virt-viewer.pod index 56ef2fa..d5d823d 100644 --- a/man/virt-viewer.pod +++ b/man/virt-viewer.pod @@ -10,12 +10,13 @@ B [OPTIONS] DOMAIN-NAME|ID|UUID =head1 DESCRIPTION B is a minimal tool for displaying the graphical console -of a virtual machine. The console is accessed using the VNC protocol. -The guest can be referred to based on its name, ID, or UUID. If the -guest is not already running, then the viewer can be told to wait -until is starts before attempting to connect to the console The viewer -can connect to remote hosts to lookup the console information and then -also connect to the remote console using the same network transport. +of a virtual machine. The console is accessed using the VNC or SPICE +protocol. The guest can be referred to based on its name, ID, or +UUID. If the guest is not already running, then the viewer can be told +to wait until is starts before attempting to connect to the console +The viewer can connect to remote hosts to lookup the console +information and then also connect to the remote console using the same +network transport. =head1 OPTIONS @@ -49,7 +50,7 @@ Automatically reconnect to the domain if it shuts down and restarts =item -z PCT, --zoom=PCT -Zoom level of the VNC window in percentage. Range 10-200. +Zoom level of the display window in percentage. Range 10-200. =item -d, --direct diff --git a/po/POTFILES.in b/po/POTFILES.in index 0649f8e..d395ba8 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -3,6 +3,8 @@ src/events.c src/main.c src/util.c src/viewer.c +src/display-spice.c +src/display-vnc.c src/about.glade src/auth.glade src/viewer.glade diff --git a/src/Makefile.am b/src/Makefile.am index 70525f6..e54e98b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,10 +11,15 @@ virt_viewer_SOURCES = \ util.h util.c \ auth.h auth.c \ events.h events.c \ - viewer.h viewer.c + viewer.h viewer.c \ + viewer-priv.h \ + display.h display.c \ + display-vnc.h display-vnc.c \ + display-spice.h display-spice.c virt_viewer_LDADD = \ @GTKVNC_LIBS@ \ + @SPICEGTK_LIBS@ \ @GTK2_LIBS@ \ @LIBXML2_LIBS@ \ @LIBGLADE2_LIBS@ \ @@ -22,6 +27,7 @@ virt_viewer_LDADD = \ virt_viewer_CFLAGS = \ @GTKVNC_CFLAGS@ \ + @SPICEGTK_CFLAGS@ \ @GTK2_CFLAGS@ \ @LIBXML2_CFLAGS@ \ @LIBGLADE2_CFLAGS@ \ diff --git a/src/auth.c b/src/auth.c index 9e20e4e..c9b3ce6 100644 --- a/src/auth.c +++ b/src/auth.c @@ -30,11 +30,8 @@ #include "auth.h" -static int -viewer_auth_collect_credentials(const char *type, - const char *address, - char **username, - char **password) +int viewer_auth_collect_credentials(const char *type, const char *address, + char **username, char **password) { GtkWidget *dialog = NULL; GladeXML *creds = viewer_load_glade("auth.glade", "auth"); diff --git a/src/auth.h b/src/auth.h index be3fd20..18d9a13 100644 --- a/src/auth.h +++ b/src/auth.h @@ -29,6 +29,9 @@ void viewer_auth_vnc_credentials(GtkWidget *vnc, GValueArray *credList, char **message); +int viewer_auth_collect_credentials(const char *type, const char *address, + char **username, char **password); + int viewer_auth_libvirt_credentials(virConnectCredentialPtr cred, unsigned int ncred, void *cbdata); diff --git a/src/display-spice.c b/src/display-spice.c new file mode 100644 index 0000000..e91f357 --- /dev/null +++ b/src/display-spice.c @@ -0,0 +1,281 @@ +/* + * Virt Viewer: A virtual machine console viewer + * + * Copyright (C) 2007-2009 Red Hat, + * Copyright (C) 2009 Daniel P. Berrange + * Copyright (C) 2010 Marc-André Lureau + * + * 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 + * + * Author: Daniel P. Berrange + */ + +#include +#include "util.h" +#include "display-spice.h" +#include "auth.h" + +G_DEFINE_TYPE (VirtViewerDisplaySpice, virt_viewer_display_spice, VIRT_VIEWER_TYPE_DISPLAY) + + +static void _spice_close(VirtViewerDisplay* display); +static void _spice_send_keys(VirtViewerDisplay* display, const guint *keyvals, int nkeyvals); +static GdkPixbuf* _spice_get_pixbuf(VirtViewerDisplay* display); +static gboolean _spice_open_fd(VirtViewerDisplay* display, int fd); +static gboolean _spice_open_host(VirtViewerDisplay* display, char *host, char *port); +static gboolean _spice_channel_open_fd(VirtViewerDisplay* display, VirtViewerDisplayChannel* channel, int fd); + + +static void +virt_viewer_display_spice_class_init(VirtViewerDisplaySpiceClass *klass) +{ + VirtViewerDisplayClass *dclass = VIRT_VIEWER_DISPLAY_CLASS(klass); + + dclass->close = _spice_close; + dclass->send_keys = _spice_send_keys; + dclass->get_pixbuf = _spice_get_pixbuf; + dclass->open_fd = _spice_open_fd; + dclass->open_host = _spice_open_host; + dclass->channel_open_fd = _spice_channel_open_fd; +} + +static void +virt_viewer_display_spice_init(VirtViewerDisplaySpice *self G_GNUC_UNUSED) +{ +} + +static void _spice_send_keys(VirtViewerDisplay* display, const guint *keyvals, int nkeyvals) +{ + VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display); + + g_return_if_fail(self != NULL); + g_return_if_fail(self->display != NULL); + + spice_display_send_keys(self->display, keyvals, nkeyvals, SPICE_DISPLAY_KEY_EVENT_CLICK); +} + +static GdkPixbuf* _spice_get_pixbuf(VirtViewerDisplay* display) +{ + VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display); + + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(self->display != NULL, NULL); + + return spice_display_get_pixbuf(self->display); +} + +static void _spice_close(VirtViewerDisplay* display) +{ + VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display); + + g_return_if_fail(self != NULL); + + if (self->session == NULL) + return; + + spice_session_disconnect(self->session); + + if (self->session) /* let viewer_quit() be reentrant */ + g_object_unref(self->session); + self->session = NULL; + + if (self->audio) + g_object_unref(self->audio); + self->audio = NULL; +} + +static gboolean _spice_open_host(VirtViewerDisplay* display, char *host, char *port) +{ + VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display); + + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(self->session != NULL, FALSE); + + g_object_set(self->session, + "host", host, + "port", port, + NULL); + + return spice_session_connect(self->session); +} + +static gboolean _spice_open_fd(VirtViewerDisplay* display, int fd) +{ + VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display); + + g_return_val_if_fail(self != NULL, FALSE); + + return spice_session_open_fd(self->session, fd); +} + +static gboolean _spice_channel_open_fd(VirtViewerDisplay* display, + VirtViewerDisplayChannel* channel, int fd) +{ + VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display); + + g_return_val_if_fail(self != NULL, FALSE); + + return spice_channel_open_fd(SPICE_CHANNEL(channel), fd); +} + +static void _spice_channel_open_fd_request(SpiceChannel *channel, + G_GNUC_UNUSED gint tls, + VirtViewerDisplay *display) +{ + VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display); + + g_return_if_fail(self != NULL); + + viewer_channel_open_fd(display->viewer, (VirtViewerDisplayChannel *)channel); +} + +static void _spice_main_channel_event(G_GNUC_UNUSED SpiceChannel *channel, + SpiceChannelEvent event, + VirtViewerDisplay *display) +{ + VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display); + char *password = NULL; + + g_return_if_fail(self != NULL); + g_return_if_fail(display->viewer != NULL); + + switch (event) { + case SPICE_CHANNEL_OPENED: + DEBUG_LOG("main channel: opened"); + break; + case SPICE_CHANNEL_CLOSED: + DEBUG_LOG("main channel: closed"); + viewer_quit(display->viewer); + break; + case SPICE_CHANNEL_ERROR_CONNECT: + DEBUG_LOG("main channel: failed to connect"); + viewer_disconnected(display->viewer); + break; + case SPICE_CHANNEL_ERROR_AUTH: + DEBUG_LOG("main channel: auth failure (wrong password?)"); + int ret = viewer_auth_collect_credentials("SPICE", + display->viewer->pretty_address, + NULL, &password); + if (ret < 0) { + viewer_quit(display->viewer); + } else { + g_object_set(self->session, "password", password, NULL); + spice_session_connect(self->session); + } + break; + default: + g_warning("unknown main channel event: %d", event); + viewer_disconnected(display->viewer); + break; + } + + g_free(password); +} + +static void _spice_channel_new(SpiceSession *s, SpiceChannel *channel, + VirtViewerDisplay *display) +{ + VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display); + int id; + + g_return_if_fail(self != NULL); + + g_signal_connect(channel, "open-fd", + G_CALLBACK(_spice_channel_open_fd_request), self); + + g_object_get(channel, "channel-id", &id, NULL); + + if (SPICE_IS_MAIN_CHANNEL(channel)) { + g_signal_connect(channel, "channel-event", + G_CALLBACK(_spice_main_channel_event), self); + } + + if (SPICE_IS_DISPLAY_CHANNEL(channel)) { + DEBUG_LOG("new display channel (#%d)", id); + if (display->widget != NULL) + return; + self->display = spice_display_new(s, id); + display->widget = GTK_WIDGET(self->display); + g_object_set(self->display, + "grab-keyboard", TRUE, + "grab-mouse", TRUE, + NULL); + viewer_add_display_and_realize(display->viewer); + viewer_initialized(display->viewer); + } + + if (SPICE_IS_INPUTS_CHANNEL(channel)) { + DEBUG_LOG("new inputs channel"); + } + + if (SPICE_IS_PLAYBACK_CHANNEL(channel)) { + DEBUG_LOG("new audio channel"); + if (self->audio != NULL) + return; + self->audio = spice_audio_new(s, NULL, NULL); + } +} + +static void _spice_channel_destroy(G_GNUC_UNUSED SpiceSession *s, SpiceChannel *channel, + VirtViewerDisplay *display) +{ + VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display); + int id; + + g_return_if_fail(self != NULL); + + g_object_get(channel, "channel-id", &id, NULL); + if (SPICE_IS_MAIN_CHANNEL(channel)) { + DEBUG_LOG("zap main channel"); + } + + if (SPICE_IS_DISPLAY_CHANNEL(channel)) { + DEBUG_LOG("zap display channel (#%d)", id); + } + + if (SPICE_IS_PLAYBACK_CHANNEL(channel)) { + if (self->audio == NULL) + return; + DEBUG_LOG("zap audio channel"); + } +} + +VirtViewerDisplaySpice* virt_viewer_display_spice_new(VirtViewer *viewer) +{ + VirtViewerDisplaySpice *self; + VirtViewerDisplay *d; + + g_return_val_if_fail(viewer != NULL, NULL); + + self = g_object_new(VIRT_VIEWER_TYPE_DISPLAY_SPICE, NULL); + d = VIRT_VIEWER_DISPLAY(self); + d->viewer = viewer; + + self->session = spice_session_new(); + g_signal_connect(self->session, "channel-new", + G_CALLBACK(_spice_channel_new), self); + g_signal_connect(self->session, "channel-destroy", + G_CALLBACK(_spice_channel_destroy), self); + + return self; +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/src/display-spice.h b/src/display-spice.h new file mode 100644 index 0000000..d1da86b --- /dev/null +++ b/src/display-spice.h @@ -0,0 +1,78 @@ +/* + * Virt Viewer: A virtual machine console viewer + * + * Copyright (C) 2007-2009 Red Hat, + * Copyright (C) 2009 Daniel P. Berrange + * Copyright (C) 2010 Marc-André Lureau + * + * 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 + * + * Author: Daniel P. Berrange + */ +#ifndef _VIRT_VIEWER_DISPLAY_SPICE_H +#define _VIRT_VIEWER_DISPLAY_SPICE_H + +#include +#include +#include + +#include "display.h" + +G_BEGIN_DECLS + +#define VIRT_VIEWER_TYPE_DISPLAY_SPICE virt_viewer_display_spice_get_type() + +#define VIRT_VIEWER_DISPLAY_SPICE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIRT_VIEWER_TYPE_DISPLAY_SPICE, VirtViewerDisplaySpice)) + +#define VIRT_VIEWER_DISPLAY_SPICE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), VIRT_VIEWER_TYPE_DISPLAY_SPICE, VirtViewerDisplaySpiceClass)) + +#define VIRT_IS_VIEWER_DISPLAY_SPICE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIRT_VIEWER_TYPE_DISPLAY_SPICE)) + +#define VIRT_IS_VIEWER_DISPLAY_SPICE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), VIRT_VIEWER_TYPE_DISPLAY_SPICE)) + +#define VIRT_VIEWER_DISPLAY_SPICE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), VIRT_VIEWER_TYPE_DISPLAY_SPICE, VirtViewerDisplaySpiceClass)) + +typedef struct { + VirtViewerDisplay parent; + + SpiceSession *session; + SpiceDisplay *display; + SpiceAudio *audio; +} VirtViewerDisplaySpice; + +typedef struct { + VirtViewerDisplayClass parent_class; +} VirtViewerDisplaySpiceClass; + +GType virt_viewer_display_spice_get_type(void); + +VirtViewerDisplaySpice* virt_viewer_display_spice_new(VirtViewer *viewer); + +G_END_DECLS + +#endif /* _VIRT_VIEWER_DISPLAY_SPICE_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/src/display-vnc.c b/src/display-vnc.c new file mode 100644 index 0000000..96bc382 --- /dev/null +++ b/src/display-vnc.c @@ -0,0 +1,333 @@ +/* + * Virt Viewer: A virtual machine console viewer + * + * Copyright (C) 2007-2009 Red Hat, + * Copyright (C) 2009 Daniel P. Berrange + * Copyright (C) 2010 Marc-André Lureau + * + * 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 + * + * Author: Daniel P. Berrange + */ + +#include "auth.h" +#include "display-vnc.h" + +G_DEFINE_TYPE(VirtViewerDisplayVNC, virt_viewer_display_vnc, VIRT_VIEWER_TYPE_DISPLAY) + +static void _vnc_close(VirtViewerDisplay* display); +static void _vnc_send_keys(VirtViewerDisplay* display, const guint *keyvals, int nkeyvals); +static GdkPixbuf* _vnc_get_pixbuf(VirtViewerDisplay* display); +static gboolean _vnc_open_fd(VirtViewerDisplay* display, int fd); +static gboolean _vnc_open_host(VirtViewerDisplay* display, char *host, char *port); +static gboolean _vnc_channel_open_fd(VirtViewerDisplay* display, + VirtViewerDisplayChannel* channel, int fd); + +static void virt_viewer_display_vnc_class_init(VirtViewerDisplayVNCClass *klass) +{ + VirtViewerDisplayClass *dclass = VIRT_VIEWER_DISPLAY_CLASS(klass); + + dclass->close = _vnc_close; + dclass->send_keys = _vnc_send_keys; + dclass->get_pixbuf = _vnc_get_pixbuf; + dclass->open_fd = _vnc_open_fd; + dclass->open_host = _vnc_open_host; + dclass->channel_open_fd = _vnc_channel_open_fd; +} + +static void virt_viewer_display_vnc_init(VirtViewerDisplayVNC *self G_GNUC_UNUSED) +{ +} + +static void _vnc_mouse_grab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewerDisplayVNC *self) +{ + viewer_set_title(VIRT_VIEWER_DISPLAY(self)->viewer, TRUE); +} + +static void _vnc_mouse_ungrab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewerDisplayVNC *self) +{ + viewer_set_title(VIRT_VIEWER_DISPLAY(self)->viewer, FALSE); +} + +static void _vnc_key_grab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewerDisplayVNC *self) +{ + viewer_disable_modifiers(VIRT_VIEWER_DISPLAY(self)->viewer); +} + +static void _vnc_key_ungrab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewerDisplayVNC *self) +{ + viewer_enable_modifiers(VIRT_VIEWER_DISPLAY(self)->viewer); +} + +static void _vnc_send_keys(VirtViewerDisplay* display, const guint *keyvals, int nkeyvals) +{ + VirtViewerDisplayVNC *self = VIRT_VIEWER_DISPLAY_VNC(display); + + g_return_if_fail(self != NULL); + g_return_if_fail(keyvals != NULL); + g_return_if_fail(self->vnc != NULL); + + vnc_display_send_keys(self->vnc, keyvals, nkeyvals); +} + +static GdkPixbuf* _vnc_get_pixbuf(VirtViewerDisplay* display) +{ + VirtViewerDisplayVNC *self = VIRT_VIEWER_DISPLAY_VNC(display); + + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(self->vnc != NULL, NULL); + + return vnc_display_get_pixbuf(self->vnc); +} + +static gboolean _vnc_open_fd(VirtViewerDisplay* display, int fd) +{ + VirtViewerDisplayVNC *self = VIRT_VIEWER_DISPLAY_VNC(display); + + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(self->vnc != NULL, FALSE); + + return vnc_display_open_fd(self->vnc, fd); +} + +static gboolean _vnc_channel_open_fd(VirtViewerDisplay* display G_GNUC_UNUSED, + VirtViewerDisplayChannel* channel G_GNUC_UNUSED, + int fd G_GNUC_UNUSED) +{ + g_warning("channel_open_fd is not supported by VNC"); + return FALSE; +} + +static gboolean _vnc_open_host(VirtViewerDisplay* display, char *host, char *port) +{ + VirtViewerDisplayVNC *self = VIRT_VIEWER_DISPLAY_VNC(display); + + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(self->vnc != NULL, FALSE); + + return vnc_display_open_host(self->vnc, host, port); +} + +static void _vnc_close(VirtViewerDisplay* display) +{ + VirtViewerDisplayVNC *self = VIRT_VIEWER_DISPLAY_VNC(display); + + g_return_if_fail(self != NULL); + + if (self->vnc != NULL) + vnc_display_close(self->vnc); + } + +static void viewer_bell(VirtViewer *viewer, gpointer data G_GNUC_UNUSED) +{ + gdk_window_beep(GTK_WIDGET(viewer->window)->window); +} + +static void viewer_vnc_auth_unsupported(VirtViewer *viewer, + unsigned int authType, gpointer data G_GNUC_UNUSED) +{ + viewer_simple_message_dialog(viewer->window, + _("Unable to authenticate with VNC server at %s\n" + "Unsupported authentication type %d"), + viewer->pretty_address, authType); +} + +static void viewer_vnc_auth_failure(VirtViewer *viewer, + const char *reason, gpointer data G_GNUC_UNUSED) +{ + GtkWidget *dialog; + int ret; + + dialog = gtk_message_dialog_new(GTK_WINDOW(viewer->window), + GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_YES_NO, + _("Unable to authenticate with VNC server at %s: %s\n" + "Retry connection again?"), + viewer->pretty_address, reason); + + ret = gtk_dialog_run(GTK_DIALOG(dialog)); + + gtk_widget_destroy(dialog); + + if (ret == GTK_RESPONSE_YES) + viewer->authretry = TRUE; + else + viewer->authretry = FALSE; +} + +/* + * Triggers a resize of the main container to indirectly cause + * the display widget to be resized to fit the available space + */ +static void +viewer_resize_display_widget(VirtViewer *viewer) +{ + GtkWidget *align; + align = glade_xml_get_widget(viewer->glade, "display-align"); + gtk_widget_queue_resize(align); +} + + +/* + * Called when desktop size changes. + * + * It either tries to resize the main window, or it triggers + * recalculation of the display within existing window size + */ +static void viewer_resize_desktop(VirtViewer *viewer, gint width, gint height) +{ + DEBUG_LOG("desktop resize %dx%d", width, height); + viewer->desktopWidth = width; + viewer->desktopHeight = height; + + if (viewer->autoResize && viewer->window && !viewer->fullscreen) { + viewer_resize_main_window(viewer); + } else { + viewer_resize_display_widget(viewer); + } +} + + +/* + * Called when the main container widget's size has been set. + * It attempts to fit the display widget into this space while + * maintaining aspect ratio + */ +static gboolean viewer_resize_align(GtkWidget *widget, + GtkAllocation *alloc, + VirtViewer *viewer) +{ + double desktopAspect; + double scrollAspect; + int height, width; + GtkAllocation child; + int dx = 0, dy = 0; + + if (!viewer->active) { + DEBUG_LOG("Skipping inactive resize"); + return TRUE; + } + + desktopAspect = (double)viewer->desktopWidth / (double)viewer->desktopHeight; + scrollAspect = (double)alloc->width / (double)alloc->height; + + if (scrollAspect > desktopAspect) { + width = alloc->height * desktopAspect; + dx = (alloc->width - width) / 2; + height = alloc->height; + } else { + width = alloc->width; + height = alloc->width / desktopAspect; + dy = (alloc->height - height) / 2; + } + + DEBUG_LOG("Align widget=%p is %dx%d, desktop is %dx%d, setting display to %dx%d", + widget, + alloc->width, alloc->height, + viewer->desktopWidth, viewer->desktopHeight, + width, height); + + child.x = alloc->x + dx; + child.y = alloc->y + dy; + child.width = width; + child.height = height; + if (viewer->display && viewer->display->widget) + gtk_widget_size_allocate(viewer->display->widget, &child); + + return FALSE; +} + +VirtViewerDisplayVNC* virt_viewer_display_vnc_new(VirtViewer *viewer) +{ + VirtViewerDisplayVNC *self; + VirtViewerDisplay *d; + GtkWidget *align; + + g_return_val_if_fail(viewer != NULL, NULL); + + self = g_object_new(VIRT_VIEWER_TYPE_DISPLAY_VNC, NULL); + d = VIRT_VIEWER_DISPLAY(self); + d->viewer = viewer; + viewer->display = d; + + d->widget = vnc_display_new(); + self->vnc = VNC_DISPLAY(d->widget); + vnc_display_set_keyboard_grab(self->vnc, TRUE); + vnc_display_set_pointer_grab(self->vnc, TRUE); + + /* + * In auto-resize mode we have things setup so that we always + * automatically resize the top level window to be exactly the + * same size as the VNC desktop, except when it won't fit on + * the local screen, at which point we let it scale down. + * The upshot is, we always want scaling enabled. + * We disable force_size because we want to allow user to + * manually size the widget smaller too + */ + vnc_display_set_force_size(self->vnc, FALSE); + vnc_display_set_scaling(self->vnc, TRUE); + + g_signal_connect_swapped(self->vnc, "vnc-connected", + G_CALLBACK(viewer_connected), viewer); + g_signal_connect_swapped(self->vnc, "vnc-initialized", + G_CALLBACK(viewer_initialized), viewer); + g_signal_connect_swapped(self->vnc, "vnc-disconnected", + G_CALLBACK(viewer_disconnected), viewer); + + /* When VNC desktop resizes, we have to resize the containing widget */ + g_signal_connect_swapped(self->vnc, "vnc-desktop-resize", + G_CALLBACK(viewer_resize_desktop), viewer); + g_signal_connect_swapped(self->vnc, "vnc-bell", + G_CALLBACK(viewer_bell), NULL); + g_signal_connect_swapped(self->vnc, "vnc-auth-failure", + G_CALLBACK(viewer_vnc_auth_failure), viewer); + g_signal_connect_swapped(self->vnc, "vnc-auth-unsupported", + G_CALLBACK(viewer_vnc_auth_unsupported), viewer); + g_signal_connect_swapped(self->vnc, "vnc-server-cut-text", + G_CALLBACK(viewer_server_cut_text), viewer); + + g_signal_connect(self->vnc, "vnc-pointer-grab", + G_CALLBACK(_vnc_mouse_grab), self); + g_signal_connect(self->vnc, "vnc-pointer-ungrab", + G_CALLBACK(_vnc_mouse_ungrab), self); + g_signal_connect(self->vnc, "vnc-keyboard-grab", + G_CALLBACK(_vnc_key_grab), self); + g_signal_connect(self->vnc, "vnc-keyboard-ungrab", + G_CALLBACK(_vnc_key_ungrab), self); + + g_signal_connect(self->vnc, "vnc-auth-credential", + G_CALLBACK(viewer_auth_vnc_credentials), &viewer->pretty_address); + + viewer_add_display_and_realize(viewer); + + align = glade_xml_get_widget(viewer->glade, "display-align"); + g_signal_connect(align, "size-allocate", + G_CALLBACK(viewer_resize_align), viewer); + + return self; +} + + + + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/src/display-vnc.h b/src/display-vnc.h new file mode 100644 index 0000000..15a7d5b --- /dev/null +++ b/src/display-vnc.h @@ -0,0 +1,75 @@ +/* + * Virt Viewer: A virtual machine console viewer + * + * Copyright (C) 2007-2009 Red Hat, + * Copyright (C) 2009 Daniel P. Berrange + * Copyright (C) 2010 Marc-André Lureau + * + * 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 + * + * Author: Daniel P. Berrange + */ +#ifndef _VIRT_VIEWER_DISPLAY_VNC_H +#define _VIRT_VIEWER_DISPLAY_VNC_H + +#include +#include + +#include "display.h" + +G_BEGIN_DECLS + +#define VIRT_VIEWER_TYPE_DISPLAY_VNC virt_viewer_display_vnc_get_type() + +#define VIRT_VIEWER_DISPLAY_VNC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIRT_VIEWER_TYPE_DISPLAY_VNC, VirtViewerDisplayVNC)) + +#define VIRT_VIEWER_DISPLAY_VNC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), VIRT_VIEWER_TYPE_DISPLAY_VNC, VirtViewerDisplayVNCClass)) + +#define VIRT_IS_VIEWER_DISPLAY_VNC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIRT_VIEWER_TYPE_DISPLAY_VNC)) + +#define VIRT_IS_VIEWER_DISPLAY_VNC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), VIRT_VIEWER_TYPE_DISPLAY_VNC)) + +#define VIRT_VIEWER_DISPLAY_VNC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), VIRT_VIEWER_TYPE_DISPLAY_VNC, VirtViewerDisplayVNCClass)) + +typedef struct { + VirtViewerDisplay parent; + + VncDisplay *vnc; +} VirtViewerDisplayVNC; + +typedef struct { + VirtViewerDisplayClass parent_class; +} VirtViewerDisplayVNCClass; + +GType virt_viewer_display_vnc_get_type(void); + +VirtViewerDisplayVNC* virt_viewer_display_vnc_new(VirtViewer *viewer); + +G_END_DECLS + +#endif /* _VIRT_VIEWER_DISPLAY_VNC_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/src/display.c b/src/display.c new file mode 100644 index 0000000..2d95a58 --- /dev/null +++ b/src/display.c @@ -0,0 +1,90 @@ +/* + * Virt Viewer: A virtual machine console viewer + * + * Copyright (C) 2007-2009 Red Hat, + * Copyright (C) 2009 Daniel P. Berrange + * Copyright (C) 2010 Marc-André Lureau + * + * 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 + * + * Author: Daniel P. Berrange + */ +#include "display.h" + +G_DEFINE_ABSTRACT_TYPE(VirtViewerDisplay, virt_viewer_display, G_TYPE_OBJECT) + + +static void virt_viewer_display_class_init(VirtViewerDisplayClass *klass G_GNUC_UNUSED) +{ +} + +static void virt_viewer_display_init(VirtViewerDisplay *self G_GNUC_UNUSED) +{ +} + +void virt_viewer_display_close(VirtViewerDisplay *self) +{ + g_return_if_fail(VIRT_VIEWER_IS_DISPLAY(self)); + + VIRT_VIEWER_DISPLAY_GET_CLASS(self)->close(self); +} + +void virt_viewer_display_send_keys(VirtViewerDisplay *self, + const guint *keyvals, int nkeyvals) +{ + g_return_if_fail(VIRT_VIEWER_IS_DISPLAY(self)); + + VIRT_VIEWER_DISPLAY_GET_CLASS(self)->send_keys(self, keyvals, nkeyvals); +} + +GdkPixbuf* virt_viewer_display_get_pixbuf(VirtViewerDisplay *self) +{ + g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(self), NULL); + + return VIRT_VIEWER_DISPLAY_GET_CLASS(self)->get_pixbuf(self); +} + +gboolean virt_viewer_display_open_fd(VirtViewerDisplay *self, int fd) +{ + g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(self), FALSE); + + return VIRT_VIEWER_DISPLAY_GET_CLASS(self)->open_fd(self, fd); +} + +gboolean virt_viewer_display_open_host(VirtViewerDisplay *self, char *host, char *port) +{ + VirtViewerDisplayClass *klass; + + g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(self), FALSE); + + klass = VIRT_VIEWER_DISPLAY_GET_CLASS(self); + return klass->open_host(self, host, port); +} + +gboolean virt_viewer_display_channel_open_fd(VirtViewerDisplay *self, + VirtViewerDisplayChannel *channel, int fd) +{ + g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(self), FALSE); + + return VIRT_VIEWER_DISPLAY_GET_CLASS(self)->channel_open_fd(self, channel, fd); +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/src/display.h b/src/display.h new file mode 100644 index 0000000..45931b4 --- /dev/null +++ b/src/display.h @@ -0,0 +1,91 @@ +/* + * Virt Viewer: A virtual machine console viewer + * + * Copyright (C) 2007-2009 Red Hat, + * Copyright (C) 2009 Daniel P. Berrange + * Copyright (C) 2010 Marc-André Lureau + * + * 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 + * + * Author: Daniel P. Berrange + */ +#ifndef _VIRT_VIEWER_DISPLAY_H +#define _VIRT_VIEWER_DISPLAY_H + +#include +#include "viewer-priv.h" + +G_BEGIN_DECLS + +#define VIRT_VIEWER_TYPE_DISPLAY virt_viewer_display_get_type() + +#define VIRT_VIEWER_DISPLAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIRT_VIEWER_TYPE_DISPLAY, VirtViewerDisplay)) + +#define VIRT_VIEWER_DISPLAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), VIRT_VIEWER_TYPE_DISPLAY, VirtViewerDisplayClass)) + +#define VIRT_VIEWER_IS_DISPLAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIRT_VIEWER_TYPE_DISPLAY)) + +#define VIRT_VIEWER_IS_DISPLAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), VIRT_VIEWER_TYPE_DISPLAY)) + +#define VIRT_VIEWER_DISPLAY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), VIRT_VIEWER_TYPE_DISPLAY, VirtViewerDisplayClass)) + +/* perhaps this become an interface, and be pushed in gtkvnc and spice? */ +struct _VirtViewerDisplay { + GObject parent; + VirtViewer *viewer; + GtkWidget *widget; +}; + +struct _VirtViewerDisplayClass { + GObjectClass parent_class; + + /* virtual methods */ + void (* close) (VirtViewerDisplay* display); + void (* send_keys) (VirtViewerDisplay* display, + const guint *keyvals, int nkeyvals); + GdkPixbuf* (* get_pixbuf) (VirtViewerDisplay* display); + gboolean (* open_fd) (VirtViewerDisplay* display, int fd); + gboolean (* open_host) (VirtViewerDisplay* display, char *host, char *port); + gboolean (* channel_open_fd) (VirtViewerDisplay* display, + VirtViewerDisplayChannel* channel, int fd); +}; + +GType virt_viewer_display_get_type(void); + +void virt_viewer_display_close(VirtViewerDisplay* display); +void virt_viewer_display_send_keys(VirtViewerDisplay* display, + const guint *keyvals, int nkeyvals); +GdkPixbuf* virt_viewer_display_get_pixbuf(VirtViewerDisplay* display); +gboolean virt_viewer_display_open_fd(VirtViewerDisplay* display, int fd); +gboolean virt_viewer_display_open_host(VirtViewerDisplay* display, char *host, char *port); +GObject* virt_viewer_display_get(VirtViewerDisplay* display); +gboolean virt_viewer_display_channel_open_fd(VirtViewerDisplay* display, + VirtViewerDisplayChannel* channel, int fd); + +G_END_DECLS + +#endif /* _VIRT_VIEWER_DISPLAY_H */ +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/src/viewer-priv.h b/src/viewer-priv.h new file mode 100644 index 0000000..9e28ac1 --- /dev/null +++ b/src/viewer-priv.h @@ -0,0 +1,123 @@ +/* + * Virt Viewer: A virtual machine console viewer + * + * Copyright (C) 2007-2009 Red Hat, + * Copyright (C) 2009 Daniel P. Berrange + * Copyright (C) 2010 Marc-André Lureau + * + * 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 + * + * Author: Daniel P. Berrange + */ +#ifndef _VIRT_VIEWER_PRIV_H +# define _VIRT_VIEWER_PRIV_H + +#include +#include +#include + +#include +#include +#include +#include + +typedef struct _VirtViewerDisplay VirtViewerDisplay; +typedef struct _VirtViewerDisplayClass VirtViewerDisplayClass; +typedef struct _VirtViewer VirtViewer; +typedef struct _VirtViewerSize VirtViewerSize; +typedef struct _VirtViewerDisplayChanne VirtViewerDisplayChannel; + +enum menuNums { + FILE_MENU, + VIEW_MENU, + SEND_KEY_MENU, + HELP_MENU, + LAST_MENU // sentinel +}; + +struct _VirtViewer { + char *uri; + virConnectPtr conn; + char *domkey; + char *domtitle; + + GladeXML *glade; + GtkWidget *window; + GtkWidget *container; + + char *pretty_address; + + int zoomlevel; + + int desktopWidth; + int desktopHeight; + gboolean autoResize; + gboolean fullscreen; + gboolean withEvents; + + gboolean active; + + gboolean accelEnabled; + GValue accelSetting; + GSList *accelList; + int accelMenuSig[LAST_MENU]; + + gboolean waitvm; + gboolean reconnect; + gboolean direct; + gboolean verbose; + gboolean authretry; + gboolean connected; + + gchar *clipboard; + + VirtViewerDisplay *display; + + char *gport; + char *host; + char *transport; + char *user; + int port; +}; + +struct _VirtViewerSize { + VirtViewer *viewer; + gint width, height; + gulong sig_id; +}; + +void viewer_connected(VirtViewer *viewer); +void viewer_initialized(VirtViewer *viewer); +void viewer_disconnected(VirtViewer *viewer); +void viewer_set_status(VirtViewer *viewer, const char *text); +void viewer_set_title(VirtViewer *viewer, gboolean grabbed); +void viewer_enable_modifiers(VirtViewer *viewer); +void viewer_disable_modifiers(VirtViewer *viewer); +void viewer_add_display_and_realize(VirtViewer *viewer); +void viewer_server_cut_text(VirtViewer *viewer, const gchar *text); +void viewer_resize_main_window(VirtViewer *viewer); +void viewer_channel_open_fd(VirtViewer *viewer, VirtViewerDisplayChannel *channel); +void viewer_quit(VirtViewer *viewer); + +void viewer_simple_message_dialog(GtkWidget *window, const char *fmt, ...); + +#endif // _VIRT_VIEWER_PRIV_H +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/src/viewer.c b/src/viewer.c index ee8a226..6ec021a 100644 --- a/src/viewer.c +++ b/src/viewer.c @@ -3,6 +3,7 @@ * * Copyright (C) 2007-2009 Red Hat, * Copyright (C) 2009 Daniel P. Berrange + * Copyright (C) 2010 Marc-André Lureau * * 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 @@ -23,7 +24,6 @@ #include -#include #include #include #include @@ -31,11 +31,6 @@ #include #include #include -#include -#include -#include -#include -#include #ifdef HAVE_SYS_SOCKET_H #include @@ -50,105 +45,54 @@ #endif #include "viewer.h" +#include "viewer-priv.h" #include "events.h" #include "auth.h" +#include "display-vnc.h" +#include "display-spice.h" #define SCALE(x) do { x = viewer->fullscreen ? x : x * viewer->zoomlevel / 100; } while (0); gboolean doDebug = FALSE; -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 { +struct keyComboDef { guint keys[MAX_KEY_COMBO]; guint nkeys; const char *label; }; 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"}, - { { 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"}, + { { 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"}, }; - -typedef struct VirtViewer { - char *uri; - virConnectPtr conn; - char *domkey; - char *domtitle; - - GladeXML *glade; - GtkWidget *window; - GtkWidget *container; - GtkWidget *vnc; - - int zoomlevel; - - int desktopWidth; - int desktopHeight; - gboolean autoResize; - gboolean fullscreen; - gboolean withEvents; - - gboolean active; - char *vncAddress; - - gboolean accelEnabled; - GValue accelSetting; - GSList *accelList; - int accelMenuSig[LAST_MENU]; - - gboolean waitvm; - gboolean reconnect; - gboolean direct; - gboolean verbose; - gboolean authretry; - gboolean connected; - - gchar *clipboard; -} VirtViewer; - -typedef struct VirtViewerSize { - VirtViewer *viewer; - gint width, height; - gulong sig_id; -} VirtViewerSize; - - static gboolean viewer_connect_timer(void *opaque); static int viewer_initial_connect(VirtViewer *viewer); -static void viewer_init_vnc_display(VirtViewer *viewer); -static void viewer_simple_message_dialog(GtkWidget *window, const char *fmt, ...) +void viewer_simple_message_dialog(GtkWidget *window, const char *fmt, ...) { GtkWidget *dialog; char *msg; @@ -176,6 +120,27 @@ static void viewer_simple_message_dialog(GtkWidget *window, const char *fmt, ... } +void viewer_add_display_and_realize(VirtViewer *viewer) +{ + GtkWidget *notebook; + GtkWidget *align; + + g_return_if_fail(viewer != NULL); + g_return_if_fail(viewer->display != NULL); + g_return_if_fail(viewer->display->widget != NULL); + + notebook = glade_xml_get_widget(viewer->glade, "notebook"); + align = glade_xml_get_widget(viewer->glade, "display-align"); + gtk_container_add(GTK_CONTAINER(align), viewer->display->widget); + + if (!viewer->window) { + gtk_container_add(GTK_CONTAINER(viewer->container), GTK_WIDGET(notebook)); + gtk_widget_show_all(viewer->container); + } + + gtk_widget_realize(viewer->display->widget); +} + /* 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 @@ -240,73 +205,13 @@ viewer_set_widget_size(VirtViewer *viewer, } -/* - * Called when the main container widget's size has been set. - * It attempts to fit the VNC widget into this space while - * maintaining aspect ratio - */ -static gboolean viewer_resize_align(GtkWidget *widget, - GtkAllocation *alloc, - VirtViewer *viewer) -{ - double desktopAspect = (double)viewer->desktopWidth / (double)viewer->desktopHeight; - double scrollAspect = (double)alloc->width / (double)alloc->height; - int height, width; - GtkAllocation child; - int dx = 0, dy = 0; - - if (!viewer->active) { - DEBUG_LOG("Skipping inactive resize"); - return TRUE; - } - - if (scrollAspect > desktopAspect) { - width = alloc->height * desktopAspect; - dx = (alloc->width - width) / 2; - height = alloc->height; - } else { - width = alloc->width; - height = alloc->width / desktopAspect; - dy = (alloc->height - height) / 2; - } - - DEBUG_LOG("Align widget=%p is %dx%d, desktop is %dx%d, setting VNC to %dx%d", - widget, - alloc->width, alloc->height, - viewer->desktopWidth, viewer->desktopHeight, - width, height); - - child.x = alloc->x + dx; - child.y = alloc->y + dy; - child.width = width; - child.height = height; - gtk_widget_size_allocate(viewer->vnc, &child); - - return FALSE; -} - - -/* - * Triggers a resize of the main container to indirectly cause - * the VNC widget to be resized to fit the available space - */ -static void -viewer_resize_vnc_widget(VirtViewer *viewer) -{ - GtkWidget *align; - align = glade_xml_get_widget(viewer->glade, "vnc-align"); - gtk_widget_queue_resize(align); -} - - /* * This code attempts to resize the top level window to be large enough - * to contain the entire VNC desktop at 1:1 ratio. If the local desktop - * isn't large enough that it goes as large as possible and lets VNC + * to contain the entire display desktop at 1:1 ratio. If the local desktop + * isn't large enough that it goes as large as possible and lets the display * scale down to fit, maintaining aspect ratio */ -static void -viewer_resize_main_window(VirtViewer *viewer) +void viewer_resize_main_window(VirtViewer *viewer) { GdkRectangle fullscreen; GdkScreen *screen; @@ -351,7 +256,7 @@ viewer_resize_main_window(VirtViewer *viewer) SCALE(height); viewer_set_widget_size(viewer, - glade_xml_get_widget(viewer->glade, "vnc-align"), + glade_xml_get_widget(viewer->glade, "display-align"), width, height); } @@ -381,27 +286,7 @@ static void viewer_menu_view_zoom_reset(G_GNUC_UNUSED GtkWidget *menu, VirtViewe viewer_resize_main_window(viewer); } -/* - * Called when VNC desktop size changes. - * - * It either tries to resize the main window, or it triggers - * recalculation of VNC within existing window size - */ -static void viewer_resize_desktop(GtkWidget *vnc G_GNUC_UNUSED, gint width, gint height, VirtViewer *viewer) -{ - DEBUG_LOG("VNC desktop resize %dx%d", width, height); - viewer->desktopWidth = width; - viewer->desktopHeight = height; - - if (viewer->autoResize && viewer->window && !viewer->fullscreen) { - viewer_resize_main_window(viewer); - } else { - viewer_resize_vnc_widget(viewer); - } -} - - -static void viewer_set_title(VirtViewer *viewer, gboolean grabbed) +void viewer_set_title(VirtViewer *viewer, gboolean grabbed) { char *title; const char *subtitle; @@ -430,7 +315,7 @@ static gboolean viewer_ignore_accel(GtkWidget *menu G_GNUC_UNUSED, } -static void viewer_disable_modifiers(VirtViewer *viewer) +void viewer_disable_modifiers(VirtViewer *viewer) { GtkSettings *settings = gtk_settings_get_default(); GValue empty; @@ -466,7 +351,7 @@ static void viewer_disable_modifiers(VirtViewer *viewer) } -static void viewer_enable_modifiers(VirtViewer *viewer) +void viewer_enable_modifiers(VirtViewer *viewer) { GtkSettings *settings = gtk_settings_get_default(); GSList *accels; @@ -495,38 +380,23 @@ static void viewer_enable_modifiers(VirtViewer *viewer) viewer->accelEnabled = TRUE; } - - -static void viewer_mouse_grab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer) +void viewer_quit(VirtViewer *viewer) { - viewer_set_title(viewer, TRUE); -} + g_return_if_fail(viewer != NULL); -static void viewer_mouse_ungrab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer) -{ - viewer_set_title(viewer, FALSE); -} - -static void viewer_key_grab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer) -{ - viewer_disable_modifiers(viewer); -} - -static void viewer_key_ungrab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer) -{ - viewer_enable_modifiers(viewer); -} - - -static void viewer_shutdown(GtkWidget *src G_GNUC_UNUSED, void *dummy G_GNUC_UNUSED, VirtViewer *viewer) -{ - vnc_display_close(VNC_DISPLAY(viewer->vnc)); + if (viewer->display) + virt_viewer_display_close(viewer->display); gtk_main_quit(); } +static void viewer_delete(GtkWidget *src G_GNUC_UNUSED, void *dummy G_GNUC_UNUSED, VirtViewer *viewer) +{ + viewer_quit(viewer); +} + static void viewer_menu_file_quit(GtkWidget *src G_GNUC_UNUSED, VirtViewer *viewer) { - viewer_shutdown(src, NULL, viewer); + viewer_quit(viewer); } static void viewer_menu_view_fullscreen(GtkWidget *menu, VirtViewer *viewer) @@ -558,26 +428,26 @@ static void viewer_menu_view_resize(GtkWidget *menu, VirtViewer *viewer) 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)); + 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 < G_N_ELEMENTS(keyCombos) ; i++) { - if (!strcmp(text, keyCombos[i].label)) { - DEBUG_LOG("Sending key combo %s", gtk_label_get_text(GTK_LABEL(label))); - vnc_display_send_keys(VNC_DISPLAY(viewer->vnc), - keyCombos[i].keys, - keyCombos[i].nkeys); - return; - } - } + for (i = 0 ; i < G_N_ELEMENTS(keyCombos) ; i++) { + if (!strcmp(text, keyCombos[i].label)) { + DEBUG_LOG("Sending key combo %s", gtk_label_get_text(GTK_LABEL(label))); + virt_viewer_display_send_keys(viewer->display, + keyCombos[i].keys, + keyCombos[i].nkeys); + return; + } + } DEBUG_LOG("Failed to find key combo %s", gtk_label_get_text(GTK_LABEL(label))); } -static void viewer_save_screenshot(GtkWidget *vnc, const char *file) +static void viewer_save_screenshot(VirtViewer *viewer, const char *file) { - GdkPixbuf *pix = vnc_display_get_pixbuf(VNC_DISPLAY(vnc)); + GdkPixbuf *pix = virt_viewer_display_get_pixbuf(viewer->display); gdk_pixbuf_save(pix, file, "png", NULL, "tEXt::Generator App", PACKAGE, NULL); gdk_pixbuf_unref(pix); @@ -587,6 +457,8 @@ static void viewer_menu_file_screenshot(GtkWidget *menu G_GNUC_UNUSED, VirtViewe { GtkWidget *dialog; + g_return_if_fail(viewer->display != NULL); + dialog = gtk_file_chooser_dialog_new ("Save screenshot", NULL, GTK_FILE_CHOOSER_ACTION_SAVE, @@ -602,7 +474,7 @@ static void viewer_menu_file_screenshot(GtkWidget *menu G_GNUC_UNUSED, VirtViewe char *filename; filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); - viewer_save_screenshot(viewer->vnc, filename); + viewer_save_screenshot(viewer, filename); g_free (filename); } @@ -811,37 +683,37 @@ static int viewer_extract_host(const char *uristr, char **host, char **transport static int viewer_open_tunnel(const char **cmd) { - int fd[2]; - pid_t pid; + int fd[2]; + pid_t pid; - if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd) < 0) - return -1; + if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd) < 0) + return -1; - pid = fork(); - if (pid == -1) { + pid = fork(); + if (pid == -1) { close(fd[0]); close(fd[1]); return -1; } - if (pid == 0) { /* child */ - close(fd[0]); - close(0); - close(1); - if (dup(fd[1]) < 0) - _exit(1); - if (dup(fd[1]) < 0) - _exit(1); - close(fd[1]); - execvp("ssh", (char *const*)cmd); - _exit(1); - } - close(fd[1]); + if (pid == 0) { /* child */ + close(fd[0]); + close(0); + close(1); + if (dup(fd[1]) < 0) + _exit(1); + if (dup(fd[1]) < 0) + _exit(1); + close(fd[1]); + execvp("ssh", (char *const*)cmd); + _exit(1); + } + close(fd[1]); return fd[0]; } -static int viewer_open_tunnel_ssh(const char *sshhost, int sshport, const char *sshuser, const char *vncport) +static int viewer_open_tunnel_ssh(const char *sshhost, int sshport, const char *sshuser, const char *port) { const char *cmd[10]; char portstr[50]; @@ -862,7 +734,7 @@ static int viewer_open_tunnel_ssh(const char *sshhost, int sshport, const char * cmd[n++] = sshhost; cmd[n++] = "nc"; cmd[n++] = "localhost"; - cmd[n++] = vncport; + cmd[n++] = port; cmd[n++] = NULL; return viewer_open_tunnel(cmd); @@ -870,7 +742,7 @@ static int viewer_open_tunnel_ssh(const char *sshhost, int sshport, const char * #endif /* defined(HAVE_SOCKETPAIR) && defined(HAVE_FORK) */ -static void viewer_set_status(VirtViewer *viewer, const char *text) +void viewer_set_status(VirtViewer *viewer, const char *text) { GtkWidget *status, *notebook; @@ -882,63 +754,136 @@ static void viewer_set_status(VirtViewer *viewer, const char *text) } -static void viewer_set_vnc(VirtViewer *viewer) +static void viewer_show_display(VirtViewer *viewer) { GtkWidget *notebook; + g_return_if_fail(viewer != NULL); + g_return_if_fail(viewer->display != NULL); + g_return_if_fail(viewer->display->widget != NULL); + notebook = glade_xml_get_widget(viewer->glade, "notebook"); gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 1); - gtk_widget_show(viewer->vnc); + gtk_widget_show(viewer->display->widget); } +static void viewer_connect_info_free(VirtViewer *viewer) +{ + free(viewer->host); + free(viewer->gport); + free(viewer->transport); + free(viewer->user); + + viewer->host = NULL; + viewer->gport = NULL; + viewer->transport = NULL; + viewer->user = NULL; + viewer->port = 0; +} + +static gboolean viewer_extract_connect_info(VirtViewer *viewer, + virDomainPtr dom) +{ + char *type = NULL; + char *xpath = NULL; + gboolean retval = FALSE; + + viewer_connect_info_free(viewer); + + if ((type = viewer_extract_xpath_string(dom, "string(/domain/devices/graphics/@type)")) == NULL) { + viewer_simple_message_dialog(viewer->window, _("Cannot determine the graphic type for the guest %s"), + viewer->domkey); + goto cleanup; + } + + if (g_strcasecmp(type, "vnc") == 0) + viewer->display = VIRT_VIEWER_DISPLAY(virt_viewer_display_vnc_new(viewer)); + else if (g_strcasecmp(type, "spice") == 0) + viewer->display = VIRT_VIEWER_DISPLAY(virt_viewer_display_spice_new(viewer)); + else { + viewer_simple_message_dialog(viewer->window, _("Unknown graphic type for the guest %s"), + viewer->domkey); + goto cleanup; + } + + xpath = g_strdup_printf("string(/domain/devices/graphics[@type='%s']/@port)", type); + if ((viewer->gport = viewer_extract_xpath_string(dom, xpath)) == NULL) { + viewer_simple_message_dialog(viewer->window, _("Cannot determine the graphic port for the guest %s"), + viewer->domkey); + goto cleanup; + } + + if (viewer_extract_host(viewer->uri, &viewer->host, &viewer->transport, &viewer->user, &viewer->port) < 0) { + viewer_simple_message_dialog(viewer->window, _("Cannot determine the host for the guest %s"), + viewer->domkey); + goto cleanup; + } + + DEBUG_LOG("Remote host is %s and transport %s user %s", + viewer->host, viewer->transport ? viewer->transport : "", viewer->user ? viewer->user : ""); + + retval = TRUE; + +cleanup: + free(xpath); + return retval; +} + +void viewer_channel_open_fd(VirtViewer *viewer, VirtViewerDisplayChannel *channel) +{ +#if defined(HAVE_SOCKETPAIR) && defined(HAVE_FORK) + int fd = -1; + + g_return_if_fail(viewer != NULL); + g_return_if_fail(viewer->display != NULL); + + if (viewer->transport && g_strcasecmp(viewer->transport, "ssh") == 0 && + !viewer->direct) { + if ((fd = viewer_open_tunnel_ssh(viewer->host, viewer->port, viewer->user, viewer->gport)) < 0) + viewer_simple_message_dialog(viewer->window, _("Connect to ssh failed.")); + } else + viewer_simple_message_dialog(viewer->window, _("Can't connect to channel, SSH only supported.")); + + if (fd >= 0) + virt_viewer_display_channel_open_fd(viewer->display, channel, fd); +#else + viewer_simple_message_dialog(viewer->window, _("Connect to channel unsupported.")); +#endif +} static int viewer_activate(VirtViewer *viewer, virDomainPtr dom) { - char *vncport = NULL; - char *host = NULL; - char *transport = NULL; - char *user = NULL; - int port, fd = -1; + int fd = -1; int ret = -1; + g_return_val_if_fail(viewer->display == NULL, -1); + if (viewer->active) goto cleanup; - viewer_init_vnc_display(viewer); - - if ((vncport = viewer_extract_xpath_string(dom, "string(/domain/devices/graphics[@type='vnc']/@port)")) == NULL) { - viewer_simple_message_dialog(viewer->window, _("Cannot determine the VNC port for the guest %s"), - viewer->domkey); + if (!viewer_extract_connect_info(viewer, dom)) goto cleanup; - } - if (viewer_extract_host(viewer->uri, &host, &transport, &user, &port) < 0) { - viewer_simple_message_dialog(viewer->window, _("Cannot determine the VNC host for the guest %s"), - viewer->domkey); - goto cleanup; - } - - DEBUG_LOG("Remote host is %s and transport %s user %s", - host, transport ? transport : "", user ? user : ""); - - viewer->vncAddress = g_strdup_printf("%s:%s", host, vncport); + viewer->pretty_address = g_strdup_printf("%s:%s", viewer->host, viewer->gport); #if defined(HAVE_SOCKETPAIR) && defined(HAVE_FORK) - if (transport && g_strcasecmp(transport, "ssh") == 0 && + if (viewer->transport && g_strcasecmp(viewer->transport, "ssh") == 0 && !viewer->direct) - if ((fd = viewer_open_tunnel_ssh(host, port, user, vncport)) < 0) + if ((fd = viewer_open_tunnel_ssh(viewer->host, viewer->port, + viewer->user, viewer->gport)) < 0) return -1; #endif if (fd >= 0) { - vnc_display_open_fd(VNC_DISPLAY(viewer->vnc), fd); + ret = virt_viewer_display_open_fd(viewer->display, fd); } else { - vnc_display_open_host(VNC_DISPLAY(viewer->vnc), host, vncport); + ret = virt_viewer_display_open_host(viewer->display, + viewer->host, viewer->gport); } - viewer_set_status(viewer, "Connecting to VNC server"); + viewer_set_status(viewer, "Connecting to graphic server"); free(viewer->domtitle); viewer->domtitle = g_strdup(virDomainGetName(dom)); @@ -947,121 +892,10 @@ static int viewer_activate(VirtViewer *viewer, viewer->active = TRUE; viewer_set_title(viewer, FALSE); - ret = 0; - cleanup: - free(host); - free(transport); - free(user); - free(vncport); +cleanup: return ret; - } - -static gboolean viewer_retryauth(gpointer opaque) -{ - VirtViewer *viewer = opaque; - viewer_initial_connect(viewer); - - return FALSE; -} - -static void viewer_deactivate(VirtViewer *viewer) -{ - if (!viewer->active) - return; - - vnc_display_close(VNC_DISPLAY(viewer->vnc)); - free(viewer->domtitle); - viewer->domtitle = NULL; - - viewer->connected = FALSE; - viewer->active = FALSE; - g_free(viewer->vncAddress); - viewer->vncAddress = NULL; - viewer_set_title(viewer, FALSE); - - if (viewer->authretry) { - viewer->authretry = FALSE; - g_idle_add(viewer_retryauth, viewer); - } else if (viewer->reconnect) { - if (!viewer->withEvents) { - DEBUG_LOG("No domain events, falling back to polling"); - g_timeout_add(500, - viewer_connect_timer, - viewer); - } - - viewer_set_status(viewer, "Waiting for guest domain to re-start"); - } else { - viewer_set_status(viewer, "Guest domain has shutdown"); - gtk_main_quit(); - } -} - -static void viewer_connected(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer) -{ - viewer->connected = TRUE; - viewer_set_status(viewer, "Connected to VNC server"); -} - -static void viewer_initialized(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer) -{ - viewer_set_vnc(viewer); - viewer_set_title(viewer, FALSE); -} - - -static void viewer_disconnected(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer) -{ - if (!viewer->connected) { - viewer_simple_message_dialog(viewer->window, _("Unable to connect to the VNC server %s"), - viewer->vncAddress); - } - viewer_deactivate(viewer); -} - - -static void viewer_vnc_auth_failure(GtkWidget *vnc G_GNUC_UNUSED, const char *reason, VirtViewer *viewer) -{ - GtkWidget *dialog; - int ret; - - dialog = gtk_message_dialog_new(GTK_WINDOW(viewer->window), - GTK_DIALOG_MODAL | - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_YES_NO, - _("Unable to authenticate with VNC server at %s: %s\n" - "Retry connection again?"), - viewer->vncAddress, reason); - - ret = gtk_dialog_run(GTK_DIALOG(dialog)); - - gtk_widget_destroy(dialog); - - if (ret == GTK_RESPONSE_YES) - viewer->authretry = TRUE; - else - viewer->authretry = FALSE; -} - - -static void viewer_vnc_auth_unsupported(GtkWidget *vnc G_GNUC_UNUSED, unsigned int authType, VirtViewer *viewer) -{ - viewer_simple_message_dialog(viewer->window, - _("Unable to authenticate with VNC server at %s\n" - "Unsupported authentication type %d"), - viewer->vncAddress, authType); -} - - -static void viewer_vnc_bell(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer) -{ - gdk_window_beep(GTK_WIDGET(viewer->window)->window); -} - - /* text was actually requested */ static void viewer_vnc_clipboard_copy(GtkClipboard *clipboard G_GNUC_UNUSED, GtkSelectionData *data, @@ -1071,12 +905,11 @@ static void viewer_vnc_clipboard_copy(GtkClipboard *clipboard G_GNUC_UNUSED, gtk_selection_data_set_text(data, viewer->clipboard, -1); } -static void viewer_vnc_server_cut_text(VncDisplay *vnc G_GNUC_UNUSED, - const gchar *text, VirtViewer *viewer) +void viewer_server_cut_text(VirtViewer *viewer, const gchar *text) { GtkClipboard *cb; gsize a, b; - GtkTargetEntry targets[] = { + GtkTargetEntry targets[] = { {g_strdup("UTF8_STRING"), 0, 0}, {g_strdup("COMPOUND_TEXT"), 0, 0}, {g_strdup("TEXT"), 0, 0}, @@ -1101,6 +934,69 @@ static void viewer_vnc_server_cut_text(VncDisplay *vnc G_GNUC_UNUSED, } } +static gboolean viewer_retryauth(gpointer opaque) +{ + VirtViewer *viewer = opaque; + viewer_initial_connect(viewer); + + return FALSE; +} + +static void viewer_deactivate(VirtViewer *viewer) +{ + if (!viewer->active) + return; + + if (viewer->display) + virt_viewer_display_close(viewer->display); + free(viewer->domtitle); + viewer->domtitle = NULL; + + viewer->connected = FALSE; + viewer->active = FALSE; + g_free(viewer->pretty_address); + viewer->pretty_address = NULL; + viewer_set_title(viewer, FALSE); + + if (viewer->authretry) { + viewer->authretry = FALSE; + g_idle_add(viewer_retryauth, viewer); + } else if (viewer->reconnect) { + if (!viewer->withEvents) { + DEBUG_LOG("No domain events, falling back to polling"); + g_timeout_add(500, + viewer_connect_timer, + viewer); + } + + viewer_set_status(viewer, "Waiting for guest domain to re-start"); + } else { + viewer_set_status(viewer, "Guest domain has shutdown"); + gtk_main_quit(); + } +} + +void viewer_connected(VirtViewer *viewer) +{ + viewer->connected = TRUE; + viewer_set_status(viewer, "Connected to graphic server"); +} + +void viewer_initialized(VirtViewer *viewer) +{ + viewer_show_display(viewer); + viewer_set_title(viewer, FALSE); +} + +void viewer_disconnected(VirtViewer *viewer) +{ + if (!viewer->connected) { + viewer_simple_message_dialog(viewer->window, _("Unable to connect to the graphic server %s"), + viewer->pretty_address); + } + viewer_deactivate(viewer); +} + static int viewer_domain_event(virConnectPtr conn G_GNUC_UNUSED, virDomainPtr dom, @@ -1158,13 +1054,18 @@ static int viewer_initial_connect(VirtViewer *viewer) if (info.state == VIR_DOMAIN_SHUTOFF) { viewer_set_status(viewer, "Waiting for guest domain to start"); } else { - if (viewer_activate(viewer, dom) < 0) { + ret = viewer_activate(viewer, dom); + if (ret < 0) { if (viewer->waitvm) { - viewer_set_status(viewer, "Waiting for guest domain to start VNC"); + viewer_set_status(viewer, "Waiting for guest domain to start server"); } else { DEBUG_LOG("Failed to activate viewer"); goto cleanup; } + } else if (ret == 0) { + DEBUG_LOG("Failed to activate viewer"); + ret = -1; + goto cleanup; } } @@ -1197,72 +1098,6 @@ static void viewer_error_func (void *data G_GNUC_UNUSED, virErrorPtr error G_GNU /* nada */ } -static void viewer_init_vnc_display(VirtViewer *viewer) -{ - GtkWidget *notebook; - GtkWidget *align; - - g_return_if_fail(viewer != NULL); - - 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); - - /* - * In auto-resize mode we have things setup so that we always - * automatically resize the top level window to be exactly the - * same size as the VNC desktop, except when it won't fit on - * the local screen, at which point we let it scale down. - * The upshot is, we always want scaling enabled. - * We disable force_size because we want to allow user to - * manually size the widget smaller too - */ - vnc_display_set_force_size(VNC_DISPLAY(viewer->vnc), FALSE); - vnc_display_set_scaling(VNC_DISPLAY(viewer->vnc), TRUE); - - g_signal_connect(viewer->vnc, "vnc-connected", - G_CALLBACK(viewer_connected), viewer); - g_signal_connect(viewer->vnc, "vnc-initialized", - G_CALLBACK(viewer_initialized), viewer); - g_signal_connect(viewer->vnc, "vnc-disconnected", - G_CALLBACK(viewer_disconnected), viewer); - - /* When VNC desktop resizes, we have to resize the containing widget */ - g_signal_connect(viewer->vnc, "vnc-desktop-resize", - G_CALLBACK(viewer_resize_desktop), viewer); - g_signal_connect(viewer->vnc, "vnc-pointer-grab", - G_CALLBACK(viewer_mouse_grab), viewer); - g_signal_connect(viewer->vnc, "vnc-pointer-ungrab", - G_CALLBACK(viewer_mouse_ungrab), viewer); - g_signal_connect(viewer->vnc, "vnc-keyboard-grab", - G_CALLBACK(viewer_key_grab), viewer); - g_signal_connect(viewer->vnc, "vnc-keyboard-ungrab", - G_CALLBACK(viewer_key_ungrab), viewer); - - g_signal_connect(viewer->vnc, "vnc-auth-credential", - G_CALLBACK(viewer_auth_vnc_credentials), &viewer->vncAddress); - g_signal_connect(viewer->vnc, "vnc-auth-failure", - G_CALLBACK(viewer_vnc_auth_failure), viewer); - g_signal_connect(viewer->vnc, "vnc-auth-unsupported", - G_CALLBACK(viewer_vnc_auth_unsupported), viewer); - - g_signal_connect(viewer->vnc, "vnc-bell", - G_CALLBACK(viewer_vnc_bell), viewer); - g_signal_connect(viewer->vnc, "vnc-server-cut-text", - G_CALLBACK(viewer_vnc_server_cut_text), viewer); - - notebook = glade_xml_get_widget(viewer->glade, "notebook"); - align = glade_xml_get_widget(viewer->glade, "vnc-align"); - gtk_container_add(GTK_CONTAINER(align), viewer->vnc); - - if (!viewer->window) { - gtk_container_add(GTK_CONTAINER(viewer->container), GTK_WIDGET(notebook)); - gtk_widget_show_all(viewer->container); - } - - gtk_widget_realize(viewer->vnc); -} - int viewer_start (const char *uri, const char *name, @@ -1276,7 +1111,6 @@ viewer_start (const char *uri, { VirtViewer *viewer; GtkWidget *notebook; - GtkWidget *align; GtkWidget *menu; int cred_types[] = { VIR_CRED_AUTHNAME, VIR_CRED_PASSPHRASE }; @@ -1291,7 +1125,7 @@ viewer_start (const char *uri, viewer = g_new0(VirtViewer, 1); - viewer->active = 0; + viewer->active = FALSE; viewer->autoResize = TRUE; viewer->direct = direct; viewer->waitvm = waitvm; @@ -1347,9 +1181,6 @@ viewer_start (const char *uri, notebook = glade_xml_get_widget(viewer->glade, "notebook"); gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE); - align = glade_xml_get_widget(viewer->glade, "vnc-align"); - g_signal_connect(align, "size-allocate", - G_CALLBACK(viewer_resize_align), viewer); if (container) { viewer->container = container; @@ -1359,7 +1190,7 @@ viewer_start (const char *uri, viewer->container = window; viewer->window = window; g_signal_connect(window, "delete-event", - G_CALLBACK(viewer_shutdown), viewer); + G_CALLBACK(viewer_delete), viewer); gtk_window_set_resizable(GTK_WINDOW(window), TRUE); viewer->accelEnabled = TRUE; accels = gtk_accel_groups_from_object(G_OBJECT(window)); diff --git a/src/viewer.glade b/src/viewer.glade index 569321f..8195ad5 100644 --- a/src/viewer.glade +++ b/src/viewer.glade @@ -309,7 +309,7 @@ - + True 0 0