From fd07d07ba931a9c3eb82439c5923a888922734d6 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 29 Apr 2014 15:08:56 +0200 Subject: [PATCH 01/24] gtk: zap scrolled_window The vte widget implements the scrollable interface, placing it into a scrolled window is pointless and creates a bunch of strange effects. Zap it. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 9f5061a1be..f6f367748e 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -110,7 +110,6 @@ typedef struct VirtualConsole GtkWidget *menu_item; GtkWidget *terminal; #if defined(CONFIG_VTE) - GtkWidget *scrolled_window; CharDriverState *chr; #endif } VirtualConsole; @@ -1189,8 +1188,6 @@ static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSL const char *label; char buffer[32]; char path[32]; - GtkWidget *scrolled_window; - GtkAdjustment *vadjustment; snprintf(buffer, sizeof(buffer), "vc%d", index); snprintf(path, sizeof(path), "/View/VC%d", index); @@ -1213,24 +1210,12 @@ static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSL vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1); -#if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0) - vadjustment = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(vc->terminal)); -#else - vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal)); -#endif - - scrolled_window = gtk_scrolled_window_new(NULL, vadjustment); - gtk_container_add(GTK_CONTAINER(scrolled_window), vc->terminal); - vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25); vc->chr->opaque = vc; - vc->scrolled_window = scrolled_window; - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vc->scrolled_window), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - - gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), scrolled_window, gtk_label_new(label)); + gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->terminal, + gtk_label_new(label)); g_signal_connect(vc->menu_item, "activate", G_CALLBACK(gd_menu_switch_vc), s); From 6fe83074d7ecda45839b6513188ee585d7b91ce1 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 29 Apr 2014 15:21:46 +0200 Subject: [PATCH 02/24] gtk: zap vte size requests The vte tabs simply get the size of the vga tab then, with whatever cols and lines are fitting in. I find this bahavior more useful than resizing the qemu window all day long. YMMV. Comments are welcome. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index f6f367748e..776e72d1d2 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -1058,19 +1058,12 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, gpointer data) { GtkDisplayState *s = data; - guint last_page; gboolean on_vga; if (!gtk_widget_get_realized(s->notebook)) { return; } - last_page = gtk_notebook_get_current_page(nb); - - if (last_page) { - gtk_widget_set_size_request(s->vc[last_page - 1].terminal, -1, -1); - } - on_vga = arg2 == 0; if (!on_vga) { @@ -1086,14 +1079,7 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, } else { #if defined(CONFIG_VTE) VirtualConsole *vc = &s->vc[arg2 - 1]; - VteTerminal *term = VTE_TERMINAL(vc->terminal); - int width, height; - - width = 80 * vte_terminal_get_char_width(term); - height = 25 * vte_terminal_get_char_height(term); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); - gtk_widget_set_size_request(vc->terminal, width, height); #else g_assert_not_reached(); #endif From ee5f31e48b5a754c44df5f006a845344f9efd753 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 29 Apr 2014 15:39:37 +0200 Subject: [PATCH 03/24] gtk: cleanup CONFIG_VTE ifdef a bit. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 776e72d1d2..068a39b04d 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -108,8 +108,8 @@ static const int modifier_keycode[] = { typedef struct VirtualConsole { GtkWidget *menu_item; - GtkWidget *terminal; #if defined(CONFIG_VTE) + GtkWidget *terminal; CharDriverState *chr; #endif } VirtualConsole; @@ -1124,13 +1124,12 @@ static gboolean gd_focus_out_event(GtkWidget *widget, /** Virtual Console Callbacks **/ +#if defined(CONFIG_VTE) static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { -#if defined(CONFIG_VTE) VirtualConsole *vc = chr->opaque; vte_terminal_feed(VTE_TERMINAL(vc->terminal), (const char *)buf, len); -#endif return len; } @@ -1151,12 +1150,6 @@ static CharDriverState *gd_vc_handler(ChardevVC *unused) return chr; } -void early_gtk_display_init(void) -{ - register_vc_handler(gd_vc_handler); -} - -#if defined(CONFIG_VTE) static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size, gpointer user_data) { @@ -1165,12 +1158,10 @@ static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size, qemu_chr_be_write(vc->chr, (uint8_t *)text, (unsigned int)size); return TRUE; } -#endif static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group, GtkWidget *view_menu) { -#if defined(CONFIG_VTE) const char *label; char buffer[32]; char path[32]; @@ -1212,10 +1203,23 @@ static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSL vc->chr->init(vc->chr); } -#endif /* CONFIG_VTE */ return group; } +static void gd_vcs_init(GtkDisplayState *s, GSList *group, + GtkWidget *view_menu) +{ + int i; + + for (i = 0; i < nb_vcs; i++) { + VirtualConsole *vc = &s->vc[i]; + + group = gd_vc_init(s, vc, i, group, view_menu); + s->nb_vcs++; + } +} +#endif /* CONFIG_VTE */ + /** Window Creation **/ static void gd_connect_signals(GtkDisplayState *s) @@ -1316,7 +1320,6 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_g GSList *group = NULL; GtkWidget *view_menu; GtkWidget *separator; - int i; view_menu = gtk_menu_new(); gtk_menu_set_accel_group(GTK_MENU(view_menu), accel_group); @@ -1378,12 +1381,9 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_g gtk_accel_map_add_entry("/View/VGA", GDK_KEY_1, HOTKEY_MODIFIERS); gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->vga_item); - for (i = 0; i < nb_vcs; i++) { - VirtualConsole *vc = &s->vc[i]; - - group = gd_vc_init(s, vc, i, group, view_menu); - s->nb_vcs++; - } +#if defined(CONFIG_VTE) + gd_vcs_init(s, group, view_menu); +#endif separator = gtk_separator_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); @@ -1512,3 +1512,10 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) global_state = s; } + +void early_gtk_display_init(void) +{ +#if defined(CONFIG_VTE) + register_vc_handler(gd_vc_handler); +#endif +} From 0fb20d1c39def06a592dd17922e0f0d6fe049000 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 29 Apr 2014 15:24:32 -0400 Subject: [PATCH 04/24] gtk: Add a scrollbar for text consoles Only show the scrollbar if the content doesn't fit on the visible space. [ kraxel: fix box packing ] Signed-off-by: Cole Robinson Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 068a39b04d..6a3fe00ed0 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -109,6 +109,8 @@ typedef struct VirtualConsole { GtkWidget *menu_item; #if defined(CONFIG_VTE) + GtkWidget *box; + GtkWidget *scrollbar; GtkWidget *terminal; CharDriverState *chr; #endif @@ -1125,6 +1127,18 @@ static gboolean gd_focus_out_event(GtkWidget *widget, /** Virtual Console Callbacks **/ #if defined(CONFIG_VTE) +static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque) +{ + VirtualConsole *vc = opaque; + + if (gtk_adjustment_get_upper(adjustment) > + gtk_adjustment_get_page_size(adjustment)) { + gtk_widget_show(vc->scrollbar); + } else { + gtk_widget_hide(vc->scrollbar); + } +} + static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { VirtualConsole *vc = chr->opaque; @@ -1165,6 +1179,9 @@ static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSL const char *label; char buffer[32]; char path[32]; + GtkWidget *box; + GtkWidget *scrollbar; + GtkAdjustment *vadjustment; snprintf(buffer, sizeof(buffer), "vc%d", index); snprintf(path, sizeof(path), "/View/VC%d", index); @@ -1186,12 +1203,33 @@ static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSL g_signal_connect(vc->terminal, "commit", G_CALLBACK(gd_vc_in), vc); vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1); - vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25); - vc->chr->opaque = vc; +#if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0) + vadjustment = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(vc->terminal)); +#else + vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal)); +#endif - gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->terminal, +#if GTK_CHECK_VERSION(3, 0, 0) + box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2); + scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment); +#else + box = gtk_hbox_new(false, 2); + scrollbar = gtk_vscrollbar_new(vadjustment); +#endif + + gtk_box_pack_start(GTK_BOX(box), vc->terminal, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0); + + vc->chr->opaque = vc; + vc->box = box; + vc->scrollbar = scrollbar; + + g_signal_connect(vadjustment, "changed", + G_CALLBACK(gd_vc_adjustment_changed), vc); + + gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), box, gtk_label_new(label)); g_signal_connect(vc->menu_item, "activate", G_CALLBACK(gd_menu_switch_vc), s); From 832189c9b191e10948df06cdb77d364e6a19d434 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 30 Apr 2014 12:56:53 +0200 Subject: [PATCH 05/24] gtk: remove page numbering assumtions from the code Lookup page numbers using gtk_notebook_page_num() instead. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 6a3fe00ed0..49753efb1f 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -190,7 +190,11 @@ static bool gd_grab_on_hover(GtkDisplayState *s) static bool gd_on_vga(GtkDisplayState *s) { - return gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)) == 0; + gint p1, p2; + + p1 = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)); + p2 = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), s->drawing_area); + return p1 == p2; } static void gd_update_cursor(GtkDisplayState *s, gboolean override) @@ -805,19 +809,25 @@ static void gd_menu_quit(GtkMenuItem *item, void *opaque) static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + gint page; if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) { - gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), 0); + page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), + s->drawing_area); + gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), page); } else { - int i; - gtk_release_modifiers(s); +#if defined(CONFIG_VTE) + gint i; for (i = 0; i < s->nb_vcs; i++) { if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) { - gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), i + 1); - break; + page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), + s->vc[i].box); + gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), page); + return; } } +#endif } } @@ -1061,12 +1071,14 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, { GtkDisplayState *s = data; gboolean on_vga; + gint page; if (!gtk_widget_get_realized(s->notebook)) { return; } - on_vga = arg2 == 0; + page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), s->drawing_area); + on_vga = arg2 == page; if (!on_vga) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), @@ -1076,12 +1088,20 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, TRUE); } - if (arg2 == 0) { + if (on_vga) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE); } else { #if defined(CONFIG_VTE) - VirtualConsole *vc = &s->vc[arg2 - 1]; - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); + VirtualConsole *vc; + gint page, i; + for (i = 0; i < s->nb_vcs; i++) { + vc = &s->vc[i]; + page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->box); + if (page == arg2) { + gtk_check_menu_item_set_active + (GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); + } + } #else g_assert_not_reached(); #endif From 271a25c0b62df9fec55cb0e43ba272d31654a835 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 30 Apr 2014 13:53:16 +0200 Subject: [PATCH 06/24] gtk: VirtualConsole restruction Move all vte-related items into VirtualVteConsole substruct. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 119 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 46 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 49753efb1f..78f6ccc384 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -105,15 +105,23 @@ static const int modifier_keycode[] = { 0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd, }; -typedef struct VirtualConsole -{ - GtkWidget *menu_item; #if defined(CONFIG_VTE) +typedef struct VirtualVteConsole { GtkWidget *box; GtkWidget *scrollbar; GtkWidget *terminal; CharDriverState *chr; +} VirtualVteConsole; #endif + +typedef struct VirtualConsole { + GtkWidget *menu_item; + GtkWidget *tab_item; + union { +#if defined(CONFIG_VTE) + VirtualVteConsole vte; +#endif + }; } VirtualConsole; typedef struct GtkDisplayState @@ -178,6 +186,36 @@ static GtkDisplayState *global_state; /** Utility Functions **/ +static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s) +{ + VirtualConsole *vc; + gint i; + + for (i = 0; i < s->nb_vcs; i++) { + vc = &s->vc[i]; + if (gtk_check_menu_item_get_active + (GTK_CHECK_MENU_ITEM(vc->menu_item))) { + return vc; + } + } + return NULL; +} + +static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page) +{ + VirtualConsole *vc; + gint i, p; + + for (i = 0; i < s->nb_vcs; i++) { + vc = &s->vc[i]; + p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item); + if (p == page) { + return vc; + } + } + return NULL; +} + static bool gd_is_grab_active(GtkDisplayState *s) { return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item)); @@ -817,17 +855,12 @@ static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque) gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), page); } else { gtk_release_modifiers(s); -#if defined(CONFIG_VTE) - gint i; - for (i = 0; i < s->nb_vcs; i++) { - if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) { - page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), - s->vc[i].box); - gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), page); - return; - } + VirtualConsole *vc = gd_vc_find_by_menu(s); + if (vc) { + page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), + vc->tab_item); + gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), page); } -#endif } } @@ -1091,20 +1124,12 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, if (on_vga) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE); } else { -#if defined(CONFIG_VTE) VirtualConsole *vc; - gint page, i; - for (i = 0; i < s->nb_vcs; i++) { - vc = &s->vc[i]; - page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->box); - if (page == arg2) { - gtk_check_menu_item_set_active - (GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); - } + vc = gd_vc_find_by_page(s, arg2); + if (vc) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), + TRUE); } -#else - g_assert_not_reached(); -#endif } gtk_widget_set_sensitive(s->grab_item, on_vga); @@ -1153,9 +1178,9 @@ static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque) if (gtk_adjustment_get_upper(adjustment) > gtk_adjustment_get_page_size(adjustment)) { - gtk_widget_show(vc->scrollbar); + gtk_widget_show(vc->vte.scrollbar); } else { - gtk_widget_hide(vc->scrollbar); + gtk_widget_hide(vc->vte.scrollbar); } } @@ -1163,7 +1188,7 @@ static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { VirtualConsole *vc = chr->opaque; - vte_terminal_feed(VTE_TERMINAL(vc->terminal), (const char *)buf, len); + vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len); return len; } @@ -1189,7 +1214,7 @@ static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size, { VirtualConsole *vc = user_data; - qemu_chr_be_write(vc->chr, (uint8_t *)text, (unsigned int)size); + qemu_chr_be_write(vc->vte.chr, (uint8_t *)text, (unsigned int)size); return TRUE; } @@ -1206,10 +1231,10 @@ static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSL snprintf(buffer, sizeof(buffer), "vc%d", index); snprintf(path, sizeof(path), "/View/VC%d", index); - vc->chr = vcs[index]; + vc->vte.chr = vcs[index]; - if (vc->chr->label) { - label = vc->chr->label; + if (vc->vte.chr->label) { + label = vc->vte.chr->label; } else { label = buffer; } @@ -1219,16 +1244,17 @@ static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSL gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path); gtk_accel_map_add_entry(path, GDK_KEY_2 + index, HOTKEY_MODIFIERS); - vc->terminal = vte_terminal_new(); - g_signal_connect(vc->terminal, "commit", G_CALLBACK(gd_vc_in), vc); + vc->vte.terminal = vte_terminal_new(); + g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc); - vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1); - vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25); + vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1); + vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal), 80, 25); #if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0) - vadjustment = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(vc->terminal)); + vadjustment = gtk_scrollable_get_vadjustment + (GTK_SCROLLABLE(vc->vte.terminal)); #else - vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal)); + vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal)); #endif #if GTK_CHECK_VERSION(3, 0, 0) @@ -1239,26 +1265,27 @@ static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSL scrollbar = gtk_vscrollbar_new(vadjustment); #endif - gtk_box_pack_start(GTK_BOX(box), vc->terminal, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0); - vc->chr->opaque = vc; - vc->box = box; - vc->scrollbar = scrollbar; + vc->vte.chr->opaque = vc; + vc->vte.box = box; + vc->vte.scrollbar = scrollbar; g_signal_connect(vadjustment, "changed", G_CALLBACK(gd_vc_adjustment_changed), vc); - gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), box, + vc->tab_item = box; + gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item, gtk_label_new(label)); g_signal_connect(vc->menu_item, "activate", G_CALLBACK(gd_menu_switch_vc), s); gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item); - qemu_chr_be_generic_open(vc->chr); - if (vc->chr->init) { - vc->chr->init(vc->chr); + qemu_chr_be_generic_open(vc->vte.chr); + if (vc->vte.chr->init) { + vc->vte.chr->init(vc->vte.chr); } return group; From e3500d1f5f0837c3ae70612c27f16845b0796040 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 30 Apr 2014 16:30:07 +0200 Subject: [PATCH 07/24] gtk: move vga state into VirtualGfxConsole Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 615 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 337 insertions(+), 278 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 78f6ccc384..bc42f680bb 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -105,6 +105,18 @@ static const int modifier_keycode[] = { 0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd, }; +typedef struct GtkDisplayState GtkDisplayState; + +typedef struct VirtualGfxConsole { + GtkWidget *drawing_area; + DisplayChangeListener dcl; + DisplaySurface *ds; + pixman_image_t *convert; + cairo_surface_t *surface; + double scale_x; + double scale_y; +} VirtualGfxConsole; + #if defined(CONFIG_VTE) typedef struct VirtualVteConsole { GtkWidget *box; @@ -114,18 +126,25 @@ typedef struct VirtualVteConsole { } VirtualVteConsole; #endif +typedef enum VirtualConsoleType { + GD_VC_GFX, + GD_VC_VTE, +} VirtualConsoleType; + typedef struct VirtualConsole { + GtkDisplayState *s; GtkWidget *menu_item; GtkWidget *tab_item; + VirtualConsoleType type; union { + VirtualGfxConsole gfx; #if defined(CONFIG_VTE) VirtualVteConsole vte; #endif }; } VirtualConsole; -typedef struct GtkDisplayState -{ +struct GtkDisplayState { GtkWidget *window; GtkWidget *menu_bar; @@ -148,7 +167,6 @@ typedef struct GtkDisplayState GtkWidget *zoom_fit_item; GtkWidget *grab_item; GtkWidget *grab_on_hover_item; - GtkWidget *vga_item; int nb_vcs; VirtualConsole vc[MAX_VCS]; @@ -157,11 +175,6 @@ typedef struct GtkDisplayState GtkWidget *vbox; GtkWidget *notebook; - GtkWidget *drawing_area; - cairo_surface_t *surface; - pixman_image_t *convert; - DisplayChangeListener dcl; - DisplaySurface *ds; int button_mask; gboolean last_set; int last_x; @@ -169,8 +182,6 @@ typedef struct GtkDisplayState int grab_x_root; int grab_y_root; - double scale_x; - double scale_y; gboolean full_screen; GdkCursor *null_cursor; @@ -180,7 +191,7 @@ typedef struct GtkDisplayState bool external_pause_update; bool modifier_pressed[ARRAY_SIZE(modifier_keycode)]; -} GtkDisplayState; +}; static GtkDisplayState *global_state; @@ -216,6 +227,14 @@ static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page) return NULL; } +static VirtualConsole *gd_vc_find_current(GtkDisplayState *s) +{ + gint page; + + page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)); + return gd_vc_find_by_page(s, page); +} + static bool gd_is_grab_active(GtkDisplayState *s) { return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item)); @@ -226,26 +245,17 @@ static bool gd_grab_on_hover(GtkDisplayState *s) return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item)); } -static bool gd_on_vga(GtkDisplayState *s) -{ - gint p1, p2; - - p1 = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)); - p2 = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), s->drawing_area); - return p1 == p2; -} - -static void gd_update_cursor(GtkDisplayState *s, gboolean override) +static void gd_update_cursor(VirtualConsole *vc) { + GtkDisplayState *s = vc->s; GdkWindow *window; - bool on_vga; - window = gtk_widget_get_window(GTK_WIDGET(s->drawing_area)); + if (vc->type != GD_VC_GFX) { + return; + } - on_vga = gd_on_vga(s); - - if ((override || on_vga) && - (s->full_screen || qemu_input_is_absolute() || gd_is_grab_active(s))) { + window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area)); + if (s->full_screen || qemu_input_is_absolute() || gd_is_grab_active(s)) { gdk_window_set_cursor(window, s->null_cursor); } else { gdk_window_set_cursor(window, NULL); @@ -282,26 +292,29 @@ static void gd_update_caption(GtkDisplayState *s) g_free(title); } -static void gd_update_windowsize(GtkDisplayState *s) +static void gd_update_windowsize(VirtualConsole *vc) { + GtkDisplayState *s = vc->s; + if (!s->full_screen) { GtkRequisition req; double sx, sy; if (s->free_scale) { - sx = s->scale_x; - sy = s->scale_y; + sx = vc->gfx.scale_x; + sy = vc->gfx.scale_y; - s->scale_y = 1.0; - s->scale_x = 1.0; + vc->gfx.scale_y = 1.0; + vc->gfx.scale_x = 1.0; } else { sx = 1.0; sy = 1.0; } - gtk_widget_set_size_request(s->drawing_area, - surface_width(s->ds) * s->scale_x, - surface_height(s->ds) * s->scale_y); + gtk_widget_set_size_request + (vc->gfx.drawing_area, + surface_width(vc->gfx.ds) * vc->gfx.scale_x, + surface_height(vc->gfx.ds) * vc->gfx.scale_y); #if GTK_CHECK_VERSION(3, 0, 0) gtk_widget_get_preferred_size(s->vbox, NULL, &req); #else @@ -313,18 +326,20 @@ static void gd_update_windowsize(GtkDisplayState *s) } } -static void gd_update_full_redraw(GtkDisplayState *s) +static void gd_update_full_redraw(VirtualConsole *vc) { + GtkWidget *area = vc->gfx.drawing_area; int ww, wh; - gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); - gtk_widget_queue_draw_area(s->drawing_area, 0, 0, ww, wh); + gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh); + gtk_widget_queue_draw_area(area, 0, 0, ww, wh); } static void gtk_release_modifiers(GtkDisplayState *s) { + VirtualConsole *vc = gd_vc_find_current(s); int i, keycode; - if (!gd_on_vga(s)) { + if (vc->type != GD_VC_GFX) { return; } for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { @@ -332,7 +347,7 @@ static void gtk_release_modifiers(GtkDisplayState *s) if (!s->modifier_pressed[i]) { continue; } - qemu_input_event_send_key_number(s->dcl.con, keycode, false); + qemu_input_event_send_key_number(vc->gfx.dcl.con, keycode, false); s->modifier_pressed[i] = false; } } @@ -342,7 +357,7 @@ static void gtk_release_modifiers(GtkDisplayState *s) static void gd_update(DisplayChangeListener *dcl, int x, int y, int w, int h) { - GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); int x1, x2, y1, y2; int mx, my; int fbw, fbh; @@ -350,21 +365,23 @@ static void gd_update(DisplayChangeListener *dcl, trace_gd_update(x, y, w, h); - if (s->convert) { - pixman_image_composite(PIXMAN_OP_SRC, s->ds->image, NULL, s->convert, + if (vc->gfx.convert) { + pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, + NULL, vc->gfx.convert, x, y, 0, 0, x, y, w, h); } - x1 = floor(x * s->scale_x); - y1 = floor(y * s->scale_y); + x1 = floor(x * vc->gfx.scale_x); + y1 = floor(y * vc->gfx.scale_y); - x2 = ceil(x * s->scale_x + w * s->scale_x); - y2 = ceil(y * s->scale_y + h * s->scale_y); + x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x); + y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y); - fbw = surface_width(s->ds) * s->scale_x; - fbh = surface_height(s->ds) * s->scale_y; + fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; + fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; - gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); + gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area), + &ww, &wh); mx = my = 0; if (ww > fbw) { @@ -374,7 +391,8 @@ static void gd_update(DisplayChangeListener *dcl, my = (wh - fbh) / 2; } - gtk_widget_queue_draw_area(s->drawing_area, mx + x1, my + y1, (x2 - x1), (y2 - y1)); + gtk_widget_queue_draw_area(vc->gfx.drawing_area, + mx + x1, my + y1, (x2 - x1), (y2 - y1)); } static void gd_refresh(DisplayChangeListener *dcl) @@ -386,7 +404,7 @@ static void gd_refresh(DisplayChangeListener *dcl) static void gd_mouse_set(DisplayChangeListener *dcl, int x, int y, int visible) { - GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GdkDisplay *dpy; GdkDeviceManager *mgr; gint x_root, y_root; @@ -395,29 +413,29 @@ static void gd_mouse_set(DisplayChangeListener *dcl, return; } - dpy = gtk_widget_get_display(s->drawing_area); + dpy = gtk_widget_get_display(vc->gfx.drawing_area); mgr = gdk_display_get_device_manager(dpy); - gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area), + gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area), x, y, &x_root, &y_root); gdk_device_warp(gdk_device_manager_get_client_pointer(mgr), - gtk_widget_get_screen(s->drawing_area), + gtk_widget_get_screen(vc->gfx.drawing_area), x_root, y_root); } #else static void gd_mouse_set(DisplayChangeListener *dcl, int x, int y, int visible) { - GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); gint x_root, y_root; if (qemu_input_is_absolute()) { return; } - gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area), + gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area), x, y, &x_root, &y_root); - gdk_display_warp_pointer(gtk_widget_get_display(s->drawing_area), - gtk_widget_get_screen(s->drawing_area), + gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area), + gtk_widget_get_screen(vc->gfx.drawing_area), x_root, y_root); } #endif @@ -425,7 +443,7 @@ static void gd_mouse_set(DisplayChangeListener *dcl, static void gd_cursor_define(DisplayChangeListener *dcl, QEMUCursor *c) { - GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GdkPixbuf *pixbuf; GdkCursor *cursor; @@ -433,9 +451,10 @@ static void gd_cursor_define(DisplayChangeListener *dcl, GDK_COLORSPACE_RGB, true, 8, c->width, c->height, c->width * 4, NULL, NULL); - cursor = gdk_cursor_new_from_pixbuf(gtk_widget_get_display(s->drawing_area), - pixbuf, c->hot_x, c->hot_y); - gdk_window_set_cursor(gtk_widget_get_window(s->drawing_area), cursor); + cursor = gdk_cursor_new_from_pixbuf + (gtk_widget_get_display(vc->gfx.drawing_area), + pixbuf, c->hot_x, c->hot_y); + gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor); g_object_unref(pixbuf); #if !GTK_CHECK_VERSION(3, 0, 0) gdk_cursor_unref(cursor); @@ -447,25 +466,25 @@ static void gd_cursor_define(DisplayChangeListener *dcl, static void gd_switch(DisplayChangeListener *dcl, DisplaySurface *surface) { - GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); bool resized = true; trace_gd_switch(surface_width(surface), surface_height(surface)); - if (s->surface) { - cairo_surface_destroy(s->surface); + if (vc->gfx.surface) { + cairo_surface_destroy(vc->gfx.surface); } - if (s->ds && - surface_width(s->ds) == surface_width(surface) && - surface_height(s->ds) == surface_height(surface)) { + if (vc->gfx.ds && + surface_width(vc->gfx.ds) == surface_width(surface) && + surface_height(vc->gfx.ds) == surface_height(surface)) { resized = false; } - s->ds = surface; + vc->gfx.ds = surface; - if (s->convert) { - pixman_image_unref(s->convert); - s->convert = NULL; + if (vc->gfx.convert) { + pixman_image_unref(vc->gfx.convert); + vc->gfx.convert = NULL; } if (surface->format == PIXMAN_x8r8g8b8) { @@ -475,7 +494,7 @@ static void gd_switch(DisplayChangeListener *dcl, * No need to convert, use surface directly. Should be the * common case as this is qemu_default_pixelformat(32) too. */ - s->surface = cairo_image_surface_create_for_data + vc->gfx.surface = cairo_image_surface_create_for_data (surface_data(surface), CAIRO_FORMAT_RGB24, surface_width(surface), @@ -483,26 +502,27 @@ static void gd_switch(DisplayChangeListener *dcl, surface_stride(surface)); } else { /* Must convert surface, use pixman to do it. */ - s->convert = pixman_image_create_bits(PIXMAN_x8r8g8b8, - surface_width(surface), - surface_height(surface), - NULL, 0); - s->surface = cairo_image_surface_create_for_data - ((void *)pixman_image_get_data(s->convert), + vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8, + surface_width(surface), + surface_height(surface), + NULL, 0); + vc->gfx.surface = cairo_image_surface_create_for_data + ((void *)pixman_image_get_data(vc->gfx.convert), CAIRO_FORMAT_RGB24, - pixman_image_get_width(s->convert), - pixman_image_get_height(s->convert), - pixman_image_get_stride(s->convert)); - pixman_image_composite(PIXMAN_OP_SRC, s->ds->image, NULL, s->convert, + pixman_image_get_width(vc->gfx.convert), + pixman_image_get_height(vc->gfx.convert), + pixman_image_get_stride(vc->gfx.convert)); + pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, + NULL, vc->gfx.convert, 0, 0, 0, 0, 0, 0, - pixman_image_get_width(s->convert), - pixman_image_get_height(s->convert)); + pixman_image_get_width(vc->gfx.convert), + pixman_image_get_height(vc->gfx.convert)); } if (resized) { - gd_update_windowsize(s); + gd_update_windowsize(vc); } else { - gd_update_full_redraw(s); + gd_update_full_redraw(vc); } } @@ -525,7 +545,7 @@ static void gd_mouse_mode_change(Notifier *notify, void *data) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE); } - gd_update_cursor(s, FALSE); + gd_update_cursor(gd_vc_find_current(s)); } /** GTK Events **/ @@ -534,9 +554,15 @@ static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event, void *opaque) { GtkDisplayState *s = opaque; + int i; if (!no_quit) { - unregister_displaychangelistener(&s->dcl); + for (i = 0; i < s->nb_vcs; i++) { + if (s->vc[i].type != GD_VC_GFX) { + continue; + } + unregister_displaychangelistener(&s->vc[i].gfx.dcl); + } qmp_quit(NULL); return FALSE; } @@ -546,7 +572,8 @@ static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event, static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) { - GtkDisplayState *s = opaque; + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; int mx, my; int ww, wh; int fbw, fbh; @@ -555,25 +582,25 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) return FALSE; } - fbw = surface_width(s->ds); - fbh = surface_height(s->ds); + fbw = surface_width(vc->gfx.ds); + fbh = surface_height(vc->gfx.ds); gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh); if (s->full_screen) { - s->scale_x = (double)ww / fbw; - s->scale_y = (double)wh / fbh; + vc->gfx.scale_x = (double)ww / fbw; + vc->gfx.scale_y = (double)wh / fbh; } else if (s->free_scale) { double sx, sy; sx = (double)ww / fbw; sy = (double)wh / fbh; - s->scale_x = s->scale_y = MIN(sx, sy); + vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); } - fbw *= s->scale_x; - fbh *= s->scale_y; + fbw *= vc->gfx.scale_x; + fbh *= vc->gfx.scale_y; mx = my = 0; if (ww > fbw) { @@ -594,8 +621,9 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) -1 * fbw, fbh); cairo_fill(cr); - cairo_scale(cr, s->scale_x, s->scale_y); - cairo_set_source_surface(cr, s->surface, mx / s->scale_x, my / s->scale_y); + cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y); + cairo_set_source_surface(cr, vc->gfx.surface, + mx / vc->gfx.scale_x, my / vc->gfx.scale_y); cairo_paint(cr); return TRUE; @@ -627,16 +655,18 @@ static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose, static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, void *opaque) { - GtkDisplayState *s = opaque; + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; int x, y; int mx, my; int fbh, fbw; int ww, wh; - fbw = surface_width(s->ds) * s->scale_x; - fbh = surface_height(s->ds) * s->scale_y; + fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; + fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; - gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); + gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area), + &ww, &wh); mx = my = 0; if (ww > fbw) { @@ -646,23 +676,23 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, my = (wh - fbh) / 2; } - x = (motion->x - mx) / s->scale_x; - y = (motion->y - my) / s->scale_y; + x = (motion->x - mx) / vc->gfx.scale_x; + y = (motion->y - my) / vc->gfx.scale_y; if (qemu_input_is_absolute()) { if (x < 0 || y < 0 || - x >= surface_width(s->ds) || - y >= surface_height(s->ds)) { + x >= surface_width(vc->gfx.ds) || + y >= surface_height(vc->gfx.ds)) { return TRUE; } - qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_X, x, - surface_width(s->ds)); - qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_Y, y, - surface_height(s->ds)); + qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x, + surface_width(vc->gfx.ds)); + qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y, + surface_height(vc->gfx.ds)); qemu_input_event_sync(); } else if (s->last_set && gd_is_grab_active(s)) { - qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_X, x - s->last_x); - qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_Y, y - s->last_y); + qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x); + qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y); qemu_input_event_sync(); } s->last_x = x; @@ -670,7 +700,7 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, s->last_set = TRUE; if (!qemu_input_is_absolute() && gd_is_grab_active(s)) { - GdkScreen *screen = gtk_widget_get_screen(s->drawing_area); + GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area); int x = (int)motion->x_root; int y = (int)motion->y_root; @@ -712,7 +742,8 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, void *opaque) { - GtkDisplayState *s = opaque; + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; InputButton btn; /* implicitly grab the input at the first click in the relative mode */ @@ -733,7 +764,8 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, return TRUE; } - qemu_input_queue_btn(s->dcl.con, btn, button->type == GDK_BUTTON_PRESS); + qemu_input_queue_btn(vc->gfx.dcl.con, btn, + button->type == GDK_BUTTON_PRESS); qemu_input_event_sync(); return TRUE; } @@ -741,7 +773,7 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll, void *opaque) { - GtkDisplayState *s = opaque; + VirtualConsole *vc = opaque; InputButton btn; if (scroll->direction == GDK_SCROLL_UP) { @@ -752,16 +784,17 @@ static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll, return TRUE; } - qemu_input_queue_btn(s->dcl.con, btn, true); + qemu_input_queue_btn(vc->gfx.dcl.con, btn, true); qemu_input_event_sync(); - qemu_input_queue_btn(s->dcl.con, btn, false); + qemu_input_queue_btn(vc->gfx.dcl.con, btn, false); qemu_input_event_sync(); return TRUE; } static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) { - GtkDisplayState *s = opaque; + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; int gdk_keycode = key->hardware_keycode; int i; @@ -799,7 +832,7 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) } } - qemu_input_event_send_key_number(s->dcl.con, qemu_keycode, + qemu_input_event_send_key_number(vc->gfx.dcl.con, qemu_keycode, key->type == GDK_KEY_PRESS); return TRUE; @@ -847,20 +880,14 @@ static void gd_menu_quit(GtkMenuItem *item, void *opaque) static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_by_menu(s); gint page; - if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) { + gtk_release_modifiers(s); + if (vc) { page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), - s->drawing_area); + vc->tab_item); gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), page); - } else { - gtk_release_modifiers(s); - VirtualConsole *vc = gd_vc_find_by_menu(s); - if (vc) { - page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), - vc->tab_item); - gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), page); - } } } @@ -878,91 +905,100 @@ static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque) static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); if (!s->full_screen) { gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); gtk_widget_set_size_request(s->menu_bar, 0, 0); - gtk_widget_set_size_request(s->drawing_area, -1, -1); - gtk_window_fullscreen(GTK_WINDOW(s->window)); - if (gd_on_vga(s)) { - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE); + if (vc->type == GD_VC_GFX) { + gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), + TRUE); } + gtk_window_fullscreen(GTK_WINDOW(s->window)); s->full_screen = TRUE; } else { gtk_window_unfullscreen(GTK_WINDOW(s->window)); gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s); gtk_widget_set_size_request(s->menu_bar, -1, -1); - gtk_widget_set_size_request(s->drawing_area, - surface_width(s->ds), - surface_height(s->ds)); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE); s->full_screen = FALSE; - s->scale_x = 1.0; - s->scale_y = 1.0; + if (vc->type == GD_VC_GFX) { + vc->gfx.scale_x = 1.0; + vc->gfx.scale_y = 1.0; + gtk_widget_set_size_request(vc->gfx.drawing_area, + surface_width(vc->gfx.ds), + surface_height(vc->gfx.ds)); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), + FALSE); + } } - gd_update_cursor(s, FALSE); + gd_update_cursor(vc); } static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), FALSE); - s->scale_x += .25; - s->scale_y += .25; + vc->gfx.scale_x += .25; + vc->gfx.scale_y += .25; - gd_update_windowsize(s); + gd_update_windowsize(vc); } static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), FALSE); - s->scale_x -= .25; - s->scale_y -= .25; + vc->gfx.scale_x -= .25; + vc->gfx.scale_y -= .25; - s->scale_x = MAX(s->scale_x, .25); - s->scale_y = MAX(s->scale_y, .25); + vc->gfx.scale_x = MAX(vc->gfx.scale_x, .25); + vc->gfx.scale_y = MAX(vc->gfx.scale_y, .25); - gd_update_windowsize(s); + gd_update_windowsize(vc); } static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); - s->scale_x = 1.0; - s->scale_y = 1.0; + vc->gfx.scale_x = 1.0; + vc->gfx.scale_y = 1.0; - gd_update_windowsize(s); + gd_update_windowsize(vc); } static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) { s->free_scale = TRUE; } else { s->free_scale = FALSE; - s->scale_x = 1.0; - s->scale_y = 1.0; - gd_update_windowsize(s); + vc->gfx.scale_x = 1.0; + vc->gfx.scale_y = 1.0; + gd_update_windowsize(vc); } - gd_update_full_redraw(s); + gd_update_full_redraw(vc); } -static void gd_grab_keyboard(GtkDisplayState *s) +static void gd_grab_keyboard(VirtualConsole *vc) { #if GTK_CHECK_VERSION(3, 0, 0) - GdkDisplay *display = gtk_widget_get_display(s->drawing_area); + GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); GdkDeviceManager *mgr = gdk_display_get_device_manager(display); GList *devices = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER); @@ -971,7 +1007,7 @@ static void gd_grab_keyboard(GtkDisplayState *s) GdkDevice *dev = tmp->data; if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) { gdk_device_grab(dev, - gtk_widget_get_window(s->drawing_area), + gtk_widget_get_window(vc->gfx.drawing_area), GDK_OWNERSHIP_NONE, FALSE, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, @@ -982,16 +1018,16 @@ static void gd_grab_keyboard(GtkDisplayState *s) } g_list_free(devices); #else - gdk_keyboard_grab(gtk_widget_get_window(s->drawing_area), + gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area), FALSE, GDK_CURRENT_TIME); #endif } -static void gd_ungrab_keyboard(GtkDisplayState *s) +static void gd_ungrab_keyboard(VirtualConsole *vc) { #if GTK_CHECK_VERSION(3, 0, 0) - GdkDisplay *display = gtk_widget_get_display(s->drawing_area); + GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); GdkDeviceManager *mgr = gdk_display_get_device_manager(display); GList *devices = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER); @@ -1010,9 +1046,9 @@ static void gd_ungrab_keyboard(GtkDisplayState *s) #endif } -static void gd_grab_pointer(GtkDisplayState *s) +static void gd_grab_pointer(VirtualConsole *vc) { - GdkDisplay *display = gtk_widget_get_display(s->drawing_area); + GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); #if GTK_CHECK_VERSION(3, 0, 0) GdkDeviceManager *mgr = gdk_display_get_device_manager(display); GList *devices = gdk_device_manager_list_devices(mgr, @@ -1022,7 +1058,7 @@ static void gd_grab_pointer(GtkDisplayState *s) GdkDevice *dev = tmp->data; if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) { gdk_device_grab(dev, - gtk_widget_get_window(s->drawing_area), + gtk_widget_get_window(vc->gfx.drawing_area), GDK_OWNERSHIP_NONE, FALSE, /* All events to come to our window directly */ @@ -1031,16 +1067,16 @@ static void gd_grab_pointer(GtkDisplayState *s) GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK | GDK_SCROLL_MASK, - s->null_cursor, + vc->s->null_cursor, GDK_CURRENT_TIME); } tmp = tmp->next; } g_list_free(devices); gdk_device_get_position(gdk_device_manager_get_client_pointer(mgr), - NULL, &s->grab_x_root, &s->grab_y_root); + NULL, &vc->s->grab_x_root, &vc->s->grab_y_root); #else - gdk_pointer_grab(gtk_widget_get_window(s->drawing_area), + gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area), FALSE, /* All events to come to our window directly */ GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | @@ -1048,16 +1084,16 @@ static void gd_grab_pointer(GtkDisplayState *s) GDK_BUTTON_MOTION_MASK | GDK_SCROLL_MASK, NULL, /* Allow cursor to move over entire desktop */ - s->null_cursor, + vc->s->null_cursor, GDK_CURRENT_TIME); gdk_display_get_pointer(display, NULL, - &s->grab_x_root, &s->grab_y_root, NULL); + &vc->s->grab_x_root, &vc->s->grab_y_root, NULL); #endif } -static void gd_ungrab_pointer(GtkDisplayState *s) +static void gd_ungrab_pointer(VirtualConsole *vc) { - GdkDisplay *display = gtk_widget_get_display(s->drawing_area); + GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); #if GTK_CHECK_VERSION(3, 0, 0) GdkDeviceManager *mgr = gdk_display_get_device_manager(display); GList *devices = gdk_device_manager_list_devices(mgr, @@ -1073,46 +1109,52 @@ static void gd_ungrab_pointer(GtkDisplayState *s) } g_list_free(devices); gdk_device_warp(gdk_device_manager_get_client_pointer(mgr), - gtk_widget_get_screen(s->drawing_area), - s->grab_x_root, s->grab_y_root); + gtk_widget_get_screen(vc->gfx.drawing_area), + vc->s->grab_x_root, vc->s->grab_y_root); #else gdk_pointer_ungrab(GDK_CURRENT_TIME); gdk_display_warp_pointer(display, - gtk_widget_get_screen(s->drawing_area), - s->grab_x_root, s->grab_y_root); + gtk_widget_get_screen(vc->gfx.drawing_area), + vc->s->grab_x_root, vc->s->grab_y_root); #endif } static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); if (gd_is_grab_active(s)) { - gd_grab_keyboard(s); - gd_grab_pointer(s); + gd_grab_keyboard(vc); + gd_grab_pointer(vc); } else { - gd_ungrab_keyboard(s); - gd_ungrab_pointer(s); + gd_ungrab_keyboard(vc); + gd_ungrab_pointer(vc); } gd_update_caption(s); - gd_update_cursor(s, FALSE); + gd_update_cursor(vc); } static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, gpointer data) { GtkDisplayState *s = data; + VirtualConsole *vc; gboolean on_vga; - gint page; if (!gtk_widget_get_realized(s->notebook)) { return; } - page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), s->drawing_area); - on_vga = arg2 == page; + vc = gd_vc_find_by_page(s, arg2); + if (!vc) { + return; + } + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), + TRUE); + on_vga = (vc->type == GD_VC_GFX); if (!on_vga) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE); @@ -1120,49 +1162,42 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE); } - - if (on_vga) { - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE); - } else { - VirtualConsole *vc; - vc = gd_vc_find_by_page(s, arg2); - if (vc) { - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), - TRUE); - } - } - gtk_widget_set_sensitive(s->grab_item, on_vga); - gd_update_cursor(s, TRUE); + gd_update_cursor(vc); } -static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data) +static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, + gpointer opaque) { - GtkDisplayState *s = data; + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) { - gd_grab_keyboard(s); + gd_grab_keyboard(vc); } return TRUE; } -static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data) +static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, + gpointer opaque) { - GtkDisplayState *s = data; + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) { - gd_ungrab_keyboard(s); + gd_ungrab_keyboard(vc); } return TRUE; } static gboolean gd_focus_out_event(GtkWidget *widget, - GdkEventCrossing *crossing, gpointer data) + GdkEventCrossing *crossing, gpointer opaque) { - GtkDisplayState *s = data; + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; gtk_release_modifiers(s); @@ -1218,8 +1253,8 @@ static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size, return TRUE; } -static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group, - GtkWidget *view_menu) +static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, int index, + GSList *group, GtkWidget *view_menu) { const char *label; char buffer[32]; @@ -1231,6 +1266,7 @@ static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSL snprintf(buffer, sizeof(buffer), "vc%d", index); snprintf(path, sizeof(path), "/View/VC%d", index); + vc->s = s; vc->vte.chr = vcs[index]; if (vc->vte.chr->label) { @@ -1275,6 +1311,7 @@ static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSL g_signal_connect(vadjustment, "changed", G_CALLBACK(gd_vc_adjustment_changed), vc); + vc->type = GD_VC_VTE; vc->tab_item = box; gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item, gtk_label_new(label)); @@ -1291,15 +1328,15 @@ static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSL return group; } -static void gd_vcs_init(GtkDisplayState *s, GSList *group, +static void gd_vcs_init(GtkDisplayState *s, int offset, GSList *group, GtkWidget *view_menu) { int i; for (i = 0; i < nb_vcs; i++) { - VirtualConsole *vc = &s->vc[i]; + VirtualConsole *vc = &s->vc[offset+i]; - group = gd_vc_init(s, vc, i, group, view_menu); + group = gd_vc_vte_init(s, vc, i, group, view_menu); s->nb_vcs++; } } @@ -1307,6 +1344,36 @@ static void gd_vcs_init(GtkDisplayState *s, GSList *group, /** Window Creation **/ +static void gd_connect_vc_gfx_signals(VirtualConsole *vc) +{ +#if GTK_CHECK_VERSION(3, 0, 0) + g_signal_connect(vc->gfx.drawing_area, "draw", + G_CALLBACK(gd_draw_event), vc); +#else + g_signal_connect(vc->gfx.drawing_area, "expose-event", + G_CALLBACK(gd_expose_event), vc); +#endif + g_signal_connect(vc->gfx.drawing_area, "event", + G_CALLBACK(gd_event), vc); + g_signal_connect(vc->gfx.drawing_area, "button-press-event", + G_CALLBACK(gd_button_event), vc); + g_signal_connect(vc->gfx.drawing_area, "button-release-event", + G_CALLBACK(gd_button_event), vc); + g_signal_connect(vc->gfx.drawing_area, "scroll-event", + G_CALLBACK(gd_scroll_event), vc); + g_signal_connect(vc->gfx.drawing_area, "key-press-event", + G_CALLBACK(gd_key_event), vc); + g_signal_connect(vc->gfx.drawing_area, "key-release-event", + G_CALLBACK(gd_key_event), vc); + + g_signal_connect(vc->gfx.drawing_area, "enter-notify-event", + G_CALLBACK(gd_enter_event), vc); + g_signal_connect(vc->gfx.drawing_area, "leave-notify-event", + G_CALLBACK(gd_leave_event), vc); + g_signal_connect(vc->gfx.drawing_area, "focus-out-event", + G_CALLBACK(gd_focus_out_event), vc); +} + static void gd_connect_signals(GtkDisplayState *s) { g_signal_connect(s->show_tabs_item, "activate", @@ -1315,26 +1382,6 @@ static void gd_connect_signals(GtkDisplayState *s) g_signal_connect(s->window, "delete-event", G_CALLBACK(gd_window_close), s); -#if GTK_CHECK_VERSION(3, 0, 0) - g_signal_connect(s->drawing_area, "draw", - G_CALLBACK(gd_draw_event), s); -#else - g_signal_connect(s->drawing_area, "expose-event", - G_CALLBACK(gd_expose_event), s); -#endif - g_signal_connect(s->drawing_area, "event", - G_CALLBACK(gd_event), s); - g_signal_connect(s->drawing_area, "button-press-event", - G_CALLBACK(gd_button_event), s); - g_signal_connect(s->drawing_area, "button-release-event", - G_CALLBACK(gd_button_event), s); - g_signal_connect(s->drawing_area, "scroll-event", - G_CALLBACK(gd_scroll_event), s); - g_signal_connect(s->drawing_area, "key-press-event", - G_CALLBACK(gd_key_event), s); - g_signal_connect(s->drawing_area, "key-release-event", - G_CALLBACK(gd_key_event), s); - g_signal_connect(s->pause_item, "activate", G_CALLBACK(gd_menu_pause), s); g_signal_connect(s->reset_item, "activate", @@ -1353,18 +1400,10 @@ static void gd_connect_signals(GtkDisplayState *s) G_CALLBACK(gd_menu_zoom_fixed), s); g_signal_connect(s->zoom_fit_item, "activate", G_CALLBACK(gd_menu_zoom_fit), s); - g_signal_connect(s->vga_item, "activate", - G_CALLBACK(gd_menu_switch_vc), s); g_signal_connect(s->grab_item, "activate", G_CALLBACK(gd_menu_grab_input), s); g_signal_connect(s->notebook, "switch-page", G_CALLBACK(gd_change_page), s); - g_signal_connect(s->drawing_area, "enter-notify-event", - G_CALLBACK(gd_enter_event), s); - g_signal_connect(s->drawing_area, "leave-notify-event", - G_CALLBACK(gd_leave_event), s); - g_signal_connect(s->drawing_area, "focus-out-event", - G_CALLBACK(gd_focus_out_event), s); } static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *accel_group) @@ -1400,6 +1439,59 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *acce return machine_menu; } +static const DisplayChangeListenerOps dcl_ops = { + .dpy_name = "gtk", + .dpy_gfx_update = gd_update, + .dpy_gfx_switch = gd_switch, + .dpy_refresh = gd_refresh, + .dpy_mouse_set = gd_mouse_set, + .dpy_cursor_define = gd_cursor_define, +}; + +static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, + QemuConsole *con, int index, + GSList *group, GtkWidget *view_menu) +{ + vc->s = s; + vc->gfx.scale_x = 1.0; + vc->gfx.scale_y = 1.0; + + vc->gfx.drawing_area = gtk_drawing_area_new(); + gtk_widget_add_events(vc->gfx.drawing_area, + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_MOTION_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_SCROLL_MASK | + GDK_KEY_PRESS_MASK); + gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE); + gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE); + + vc->type = GD_VC_GFX; + vc->tab_item = vc->gfx.drawing_area; + gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), + vc->tab_item, gtk_label_new("VGA")); + gd_connect_vc_gfx_signals(vc); + + vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA"); + group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); + gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), + "/View/VGA"); + gtk_accel_map_add_entry("/View/VGA", GDK_KEY_1, HOTKEY_MODIFIERS); + gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item); + + g_signal_connect(vc->menu_item, "activate", + G_CALLBACK(gd_menu_switch_vc), s); + + vc->gfx.dcl.ops = &dcl_ops; + vc->gfx.dcl.con = con; + register_displaychangelistener(&vc->gfx.dcl); + + return group; +} + static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_group) { GSList *group = NULL; @@ -1459,15 +1551,13 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_g separator = gtk_separator_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); - s->vga_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA"); - group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s->vga_item)); - gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->vga_item), - "/View/VGA"); - gtk_accel_map_add_entry("/View/VGA", GDK_KEY_1, HOTKEY_MODIFIERS); - gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->vga_item); + /* gfx */ + group = gd_vc_gfx_init(s, &s->vc[0], qemu_console_lookup_by_index(0), 0, + group, view_menu); #if defined(CONFIG_VTE) - gd_vcs_init(s, group, view_menu); + /* vte */ + gd_vcs_init(s, 1, group, view_menu); #endif separator = gtk_separator_menu_item_new(); @@ -1501,15 +1591,6 @@ static void gd_create_menus(GtkDisplayState *s) s->accel_group = accel_group; } -static const DisplayChangeListenerOps dcl_ops = { - .dpy_name = "gtk", - .dpy_gfx_update = gd_update, - .dpy_gfx_switch = gd_switch, - .dpy_refresh = gd_refresh, - .dpy_mouse_set = gd_mouse_set, - .dpy_cursor_define = gd_cursor_define, -}; - void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) { GtkDisplayState *s = g_malloc0(sizeof(*s)); @@ -1517,9 +1598,6 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) gtk_init(NULL, NULL); - s->dcl.ops = &dcl_ops; - s->dcl.con = qemu_console_lookup_by_index(0); - s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); #if GTK_CHECK_VERSION(3, 2, 0) s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); @@ -1527,11 +1605,8 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) s->vbox = gtk_vbox_new(FALSE, 0); #endif s->notebook = gtk_notebook_new(); - s->drawing_area = gtk_drawing_area_new(); s->menu_bar = gtk_menu_bar_new(); - s->scale_x = 1.0; - s->scale_y = 1.0; s->free_scale = FALSE; setlocale(LC_ALL, ""); @@ -1544,8 +1619,6 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier); qemu_add_vm_change_state_handler(gd_change_runstate, s); - gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), s->drawing_area, gtk_label_new("VGA")); - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg"); if (filename) { GError *error = NULL; @@ -1562,18 +1635,6 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) gd_connect_signals(s); - gtk_widget_add_events(s->drawing_area, - GDK_POINTER_MOTION_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_BUTTON_MOTION_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK | - GDK_SCROLL_MASK | - GDK_KEY_PRESS_MASK); - gtk_widget_set_double_buffered(s->drawing_area, FALSE); - gtk_widget_set_can_focus(s->drawing_area, TRUE); - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE); @@ -1593,8 +1654,6 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item)); } - register_displaychangelistener(&s->dcl); - global_state = s; } From ed1132e41a31ded554adc542ddf043714f05b9d0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 30 Apr 2014 16:39:18 +0200 Subject: [PATCH 08/24] gtk: support multiple gfx displays Each display gets its own tab. Tab switching continues to work like it did, just the hotkeys of the vte consoles changes in case a secondary display is present as it will get ctrl-alt-2 assigned and the vtes are shifted by one. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 80 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index bc42f680bb..0756432e8d 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -1206,6 +1206,26 @@ static gboolean gd_focus_out_event(GtkWidget *widget, /** Virtual Console Callbacks **/ +static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc, + const char *label, int idx, + GSList *group, GtkWidget *view_menu) +{ + char path[32]; + + snprintf(path, sizeof(path), "/View/VC%d", idx); + + vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label); + group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); + gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path); + gtk_accel_map_add_entry(path, GDK_KEY_1 + idx, HOTKEY_MODIFIERS); + + g_signal_connect(vc->menu_item, "activate", + G_CALLBACK(gd_menu_switch_vc), s); + gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item); + + return group; +} + #if defined(CONFIG_VTE) static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque) { @@ -1253,32 +1273,22 @@ static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size, return TRUE; } -static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, int index, +static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, + CharDriverState *chr, int idx, GSList *group, GtkWidget *view_menu) { const char *label; char buffer[32]; - char path[32]; GtkWidget *box; GtkWidget *scrollbar; GtkAdjustment *vadjustment; - snprintf(buffer, sizeof(buffer), "vc%d", index); - snprintf(path, sizeof(path), "/View/VC%d", index); - vc->s = s; - vc->vte.chr = vcs[index]; + vc->vte.chr = chr; - if (vc->vte.chr->label) { - label = vc->vte.chr->label; - } else { - label = buffer; - } - - vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label); - group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); - gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path); - gtk_accel_map_add_entry(path, GDK_KEY_2 + index, HOTKEY_MODIFIERS); + snprintf(buffer, sizeof(buffer), "vc%d", idx); + label = vc->vte.chr->label ? vc->vte.chr->label : buffer; + group = gd_vc_menu_init(s, vc, vc->vte.chr->label, idx, group, view_menu); vc->vte.terminal = vte_terminal_new(); g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc); @@ -1315,10 +1325,6 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, int index, vc->tab_item = box; gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item, gtk_label_new(label)); - g_signal_connect(vc->menu_item, "activate", - G_CALLBACK(gd_menu_switch_vc), s); - - gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item); qemu_chr_be_generic_open(vc->vte.chr); if (vc->vte.chr->init) { @@ -1328,15 +1334,14 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, int index, return group; } -static void gd_vcs_init(GtkDisplayState *s, int offset, GSList *group, +static void gd_vcs_init(GtkDisplayState *s, GSList *group, GtkWidget *view_menu) { int i; for (i = 0; i < nb_vcs; i++) { - VirtualConsole *vc = &s->vc[offset+i]; - - group = gd_vc_vte_init(s, vc, i, group, view_menu); + VirtualConsole *vc = &s->vc[s->nb_vcs]; + group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu); s->nb_vcs++; } } @@ -1449,7 +1454,7 @@ static const DisplayChangeListenerOps dcl_ops = { }; static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, - QemuConsole *con, int index, + QemuConsole *con, int idx, GSList *group, GtkWidget *view_menu) { vc->s = s; @@ -1475,15 +1480,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, vc->tab_item, gtk_label_new("VGA")); gd_connect_vc_gfx_signals(vc); - vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA"); - group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); - gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), - "/View/VGA"); - gtk_accel_map_add_entry("/View/VGA", GDK_KEY_1, HOTKEY_MODIFIERS); - gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item); - - g_signal_connect(vc->menu_item, "activate", - G_CALLBACK(gd_menu_switch_vc), s); + group = gd_vc_menu_init(s, vc, "VGA", idx, group, view_menu); vc->gfx.dcl.ops = &dcl_ops; vc->gfx.dcl.con = con; @@ -1497,6 +1494,8 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_g GSList *group = NULL; GtkWidget *view_menu; GtkWidget *separator; + QemuConsole *con; + int vc; view_menu = gtk_menu_new(); gtk_menu_set_accel_group(GTK_MENU(view_menu), accel_group); @@ -1552,12 +1551,19 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_g gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); /* gfx */ - group = gd_vc_gfx_init(s, &s->vc[0], qemu_console_lookup_by_index(0), 0, - group, view_menu); + for (vc = 0;; vc++) { + con = qemu_console_lookup_by_index(vc); + if (!con || !qemu_console_is_graphic(con)) { + break; + } + group = gd_vc_gfx_init(s, &s->vc[vc], con, + vc, group, view_menu); + s->nb_vcs++; + } #if defined(CONFIG_VTE) /* vte */ - gd_vcs_init(s, 1, group, view_menu); + gd_vcs_init(s, group, view_menu); #endif separator = gtk_separator_menu_item_new(); From 6a24ced5cab3e7cc5b2efd7122b5e191950998aa Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 5 May 2014 14:36:56 +0200 Subject: [PATCH 09/24] gtk: use device type as label IMO useful than showing VGA for any graphic device even in case it is something completely different. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 0756432e8d..a8393ddd76 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -67,6 +67,7 @@ #include "x_keymap.h" #include "keymaps.h" #include "sysemu/char.h" +#include "qom/object.h" #define MAX_VCS 10 @@ -1457,6 +1458,15 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, QemuConsole *con, int idx, GSList *group, GtkWidget *view_menu) { + const char *label = "VGA"; + Error *local_err = NULL; + Object *obj; + + obj = object_property_get_link(OBJECT(con), "device", &local_err); + if (obj) { + label = object_get_typename(obj); + } + vc->s = s; vc->gfx.scale_x = 1.0; vc->gfx.scale_y = 1.0; @@ -1477,10 +1487,10 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, vc->type = GD_VC_GFX; vc->tab_item = vc->gfx.drawing_area; gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), - vc->tab_item, gtk_label_new("VGA")); + vc->tab_item, gtk_label_new(label)); gd_connect_vc_gfx_signals(vc); - group = gd_vc_menu_init(s, vc, "VGA", idx, group, view_menu); + group = gd_vc_menu_init(s, vc, label, idx, group, view_menu); vc->gfx.dcl.ops = &dcl_ops; vc->gfx.dcl.con = con; From d3ef575080c1d7a59a7df0678702f5e320000e87 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 5 May 2014 14:55:18 +0200 Subject: [PATCH 10/24] gtk: simplify resize Simply ask for a small window size. When the widgets don't fit in gtk will automatically make the window large enougth to make things fit, no need to try (and fail) duplicate that logic in qemu. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 42 +++++++++++++++--------------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index a8393ddd76..6790cf8f7c 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -296,35 +296,23 @@ static void gd_update_caption(GtkDisplayState *s) static void gd_update_windowsize(VirtualConsole *vc) { GtkDisplayState *s = vc->s; + double sx, sy; - if (!s->full_screen) { - GtkRequisition req; - double sx, sy; - - if (s->free_scale) { - sx = vc->gfx.scale_x; - sy = vc->gfx.scale_y; - - vc->gfx.scale_y = 1.0; - vc->gfx.scale_x = 1.0; - } else { - sx = 1.0; - sy = 1.0; - } - - gtk_widget_set_size_request - (vc->gfx.drawing_area, - surface_width(vc->gfx.ds) * vc->gfx.scale_x, - surface_height(vc->gfx.ds) * vc->gfx.scale_y); -#if GTK_CHECK_VERSION(3, 0, 0) - gtk_widget_get_preferred_size(s->vbox, NULL, &req); -#else - gtk_widget_size_request(s->vbox, &req); -#endif - - gtk_window_resize(GTK_WINDOW(s->window), - req.width * sx, req.height * sy); + if (vc->type != GD_VC_GFX || s->full_screen) { + return; } + + if (s->free_scale) { + sx = 1.0; + sy = 1.0; + } else { + sx = vc->gfx.scale_x; + sy = vc->gfx.scale_y; + } + gtk_widget_set_size_request(vc->gfx.drawing_area, + surface_width(vc->gfx.ds) * sx, + surface_height(vc->gfx.ds) * sy); + gtk_window_resize(GTK_WINDOW(s->window), 320, 240); } static void gd_update_full_redraw(VirtualConsole *vc) From cdeb7090ee974f80971490dc013ac3548405bf36 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 5 May 2014 16:23:33 +0200 Subject: [PATCH 11/24] gtk: allow moving tabs to windows and back. "View->Detach tab" will move to tab to a new window. Simply closing the window will move it back into a notebook tab. The label will be permamently stored in VirtualConsole->label, so it can easily be reused to (re-)label tabs and windows. Works for vte tabs only for now. pointer/kbd grab code needs adaptions before we can enable it for gfx tabs too. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 6790cf8f7c..07883a1a3e 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -134,6 +134,8 @@ typedef enum VirtualConsoleType { typedef struct VirtualConsole { GtkDisplayState *s; + char *label; + GtkWidget *window; GtkWidget *menu_item; GtkWidget *tab_item; VirtualConsoleType type; @@ -173,6 +175,7 @@ struct GtkDisplayState { VirtualConsole vc[MAX_VCS]; GtkWidget *show_tabs_item; + GtkWidget *untabify_item; GtkWidget *vbox; GtkWidget *notebook; @@ -891,6 +894,50 @@ static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque) } } +static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event, + void *opaque) +{ + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; + + gtk_widget_set_sensitive(vc->menu_item, true); + gtk_widget_reparent(vc->tab_item, s->notebook); + gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook), + vc->tab_item, vc->label); + gtk_widget_destroy(vc->window); + vc->window = NULL; + return TRUE; +} + +static void gd_menu_untabify(GtkMenuItem *item, void *opaque) +{ + GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); + char *title; + + if (vc->type == GD_VC_GFX) { + /* temporary: needs more work to get grabs etc correct */ + return; + } + if (!vc->window) { + gtk_widget_set_sensitive(vc->menu_item, false); + vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_reparent(vc->tab_item, vc->window); + + if (qemu_name) { + title = g_strdup_printf("QEMU (%s): %s", qemu_name, vc->label); + } else { + title = g_strdup_printf("QEMU: %s", vc->label); + } + gtk_window_set_title(GTK_WINDOW(vc->window), title); + g_free(title); + + g_signal_connect(vc->window, "delete-event", + G_CALLBACK(gd_tab_window_close), vc); + gtk_widget_show_all(vc->window); + } +} + static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; @@ -1196,14 +1243,13 @@ static gboolean gd_focus_out_event(GtkWidget *widget, /** Virtual Console Callbacks **/ static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc, - const char *label, int idx, - GSList *group, GtkWidget *view_menu) + int idx, GSList *group, GtkWidget *view_menu) { char path[32]; snprintf(path, sizeof(path), "/View/VC%d", idx); - vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label); + vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label); group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path); gtk_accel_map_add_entry(path, GDK_KEY_1 + idx, HOTKEY_MODIFIERS); @@ -1266,7 +1312,6 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, CharDriverState *chr, int idx, GSList *group, GtkWidget *view_menu) { - const char *label; char buffer[32]; GtkWidget *box; GtkWidget *scrollbar; @@ -1276,8 +1321,9 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, vc->vte.chr = chr; snprintf(buffer, sizeof(buffer), "vc%d", idx); - label = vc->vte.chr->label ? vc->vte.chr->label : buffer; - group = gd_vc_menu_init(s, vc, vc->vte.chr->label, idx, group, view_menu); + vc->label = g_strdup_printf("%s", vc->vte.chr->label + ? vc->vte.chr->label : buffer); + group = gd_vc_menu_init(s, vc, idx, group, view_menu); vc->vte.terminal = vte_terminal_new(); g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc); @@ -1313,7 +1359,7 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, vc->type = GD_VC_VTE; vc->tab_item = box; gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item, - gtk_label_new(label)); + gtk_label_new(vc->label)); qemu_chr_be_generic_open(vc->vte.chr); if (vc->vte.chr->init) { @@ -1372,6 +1418,8 @@ static void gd_connect_signals(GtkDisplayState *s) { g_signal_connect(s->show_tabs_item, "activate", G_CALLBACK(gd_menu_show_tabs), s); + g_signal_connect(s->untabify_item, "activate", + G_CALLBACK(gd_menu_untabify), s); g_signal_connect(s->window, "delete-event", G_CALLBACK(gd_window_close), s); @@ -1446,13 +1494,14 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, QemuConsole *con, int idx, GSList *group, GtkWidget *view_menu) { - const char *label = "VGA"; Error *local_err = NULL; Object *obj; obj = object_property_get_link(OBJECT(con), "device", &local_err); if (obj) { - label = object_get_typename(obj); + vc->label = g_strdup_printf("%s", object_get_typename(obj)); + } else { + vc->label = g_strdup_printf("VGA"); } vc->s = s; @@ -1475,10 +1524,10 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, vc->type = GD_VC_GFX; vc->tab_item = vc->gfx.drawing_area; gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), - vc->tab_item, gtk_label_new(label)); + vc->tab_item, gtk_label_new(vc->label)); gd_connect_vc_gfx_signals(vc); - group = gd_vc_menu_init(s, vc, label, idx, group, view_menu); + group = gd_vc_menu_init(s, vc, idx, group, view_menu); vc->gfx.dcl.ops = &dcl_ops; vc->gfx.dcl.con = con; @@ -1570,6 +1619,9 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_g s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs")); gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item); + s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab")); + gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item); + return view_menu; } From 74444bc1983bfe4cea794a5b4f2e6bc7ea5552ed Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 6 May 2014 10:27:54 +0200 Subject: [PATCH 12/24] gtk: add tab to trace events So you can see which of multiple displays (if present) was resized ;) Signed-off-by: Gerd Hoffmann --- trace-events | 6 +++--- ui/gtk.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/trace-events b/trace-events index b6d289d720..3a41abf249 100644 --- a/trace-events +++ b/trace-events @@ -1045,9 +1045,9 @@ displaychangelistener_unregister(void *dcl, const char *name) "%p [ %s ]" ppm_save(const char *filename, void *display_surface) "%s surface=%p" # ui/gtk.c -gd_switch(int width, int height) "width=%d, height=%d" -gd_update(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d" -gd_key_event(int gdk_keycode, int qemu_keycode, const char *action) "translated GDK keycode %d to QEMU keycode %d (%s)" +gd_switch(const char *tab, int width, int height) "tab=%s, width=%d, height=%d" +gd_update(const char *tab, int x, int y, int w, int h) "tab=%s, x=%d, y=%d, w=%d, h=%d" +gd_key_event(const char *tab, int gdk_keycode, int qemu_keycode, const char *action) "tab=%s, translated GDK keycode %d to QEMU keycode %d (%s)" # ui/input.c input_event_key_number(int conidx, int number, bool down) "con %d, key number 0x%x, down %d" diff --git a/ui/gtk.c b/ui/gtk.c index 07883a1a3e..3ee946523d 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -355,7 +355,7 @@ static void gd_update(DisplayChangeListener *dcl, int fbw, fbh; int ww, wh; - trace_gd_update(x, y, w, h); + trace_gd_update(vc->label, x, y, w, h); if (vc->gfx.convert) { pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, @@ -461,7 +461,7 @@ static void gd_switch(DisplayChangeListener *dcl, VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); bool resized = true; - trace_gd_switch(surface_width(surface), surface_height(surface)); + trace_gd_switch(vc->label, surface_width(surface), surface_height(surface)); if (vc->gfx.surface) { cairo_surface_destroy(vc->gfx.surface); @@ -815,7 +815,7 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) } #endif - trace_gd_key_event(gdk_keycode, qemu_keycode, + trace_gd_key_event(vc->label, gdk_keycode, qemu_keycode, (key->type == GDK_KEY_PRESS) ? "down" : "up"); for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { From 1c856da57b29e41c73701f6f798589d50274b368 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 6 May 2014 10:43:52 +0200 Subject: [PATCH 13/24] gtk: add gd_grab trace event Input grab code is tricky, add some debug & trouble shooting aid. Signed-off-by: Gerd Hoffmann --- trace-events | 1 + ui/gtk.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/trace-events b/trace-events index 3a41abf249..aaaae42e69 100644 --- a/trace-events +++ b/trace-events @@ -1048,6 +1048,7 @@ ppm_save(const char *filename, void *display_surface) "%s surface=%p" gd_switch(const char *tab, int width, int height) "tab=%s, width=%d, height=%d" gd_update(const char *tab, int x, int y, int w, int h) "tab=%s, x=%d, y=%d, w=%d, h=%d" gd_key_event(const char *tab, int gdk_keycode, int qemu_keycode, const char *action) "tab=%s, translated GDK keycode %d to QEMU keycode %d (%s)" +gd_grab(const char *tab, const char *device, bool on) "tab=%s, %s %d" # ui/input.c input_event_key_number(int conidx, int number, bool down) "con %d, key number 0x%x, down %d" diff --git a/ui/gtk.c b/ui/gtk.c index 3ee946523d..0e35abfea3 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -1058,6 +1058,7 @@ static void gd_grab_keyboard(VirtualConsole *vc) FALSE, GDK_CURRENT_TIME); #endif + trace_gd_grab(vc->label, "kbd", true); } static void gd_ungrab_keyboard(VirtualConsole *vc) @@ -1080,6 +1081,7 @@ static void gd_ungrab_keyboard(VirtualConsole *vc) #else gdk_keyboard_ungrab(GDK_CURRENT_TIME); #endif + trace_gd_grab(vc->label, "kbd", false); } static void gd_grab_pointer(VirtualConsole *vc) @@ -1125,6 +1127,7 @@ static void gd_grab_pointer(VirtualConsole *vc) gdk_display_get_pointer(display, NULL, &vc->s->grab_x_root, &vc->s->grab_y_root, NULL); #endif + trace_gd_grab(vc->label, "ptr", true); } static void gd_ungrab_pointer(VirtualConsole *vc) @@ -1153,6 +1156,7 @@ static void gd_ungrab_pointer(VirtualConsole *vc) gtk_widget_get_screen(vc->gfx.drawing_area), vc->s->grab_x_root, vc->s->grab_y_root); #endif + trace_gd_grab(vc->label, "ptr", false); } static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) From 4c638e2e4ba03cc9c1bca4400f4c68870208afe3 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 6 May 2014 11:01:31 +0200 Subject: [PATCH 14/24] gtk: keep track of grab owner Simplifies grab state tracking and makes ungrab more reliable. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 0e35abfea3..d87bd1bfeb 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -185,6 +185,8 @@ struct GtkDisplayState { int last_y; int grab_x_root; int grab_y_root; + VirtualConsole *kbd_owner; + VirtualConsole *ptr_owner; gboolean full_screen; @@ -1058,11 +1060,19 @@ static void gd_grab_keyboard(VirtualConsole *vc) FALSE, GDK_CURRENT_TIME); #endif + vc->s->kbd_owner = vc; trace_gd_grab(vc->label, "kbd", true); } -static void gd_ungrab_keyboard(VirtualConsole *vc) +static void gd_ungrab_keyboard(GtkDisplayState *s) { + VirtualConsole *vc = s->kbd_owner; + + if (vc == NULL) { + return; + } + s->kbd_owner = NULL; + #if GTK_CHECK_VERSION(3, 0, 0) GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); GdkDeviceManager *mgr = gdk_display_get_device_manager(display); @@ -1127,11 +1137,19 @@ static void gd_grab_pointer(VirtualConsole *vc) gdk_display_get_pointer(display, NULL, &vc->s->grab_x_root, &vc->s->grab_y_root, NULL); #endif + vc->s->ptr_owner = vc; trace_gd_grab(vc->label, "ptr", true); } -static void gd_ungrab_pointer(VirtualConsole *vc) +static void gd_ungrab_pointer(GtkDisplayState *s) { + VirtualConsole *vc = s->ptr_owner; + + if (vc == NULL) { + return; + } + s->ptr_owner = NULL; + GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); #if GTK_CHECK_VERSION(3, 0, 0) GdkDeviceManager *mgr = gdk_display_get_device_manager(display); @@ -1168,8 +1186,8 @@ static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) gd_grab_keyboard(vc); gd_grab_pointer(vc); } else { - gd_ungrab_keyboard(vc); - gd_ungrab_pointer(vc); + gd_ungrab_keyboard(s); + gd_ungrab_pointer(s); } gd_update_caption(s); @@ -1227,7 +1245,7 @@ static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, GtkDisplayState *s = vc->s; if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) { - gd_ungrab_keyboard(vc); + gd_ungrab_keyboard(s); } return TRUE; From 746b86703003b4a6e5323b51b324f591dcd1a84b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 6 May 2014 11:04:57 +0200 Subject: [PATCH 15/24] gtk: skip keyboard grab when hover autograb is active It's pointless. With grab on hover enabled the keyboard grab is already active when you press Ctrl-Alt-G ;) Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/gtk.c b/ui/gtk.c index d87bd1bfeb..f8048138fe 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -1183,7 +1183,9 @@ static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) VirtualConsole *vc = gd_vc_find_current(s); if (gd_is_grab_active(s)) { - gd_grab_keyboard(vc); + if (!gd_grab_on_hover(s)) { + gd_grab_keyboard(vc); + } gd_grab_pointer(vc); } else { gd_ungrab_keyboard(s); From 4eeaa3a88557c7540a75f7ca2cb1171c57d56db9 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 6 May 2014 11:20:17 +0200 Subject: [PATCH 16/24] gtk: update gd_update_caption Adapt to recent changes, handle multiple windows. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index f8048138fe..7d6a20dee8 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -271,11 +271,20 @@ static void gd_update_cursor(VirtualConsole *vc) static void gd_update_caption(GtkDisplayState *s) { const char *status = ""; + gchar *prefix; gchar *title; const char *grab = ""; bool is_paused = !runstate_is_running(); + int i; - if (gd_is_grab_active(s)) { + if (qemu_name) { + prefix = g_strdup_printf("QEMU (%s)", qemu_name); + } else { + prefix = g_strdup_printf("QEMU"); + } + + if (s->ptr_owner != NULL && + s->ptr_owner->window == NULL) { grab = _(" - Press Ctrl+Alt+G to release grab"); } @@ -287,15 +296,24 @@ static void gd_update_caption(GtkDisplayState *s) is_paused); s->external_pause_update = false; - if (qemu_name) { - title = g_strdup_printf("QEMU (%s)%s%s", qemu_name, status, grab); - } else { - title = g_strdup_printf("QEMU%s%s", status, grab); + title = g_strdup_printf("%s%s%s", prefix, status, grab); + gtk_window_set_title(GTK_WINDOW(s->window), title); + g_free(title); + + for (i = 0; i < s->nb_vcs; i++) { + VirtualConsole *vc = &s->vc[i]; + + if (!vc->window) { + continue; + } + title = g_strdup_printf("%s: %s%s%s", prefix, vc->label, + vc == s->kbd_owner ? " +kbd" : "", + vc == s->ptr_owner ? " +ptr" : ""); + gtk_window_set_title(GTK_WINDOW(vc->window), title); + g_free(title); } - gtk_window_set_title(GTK_WINDOW(s->window), title); - - g_free(title); + g_free(prefix); } static void gd_update_windowsize(VirtualConsole *vc) @@ -915,7 +933,6 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; VirtualConsole *vc = gd_vc_find_current(s); - char *title; if (vc->type == GD_VC_GFX) { /* temporary: needs more work to get grabs etc correct */ @@ -926,17 +943,11 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque) vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_reparent(vc->tab_item, vc->window); - if (qemu_name) { - title = g_strdup_printf("QEMU (%s): %s", qemu_name, vc->label); - } else { - title = g_strdup_printf("QEMU: %s", vc->label); - } - gtk_window_set_title(GTK_WINDOW(vc->window), title); - g_free(title); - g_signal_connect(vc->window, "delete-event", G_CALLBACK(gd_tab_window_close), vc); gtk_widget_show_all(vc->window); + + gd_update_caption(s); } } From 2884cf5b934808f547b5268a51be631805c25857 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 6 May 2014 11:45:30 +0200 Subject: [PATCH 17/24] gtk: fix grab checks Make it handle multiple windows case correctly. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 7d6a20dee8..91b68241e3 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -201,6 +201,9 @@ struct GtkDisplayState { static GtkDisplayState *global_state; +static void gd_grab_pointer(VirtualConsole *vc); +static void gd_ungrab_pointer(GtkDisplayState *s); + /** Utility Functions **/ static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s) @@ -261,7 +264,7 @@ static void gd_update_cursor(VirtualConsole *vc) } window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area)); - if (s->full_screen || qemu_input_is_absolute() || gd_is_grab_active(s)) { + if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) { gdk_window_set_cursor(window, s->null_cursor); } else { gdk_window_set_cursor(window, NULL); @@ -702,7 +705,7 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y, surface_height(vc->gfx.ds)); qemu_input_event_sync(); - } else if (s->last_set && gd_is_grab_active(s)) { + } else if (s->last_set && s->ptr_owner == vc) { qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x); qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y); qemu_input_event_sync(); @@ -711,7 +714,7 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, s->last_y = y; s->last_set = TRUE; - if (!qemu_input_is_absolute() && gd_is_grab_active(s)) { + if (!qemu_input_is_absolute() && s->ptr_owner == vc) { GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area); int x = (int)motion->x_root; int y = (int)motion->y_root; @@ -760,9 +763,18 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, /* implicitly grab the input at the first click in the relative mode */ if (button->button == 1 && button->type == GDK_BUTTON_PRESS && - !qemu_input_is_absolute() && !gd_is_grab_active(s)) { - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), - TRUE); + !qemu_input_is_absolute() && s->ptr_owner != vc) { + gd_ungrab_pointer(s); + if (!vc->window) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), + TRUE); + } else { +#if 0 + /* FIXME: no way (yet) to ungrab */ + gd_grab_pointer(vc); +#endif + gd_update_caption(s); + } return TRUE; } @@ -1224,7 +1236,6 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, } gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); - on_vga = (vc->type == GD_VC_GFX); if (!on_vga) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), @@ -1244,10 +1255,11 @@ static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, VirtualConsole *vc = opaque; GtkDisplayState *s = vc->s; - if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) { + if (gd_grab_on_hover(s)) { + gd_ungrab_keyboard(s); gd_grab_keyboard(vc); + gd_update_caption(s); } - return TRUE; } @@ -1257,10 +1269,10 @@ static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, VirtualConsole *vc = opaque; GtkDisplayState *s = vc->s; - if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) { + if (gd_grab_on_hover(s)) { gd_ungrab_keyboard(s); + gd_update_caption(s); } - return TRUE; } @@ -1271,7 +1283,6 @@ static gboolean gd_focus_out_event(GtkWidget *widget, GtkDisplayState *s = vc->s; gtk_release_modifiers(s); - return TRUE; } From 99623c90d14d3112b88ace3ed0abded915c9116e Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 6 May 2014 11:48:28 +0200 Subject: [PATCH 18/24] gtk: update all windows on mouse mode changes We might have multiple graphic displays now which all need a cursor update. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/gtk.c b/ui/gtk.c index 91b68241e3..8215841067 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -553,6 +553,7 @@ static void gd_change_runstate(void *opaque, int running, RunState state) static void gd_mouse_mode_change(Notifier *notify, void *data) { GtkDisplayState *s; + int i; s = container_of(notify, GtkDisplayState, mouse_mode_notifier); /* release the grab at switching to absolute mode */ @@ -560,7 +561,10 @@ static void gd_mouse_mode_change(Notifier *notify, void *data) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE); } - gd_update_cursor(gd_vc_find_current(s)); + for (i = 0; i < s->nb_vcs; i++) { + VirtualConsole *vc = &s->vc[i]; + gd_update_cursor(vc); + } } /** GTK Events **/ From 0c77a37f1118fabed18b9e1aa04ddedf4c06d3d5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 6 May 2014 12:51:00 +0200 Subject: [PATCH 19/24] gtk: detached window pointer grabs Make ungrab hotkey work with detached windows. Enable pointer grabs for detached windows. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 8215841067..298419b59d 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -773,10 +773,7 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE); } else { -#if 0 - /* FIXME: no way (yet) to ungrab */ gd_grab_pointer(vc); -#endif gd_update_caption(s); } return TRUE; @@ -945,6 +942,20 @@ static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event, return TRUE; } +static gboolean gd_win_grab(void *opaque) +{ + VirtualConsole *vc = opaque; + + fprintf(stderr, "%s: %s\n", __func__, vc->label); + if (vc->s->ptr_owner) { + gd_ungrab_pointer(vc->s); + } else { + gd_grab_pointer(vc); + } + gd_update_caption(vc->s); + return TRUE; +} + static void gd_menu_untabify(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; @@ -963,6 +974,13 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque) G_CALLBACK(gd_tab_window_close), vc); gtk_widget_show_all(vc->window); + GtkAccelGroup *ag = gtk_accel_group_new(); + gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag); + + GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab), vc, NULL); + gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb); + + fprintf(stderr, "%s: %p\n", __func__, vc); gd_update_caption(s); } } From aa0a55d42d0874bad9ff0f709c91179be727b8ce Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 6 May 2014 10:20:53 +0200 Subject: [PATCH 20/24] gtk: enable untabify for gfx Now we have all grab fixes in place, so we can allow detaching graphic display tabs too. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 298419b59d..79dc8dba9f 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -338,7 +338,11 @@ static void gd_update_windowsize(VirtualConsole *vc) gtk_widget_set_size_request(vc->gfx.drawing_area, surface_width(vc->gfx.ds) * sx, surface_height(vc->gfx.ds) * sy); - gtk_window_resize(GTK_WINDOW(s->window), 320, 240); + if (vc->window) { + gtk_window_resize(GTK_WINDOW(vc->window), 320, 240); + } else { + gtk_window_resize(GTK_WINDOW(s->window), 320, 240); + } } static void gd_update_full_redraw(VirtualConsole *vc) @@ -962,8 +966,8 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque) VirtualConsole *vc = gd_vc_find_current(s); if (vc->type == GD_VC_GFX) { - /* temporary: needs more work to get grabs etc correct */ - return; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), + FALSE); } if (!vc->window) { gtk_widget_set_sensitive(vc->menu_item, false); From 3158a3482b0093e41f2b2596fba50774ea31ae08 Mon Sep 17 00:00:00 2001 From: Bruce Rogers Date: Wed, 30 Apr 2014 20:29:42 -0600 Subject: [PATCH 21/24] gtk: Add handling for the xfree86 keycodes Currently only evdev keycodes are handled by the gtk-ui. SDL has code to handle both. This patch adds similar processing so that both keycode types will be handled via the gtk-ui. Signed-off-by: Bruce Rogers Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/ui/gtk.c b/ui/gtk.c index 79dc8dba9f..a55ceb40d9 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -68,6 +68,10 @@ #include "keymaps.h" #include "sysemu/char.h" #include "qom/object.h" +#ifndef _WIN32 +#include +#include +#endif #define MAX_VCS 10 @@ -197,6 +201,7 @@ struct GtkDisplayState { bool external_pause_update; bool modifier_pressed[ARRAY_SIZE(modifier_keycode)]; + bool has_evdev; }; static GtkDisplayState *global_state; @@ -842,7 +847,11 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) } else if (gdk_keycode < 97) { qemu_keycode = gdk_keycode - 8; } else if (gdk_keycode < 158) { - qemu_keycode = translate_evdev_keycode(gdk_keycode - 97); + if (s->has_evdev) { + qemu_keycode = translate_evdev_keycode(gdk_keycode - 97); + } else { + qemu_keycode = translate_xfree86_keycode(gdk_keycode - 97); + } } else if (gdk_keycode == 208) { /* Hiragana_Katakana */ qemu_keycode = 0x70; } else if (gdk_keycode == 211) { /* backslash */ @@ -1719,6 +1728,29 @@ static void gd_create_menus(GtkDisplayState *s) s->accel_group = accel_group; } +static void gd_set_keycode_type(GtkDisplayState *s) +{ +#ifndef _WIN32 + char *keycodes = NULL; + GdkDisplay *display = gtk_widget_get_display(s->window); + Display *x11_display = gdk_x11_display_get_xdisplay(display); + XkbDescPtr desc = XkbGetKeyboard(x11_display, XkbGBN_AllComponentsMask, + XkbUseCoreKbd); + + if (desc && desc->names) { + keycodes = XGetAtomName(x11_display, desc->names->keycodes); + } + if (keycodes == NULL) { + fprintf(stderr, "could not lookup keycode name\n"); + } else if (strstart(keycodes, "evdev", NULL)) { + s->has_evdev = true; + } else if (!strstart(keycodes, "xfree86", NULL)) { + fprintf(stderr, "unknown keycodes `%s', please report to " + "qemu-devel@nongnu.org\n", keycodes); + } +#endif +} + void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) { GtkDisplayState *s = g_malloc0(sizeof(*s)); @@ -1782,6 +1814,8 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item)); } + gd_set_keycode_type(s); + global_state = s; } From 0f61a61df377bd99f6f3a7f8fe9abd05f023a94b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 6 May 2014 13:39:11 +0200 Subject: [PATCH 22/24] gtk: zap unused global_state Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index a55ceb40d9..0d86025877 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -204,8 +204,6 @@ struct GtkDisplayState { bool has_evdev; }; -static GtkDisplayState *global_state; - static void gd_grab_pointer(VirtualConsole *vc); static void gd_ungrab_pointer(GtkDisplayState *s); @@ -1815,8 +1813,6 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) } gd_set_keycode_type(s); - - global_state = s; } void early_gtk_display_init(void) From 82fc18099aa8ee2533add523cc0069f26a83e7b6 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 16 May 2014 12:26:12 +0200 Subject: [PATCH 23/24] gtk: window sizing overhaul Major overhaul for window size handling. This basically switches qemu over to use geometry hints for the window manager instead of trying to get the job done with widget resize requests. This allows to specify better what we need and also avoids window resizes. FIXME: on gtk2 someone overwrites the geometry hints :( Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 100 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 30 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 0d86025877..ac5dbe06cc 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -74,6 +74,12 @@ #endif #define MAX_VCS 10 +#define VC_WINDOW_X_MIN 320 +#define VC_WINDOW_Y_MIN 240 +#define VC_TERM_X_MIN 80 +#define VC_TERM_Y_MIN 25 +#define VC_SCALE_MIN 0.25 +#define VC_SCALE_STEP 0.25 #if !defined(CONFIG_VTE) # define VTE_CHECK_VERSION(a, b, c) 0 @@ -322,29 +328,63 @@ static void gd_update_caption(GtkDisplayState *s) g_free(prefix); } +static void gd_update_geometry_hints(VirtualConsole *vc) +{ + GtkDisplayState *s = vc->s; + GdkWindowHints mask = 0; + GdkGeometry geo = {}; + GtkWidget *geo_widget = NULL; + GtkWindow *geo_window; + + if (vc->type == GD_VC_GFX) { + if (s->free_scale) { + geo.min_width = surface_width(vc->gfx.ds) * VC_SCALE_MIN; + geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN; + mask |= GDK_HINT_MIN_SIZE; + } else { + geo.min_width = surface_width(vc->gfx.ds) * vc->gfx.scale_x; + geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y; + mask |= GDK_HINT_MIN_SIZE; + } + geo_widget = vc->gfx.drawing_area; + gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height); + +#if defined(CONFIG_VTE) + } else if (vc->type == GD_VC_VTE) { + VteTerminal *term = VTE_TERMINAL(vc->vte.terminal); + GtkBorder *ib; + + geo.width_inc = vte_terminal_get_char_width(term); + geo.height_inc = vte_terminal_get_char_height(term); + mask |= GDK_HINT_RESIZE_INC; + geo.base_width = geo.width_inc; + geo.base_height = geo.height_inc; + mask |= GDK_HINT_BASE_SIZE; + geo.min_width = geo.width_inc * VC_TERM_X_MIN; + geo.min_height = geo.height_inc * VC_TERM_Y_MIN; + mask |= GDK_HINT_MIN_SIZE; + gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL); + geo.base_width += ib->left + ib->right; + geo.base_height += ib->top + ib->bottom; + geo.min_width += ib->left + ib->right; + geo.min_height += ib->top + ib->bottom; + geo_widget = vc->vte.terminal; +#endif + } + + geo_window = GTK_WINDOW(vc->window ? vc->window : s->window); + gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask); +} + static void gd_update_windowsize(VirtualConsole *vc) { GtkDisplayState *s = vc->s; - double sx, sy; - if (vc->type != GD_VC_GFX || s->full_screen) { - return; - } + gd_update_geometry_hints(vc); - if (s->free_scale) { - sx = 1.0; - sy = 1.0; - } else { - sx = vc->gfx.scale_x; - sy = vc->gfx.scale_y; - } - gtk_widget_set_size_request(vc->gfx.drawing_area, - surface_width(vc->gfx.ds) * sx, - surface_height(vc->gfx.ds) * sy); - if (vc->window) { - gtk_window_resize(GTK_WINDOW(vc->window), 320, 240); - } else { - gtk_window_resize(GTK_WINDOW(s->window), 320, 240); + if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) { + gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window), + VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN); } } @@ -991,7 +1031,7 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque) GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab), vc, NULL); gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb); - fprintf(stderr, "%s: %p\n", __func__, vc); + gd_update_geometry_hints(vc); gd_update_caption(s); } } @@ -1019,9 +1059,7 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) if (vc->type == GD_VC_GFX) { vc->gfx.scale_x = 1.0; vc->gfx.scale_y = 1.0; - gtk_widget_set_size_request(vc->gfx.drawing_area, - surface_width(vc->gfx.ds), - surface_height(vc->gfx.ds)); + gd_update_windowsize(vc); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE); } @@ -1038,8 +1076,8 @@ static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), FALSE); - vc->gfx.scale_x += .25; - vc->gfx.scale_y += .25; + vc->gfx.scale_x += VC_SCALE_STEP; + vc->gfx.scale_y += VC_SCALE_STEP; gd_update_windowsize(vc); } @@ -1052,11 +1090,11 @@ static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), FALSE); - vc->gfx.scale_x -= .25; - vc->gfx.scale_y -= .25; + vc->gfx.scale_x -= VC_SCALE_STEP; + vc->gfx.scale_y -= VC_SCALE_STEP; - vc->gfx.scale_x = MAX(vc->gfx.scale_x, .25); - vc->gfx.scale_y = MAX(vc->gfx.scale_y, .25); + vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN); + vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN); gd_update_windowsize(vc); } @@ -1083,9 +1121,9 @@ static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque) s->free_scale = FALSE; vc->gfx.scale_x = 1.0; vc->gfx.scale_y = 1.0; - gd_update_windowsize(vc); } + gd_update_windowsize(vc); gd_update_full_redraw(vc); } @@ -1279,6 +1317,7 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, } gtk_widget_set_sensitive(s->grab_item, on_vga); + gd_update_windowsize(vc); gd_update_cursor(vc); } @@ -1408,7 +1447,8 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc); vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1); - vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal), 80, 25); + vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal), + VC_TERM_X_MIN, VC_TERM_Y_MIN); #if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0) vadjustment = gtk_scrollable_get_vadjustment From 6fa27697514bb45e80fcb57f38054a8833f87ff9 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 16 May 2014 15:34:31 +0200 Subject: [PATCH 24/24] gtk: workaround gtk2 vte resize issue Hack isn't pretty, but gets the job done. See source code comment for details. Signed-off-by: Gerd Hoffmann --- ui/gtk.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/ui/gtk.c b/ui/gtk.c index ac5dbe06cc..b9089360f0 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -85,6 +85,20 @@ # define VTE_CHECK_VERSION(a, b, c) 0 #endif +#if defined(CONFIG_VTE) && !GTK_CHECK_VERSION(3, 0, 0) +/* + * The gtk2 vte terminal widget seriously messes up the window resize + * for some reason. You basically can't make the qemu window smaller + * any more because the toplevel window geoemtry hints are overridden. + * + * Workaround that by hiding all vte widgets, except the one in the + * current tab. + * + * Luckily everything works smooth in gtk3. + */ +# define VTE_RESIZE_HACK 1 +#endif + /* Compatibility define to let us build on both Gtk2 and Gtk3 */ #if GTK_CHECK_VERSION(3, 0, 0) static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh) @@ -1301,10 +1315,21 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, return; } +#ifdef VTE_RESIZE_HACK + vc = gd_vc_find_current(s); + if (vc && vc->type == GD_VC_VTE) { + gtk_widget_hide(vc->vte.terminal); + } +#endif vc = gd_vc_find_by_page(s, arg2); if (!vc) { return; } +#ifdef VTE_RESIZE_HACK + if (vc->type == GD_VC_VTE) { + gtk_widget_show(vc->vte.terminal); + } +#endif gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); on_vga = (vc->type == GD_VC_GFX); @@ -1845,6 +1870,21 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) gtk_widget_show_all(s->window); +#ifdef VTE_RESIZE_HACK + { + VirtualConsole *cur = gd_vc_find_current(s); + int i; + + for (i = 0; i < s->nb_vcs; i++) { + VirtualConsole *vc = &s->vc[i]; + if (vc && vc->type == GD_VC_VTE && vc != cur) { + gtk_widget_hide(vc->vte.terminal); + } + } + gd_update_windowsize(cur); + } +#endif + if (full_screen) { gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item)); }