diff --git a/src/Makefile.am b/src/Makefile.am index dea1514..7e7a9c3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,6 +20,8 @@ virt_viewer_SOURCES = \ virt-viewer-session-vnc.h virt-viewer-session-vnc.c \ virt-viewer-display.h virt-viewer-display.c \ virt-viewer-display-vnc.h virt-viewer-display-vnc.c \ + virt-viewer-notebook.h virt-viewer-notebook.c \ + virt-viewer-window.h virt-viewer-window.c \ view/autoDrawer.c \ view/autoDrawer.h \ view/drawer.c \ diff --git a/src/virt-viewer-app.c b/src/virt-viewer-app.c index 687927c..06502e9 100644 --- a/src/virt-viewer-app.c +++ b/src/virt-viewer-app.c @@ -52,28 +52,15 @@ #include "virt-viewer-app.h" #include "virt-viewer-auth.h" +#include "virt-viewer-window.h" #include "virt-viewer-session.h" #include "virt-viewer-session-vnc.h" #ifdef HAVE_SPICE_GTK #include "virt-viewer-session-spice.h" #endif -#include "view/autoDrawer.h" - gboolean doDebug = FALSE; -/* Signal handlers for main window */ -void virt_viewer_app_menu_view_zoom_out(GtkWidget *menu, VirtViewerApp *self); -void virt_viewer_app_menu_view_zoom_in(GtkWidget *menu, VirtViewerApp *self); -void virt_viewer_app_menu_view_zoom_reset(GtkWidget *menu, VirtViewerApp *self); -void virt_viewer_app_delete(GtkWidget *src, void *dummy, VirtViewerApp *self); -void virt_viewer_app_menu_file_quit(GtkWidget *src, VirtViewerApp *self); -void virt_viewer_app_menu_help_about(GtkWidget *menu, VirtViewerApp *self); -void virt_viewer_app_menu_view_fullscreen(GtkWidget *menu, VirtViewerApp *self); -void virt_viewer_app_menu_view_resize(GtkWidget *menu, VirtViewerApp *self); -void virt_viewer_app_menu_send(GtkWidget *menu, VirtViewerApp *self); -void virt_viewer_app_menu_file_screenshot(GtkWidget *menu, VirtViewerApp *self); - /* Signal handlers for about dialog */ void virt_viewer_app_about_close(GtkWidget *dialog, VirtViewerApp *self); void virt_viewer_app_about_delete(GtkWidget *dialog, void *dummy, VirtViewerApp *self); @@ -98,65 +85,20 @@ static void virt_viewer_app_server_cut_text(VirtViewerSession *session, VirtViewerApp *self); static void virt_viewer_app_bell(VirtViewerSession *session, VirtViewerApp *self); -static void virt_viewer_app_enable_modifiers(VirtViewerApp *self); -static void virt_viewer_app_disable_modifiers(VirtViewerApp *self); -static void virt_viewer_app_resize_main_window(VirtViewerApp *self); -static void virt_viewer_app_update_title(VirtViewerApp *self); static void virt_viewer_app_channel_open(VirtViewerSession *session, VirtViewerSessionChannel *channel, VirtViewerApp *self); -static void virt_viewer_app_quit(VirtViewerApp *self); static void virt_viewer_app_update_pretty_address(VirtViewerApp *self); -#if GTK_CHECK_VERSION(3, 0, 0) -#define GDK_Control_L GDK_KEY_Control_L -#define GDK_Alt_L GDK_KEY_Alt_L -#define GDK_Delete GDK_KEY_Delete -#define GDK_BackSpace GDK_KEY_BackSpace -#define GDK_Print GDK_KEY_Print -#define GDK_F1 GDK_KEY_F1 -#define GDK_F2 GDK_KEY_F2 -#define GDK_F3 GDK_KEY_F3 -#define GDK_F4 GDK_KEY_F4 -#define GDK_F5 GDK_KEY_F5 -#define GDK_F6 GDK_KEY_F6 -#define GDK_F7 GDK_KEY_F7 -#define GDK_F8 GDK_KEY_F8 -#define GDK_F9 GDK_KEY_F9 -#define GDK_F10 GDK_KEY_F10 -#define GDK_F11 GDK_KEY_F11 -#define GDK_F12 GDK_KEY_F12 -#endif - -enum menuNums { - FILE_MENU, - VIEW_MENU, - SEND_KEY_MENU, - HELP_MENU, - LAST_MENU // sentinel -}; - struct _VirtViewerAppPrivate { - GtkBuilder *builder; - GtkWidget *window; + VirtViewerWindow *main_window; + GtkWidget *main_notebook; GtkWidget *container; - GtkWidget *notebook; - GtkWidget *status; - GtkWidget *toolbar; - GtkWidget *layout; - GtkWidget *display; - - gboolean accelEnabled; - GValue accelSetting; - GSList *accelList; - int accelMenuSig[LAST_MENU]; + GHashTable *windows; gchar *clipboard; - gint zoomlevel; - gboolean autoResize; - gboolean fullscreen; gboolean direct; gboolean verbose; gboolean authretry; @@ -174,43 +116,10 @@ struct _VirtViewerAppPrivate { char *user; /* ssh */ char *transport; char *pretty_address; - gchar *subtitle; gchar *guest_name; gboolean grabbed; }; -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]; - 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"}, -}; - G_DEFINE_ABSTRACT_TYPE(VirtViewerApp, virt_viewer_app, G_TYPE_OBJECT) #define GET_PRIVATE(o) \ @@ -218,7 +127,6 @@ G_DEFINE_ABSTRACT_TYPE(VirtViewerApp, virt_viewer_app, G_TYPE_OBJECT) enum { PROP_0, - PROP_SUBTITLE, PROP_VERBOSE, PROP_CONTAINER, PROP_SESSION, @@ -236,7 +144,7 @@ virt_viewer_app_simple_message_dialog(VirtViewerApp *self, const char *fmt, ...) { g_return_if_fail(VIRT_VIEWER_IS_APP(self)); - GtkWindow *window = GTK_WINDOW(self->priv->window); + GtkWindow *window = GTK_WINDOW(virt_viewer_window_get_window(self->priv->main_window)); GtkWidget *dialog; char *msg; va_list vargs; @@ -262,219 +170,13 @@ virt_viewer_app_simple_message_dialog(VirtViewerApp *self, g_free(msg); } - -/* - * This code attempts to resize the top level window to be large enough - * 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 -virt_viewer_app_resize_main_window(VirtViewerApp *self) +static VirtViewerWindow* +virt_viewer_app_window_new(VirtViewerApp *self, GtkWidget *container) { - GdkRectangle fullscreen; - GdkScreen *screen; - int width, height; - double desktopAspect; - double screenAspect; - guint desktopWidth; - guint desktopHeight; - VirtViewerAppPrivate *priv = self->priv; - - DEBUG_LOG("Preparing main window resize"); - if (!priv->active) { - DEBUG_LOG("Skipping inactive resize"); - return; - } - - gtk_window_resize(GTK_WINDOW(priv->window), 1, 1); - - virt_viewer_display_get_desktop_size(VIRT_VIEWER_DISPLAY(priv->display), - &desktopWidth, &desktopHeight); - - screen = gtk_widget_get_screen(priv->window); - gdk_screen_get_monitor_geometry(screen, - gdk_screen_get_monitor_at_window - (screen, gtk_widget_get_window(priv->window)), - &fullscreen); - - desktopAspect = (double)desktopWidth / (double)desktopHeight; - screenAspect = (double)(fullscreen.width - 128) / (double)(fullscreen.height - 128); - - if ((desktopWidth > (fullscreen.width - 128)) || - (desktopHeight > (fullscreen.height - 128))) { - /* Doesn't fit native res, so go as large as possible - maintaining aspect ratio */ - if (screenAspect > desktopAspect) { - width = desktopHeight * desktopAspect; - height = desktopHeight; - } else { - width = desktopWidth; - height = desktopWidth / desktopAspect; - } - } else { - width = desktopWidth; - height = desktopHeight; - } - - DEBUG_LOG("Decided todo %dx%d (desktop is %dx%d, fullscreen is %dx%d", - width, height, desktopWidth, desktopHeight, - fullscreen.width, fullscreen.height); - - virt_viewer_display_set_desktop_size(VIRT_VIEWER_DISPLAY(priv->display), - width, height); -} - -static void -virt_viewer_app_desktop_resize(VirtViewerDisplay *display G_GNUC_UNUSED, - VirtViewerApp *self) -{ - VirtViewerAppPrivate *priv = self->priv; - if (priv->autoResize && priv->window && !priv->fullscreen) - virt_viewer_app_resize_main_window(self); -} - - -void -virt_viewer_app_menu_view_zoom_out(GtkWidget *menu G_GNUC_UNUSED, - VirtViewerApp *self) -{ - VirtViewerAppPrivate *priv = self->priv; - gtk_window_resize(GTK_WINDOW(priv->window), 1, 1); - if (priv->zoomlevel > 10) - priv->zoomlevel -= 10; - - if (priv->display) - virt_viewer_display_set_zoom_level(VIRT_VIEWER_DISPLAY(priv->display), priv->zoomlevel); -} - -void -virt_viewer_app_menu_view_zoom_in(GtkWidget *menu G_GNUC_UNUSED, - VirtViewerApp *self) -{ - VirtViewerAppPrivate *priv = self->priv; - gtk_window_resize(GTK_WINDOW(priv->window), 1, 1); - if (priv->zoomlevel < 400) - priv->zoomlevel += 10; - - if (priv->display) - virt_viewer_display_set_zoom_level(VIRT_VIEWER_DISPLAY(priv->display), priv->zoomlevel); -} - -void -virt_viewer_app_menu_view_zoom_reset(GtkWidget *menu G_GNUC_UNUSED, - VirtViewerApp *self) -{ - VirtViewerAppPrivate *priv = self->priv; - gtk_window_resize(GTK_WINDOW(priv->window), 1, 1); - priv->zoomlevel = 100; - - if (priv->display) - virt_viewer_display_set_zoom_level(VIRT_VIEWER_DISPLAY(priv->display), priv->zoomlevel); -} - -void -virt_viewer_app_update_title(VirtViewerApp *self) -{ - VirtViewerAppPrivate *priv = self->priv; - char *title; - const char *subtitle; - - if (!priv->window) - return; - - if (priv->grabbed) - subtitle = "(Press Ctrl+Alt to release pointer) "; - else - subtitle = ""; - - if (priv->subtitle) - title = g_strdup_printf("%s%s - Virt Viewer", - subtitle, priv->subtitle); - else - title = g_strdup("Virt Viewer"); - - gtk_window_set_title(GTK_WINDOW(priv->window), title); - - g_free(title); -} - -static gboolean -virt_viewer_app_ignore_accel(GtkWidget *menu G_GNUC_UNUSED, - VirtViewerApp *self G_GNUC_UNUSED) -{ - /* ignore accelerator */ - return TRUE; -} - - -void -virt_viewer_app_disable_modifiers(VirtViewerApp *self) -{ - GtkSettings *settings = gtk_settings_get_default(); - VirtViewerAppPrivate *priv = self->priv; - GValue empty; - GSList *accels; - int i; - - if (!priv->window) - return; - - if (!priv->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", &priv->accelSetting); - g_object_set_property(G_OBJECT(settings), "gtk-menu-bar-accel", &empty); - - /* This stops global accelerators like Ctrl+Q == Quit */ - for (accels = priv->accelList ; accels ; accels = accels->next) { - gtk_window_remove_accel_group(GTK_WINDOW(priv->window), accels->data); - } - - /* This stops menu bar shortcuts like Alt+F == File */ - for (i = 0 ; i < LAST_MENU ; i++) { - GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, menuNames[i])); - priv->accelMenuSig[i] = - g_signal_connect(menu, "mnemonic-activate", - G_CALLBACK(virt_viewer_app_ignore_accel), self); - } - - priv->accelEnabled = FALSE; -} - - -void -virt_viewer_app_enable_modifiers(VirtViewerApp *self) -{ - GtkSettings *settings = gtk_settings_get_default(); - VirtViewerAppPrivate *priv = self->priv; - GSList *accels; - int i; - - if (!priv->window) - return; - - if (priv->accelEnabled) - return; - - /* This allows F10 activating menu bar */ - g_object_set_property(G_OBJECT(settings), "gtk-menu-bar-accel", &priv->accelSetting); - - /* This allows global accelerators like Ctrl+Q == Quit */ - for (accels = priv->accelList ; accels ; accels = accels->next) { - gtk_window_add_accel_group(GTK_WINDOW(priv->window), accels->data); - } - - /* This allows menu bar shortcuts like Alt+F == File */ - for (i = 0 ; i < LAST_MENU ; i++) { - GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, menuNames[i])); - g_signal_handler_disconnect(menu, priv->accelMenuSig[i]); - } - - priv->accelEnabled = TRUE; + return g_object_new(VIRT_VIEWER_TYPE_WINDOW, + "app", self, + "container", container, + NULL); } void @@ -488,180 +190,6 @@ virt_viewer_app_quit(VirtViewerApp *self) gtk_main_quit(); } -void -virt_viewer_app_delete(GtkWidget *src G_GNUC_UNUSED, - void *dummy G_GNUC_UNUSED, - VirtViewerApp *self) -{ - virt_viewer_app_quit(self); -} - -void -virt_viewer_app_menu_file_quit(GtkWidget *src G_GNUC_UNUSED, - VirtViewerApp *self) -{ - virt_viewer_app_quit(self); -} - - -static void -virt_viewer_app_leave_fullscreen(VirtViewerApp *self) -{ - VirtViewerAppPrivate *priv = self->priv; - GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "top-menu")); - - if (!priv->fullscreen) - return; - priv->fullscreen = FALSE; - ViewAutoDrawer_SetActive(VIEW_AUTODRAWER(priv->layout), FALSE); - gtk_widget_show(menu); - gtk_widget_hide(priv->toolbar); - gtk_window_unfullscreen(GTK_WINDOW(priv->window)); - if (priv->autoResize) - virt_viewer_app_resize_main_window(self); -} - -static void -virt_viewer_app_enter_fullscreen(VirtViewerApp *self) -{ - VirtViewerAppPrivate *priv = self->priv; - GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "top-menu")); - - if (priv->fullscreen) - return; - priv->fullscreen = TRUE; - gtk_widget_hide(menu); - gtk_window_fullscreen(GTK_WINDOW(priv->window)); - gtk_widget_show(priv->toolbar); - ViewAutoDrawer_SetActive(VIEW_AUTODRAWER(priv->layout), TRUE); - ViewAutoDrawer_Close(VIEW_AUTODRAWER(priv->layout)); -} - -static gboolean -window_state_cb(GtkWidget *widget G_GNUC_UNUSED, GdkEventWindowState *event, - gpointer data) -{ - VirtViewerApp *self = data; - - if (!(event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)) - return TRUE; - - if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) - virt_viewer_app_enter_fullscreen(self); - else - virt_viewer_app_leave_fullscreen(self); - - return TRUE; -} - -static void -virt_viewer_app_toolbar_leave_fullscreen(GtkWidget *button G_GNUC_UNUSED, - VirtViewerApp *self) -{ - VirtViewerAppPrivate *priv = self->priv; - GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-view-fullscreen")); - - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), FALSE); - virt_viewer_app_leave_fullscreen(self); -} - - -void -virt_viewer_app_menu_view_fullscreen(GtkWidget *menu, - VirtViewerApp *self) -{ - VirtViewerAppPrivate *priv = self->priv; - if (!priv->window) - return; - - if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu))) { - virt_viewer_app_enter_fullscreen(self); - } else { - virt_viewer_app_leave_fullscreen(self); - } -} - -void -virt_viewer_app_menu_view_resize(GtkWidget *menu, - VirtViewerApp *self) -{ - VirtViewerAppPrivate *priv = self->priv; - - if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu))) { - priv->autoResize = TRUE; - if (!priv->fullscreen) - virt_viewer_app_resize_main_window(self); - } else { - priv->autoResize = FALSE; - } -} - -void -virt_viewer_app_menu_send(GtkWidget *menu G_GNUC_UNUSED, - VirtViewerApp *self) -{ - int i; - GtkWidget *label = gtk_bin_get_child(GTK_BIN(menu)); - const char *text = gtk_label_get_label(GTK_LABEL(label)); - VirtViewerAppPrivate *priv = self->priv; - - 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(VIRT_VIEWER_DISPLAY(priv->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 -virt_viewer_app_save_screenshot(VirtViewerApp *self, - const char *file) -{ - VirtViewerAppPrivate *priv = self->priv; - GdkPixbuf *pix = virt_viewer_display_get_pixbuf(VIRT_VIEWER_DISPLAY(priv->display)); - - gdk_pixbuf_save(pix, file, "png", NULL, - "tEXt::Generator App", PACKAGE, NULL); - gdk_pixbuf_unref(pix); -} - - -void -virt_viewer_app_menu_file_screenshot(GtkWidget *menu G_GNUC_UNUSED, - VirtViewerApp *self) -{ - GtkWidget *dialog; - VirtViewerAppPrivate *priv = self->priv; - - g_return_if_fail(priv->display != NULL); - - dialog = gtk_file_chooser_dialog_new ("Save screenshot", - NULL, - GTK_FILE_CHOOSER_ACTION_SAVE, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, - NULL); - gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); - - //gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), default_folder_for_saving); - //gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), "Screenshot"); - - if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { - char *filename; - - filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); - virt_viewer_app_save_screenshot(self, filename); - g_free (filename); - } - - gtk_widget_destroy (dialog); -} - void virt_viewer_app_about_close(GtkWidget *dialog, VirtViewerApp *self G_GNUC_UNUSED) @@ -679,22 +207,6 @@ virt_viewer_app_about_delete(GtkWidget *dialog, gtk_widget_destroy(dialog); } -void -virt_viewer_app_menu_help_about(GtkWidget *menu G_GNUC_UNUSED, - VirtViewerApp *self) -{ - GtkBuilder *about = virt_viewer_util_load_ui("virt-viewer-about.xml"); - - GtkWidget *dialog = GTK_WIDGET(gtk_builder_get_object(about, "about")); - gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), VERSION); - - gtk_builder_connect_signals(about, self); - - gtk_widget_show_all(dialog); - - g_object_unref(G_OBJECT(about)); -} - #if defined(HAVE_SOCKETPAIR) && defined(HAVE_FORK) static int @@ -812,110 +324,101 @@ virt_viewer_app_trace(VirtViewerApp *self, } } - void virt_viewer_app_set_status(VirtViewerApp *self, const char *text) { g_return_if_fail(VIRT_VIEWER_IS_APP(self)); - VirtViewerAppPrivate *priv = self->priv; - gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->notebook), 0); - gtk_label_set_text(GTK_LABEL(priv->status), text); + virt_viewer_notebook_show_status(VIRT_VIEWER_NOTEBOOK(self->priv->main_notebook), text); } - -static void -virt_viewer_app_show_display(VirtViewerApp *self) +static void update_title(gpointer key G_GNUC_UNUSED, + gpointer value, + gpointer user_data G_GNUC_UNUSED) { - g_return_if_fail(self != NULL); - - VirtViewerAppPrivate *priv = self->priv; - g_return_if_fail(priv->display != NULL); - - gtk_widget_grab_focus(gtk_bin_get_child(GTK_BIN(priv->display))); - gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->notebook), 1); + virt_viewer_window_update_title(VIRT_VIEWER_WINDOW(value)); } static void -virt_viewer_app_pointer_grab(VirtViewerDisplay *display G_GNUC_UNUSED, - VirtViewerApp *self) +virt_viewer_app_update_title(VirtViewerApp *self) { - VirtViewerAppPrivate *priv = self->priv; + g_hash_table_foreach(self->priv->windows, update_title, NULL); +} - priv->grabbed = TRUE; - virt_viewer_app_update_title(self); +static VirtViewerWindow * +virt_viewer_app_get_nth_window(VirtViewerApp *self, gint nth) +{ + return g_hash_table_lookup(self->priv->windows, &nth); +} + +static gboolean +virt_viewer_app_remove_nth_window(VirtViewerApp *self, gint nth) +{ + gboolean removed; + + g_return_val_if_fail(nth != 0, FALSE); + removed = g_hash_table_remove(self->priv->windows, &nth); + g_warn_if_fail(removed); + + return removed; } static void -virt_viewer_app_pointer_ungrab(VirtViewerDisplay *display G_GNUC_UNUSED, - VirtViewerApp *self) +virt_viewer_app_set_nth_window(VirtViewerApp *self, gint nth, VirtViewerWindow *win) { - VirtViewerAppPrivate *priv = self->priv; + gint *key; - priv->grabbed = FALSE; - virt_viewer_app_update_title(self); + g_return_if_fail(virt_viewer_app_get_nth_window(self, nth) == NULL); + key = g_malloc(sizeof(gint)); + *key = nth; + g_hash_table_insert(self->priv->windows, key, win); } -static void -virt_viewer_app_keyboard_grab(VirtViewerDisplay *display G_GNUC_UNUSED, - VirtViewerApp *self) -{ - virt_viewer_app_disable_modifiers(self); -} - -static void -virt_viewer_app_keyboard_ungrab(VirtViewerDisplay *display G_GNUC_UNUSED, - VirtViewerApp *self) -{ - virt_viewer_app_enable_modifiers(self); -} - - static void virt_viewer_app_display_added(VirtViewerSession *session G_GNUC_UNUSED, VirtViewerDisplay *display, VirtViewerApp *self) { VirtViewerAppPrivate *priv = self->priv; + VirtViewerWindow *window; + gint nth; - /* XXX multihead */ - if (priv->display) - return; - priv->display = GTK_WIDGET(display); + g_object_get(display, "nth-display", &nth, NULL); + if (nth == 0) { + window = priv->main_window; + } else { + if (priv->container) { + g_warning("multi-head not yet supported within container"); + return; + } - g_signal_connect(priv->display, "display-pointer-grab", - G_CALLBACK(virt_viewer_app_pointer_grab), self); - g_signal_connect(priv->display, "display-pointer-ungrab", - G_CALLBACK(virt_viewer_app_pointer_ungrab), self); - g_signal_connect(priv->display, "display-keyboard-grab", - G_CALLBACK(virt_viewer_app_keyboard_grab), self); - g_signal_connect(priv->display, "display-keyboard-ungrab", - G_CALLBACK(virt_viewer_app_keyboard_ungrab), self); + g_return_if_fail(virt_viewer_app_get_nth_window(self, nth) == NULL); + window = virt_viewer_app_window_new(self, NULL); + /* TODO: track fullscreen state */ + gtk_widget_show_all(GTK_WIDGET(virt_viewer_window_get_window(window))); + virt_viewer_app_set_nth_window(self, nth, window); + } - g_signal_connect(priv->display, "display-desktop-resize", - G_CALLBACK(virt_viewer_app_desktop_resize), self); - - gtk_notebook_append_page(GTK_NOTEBOOK(priv->notebook), priv->display, NULL); - if (gtk_bin_get_child(GTK_BIN(priv->display))) - gtk_widget_realize(GTK_WIDGET(gtk_bin_get_child(GTK_BIN(priv->display)))); - gtk_widget_show_all(priv->display); + virt_viewer_window_set_display(window, display); } + static void virt_viewer_app_display_removed(VirtViewerSession *session G_GNUC_UNUSED, VirtViewerDisplay *display, VirtViewerApp *self) { - VirtViewerAppPrivate *priv = self->priv; - - /* XXX multihead */ - if (priv->display != GTK_WIDGET(display)) - return; + VirtViewerWindow *win = NULL; + gint nth; gtk_widget_hide(GTK_WIDGET(display)); - gtk_notebook_remove_page(GTK_NOTEBOOK(priv->notebook), 1); - priv->display = NULL; + g_object_get(display, "nth-display", &nth, NULL); + win = virt_viewer_app_get_nth_window(self, nth); + virt_viewer_window_set_display(win, NULL); + + if (nth != 0) + virt_viewer_app_remove_nth_window(self, nth); } int @@ -1112,7 +615,7 @@ static void virt_viewer_app_bell(VirtViewerSession *session G_GNUC_UNUSED, { VirtViewerAppPrivate *priv = self->priv; - gdk_window_beep(gtk_widget_get_window(GTK_WIDGET(priv->window))); + gdk_window_beep(gtk_widget_get_window(GTK_WIDGET(virt_viewer_window_get_window(priv->main_window)))); } @@ -1237,7 +740,7 @@ static void virt_viewer_app_initialized(VirtViewerSession *session G_GNUC_UNUSED, VirtViewerApp *self) { - virt_viewer_app_show_display(self); + virt_viewer_notebook_show_display(VIRT_VIEWER_NOTEBOOK(self->priv->main_notebook)); virt_viewer_app_update_title(self); } @@ -1264,7 +767,7 @@ static void virt_viewer_app_auth_refused(VirtViewerSession *session G_GNUC_UNUSE int ret; VirtViewerAppPrivate *priv = self->priv; - dialog = gtk_message_dialog_new(GTK_WINDOW(priv->window), + dialog = gtk_message_dialog_new(virt_viewer_window_get_window(priv->main_window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, @@ -1295,45 +798,6 @@ static void virt_viewer_app_auth_failed(VirtViewerSession *session G_GNUC_UNUSED priv->pretty_address, msg); } -static void -virt_viewer_app_toolbar_setup(VirtViewerApp *self) -{ - GtkWidget *button; - VirtViewerAppPrivate *priv = self->priv; - - priv->toolbar = gtk_toolbar_new(); - gtk_toolbar_set_show_arrow(GTK_TOOLBAR(priv->toolbar), FALSE); - gtk_widget_set_no_show_all(priv->toolbar, TRUE); - gtk_toolbar_set_style(GTK_TOOLBAR(priv->toolbar), GTK_TOOLBAR_BOTH_HORIZ); - - /* Close connection */ - button = GTK_WIDGET(gtk_tool_button_new_from_stock(GTK_STOCK_CLOSE)); - gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), _("Disconnect")); - gtk_widget_show(GTK_WIDGET(button)); - gtk_toolbar_insert(GTK_TOOLBAR(priv->toolbar), GTK_TOOL_ITEM (button), 0); - g_signal_connect(button, "clicked", G_CALLBACK(gtk_main_quit), NULL); - - /* Leave fullscreen */ - button = GTK_WIDGET(gtk_tool_button_new_from_stock(GTK_STOCK_LEAVE_FULLSCREEN)); - gtk_tool_button_set_label(GTK_TOOL_BUTTON(button), _("Leave fullscreen")); - gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), _("Leave fullscreen")); - gtk_tool_item_set_is_important(GTK_TOOL_ITEM(button), TRUE); - gtk_widget_show(GTK_WIDGET(button)); - gtk_toolbar_insert(GTK_TOOLBAR(priv->toolbar), GTK_TOOL_ITEM(button), 0); - g_signal_connect(button, "clicked", G_CALLBACK(virt_viewer_app_toolbar_leave_fullscreen), self); - - priv->layout = ViewAutoDrawer_New(); - - ViewAutoDrawer_SetActive(VIEW_AUTODRAWER(priv->layout), FALSE); - ViewOvBox_SetOver(VIEW_OV_BOX(priv->layout), priv->toolbar); - ViewOvBox_SetUnder(VIEW_OV_BOX(priv->layout), priv->notebook); - ViewAutoDrawer_SetOffset(VIEW_AUTODRAWER(priv->layout), -1); - ViewAutoDrawer_SetFill(VIEW_AUTODRAWER(priv->layout), FALSE); - ViewAutoDrawer_SetOverlapPixels(VIEW_AUTODRAWER(priv->layout), 1); - ViewAutoDrawer_SetNoOverlapPixels(VIEW_AUTODRAWER(priv->layout), 0); -} - - static void virt_viewer_app_get_property (GObject *object, guint property_id, GValue *value G_GNUC_UNUSED, GParamSpec *pspec) @@ -1343,10 +807,6 @@ virt_viewer_app_get_property (GObject *object, guint property_id, VirtViewerAppPrivate *priv = self->priv; switch (property_id) { - case PROP_SUBTITLE: - g_value_set_string(value, priv->subtitle); - break; - case PROP_VERBOSE: g_value_set_boolean(value, priv->verbose); break; @@ -1377,11 +837,6 @@ virt_viewer_app_set_property (GObject *object, guint property_id, VirtViewerAppPrivate *priv = self->priv; switch (property_id) { - case PROP_SUBTITLE: - g_free(priv->subtitle); - priv->subtitle = g_value_dup_string(value); - break; - case PROP_VERBOSE: priv->verbose = g_value_get_boolean(value); break; @@ -1407,14 +862,21 @@ virt_viewer_app_dispose (GObject *object) VirtViewerApp *self = VIRT_VIEWER_APP(object); VirtViewerAppPrivate *priv = self->priv; + if (priv->windows) { + g_hash_table_unref(priv->windows); + priv->windows = NULL; + } + + if (priv->main_window) { + g_object_unref(priv->main_window); + priv->main_window = NULL; + } + if (priv->container) { g_object_unref(priv->container); priv->container = NULL; } - g_free(priv->subtitle); - priv->subtitle = NULL; - virt_viewer_app_free_connect_info(self); G_OBJECT_CLASS (virt_viewer_app_parent_class)->dispose (object); @@ -1424,52 +886,17 @@ static gboolean virt_viewer_app_default_start(VirtViewerApp *self, gboolean fullscreen) { VirtViewerAppPrivate *priv; - GtkWidget *menu; - GdkColor color; - + GtkWindow *win; priv = self->priv; - if (!priv->container) { - priv->builder = virt_viewer_util_load_ui("virt-viewer.xml"); - - menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-view-resize")); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), TRUE); - - gtk_builder_connect_signals(priv->builder, self); - } - - if (priv->container) { - gtk_box_pack_end(GTK_BOX(priv->container), priv->notebook, TRUE, TRUE, 0); - gtk_widget_show_all(GTK_WIDGET(priv->container)); - } else { - GtkWidget *vbox = GTK_WIDGET(gtk_builder_get_object(priv->builder, "viewer-box")); - virt_viewer_app_toolbar_setup(self); - - gtk_box_pack_end(GTK_BOX(vbox), priv->layout, TRUE, TRUE, 0); - gdk_color_parse("black", &color); - gtk_widget_modify_bg(priv->layout, GTK_STATE_NORMAL, &color); - gdk_color_parse("white", &color); - gtk_widget_modify_fg(priv->status, GTK_STATE_NORMAL, &color); - - GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(priv->builder, "viewer")); - GSList *accels; - priv->container = window; - priv->window = window; - virt_viewer_app_update_title(self); - gtk_window_set_resizable(GTK_WINDOW(window), TRUE); -#if GTK_CHECK_VERSION(3, 0, 0) - gtk_window_set_has_resize_grip(GTK_WINDOW(window), FALSE); -#endif - priv->accelEnabled = TRUE; - accels = gtk_accel_groups_from_object(G_OBJECT(window)); - for ( ; accels ; accels = accels->next) { - priv->accelList = g_slist_append(priv->accelList, accels->data); - g_object_ref(G_OBJECT(accels->data)); - } - g_signal_connect(G_OBJECT(window), "window-state-event", G_CALLBACK(window_state_cb), self); + win = virt_viewer_window_get_window(priv->main_window); + if (win) { if (fullscreen) - gtk_window_fullscreen(GTK_WINDOW(window)); - gtk_widget_show_all(priv->window); + gtk_window_fullscreen(win); + gtk_widget_show_all(GTK_WIDGET(win)); + } else { + gtk_box_pack_end(GTK_BOX(priv->container), priv->main_notebook, TRUE, TRUE, 0); + gtk_widget_show_all(GTK_WIDGET(priv->main_notebook)); } return TRUE; @@ -1488,6 +915,33 @@ gboolean virt_viewer_app_start(VirtViewerApp *self, gboolean fullscreen) return self->priv->started; } +static void +virt_viewer_app_init (VirtViewerApp *self) +{ + self->priv = GET_PRIVATE(self); + self->priv->windows = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_object_unref); +} + +static GObject * +virt_viewer_app_constructor (GType gtype, + guint n_properties, + GObjectConstructParam *properties) +{ + GObject *obj; + VirtViewerApp *self; + VirtViewerAppPrivate *priv; + + obj = G_OBJECT_CLASS (virt_viewer_app_parent_class)->constructor (gtype, n_properties, properties); + self = VIRT_VIEWER_APP(obj); + priv = self->priv; + + priv->main_window = virt_viewer_app_window_new(self, priv->container); + priv->main_notebook = GTK_WIDGET(virt_viewer_window_get_notebook(priv->main_window)); + virt_viewer_app_set_nth_window(self, 0, priv->main_window); + + return obj; +} + static void virt_viewer_app_class_init (VirtViewerAppClass *klass) { @@ -1495,6 +949,7 @@ virt_viewer_app_class_init (VirtViewerAppClass *klass) g_type_class_add_private (klass, sizeof (VirtViewerAppPrivate)); + object_class->constructor = virt_viewer_app_constructor; object_class->get_property = virt_viewer_app_get_property; object_class->set_property = virt_viewer_app_set_property; object_class->dispose = virt_viewer_app_dispose; @@ -1503,16 +958,6 @@ virt_viewer_app_class_init (VirtViewerAppClass *klass) klass->initial_connect = virt_viewer_app_default_initial_connect; klass->deactivated = virt_viewer_app_default_deactivated; - g_object_class_install_property(object_class, - PROP_SUBTITLE, - g_param_spec_string("subtitle", - "Subtitle", - "Window subtitle", - "", - G_PARAM_READABLE | - G_PARAM_WRITABLE | - G_PARAM_STATIC_STRINGS)); - g_object_class_install_property(object_class, PROP_VERBOSE, g_param_spec_boolean("verbose", @@ -1555,34 +1000,6 @@ virt_viewer_app_class_init (VirtViewerAppClass *klass) } -static void -virt_viewer_app_init (VirtViewerApp *self) -{ - VirtViewerAppPrivate *priv; - - self->priv = GET_PRIVATE(self); - priv = self->priv; - - priv->autoResize = TRUE; - g_value_init(&priv->accelSetting, G_TYPE_STRING); - priv->status = gtk_label_new(""); - priv->notebook = gtk_notebook_new(); - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(priv->notebook), FALSE); - gtk_notebook_set_show_border(GTK_NOTEBOOK(priv->notebook), FALSE); - - gtk_notebook_append_page(GTK_NOTEBOOK(priv->notebook), priv->status, NULL); - -} - -void -virt_viewer_app_set_zoom_level(VirtViewerApp *self, gint zoom_level) -{ - g_return_if_fail(VIRT_VIEWER_IS_APP(self)); - - /* FIXME: turn into a dynamic property */ - self->priv->zoomlevel = zoom_level; -} - void virt_viewer_app_set_direct(VirtViewerApp *self, gboolean direct) { @@ -1660,6 +1077,12 @@ virt_viewer_app_free_connect_info(VirtViewerApp *self) virt_viewer_app_set_connect_info(self, NULL, NULL, NULL, NULL, NULL, NULL, 0); } +VirtViewerWindow* +virt_viewer_app_get_main_window(VirtViewerApp *self) +{ + return self->priv->main_window; +} + /* * Local variables: * c-indent-level: 8 diff --git a/src/virt-viewer-app.h b/src/virt-viewer-app.h index eccfe04..8b999b5 100644 --- a/src/virt-viewer-app.h +++ b/src/virt-viewer-app.h @@ -25,6 +25,7 @@ #include #include "virt-viewer-util.h" +#include "virt-viewer-window.h" G_BEGIN_DECLS @@ -52,6 +53,8 @@ typedef struct { GType virt_viewer_app_get_type (void); +void virt_viewer_app_quit(VirtViewerApp *self); +VirtViewerWindow* virt_viewer_app_get_main_window(VirtViewerApp *self); void virt_viewer_app_set_debug(gboolean debug); gboolean virt_viewer_app_start(VirtViewerApp *app, gboolean fullscreen); void virt_viewer_app_trace(VirtViewerApp *self, const char *fmt, ...); diff --git a/src/virt-viewer-display-spice.c b/src/virt-viewer-display-spice.c index 6d0e0a0..c501b3f 100644 --- a/src/virt-viewer-display-spice.c +++ b/src/virt-viewer-display-spice.c @@ -121,14 +121,18 @@ virt_viewer_display_spice_new(SpiceChannel *channel, SpiceDisplay *display) { VirtViewerDisplaySpice *self; + gint channelid; - self = g_object_new(VIRT_VIEWER_TYPE_DISPLAY_SPICE, NULL); + g_return_val_if_fail(SPICE_IS_DISPLAY_CHANNEL(channel), NULL); + g_return_val_if_fail(SPICE_IS_DISPLAY(display), NULL); - self->priv->channel = channel; - self->priv->display = display; + g_object_get(channel, "channel-id", &channelid, NULL); - g_object_ref(channel); - g_object_ref(display); + self = g_object_new(VIRT_VIEWER_TYPE_DISPLAY_SPICE, + "nth-display", channelid, + NULL); + self->priv->channel = g_object_ref(channel); + self->priv->display = g_object_ref(display); g_signal_connect(channel, "display-primary-create", G_CALLBACK(virt_viewer_display_spice_primary_create), self); diff --git a/src/virt-viewer-display.c b/src/virt-viewer-display.c index b4770f5..a21913f 100644 --- a/src/virt-viewer-display.c +++ b/src/virt-viewer-display.c @@ -38,6 +38,7 @@ struct _VirtViewerDisplayPrivate guint desktopHeight; guint zoom_level; gboolean zoom; + gint nth_display; }; static void virt_viewer_display_size_request(GtkWidget *widget, @@ -68,6 +69,7 @@ enum { PROP_DESKTOP_WIDTH, PROP_DESKTOP_HEIGHT, + PROP_NTH_DISPLAY, PROP_ZOOM, PROP_ZOOM_LEVEL, }; @@ -124,6 +126,17 @@ virt_viewer_display_class_init(VirtViewerDisplayClass *class) 100, G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_NTH_DISPLAY, + g_param_spec_int("nth-display", + "Nth display", + "Nth display", + 0, + G_MAXINT32, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_signal_new("display-pointer-grab", G_OBJECT_CLASS_TYPE(object_class), @@ -218,7 +231,9 @@ virt_viewer_display_set_property(GObject *object, priv->desktopWidth, g_value_get_int(value)); break; - + case PROP_NTH_DISPLAY: + priv->nth_display = g_value_get_int(value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -241,6 +256,9 @@ virt_viewer_display_get_property(GObject *object, case PROP_DESKTOP_HEIGHT: g_value_set_int(value, priv->desktopHeight); break; + case PROP_NTH_DISPLAY: + g_value_set_int(value, priv->nth_display); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); diff --git a/src/virt-viewer-notebook.c b/src/virt-viewer-notebook.c new file mode 100644 index 0000000..ab51881 --- /dev/null +++ b/src/virt-viewer-notebook.c @@ -0,0 +1,135 @@ +/* + * 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 "virt-viewer-notebook.h" +#include "virt-viewer-util.h" + +G_DEFINE_TYPE (VirtViewerNotebook, virt_viewer_notebook, GTK_TYPE_NOTEBOOK) + +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), VIRT_VIEWER_TYPE_NOTEBOOK, VirtViewerNotebookPrivate)) + +typedef struct _VirtViewerNotebookPrivate VirtViewerNotebookPrivate; + +struct _VirtViewerNotebookPrivate { + GtkWidget *status; +}; + +static void +virt_viewer_notebook_get_property (GObject *object, guint property_id, + GValue *value G_GNUC_UNUSED, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +virt_viewer_notebook_set_property (GObject *object, guint property_id, + const GValue *value G_GNUC_UNUSED, GParamSpec *pspec) +{ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +virt_viewer_notebook_dispose (GObject *object) +{ + G_OBJECT_CLASS (virt_viewer_notebook_parent_class)->dispose (object); +} + +static void +virt_viewer_notebook_class_init (VirtViewerNotebookClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (VirtViewerNotebookPrivate)); + + object_class->get_property = virt_viewer_notebook_get_property; + object_class->set_property = virt_viewer_notebook_set_property; + object_class->dispose = virt_viewer_notebook_dispose; +} + +static void +virt_viewer_notebook_init (VirtViewerNotebook *self) +{ + VirtViewerNotebookPrivate *priv; + GdkColor color; + + self->priv = GET_PRIVATE(self); + priv = self->priv; + + priv->status = gtk_label_new(""); + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(self), FALSE); + gtk_notebook_set_show_border(GTK_NOTEBOOK(self), FALSE); + gtk_notebook_append_page(GTK_NOTEBOOK(self), priv->status, NULL); + gdk_color_parse("white", &color); + gtk_widget_modify_fg(priv->status, GTK_STATE_NORMAL, &color); +} + +void +virt_viewer_notebook_show_status(VirtViewerNotebook *self, const gchar *text) +{ + VirtViewerNotebookPrivate *priv; + + DEBUG_LOG("notebook show status %p", self); + g_return_if_fail(VIRT_VIEWER_IS_NOTEBOOK(self)); + + priv = self->priv; + gtk_label_set_text(GTK_LABEL(priv->status), text); + gtk_notebook_set_current_page(GTK_NOTEBOOK(self), 0); + gtk_widget_show_all(GTK_WIDGET(self)); +} + +void +virt_viewer_notebook_show_display(VirtViewerNotebook *self) +{ + GtkWidget *display; + + DEBUG_LOG("notebook show display %p", self); + g_return_if_fail(VIRT_VIEWER_IS_NOTEBOOK(self)); + + display = gtk_notebook_get_nth_page(GTK_NOTEBOOK(self), 1); + g_warn_if_fail(display != NULL); + + gtk_notebook_set_current_page(GTK_NOTEBOOK(self), 1); + gtk_widget_show_all(GTK_WIDGET(self)); +} + +VirtViewerNotebook* +virt_viewer_notebook_new (void) +{ + return g_object_new (VIRT_VIEWER_TYPE_NOTEBOOK, NULL); +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/src/virt-viewer-notebook.h b/src/virt-viewer-notebook.h new file mode 100644 index 0000000..05dadc7 --- /dev/null +++ b/src/virt-viewer-notebook.h @@ -0,0 +1,77 @@ +/* + * 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_NOTEBOOK +#define _VIRT_VIEWER_NOTEBOOK + +#include +#include + +G_BEGIN_DECLS + +#define VIRT_VIEWER_TYPE_NOTEBOOK virt_viewer_notebook_get_type() + +#define VIRT_VIEWER_NOTEBOOK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIRT_VIEWER_TYPE_NOTEBOOK, VirtViewerNotebook)) + +#define VIRT_VIEWER_NOTEBOOK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), VIRT_VIEWER_TYPE_NOTEBOOK, VirtViewerNotebookClass)) + +#define VIRT_VIEWER_IS_NOTEBOOK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIRT_VIEWER_TYPE_NOTEBOOK)) + +#define VIRT_VIEWER_IS_NOTEBOOK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), VIRT_VIEWER_TYPE_NOTEBOOK)) + +#define VIRT_VIEWER_NOTEBOOK_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), VIRT_VIEWER_TYPE_NOTEBOOK, VirtViewerNotebookClass)) + +typedef struct _VirtViewerNotebookPrivate VirtViewerNotebookPrivate; + +typedef struct { + GtkNotebook parent; + VirtViewerNotebookPrivate *priv; +} VirtViewerNotebook; + +typedef struct { + GtkNotebookClass parent_class; +} VirtViewerNotebookClass; + +GType virt_viewer_notebook_get_type (void); + +VirtViewerNotebook* virt_viewer_notebook_new (void); +void virt_viewer_notebook_show_status(VirtViewerNotebook *nb, const gchar *text); +void virt_viewer_notebook_show_display(VirtViewerNotebook *self); + +G_END_DECLS + +#endif /* _VIRT_VIEWER_NOTEBOOK */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/src/virt-viewer-window.c b/src/virt-viewer-window.c new file mode 100644 index 0000000..f6f5492 --- /dev/null +++ b/src/virt-viewer-window.c @@ -0,0 +1,891 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "virt-viewer-window.h" +#include "virt-viewer-app.h" +#include "virt-viewer-util.h" +#include "view/autoDrawer.h" + +/* Signal handlers for main window (move in a VirtViewerMainWindow?) */ +void virt_viewer_window_menu_view_zoom_out(GtkWidget *menu, VirtViewerWindow *self); +void virt_viewer_window_menu_view_zoom_in(GtkWidget *menu, VirtViewerWindow *self); +void virt_viewer_window_menu_view_zoom_reset(GtkWidget *menu, VirtViewerWindow *self); +void virt_viewer_window_delete(GtkWidget *src, void *dummy, VirtViewerWindow *self); +void virt_viewer_window_menu_file_quit(GtkWidget *src, VirtViewerWindow *self); +void virt_viewer_window_menu_help_about(GtkWidget *menu, VirtViewerWindow *self); +void virt_viewer_window_menu_view_fullscreen(GtkWidget *menu, VirtViewerWindow *self); +void virt_viewer_window_menu_view_resize(GtkWidget *menu, VirtViewerWindow *self); +void virt_viewer_window_menu_send(GtkWidget *menu, VirtViewerWindow *self); +void virt_viewer_window_menu_file_screenshot(GtkWidget *menu, VirtViewerWindow *self); + +/* Internal methods */ +static void virt_viewer_window_enable_modifiers(VirtViewerWindow *self); +static void virt_viewer_window_disable_modifiers(VirtViewerWindow *self); +static void virt_viewer_window_resize(VirtViewerWindow *self); +static void virt_viewer_window_toolbar_setup(VirtViewerWindow *self); +static gboolean window_state_cb(GtkWidget *widget, GdkEventWindowState *event, gpointer data); + +G_DEFINE_TYPE (VirtViewerWindow, virt_viewer_window, G_TYPE_OBJECT) + +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), VIRT_VIEWER_TYPE_WINDOW, VirtViewerWindowPrivate)) + +enum { + PROP_0, + PROP_WINDOW, + PROP_DISPLAY, + PROP_SUBTITLE, + PROP_CONTAINER, + PROP_APP, +}; + +enum menuNums { + FILE_MENU, + VIEW_MENU, + SEND_KEY_MENU, + HELP_MENU, + LAST_MENU // sentinel +}; + +struct _VirtViewerWindowPrivate { + VirtViewerApp *app; + GtkContainer *container; /* if any, then there is no window */ + + GtkBuilder *builder; + GtkWidget *window; + GtkWidget *layout; + GtkWidget *toolbar; + VirtViewerNotebook *notebook; + VirtViewerDisplay *display; + + gboolean accel_enabled; + GValue accel_setting; + GSList *accel_list; + int accel_menu_sig[LAST_MENU]; + gboolean grabbed; + + gint zoomlevel; + gboolean auto_resize; + gboolean fullscreen; + gchar *subtitle; +}; + +#if GTK_CHECK_VERSION(3, 0, 0) +#define GDK_Control_L GDK_KEY_Control_L +#define GDK_Alt_L GDK_KEY_Alt_L +#define GDK_Delete GDK_KEY_Delete +#define GDK_BackSpace GDK_KEY_BackSpace +#define GDK_Print GDK_KEY_Print +#define GDK_F1 GDK_KEY_F1 +#define GDK_F2 GDK_KEY_F2 +#define GDK_F3 GDK_KEY_F3 +#define GDK_F4 GDK_KEY_F4 +#define GDK_F5 GDK_KEY_F5 +#define GDK_F6 GDK_KEY_F6 +#define GDK_F7 GDK_KEY_F7 +#define GDK_F8 GDK_KEY_F8 +#define GDK_F9 GDK_KEY_F9 +#define GDK_F10 GDK_KEY_F10 +#define GDK_F11 GDK_KEY_F11 +#define GDK_F12 GDK_KEY_F12 +#endif + +static void +virt_viewer_window_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + VirtViewerWindowPrivate *priv = VIRT_VIEWER_WINDOW(object)->priv; + + switch (property_id) { + case PROP_SUBTITLE: + g_value_set_string(value, priv->subtitle); + break; + + case PROP_WINDOW: + g_value_set_object(value, priv->window); + break; + + case PROP_DISPLAY: + g_value_set_object(value, priv->display); + break; + + case PROP_CONTAINER: + g_value_set_object(value, priv->container); + break; + + case PROP_APP: + g_value_set_object(value, priv->app); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +virt_viewer_window_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + VirtViewerWindowPrivate *priv = VIRT_VIEWER_WINDOW(object)->priv; + + switch (property_id) { + case PROP_SUBTITLE: + g_free(priv->subtitle); + priv->subtitle = g_value_dup_string(value); + break; + + case PROP_CONTAINER: + g_return_if_fail(priv->container == NULL); + priv->container = g_value_dup_object(value); + break; + + case PROP_APP: + g_return_if_fail(priv->app == NULL); + priv->app = g_value_dup_object(value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +virt_viewer_window_dispose (GObject *object) +{ + VirtViewerWindowPrivate *priv = VIRT_VIEWER_WINDOW(object)->priv; + G_OBJECT_CLASS (virt_viewer_window_parent_class)->dispose (object); + + if (priv->display) { + g_object_unref(priv->display); + priv->display = NULL; + } + + if (priv->app) { + g_object_unref(priv->app); + priv->app = NULL; + } + + g_free(priv->subtitle); + priv->subtitle = NULL; +} + +static void +virt_viewer_window_class_init (VirtViewerWindowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (VirtViewerWindowPrivate)); + + object_class->get_property = virt_viewer_window_get_property; + object_class->set_property = virt_viewer_window_set_property; + object_class->dispose = virt_viewer_window_dispose; + + g_object_class_install_property(object_class, + PROP_SUBTITLE, + g_param_spec_string("subtitle", + "Subtitle", + "Window subtitle", + "", + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_WINDOW, + g_param_spec_object("window", + "Window", + "GtkWindow", + GTK_TYPE_WIDGET, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_DISPLAY, + g_param_spec_object("display", + "Display", + "VirtDisplay", + VIRT_VIEWER_TYPE_DISPLAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_CONTAINER, + g_param_spec_object("container", + "Container", + "Container widget", + VIRT_VIEWER_TYPE_DISPLAY, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property(object_class, + PROP_APP, + g_param_spec_object("app", + "App", + "VirtViewerApp", + VIRT_VIEWER_TYPE_APP, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static void +virt_viewer_window_init (VirtViewerWindow *self) +{ + VirtViewerWindowPrivate *priv; + GtkWidget *vbox; + GtkWidget *menu; + GdkColor color; + GSList *accels; + + self->priv = GET_PRIVATE(self); + priv = self->priv; + + priv->auto_resize = TRUE; + g_value_init(&priv->accel_setting, G_TYPE_STRING); + + priv->notebook = virt_viewer_notebook_new(); + priv->builder = virt_viewer_util_load_ui("virt-viewer.xml"); + + menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-view-resize")); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), TRUE); + + gtk_builder_connect_signals(priv->builder, self); + + vbox = GTK_WIDGET(gtk_builder_get_object(priv->builder, "viewer-box")); + virt_viewer_window_toolbar_setup(self); + + gtk_box_pack_end(GTK_BOX(vbox), priv->layout, TRUE, TRUE, 0); + gdk_color_parse("black", &color); + gtk_widget_modify_bg(priv->layout, GTK_STATE_NORMAL, &color); + + priv->window = GTK_WIDGET(gtk_builder_get_object(priv->builder, "viewer")); + virt_viewer_window_update_title(self); + gtk_window_set_resizable(GTK_WINDOW(priv->window), TRUE); +#if GTK_CHECK_VERSION(3, 0, 0) + gtk_window_set_has_resize_grip(GTK_WINDOW(priv->window), FALSE); +#endif + priv->accel_enabled = TRUE; + + accels = gtk_accel_groups_from_object(G_OBJECT(priv->window)); + for ( ; accels ; accels = accels->next) { + priv->accel_list = g_slist_append(priv->accel_list, accels->data); + g_object_ref(G_OBJECT(accels->data)); + } + g_signal_connect(G_OBJECT(priv->window), "window-state-event", G_CALLBACK(window_state_cb), self); +} + +static void +virt_viewer_window_desktop_resize(VirtViewerDisplay *display G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + VirtViewerWindowPrivate *priv = self->priv; + if (priv->auto_resize && priv->window && !priv->fullscreen) + virt_viewer_window_resize(self); +} + + +void +virt_viewer_window_menu_view_zoom_out(GtkWidget *menu G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + VirtViewerWindowPrivate *priv = self->priv; + + if (priv->zoomlevel > 10) + priv->zoomlevel -= 10; + + if (!priv->display) + return; + + gtk_window_resize(GTK_WINDOW(priv->window), 1, 1); + if (priv->display) + virt_viewer_display_set_zoom_level(VIRT_VIEWER_DISPLAY(priv->display), priv->zoomlevel); +} + +void +virt_viewer_window_menu_view_zoom_in(GtkWidget *menu G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + VirtViewerWindowPrivate *priv = self->priv; + + if (priv->zoomlevel < 400) + priv->zoomlevel += 10; + + if (!priv->display) + return; + + gtk_window_resize(GTK_WINDOW(priv->window), 1, 1); + if (priv->display) + virt_viewer_display_set_zoom_level(VIRT_VIEWER_DISPLAY(priv->display), priv->zoomlevel); +} + +void +virt_viewer_window_menu_view_zoom_reset(GtkWidget *menu G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + VirtViewerWindowPrivate *priv = self->priv; + gtk_window_resize(GTK_WINDOW(priv->window), 1, 1); + priv->zoomlevel = 100; + + if (priv->display) + virt_viewer_display_set_zoom_level(VIRT_VIEWER_DISPLAY(priv->display), priv->zoomlevel); +} + +/* + * This code attempts to resize the top level window to be large enough + * 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 +virt_viewer_window_resize(VirtViewerWindow *self) +{ + GdkRectangle fullscreen; + GdkScreen *screen; + int width, height; + double desktopAspect; + double screenAspect; + guint desktopWidth; + guint desktopHeight; + VirtViewerWindowPrivate *priv = self->priv; + + DEBUG_LOG("Preparing main window resize"); + if (!priv->display) { + DEBUG_LOG("Skipping inactive resize"); + return; + } + + gtk_window_resize(GTK_WINDOW(priv->window), 1, 1); + + virt_viewer_display_get_desktop_size(VIRT_VIEWER_DISPLAY(priv->display), + &desktopWidth, &desktopHeight); + + screen = gtk_widget_get_screen(priv->window); + gdk_screen_get_monitor_geometry(screen, + gdk_screen_get_monitor_at_window + (screen, gtk_widget_get_window(priv->window)), + &fullscreen); + + desktopAspect = (double)desktopWidth / (double)desktopHeight; + screenAspect = (double)(fullscreen.width - 128) / (double)(fullscreen.height - 128); + + if ((desktopWidth > (fullscreen.width - 128)) || + (desktopHeight > (fullscreen.height - 128))) { + /* Doesn't fit native res, so go as large as possible + maintaining aspect ratio */ + if (screenAspect > desktopAspect) { + width = desktopHeight * desktopAspect; + height = desktopHeight; + } else { + width = desktopWidth; + height = desktopWidth / desktopAspect; + } + } else { + width = desktopWidth; + height = desktopHeight; + } + + DEBUG_LOG("Decided todo %dx%d (desktop is %dx%d, fullscreen is %dx%d", + width, height, desktopWidth, desktopHeight, + fullscreen.width, fullscreen.height); + + virt_viewer_display_set_desktop_size(VIRT_VIEWER_DISPLAY(priv->display), + width, height); +} + +static void +virt_viewer_window_leave_fullscreen(VirtViewerWindow *self) +{ + VirtViewerWindowPrivate *priv = self->priv; + GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "top-menu")); + + if (!priv->fullscreen) + return; + priv->fullscreen = FALSE; + ViewAutoDrawer_SetActive(VIEW_AUTODRAWER(priv->layout), FALSE); + gtk_widget_show(menu); + gtk_widget_hide(priv->toolbar); + gtk_window_unfullscreen(GTK_WINDOW(priv->window)); + if (priv->auto_resize) + virt_viewer_window_resize(self); +} + +static void +virt_viewer_window_enter_fullscreen(VirtViewerWindow *self) +{ + VirtViewerWindowPrivate *priv = self->priv; + GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "top-menu")); + + if (priv->fullscreen) + return; + priv->fullscreen = TRUE; + gtk_widget_hide(menu); + gtk_window_fullscreen(GTK_WINDOW(priv->window)); + gtk_widget_show(priv->toolbar); + ViewAutoDrawer_SetActive(VIEW_AUTODRAWER(priv->layout), TRUE); + ViewAutoDrawer_Close(VIEW_AUTODRAWER(priv->layout)); +} + +static gboolean +window_state_cb(GtkWidget *widget G_GNUC_UNUSED, GdkEventWindowState *event, + gpointer data) +{ + VirtViewerWindow *self = data; + + if (!(event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)) + return TRUE; + + if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) + virt_viewer_window_enter_fullscreen(self); + else + virt_viewer_window_leave_fullscreen(self); + + return TRUE; +} + +#define MAX_KEY_COMBO 3 +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"}, +}; + +void +virt_viewer_window_menu_send(GtkWidget *menu G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + int i; + GtkWidget *label = gtk_bin_get_child(GTK_BIN(menu)); + const char *text = gtk_label_get_label(GTK_LABEL(label)); + VirtViewerWindowPrivate *priv = self->priv; + + 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(VIRT_VIEWER_DISPLAY(priv->display), + keyCombos[i].keys, + keyCombos[i].nkeys); + return; + } + } + DEBUG_LOG("Failed to find key combo %s", gtk_label_get_text(GTK_LABEL(label))); +} + +static gboolean +virt_viewer_window_ignore_accel(GtkWidget *menu G_GNUC_UNUSED, + VirtViewerWindow *self G_GNUC_UNUSED) +{ + /* ignore accelerator */ + return TRUE; +} + +static const char * const menuNames[LAST_MENU] = { + "menu-file", "menu-view", "menu-send", "menu-help" +}; + +void +virt_viewer_window_disable_modifiers(VirtViewerWindow *self) +{ + GtkSettings *settings = gtk_settings_get_default(); + VirtViewerWindowPrivate *priv = self->priv; + GValue empty; + GSList *accels; + int i; + + if (!priv->window) + return; + + if (!priv->accel_enabled) + 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", &priv->accel_setting); + g_object_set_property(G_OBJECT(settings), "gtk-menu-bar-accel", &empty); + + /* This stops global accelerators like Ctrl+Q == Quit */ + for (accels = priv->accel_list ; accels ; accels = accels->next) { + gtk_window_remove_accel_group(GTK_WINDOW(priv->window), accels->data); + } + + /* This stops menu bar shortcuts like Alt+F == File */ + for (i = 0 ; i < LAST_MENU ; i++) { + GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, menuNames[i])); + priv->accel_menu_sig[i] = + g_signal_connect(menu, "mnemonic-activate", + G_CALLBACK(virt_viewer_window_ignore_accel), self); + } + + priv->accel_enabled = FALSE; +} + +void +virt_viewer_window_enable_modifiers(VirtViewerWindow *self) +{ + GtkSettings *settings = gtk_settings_get_default(); + VirtViewerWindowPrivate *priv = self->priv; + GSList *accels; + int i; + + if (!priv->window) + return; + + if (priv->accel_enabled) + return; + + /* This allows F10 activating menu bar */ + g_object_set_property(G_OBJECT(settings), "gtk-menu-bar-accel", &priv->accel_setting); + + /* This allows global accelerators like Ctrl+Q == Quit */ + for (accels = priv->accel_list ; accels ; accels = accels->next) { + gtk_window_add_accel_group(GTK_WINDOW(priv->window), accels->data); + } + + /* This allows menu bar shortcuts like Alt+F == File */ + for (i = 0 ; i < LAST_MENU ; i++) { + GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, menuNames[i])); + g_signal_handler_disconnect(menu, priv->accel_menu_sig[i]); + } + + priv->accel_enabled = TRUE; +} + + +void +virt_viewer_window_delete(GtkWidget *src G_GNUC_UNUSED, + void *dummy G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + g_warning("TODO: we might want some other behaviour"); + virt_viewer_app_quit(self->priv->app); +} + + +void +virt_viewer_window_menu_file_quit(GtkWidget *src G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + virt_viewer_app_quit(self->priv->app); +} + + +static void +virt_viewer_window_toolbar_leave_fullscreen(GtkWidget *button G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + VirtViewerWindowPrivate *priv = self->priv; + GtkWidget *menu = GTK_WIDGET(gtk_builder_get_object(priv->builder, "menu-view-fullscreen")); + + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), FALSE); + virt_viewer_window_leave_fullscreen(self); +} + + +void +virt_viewer_window_menu_view_fullscreen(GtkWidget *menu, + VirtViewerWindow *self) +{ + VirtViewerWindowPrivate *priv = self->priv; + if (!priv->window) + return; + + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu))) { + virt_viewer_window_enter_fullscreen(self); + } else { + virt_viewer_window_leave_fullscreen(self); + } +} + +void +virt_viewer_window_menu_view_resize(GtkWidget *menu, + VirtViewerWindow *self) +{ + VirtViewerWindowPrivate *priv = self->priv; + + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu))) { + priv->auto_resize = TRUE; + if (!priv->fullscreen) + virt_viewer_window_resize(self); + } else { + priv->auto_resize = FALSE; + } +} + +static void +virt_viewer_window_save_screenshot(VirtViewerWindow *self, + const char *file) +{ + VirtViewerWindowPrivate *priv = self->priv; + GdkPixbuf *pix = virt_viewer_display_get_pixbuf(VIRT_VIEWER_DISPLAY(priv->display)); + + gdk_pixbuf_save(pix, file, "png", NULL, + "tEXt::Generator App", PACKAGE, NULL); + gdk_pixbuf_unref(pix); +} + +void +virt_viewer_window_menu_file_screenshot(GtkWidget *menu G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + GtkWidget *dialog; + VirtViewerWindowPrivate *priv = self->priv; + + g_return_if_fail(priv->display != NULL); + + dialog = gtk_file_chooser_dialog_new ("Save screenshot", + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); + + //gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), default_folder_for_saving); + //gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), "Screenshot"); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { + char *filename; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + virt_viewer_window_save_screenshot(self, filename); + g_free (filename); + } + + gtk_widget_destroy (dialog); +} + +void +virt_viewer_window_menu_help_about(GtkWidget *menu G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + GtkBuilder *about = virt_viewer_util_load_ui("virt-viewer-about.xml"); + + GtkWidget *dialog = GTK_WIDGET(gtk_builder_get_object(about, "about")); + gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), VERSION); + + gtk_builder_connect_signals(about, self); + + gtk_widget_show_all(dialog); + + g_object_unref(G_OBJECT(about)); +} + + +static void +virt_viewer_window_toolbar_setup(VirtViewerWindow *self) +{ + GtkWidget *button; + VirtViewerWindowPrivate *priv = self->priv; + + priv->toolbar = gtk_toolbar_new(); + gtk_toolbar_set_show_arrow(GTK_TOOLBAR(priv->toolbar), FALSE); + gtk_widget_set_no_show_all(priv->toolbar, TRUE); + gtk_toolbar_set_style(GTK_TOOLBAR(priv->toolbar), GTK_TOOLBAR_BOTH_HORIZ); + + /* Close connection */ + button = GTK_WIDGET(gtk_tool_button_new_from_stock(GTK_STOCK_CLOSE)); + gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), _("Disconnect")); + gtk_widget_show(GTK_WIDGET(button)); + gtk_toolbar_insert(GTK_TOOLBAR(priv->toolbar), GTK_TOOL_ITEM (button), 0); + g_signal_connect(button, "clicked", G_CALLBACK(gtk_main_quit), NULL); + + /* Leave fullscreen */ + button = GTK_WIDGET(gtk_tool_button_new_from_stock(GTK_STOCK_LEAVE_FULLSCREEN)); + gtk_tool_button_set_label(GTK_TOOL_BUTTON(button), _("Leave fullscreen")); + gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), _("Leave fullscreen")); + gtk_tool_item_set_is_important(GTK_TOOL_ITEM(button), TRUE); + gtk_widget_show(GTK_WIDGET(button)); + gtk_toolbar_insert(GTK_TOOLBAR(priv->toolbar), GTK_TOOL_ITEM(button), 0); + g_signal_connect(button, "clicked", G_CALLBACK(virt_viewer_window_toolbar_leave_fullscreen), self); + + priv->layout = ViewAutoDrawer_New(); + + ViewAutoDrawer_SetActive(VIEW_AUTODRAWER(priv->layout), FALSE); + ViewOvBox_SetOver(VIEW_OV_BOX(priv->layout), priv->toolbar); + ViewOvBox_SetUnder(VIEW_OV_BOX(priv->layout), GTK_WIDGET(priv->notebook)); + ViewAutoDrawer_SetOffset(VIEW_AUTODRAWER(priv->layout), -1); + ViewAutoDrawer_SetFill(VIEW_AUTODRAWER(priv->layout), FALSE); + ViewAutoDrawer_SetOverlapPixels(VIEW_AUTODRAWER(priv->layout), 1); + ViewAutoDrawer_SetNoOverlapPixels(VIEW_AUTODRAWER(priv->layout), 0); +} + +VirtViewerNotebook* +virt_viewer_window_get_notebook (VirtViewerWindow *self) +{ + return VIRT_VIEWER_NOTEBOOK(self->priv->notebook); +} + +GtkWindow* +virt_viewer_window_get_window (VirtViewerWindow *self) +{ + return GTK_WINDOW(self->priv->window); +} + +static void +virt_viewer_window_pointer_grab(VirtViewerDisplay *display G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + VirtViewerWindowPrivate *priv = self->priv; + + priv->grabbed = TRUE; + virt_viewer_window_update_title(self); +} + +static void +virt_viewer_window_pointer_ungrab(VirtViewerDisplay *display G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + VirtViewerWindowPrivate *priv = self->priv; + + priv->grabbed = FALSE; + virt_viewer_window_update_title(self); +} + +static void +virt_viewer_window_keyboard_grab(VirtViewerDisplay *display G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + virt_viewer_window_disable_modifiers(self); +} + +static void +virt_viewer_window_keyboard_ungrab(VirtViewerDisplay *display G_GNUC_UNUSED, + VirtViewerWindow *self) +{ + virt_viewer_window_enable_modifiers(self); +} + +void +virt_viewer_window_update_title(VirtViewerWindow *self) +{ + VirtViewerWindowPrivate *priv = self->priv; + char *title; + const char *subtitle; + + if (priv->grabbed) + subtitle = "(Press Ctrl+Alt to release pointer) "; + else + subtitle = ""; + + if (priv->subtitle) + title = g_strdup_printf("%s%s - Virt Viewer", + subtitle, priv->subtitle); + else + title = g_strdup("Virt Viewer"); + + gtk_window_set_title(GTK_WINDOW(priv->window), title); + + g_free(title); +} + +void +virt_viewer_window_set_display(VirtViewerWindow *self, VirtViewerDisplay *display) +{ + VirtViewerWindowPrivate *priv; + + g_return_if_fail(VIRT_VIEWER_IS_WINDOW(self)); + g_return_if_fail(display == NULL || VIRT_VIEWER_IS_DISPLAY(display)); + + priv = self->priv; + if (priv->display) { + gtk_notebook_remove_page(GTK_NOTEBOOK(priv->notebook), 1); + g_object_unref(priv->display); + priv->display = NULL; + } + + if (display != NULL) { + priv->display = g_object_ref(display); + + gtk_notebook_append_page(GTK_NOTEBOOK(priv->notebook), GTK_WIDGET(display), NULL); + if (gtk_bin_get_child(GTK_BIN(display))) + gtk_widget_realize(GTK_WIDGET(gtk_bin_get_child(GTK_BIN(display)))); + gtk_widget_show_all(GTK_WIDGET(display)); + + g_signal_connect(display, "display-pointer-grab", + G_CALLBACK(virt_viewer_window_pointer_grab), self); + g_signal_connect(display, "display-pointer-ungrab", + G_CALLBACK(virt_viewer_window_pointer_ungrab), self); + g_signal_connect(display, "display-keyboard-grab", + G_CALLBACK(virt_viewer_window_keyboard_grab), self); + g_signal_connect(display, "display-keyboard-ungrab", + G_CALLBACK(virt_viewer_window_keyboard_ungrab), self); + g_signal_connect(display, "display-desktop-resize", + G_CALLBACK(virt_viewer_window_desktop_resize), self); + } +} + +void +virt_viewer_window_set_zoom_level(VirtViewerWindow *self, gint zoom_level) +{ + g_return_if_fail(VIRT_VIEWER_IS_WINDOW(self)); + + /* FIXME: turn into a dynamic property */ + self->priv->zoomlevel = zoom_level; +} + + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/src/virt-viewer-window.h b/src/virt-viewer-window.h new file mode 100644 index 0000000..625ef68 --- /dev/null +++ b/src/virt-viewer-window.h @@ -0,0 +1,79 @@ +/* + * 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_WINDOW +#define _VIRT_VIEWER_WINDOW + +#include +#include "virt-viewer-notebook.h" +#include "virt-viewer-display.h" + +G_BEGIN_DECLS + +#define VIRT_VIEWER_TYPE_WINDOW virt_viewer_window_get_type() + +#define VIRT_VIEWER_WINDOW(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIRT_VIEWER_TYPE_WINDOW, VirtViewerWindow)) + +#define VIRT_VIEWER_WINDOW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), VIRT_VIEWER_TYPE_WINDOW, VirtViewerWindowClass)) + +#define VIRT_VIEWER_IS_WINDOW(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIRT_VIEWER_TYPE_WINDOW)) + +#define VIRT_VIEWER_IS_WINDOW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), VIRT_VIEWER_TYPE_WINDOW)) + +#define VIRT_VIEWER_WINDOW_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), VIRT_VIEWER_TYPE_WINDOW, VirtViewerWindowClass)) + +typedef struct _VirtViewerWindowPrivate VirtViewerWindowPrivate; + +typedef struct { + GObject parent; + VirtViewerWindowPrivate *priv; +} VirtViewerWindow; + +typedef struct { + GObjectClass parent_class; +} VirtViewerWindowClass; + +GType virt_viewer_window_get_type (void); + +GtkWindow* virt_viewer_window_get_window (VirtViewerWindow* window); +VirtViewerNotebook* virt_viewer_window_get_notebook (VirtViewerWindow* window); +void virt_viewer_window_set_display(VirtViewerWindow *self, VirtViewerDisplay *display); +void virt_viewer_window_update_title(VirtViewerWindow *self); +void virt_viewer_window_set_zoom_level(VirtViewerWindow *self, gint zoom_level); + +G_END_DECLS + +#endif /* _VIRT_VIEWER_WINDOW */ +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/src/virt-viewer.c b/src/virt-viewer.c index c8bea8f..088986f 100644 --- a/src/virt-viewer.c +++ b/src/virt-viewer.c @@ -532,7 +532,7 @@ virt_viewer_new(const char *uri, app = VIRT_VIEWER_APP(self); priv = self->priv; - virt_viewer_app_set_zoom_level(app, zoom); + virt_viewer_window_set_zoom_level(virt_viewer_app_get_main_window(app), zoom); virt_viewer_app_set_direct(app, direct); /* should probably be properties instead */ diff --git a/src/virt-viewer.xml b/src/virt-viewer.xml index d11d3e4..d3b1b86 100644 --- a/src/virt-viewer.xml +++ b/src/virt-viewer.xml @@ -5,7 +5,7 @@ 400 400 - + True @@ -26,7 +26,7 @@ True Screenshot True - + @@ -40,7 +40,7 @@ True True True - + @@ -62,7 +62,7 @@ Full screen True - + @@ -80,7 +80,7 @@ True True - + @@ -90,7 +90,7 @@ True True - + @@ -105,7 +105,7 @@ True True - + @@ -117,7 +117,7 @@ True Automatically resize True - + @@ -137,7 +137,7 @@ True Ctrl+Alt+_Del True - + @@ -145,7 +145,7 @@ True Ctrl+Alt+_Backspace True - + @@ -158,7 +158,7 @@ True Ctrl+Alt+F_1 True - + @@ -166,7 +166,7 @@ True Ctrl+Alt+F_2 True - + @@ -174,7 +174,7 @@ True Ctrl+Alt+F_3 True - + @@ -182,7 +182,7 @@ True Ctrl+Alt+F_4 True - + @@ -190,7 +190,7 @@ True Ctrl+Alt+F_5 True - + @@ -198,7 +198,7 @@ True Ctrl+Alt+F_6 True - + @@ -206,7 +206,7 @@ True Ctrl+Alt+F_7 True - + @@ -214,7 +214,7 @@ True Ctrl+Alt+F_8 True - + @@ -222,7 +222,7 @@ True Ctrl+Alt+F_9 True - + @@ -230,7 +230,7 @@ True Ctrl+Alt+F1_0 True - + @@ -238,7 +238,7 @@ True Ctrl+Alt+F11 True - + @@ -246,7 +246,7 @@ True Ctrl+Alt+F12 True - + @@ -259,7 +259,7 @@ True _PrintScreen True - + @@ -280,7 +280,7 @@ True True True - +