xref: /qemu/ui/gtk.c (revision c8f3f17cf1015d6621f79aa6a88280539621a108)
1a4ccabcfSAnthony Liguori /*
2a4ccabcfSAnthony Liguori  * GTK UI
3a4ccabcfSAnthony Liguori  *
4a4ccabcfSAnthony Liguori  * Copyright IBM, Corp. 2012
5a4ccabcfSAnthony Liguori  *
6a4ccabcfSAnthony Liguori  * Authors:
7a4ccabcfSAnthony Liguori  *  Anthony Liguori   <aliguori@us.ibm.com>
8a4ccabcfSAnthony Liguori  *
9a4ccabcfSAnthony Liguori  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10a4ccabcfSAnthony Liguori  * See the COPYING file in the top-level directory.
11a4ccabcfSAnthony Liguori  *
12a4ccabcfSAnthony Liguori  * Portions from gtk-vnc:
13a4ccabcfSAnthony Liguori  *
14a4ccabcfSAnthony Liguori  * GTK VNC Widget
15a4ccabcfSAnthony Liguori  *
16a4ccabcfSAnthony Liguori  * Copyright (C) 2006  Anthony Liguori <anthony@codemonkey.ws>
17a4ccabcfSAnthony Liguori  * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
18a4ccabcfSAnthony Liguori  *
19a4ccabcfSAnthony Liguori  * This library is free software; you can redistribute it and/or
20a4ccabcfSAnthony Liguori  * modify it under the terms of the GNU Lesser General Public
21a4ccabcfSAnthony Liguori  * License as published by the Free Software Foundation; either
22a4ccabcfSAnthony Liguori  * version 2.0 of the License, or (at your option) any later version.
23a4ccabcfSAnthony Liguori  *
24a4ccabcfSAnthony Liguori  * This library is distributed in the hope that it will be useful,
25a4ccabcfSAnthony Liguori  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26a4ccabcfSAnthony Liguori  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
27a4ccabcfSAnthony Liguori  * Lesser General Public License for more details.
28a4ccabcfSAnthony Liguori  *
29a4ccabcfSAnthony Liguori  * You should have received a copy of the GNU Lesser General Public
30a4ccabcfSAnthony Liguori  * License along with this library; if not, write to the Free Software
31a4ccabcfSAnthony Liguori  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
32a4ccabcfSAnthony Liguori  */
33a4ccabcfSAnthony Liguori 
34834574eaSAnthony Liguori #define GETTEXT_PACKAGE "qemu"
35834574eaSAnthony Liguori #define LOCALEDIR "po"
36834574eaSAnthony Liguori 
37c95e3080SKevin Wolf #include "qemu-common.h"
38c95e3080SKevin Wolf 
39dc7ff344SGerd Hoffmann #include "ui/console.h"
40dc7ff344SGerd Hoffmann #include "ui/gtk.h"
41c95e3080SKevin Wolf 
42834574eaSAnthony Liguori #include <glib/gi18n.h>
433f58eadeSStefan Weil #include <locale.h>
44bbbf9bfbSStefan Weil #if defined(CONFIG_VTE)
45a4ccabcfSAnthony Liguori #include <vte/vte.h>
46bbbf9bfbSStefan Weil #endif
47a4ccabcfSAnthony Liguori #include <math.h>
48a4ccabcfSAnthony Liguori 
49ef0dd982SStefan Weil #include "trace.h"
50af98ba92SGerd Hoffmann #include "ui/input.h"
51a4ccabcfSAnthony Liguori #include "sysemu/sysemu.h"
52a4ccabcfSAnthony Liguori #include "qmp-commands.h"
53a4ccabcfSAnthony Liguori #include "x_keymap.h"
54a4ccabcfSAnthony Liguori #include "keymaps.h"
55dccfcd0eSPaolo Bonzini #include "sysemu/char.h"
566a24ced5SGerd Hoffmann #include "qom/object.h"
57a4ccabcfSAnthony Liguori 
58d861def3SAnthony Liguori #define MAX_VCS 10
5982fc1809SGerd Hoffmann #define VC_WINDOW_X_MIN  320
6082fc1809SGerd Hoffmann #define VC_WINDOW_Y_MIN  240
6182fc1809SGerd Hoffmann #define VC_TERM_X_MIN     80
6282fc1809SGerd Hoffmann #define VC_TERM_Y_MIN     25
6382fc1809SGerd Hoffmann #define VC_SCALE_MIN    0.25
6482fc1809SGerd Hoffmann #define VC_SCALE_STEP   0.25
65d861def3SAnthony Liguori 
66bbbf9bfbSStefan Weil #if !defined(CONFIG_VTE)
67bbbf9bfbSStefan Weil # define VTE_CHECK_VERSION(a, b, c) 0
68bbbf9bfbSStefan Weil #endif
69cba68834SDaniel P. Berrange 
706fa27697SGerd Hoffmann #if defined(CONFIG_VTE) && !GTK_CHECK_VERSION(3, 0, 0)
716fa27697SGerd Hoffmann /*
726fa27697SGerd Hoffmann  * The gtk2 vte terminal widget seriously messes up the window resize
736fa27697SGerd Hoffmann  * for some reason.  You basically can't make the qemu window smaller
746fa27697SGerd Hoffmann  * any more because the toplevel window geoemtry hints are overridden.
756fa27697SGerd Hoffmann  *
766fa27697SGerd Hoffmann  * Workaround that by hiding all vte widgets, except the one in the
776fa27697SGerd Hoffmann  * current tab.
786fa27697SGerd Hoffmann  *
796fa27697SGerd Hoffmann  * Luckily everything works smooth in gtk3.
806fa27697SGerd Hoffmann  */
816fa27697SGerd Hoffmann # define VTE_RESIZE_HACK 1
826fa27697SGerd Hoffmann #endif
836fa27697SGerd Hoffmann 
84ef6413a2SDaniel P. Berrange #if !GTK_CHECK_VERSION(2, 20, 0)
85ef6413a2SDaniel P. Berrange #define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget)
86ef6413a2SDaniel P. Berrange #endif
87ef6413a2SDaniel P. Berrange 
880a337ed0SGerd Hoffmann #ifndef GDK_IS_X11_DISPLAY
890a337ed0SGerd Hoffmann #define GDK_IS_X11_DISPLAY(dpy) (dpy == dpy)
900a337ed0SGerd Hoffmann #endif
910a337ed0SGerd Hoffmann #ifndef GDK_IS_WIN32_DISPLAY
920a337ed0SGerd Hoffmann #define GDK_IS_WIN32_DISPLAY(dpy) (dpy == dpy)
930a337ed0SGerd Hoffmann #endif
940a337ed0SGerd Hoffmann 
95bc0477c7SDaniel P. Berrange #ifndef GDK_KEY_0
96bc0477c7SDaniel P. Berrange #define GDK_KEY_0 GDK_0
97bc0477c7SDaniel P. Berrange #define GDK_KEY_1 GDK_1
98bc0477c7SDaniel P. Berrange #define GDK_KEY_2 GDK_2
99bc0477c7SDaniel P. Berrange #define GDK_KEY_f GDK_f
100bc0477c7SDaniel P. Berrange #define GDK_KEY_g GDK_g
101db1da1f2SCole Robinson #define GDK_KEY_q GDK_q
102bc0477c7SDaniel P. Berrange #define GDK_KEY_plus GDK_plus
103bc0477c7SDaniel P. Berrange #define GDK_KEY_minus GDK_minus
104dc520171SGerd Hoffmann #define GDK_KEY_Pause GDK_Pause
105bc0477c7SDaniel P. Berrange #endif
106ef6413a2SDaniel P. Berrange 
107*c8f3f17cSDaniel P. Berrange /* Some older mingw versions lack this constant or have
108*c8f3f17cSDaniel P. Berrange  * it conditionally defined */
109*c8f3f17cSDaniel P. Berrange #ifdef _WIN32
110*c8f3f17cSDaniel P. Berrange # ifndef MAPVK_VK_TO_VSC
111*c8f3f17cSDaniel P. Berrange #  define MAPVK_VK_TO_VSC 0
112*c8f3f17cSDaniel P. Berrange # endif
113*c8f3f17cSDaniel P. Berrange #endif
114*c8f3f17cSDaniel P. Berrange 
115*c8f3f17cSDaniel P. Berrange 
116b1e749c0SJan Kiszka #define HOTKEY_MODIFIERS        (GDK_CONTROL_MASK | GDK_MOD1_MASK)
117b1e749c0SJan Kiszka 
1186db253caSJan Kiszka static const int modifier_keycode[] = {
1196db253caSJan Kiszka     /* shift, control, alt keys, meta keys, both left & right */
1206db253caSJan Kiszka     0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd,
1216db253caSJan Kiszka };
1226db253caSJan Kiszka 
123e3500d1fSGerd Hoffmann struct GtkDisplayState {
124a4ccabcfSAnthony Liguori     GtkWidget *window;
125a4ccabcfSAnthony Liguori 
126a4ccabcfSAnthony Liguori     GtkWidget *menu_bar;
127a4ccabcfSAnthony Liguori 
12873d4dc71SAnthony Liguori     GtkAccelGroup *accel_group;
12973d4dc71SAnthony Liguori 
13030e8f22bSJan Kiszka     GtkWidget *machine_menu_item;
13130e8f22bSJan Kiszka     GtkWidget *machine_menu;
13230e8f22bSJan Kiszka     GtkWidget *pause_item;
13330e8f22bSJan Kiszka     GtkWidget *reset_item;
13430e8f22bSJan Kiszka     GtkWidget *powerdown_item;
135a4ccabcfSAnthony Liguori     GtkWidget *quit_item;
136a4ccabcfSAnthony Liguori 
137a4ccabcfSAnthony Liguori     GtkWidget *view_menu_item;
138a4ccabcfSAnthony Liguori     GtkWidget *view_menu;
139c6158483SAnthony Liguori     GtkWidget *full_screen_item;
140c6158483SAnthony Liguori     GtkWidget *zoom_in_item;
141c6158483SAnthony Liguori     GtkWidget *zoom_out_item;
142c6158483SAnthony Liguori     GtkWidget *zoom_fixed_item;
143c6158483SAnthony Liguori     GtkWidget *zoom_fit_item;
1445104a1f6SAnthony Liguori     GtkWidget *grab_item;
1455104a1f6SAnthony Liguori     GtkWidget *grab_on_hover_item;
146a4ccabcfSAnthony Liguori 
147d861def3SAnthony Liguori     int nb_vcs;
148d861def3SAnthony Liguori     VirtualConsole vc[MAX_VCS];
149d861def3SAnthony Liguori 
150a4ccabcfSAnthony Liguori     GtkWidget *show_tabs_item;
151cdeb7090SGerd Hoffmann     GtkWidget *untabify_item;
152a4ccabcfSAnthony Liguori 
153a4ccabcfSAnthony Liguori     GtkWidget *vbox;
154a4ccabcfSAnthony Liguori     GtkWidget *notebook;
155a4ccabcfSAnthony Liguori     int button_mask;
156e61031cdSTakashi Iwai     gboolean last_set;
157a4ccabcfSAnthony Liguori     int last_x;
158a4ccabcfSAnthony Liguori     int last_y;
159ecce1929STakashi Iwai     int grab_x_root;
160ecce1929STakashi Iwai     int grab_y_root;
1614c638e2eSGerd Hoffmann     VirtualConsole *kbd_owner;
1624c638e2eSGerd Hoffmann     VirtualConsole *ptr_owner;
163a4ccabcfSAnthony Liguori 
164c6158483SAnthony Liguori     gboolean full_screen;
165a4ccabcfSAnthony Liguori 
166a4ccabcfSAnthony Liguori     GdkCursor *null_cursor;
167a4ccabcfSAnthony Liguori     Notifier mouse_mode_notifier;
168c6158483SAnthony Liguori     gboolean free_scale;
16930e8f22bSJan Kiszka 
17030e8f22bSJan Kiszka     bool external_pause_update;
1716db253caSJan Kiszka 
1726db253caSJan Kiszka     bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
1733158a348SBruce Rogers     bool has_evdev;
1741a01716aSJan Kiszka     bool ignore_keys;
175e3500d1fSGerd Hoffmann };
176a4ccabcfSAnthony Liguori 
177d531deefSGerd Hoffmann static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
1782884cf5bSGerd Hoffmann static void gd_ungrab_pointer(GtkDisplayState *s);
179d531deefSGerd Hoffmann static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
180aa4f4058SGerd Hoffmann static void gd_ungrab_keyboard(GtkDisplayState *s);
1812884cf5bSGerd Hoffmann 
182a4ccabcfSAnthony Liguori /** Utility Functions **/
183a4ccabcfSAnthony Liguori 
184271a25c0SGerd Hoffmann static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s)
185271a25c0SGerd Hoffmann {
186271a25c0SGerd Hoffmann     VirtualConsole *vc;
187271a25c0SGerd Hoffmann     gint i;
188271a25c0SGerd Hoffmann 
189271a25c0SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
190271a25c0SGerd Hoffmann         vc = &s->vc[i];
191271a25c0SGerd Hoffmann         if (gtk_check_menu_item_get_active
192271a25c0SGerd Hoffmann             (GTK_CHECK_MENU_ITEM(vc->menu_item))) {
193271a25c0SGerd Hoffmann             return vc;
194271a25c0SGerd Hoffmann         }
195271a25c0SGerd Hoffmann     }
196271a25c0SGerd Hoffmann     return NULL;
197271a25c0SGerd Hoffmann }
198271a25c0SGerd Hoffmann 
199271a25c0SGerd Hoffmann static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page)
200271a25c0SGerd Hoffmann {
201271a25c0SGerd Hoffmann     VirtualConsole *vc;
202271a25c0SGerd Hoffmann     gint i, p;
203271a25c0SGerd Hoffmann 
204271a25c0SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
205271a25c0SGerd Hoffmann         vc = &s->vc[i];
206271a25c0SGerd Hoffmann         p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item);
207271a25c0SGerd Hoffmann         if (p == page) {
208271a25c0SGerd Hoffmann             return vc;
209271a25c0SGerd Hoffmann         }
210271a25c0SGerd Hoffmann     }
211271a25c0SGerd Hoffmann     return NULL;
212271a25c0SGerd Hoffmann }
213271a25c0SGerd Hoffmann 
214e3500d1fSGerd Hoffmann static VirtualConsole *gd_vc_find_current(GtkDisplayState *s)
215e3500d1fSGerd Hoffmann {
216e3500d1fSGerd Hoffmann     gint page;
217e3500d1fSGerd Hoffmann 
218e3500d1fSGerd Hoffmann     page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook));
219e3500d1fSGerd Hoffmann     return gd_vc_find_by_page(s, page);
220e3500d1fSGerd Hoffmann }
221e3500d1fSGerd Hoffmann 
2225104a1f6SAnthony Liguori static bool gd_is_grab_active(GtkDisplayState *s)
2235104a1f6SAnthony Liguori {
2245104a1f6SAnthony Liguori     return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
2255104a1f6SAnthony Liguori }
2265104a1f6SAnthony Liguori 
2275104a1f6SAnthony Liguori static bool gd_grab_on_hover(GtkDisplayState *s)
2285104a1f6SAnthony Liguori {
2295104a1f6SAnthony Liguori     return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
2305104a1f6SAnthony Liguori }
2315104a1f6SAnthony Liguori 
232e3500d1fSGerd Hoffmann static void gd_update_cursor(VirtualConsole *vc)
2335104a1f6SAnthony Liguori {
234e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
235e3500d1fSGerd Hoffmann     GdkWindow *window;
236832189c9SGerd Hoffmann 
237f8c223f6SGerd Hoffmann     if (vc->type != GD_VC_GFX ||
238f8c223f6SGerd Hoffmann         !qemu_console_is_graphic(vc->gfx.dcl.con)) {
239e3500d1fSGerd Hoffmann         return;
2405104a1f6SAnthony Liguori     }
2415104a1f6SAnthony Liguori 
2424cdfc935SHervé Poussineau     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
2434cdfc935SHervé Poussineau         return;
2444cdfc935SHervé Poussineau     }
2454cdfc935SHervé Poussineau 
246e3500d1fSGerd Hoffmann     window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area));
2472884cf5bSGerd Hoffmann     if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) {
248a4ccabcfSAnthony Liguori         gdk_window_set_cursor(window, s->null_cursor);
249a4ccabcfSAnthony Liguori     } else {
250a4ccabcfSAnthony Liguori         gdk_window_set_cursor(window, NULL);
251a4ccabcfSAnthony Liguori     }
252a4ccabcfSAnthony Liguori }
253a4ccabcfSAnthony Liguori 
254a4ccabcfSAnthony Liguori static void gd_update_caption(GtkDisplayState *s)
255a4ccabcfSAnthony Liguori {
256a4ccabcfSAnthony Liguori     const char *status = "";
2574eeaa3a8SGerd Hoffmann     gchar *prefix;
258a4ccabcfSAnthony Liguori     gchar *title;
2595104a1f6SAnthony Liguori     const char *grab = "";
26030e8f22bSJan Kiszka     bool is_paused = !runstate_is_running();
2614eeaa3a8SGerd Hoffmann     int i;
2625104a1f6SAnthony Liguori 
2634eeaa3a8SGerd Hoffmann     if (qemu_name) {
2644eeaa3a8SGerd Hoffmann         prefix = g_strdup_printf("QEMU (%s)", qemu_name);
2654eeaa3a8SGerd Hoffmann     } else {
2664eeaa3a8SGerd Hoffmann         prefix = g_strdup_printf("QEMU");
2674eeaa3a8SGerd Hoffmann     }
2684eeaa3a8SGerd Hoffmann 
2694eeaa3a8SGerd Hoffmann     if (s->ptr_owner != NULL &&
2704eeaa3a8SGerd Hoffmann         s->ptr_owner->window == NULL) {
271d8da9ee8SAurelien Jarno         grab = _(" - Press Ctrl+Alt+G to release grab");
2725104a1f6SAnthony Liguori     }
273a4ccabcfSAnthony Liguori 
27430e8f22bSJan Kiszka     if (is_paused) {
275d8da9ee8SAurelien Jarno         status = _(" [Paused]");
276a4ccabcfSAnthony Liguori     }
27730e8f22bSJan Kiszka     s->external_pause_update = true;
27830e8f22bSJan Kiszka     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
27930e8f22bSJan Kiszka                                    is_paused);
28030e8f22bSJan Kiszka     s->external_pause_update = false;
281a4ccabcfSAnthony Liguori 
2824eeaa3a8SGerd Hoffmann     title = g_strdup_printf("%s%s%s", prefix, status, grab);
2834eeaa3a8SGerd Hoffmann     gtk_window_set_title(GTK_WINDOW(s->window), title);
2844eeaa3a8SGerd Hoffmann     g_free(title);
2854eeaa3a8SGerd Hoffmann 
2864eeaa3a8SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
2874eeaa3a8SGerd Hoffmann         VirtualConsole *vc = &s->vc[i];
2884eeaa3a8SGerd Hoffmann 
2894eeaa3a8SGerd Hoffmann         if (!vc->window) {
2904eeaa3a8SGerd Hoffmann             continue;
2914eeaa3a8SGerd Hoffmann         }
2924eeaa3a8SGerd Hoffmann         title = g_strdup_printf("%s: %s%s%s", prefix, vc->label,
2934eeaa3a8SGerd Hoffmann                                 vc == s->kbd_owner ? " +kbd" : "",
2944eeaa3a8SGerd Hoffmann                                 vc == s->ptr_owner ? " +ptr" : "");
2954eeaa3a8SGerd Hoffmann         gtk_window_set_title(GTK_WINDOW(vc->window), title);
2964eeaa3a8SGerd Hoffmann         g_free(title);
297a4ccabcfSAnthony Liguori     }
298a4ccabcfSAnthony Liguori 
2994eeaa3a8SGerd Hoffmann     g_free(prefix);
300a4ccabcfSAnthony Liguori }
301a4ccabcfSAnthony Liguori 
30282fc1809SGerd Hoffmann static void gd_update_geometry_hints(VirtualConsole *vc)
30382fc1809SGerd Hoffmann {
30482fc1809SGerd Hoffmann     GtkDisplayState *s = vc->s;
30582fc1809SGerd Hoffmann     GdkWindowHints mask = 0;
30682fc1809SGerd Hoffmann     GdkGeometry geo = {};
30782fc1809SGerd Hoffmann     GtkWidget *geo_widget = NULL;
30882fc1809SGerd Hoffmann     GtkWindow *geo_window;
30982fc1809SGerd Hoffmann 
31082fc1809SGerd Hoffmann     if (vc->type == GD_VC_GFX) {
311f98f43eaSGerd Hoffmann         if (!vc->gfx.ds) {
312f98f43eaSGerd Hoffmann             return;
313f98f43eaSGerd Hoffmann         }
31482fc1809SGerd Hoffmann         if (s->free_scale) {
31582fc1809SGerd Hoffmann             geo.min_width  = surface_width(vc->gfx.ds) * VC_SCALE_MIN;
31682fc1809SGerd Hoffmann             geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN;
31782fc1809SGerd Hoffmann             mask |= GDK_HINT_MIN_SIZE;
31882fc1809SGerd Hoffmann         } else {
31982fc1809SGerd Hoffmann             geo.min_width  = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
32082fc1809SGerd Hoffmann             geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
32182fc1809SGerd Hoffmann             mask |= GDK_HINT_MIN_SIZE;
32282fc1809SGerd Hoffmann         }
32382fc1809SGerd Hoffmann         geo_widget = vc->gfx.drawing_area;
32482fc1809SGerd Hoffmann         gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height);
32582fc1809SGerd Hoffmann 
32682fc1809SGerd Hoffmann #if defined(CONFIG_VTE)
32782fc1809SGerd Hoffmann     } else if (vc->type == GD_VC_VTE) {
32882fc1809SGerd Hoffmann         VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
32982fc1809SGerd Hoffmann         GtkBorder *ib;
33082fc1809SGerd Hoffmann 
33182fc1809SGerd Hoffmann         geo.width_inc  = vte_terminal_get_char_width(term);
33282fc1809SGerd Hoffmann         geo.height_inc = vte_terminal_get_char_height(term);
33382fc1809SGerd Hoffmann         mask |= GDK_HINT_RESIZE_INC;
33482fc1809SGerd Hoffmann         geo.base_width  = geo.width_inc;
33582fc1809SGerd Hoffmann         geo.base_height = geo.height_inc;
33682fc1809SGerd Hoffmann         mask |= GDK_HINT_BASE_SIZE;
33782fc1809SGerd Hoffmann         geo.min_width  = geo.width_inc * VC_TERM_X_MIN;
33882fc1809SGerd Hoffmann         geo.min_height = geo.height_inc * VC_TERM_Y_MIN;
33982fc1809SGerd Hoffmann         mask |= GDK_HINT_MIN_SIZE;
34082fc1809SGerd Hoffmann         gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL);
34182fc1809SGerd Hoffmann         geo.base_width  += ib->left + ib->right;
34282fc1809SGerd Hoffmann         geo.base_height += ib->top + ib->bottom;
34382fc1809SGerd Hoffmann         geo.min_width   += ib->left + ib->right;
34482fc1809SGerd Hoffmann         geo.min_height  += ib->top + ib->bottom;
34582fc1809SGerd Hoffmann         geo_widget = vc->vte.terminal;
34682fc1809SGerd Hoffmann #endif
34782fc1809SGerd Hoffmann     }
34882fc1809SGerd Hoffmann 
34982fc1809SGerd Hoffmann     geo_window = GTK_WINDOW(vc->window ? vc->window : s->window);
35082fc1809SGerd Hoffmann     gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
35182fc1809SGerd Hoffmann }
35282fc1809SGerd Hoffmann 
35397edf3bdSGerd Hoffmann void gd_update_windowsize(VirtualConsole *vc)
354a4ccabcfSAnthony Liguori {
355e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
356c6158483SAnthony Liguori 
35782fc1809SGerd Hoffmann     gd_update_geometry_hints(vc);
358c6158483SAnthony Liguori 
35982fc1809SGerd Hoffmann     if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) {
36082fc1809SGerd Hoffmann         gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window),
36182fc1809SGerd Hoffmann                           VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN);
362a4ccabcfSAnthony Liguori     }
363aa0a55d4SGerd Hoffmann }
364a4ccabcfSAnthony Liguori 
365e3500d1fSGerd Hoffmann static void gd_update_full_redraw(VirtualConsole *vc)
3669d9801cfSGerd Hoffmann {
367e3500d1fSGerd Hoffmann     GtkWidget *area = vc->gfx.drawing_area;
3689d9801cfSGerd Hoffmann     int ww, wh;
369e3500d1fSGerd Hoffmann     gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
370e3500d1fSGerd Hoffmann     gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
3719d9801cfSGerd Hoffmann }
3729d9801cfSGerd Hoffmann 
3736db253caSJan Kiszka static void gtk_release_modifiers(GtkDisplayState *s)
3746db253caSJan Kiszka {
375e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
3766db253caSJan Kiszka     int i, keycode;
3776db253caSJan Kiszka 
378f8c223f6SGerd Hoffmann     if (vc->type != GD_VC_GFX ||
379f8c223f6SGerd Hoffmann         !qemu_console_is_graphic(vc->gfx.dcl.con)) {
3806db253caSJan Kiszka         return;
3816db253caSJan Kiszka     }
3826db253caSJan Kiszka     for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
3836db253caSJan Kiszka         keycode = modifier_keycode[i];
3846db253caSJan Kiszka         if (!s->modifier_pressed[i]) {
3856db253caSJan Kiszka             continue;
3866db253caSJan Kiszka         }
387e3500d1fSGerd Hoffmann         qemu_input_event_send_key_number(vc->gfx.dcl.con, keycode, false);
3886db253caSJan Kiszka         s->modifier_pressed[i] = false;
3896db253caSJan Kiszka     }
3906db253caSJan Kiszka }
3916db253caSJan Kiszka 
392316cb068SGerd Hoffmann static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
393316cb068SGerd Hoffmann                                GtkWidget *widget)
394316cb068SGerd Hoffmann {
395316cb068SGerd Hoffmann     g_object_ref(G_OBJECT(widget));
396316cb068SGerd Hoffmann     gtk_container_remove(GTK_CONTAINER(from), widget);
397316cb068SGerd Hoffmann     gtk_container_add(GTK_CONTAINER(to), widget);
398316cb068SGerd Hoffmann     g_object_unref(G_OBJECT(widget));
399316cb068SGerd Hoffmann }
400316cb068SGerd Hoffmann 
4019d9801cfSGerd Hoffmann /** DisplayState Callbacks **/
4029d9801cfSGerd Hoffmann 
4039d9801cfSGerd Hoffmann static void gd_update(DisplayChangeListener *dcl,
404bc2ed970SGerd Hoffmann                       int x, int y, int w, int h)
4059d9801cfSGerd Hoffmann {
406e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
407f8c223f6SGerd Hoffmann     GdkWindow *win;
4089d9801cfSGerd Hoffmann     int x1, x2, y1, y2;
4099d9801cfSGerd Hoffmann     int mx, my;
4109d9801cfSGerd Hoffmann     int fbw, fbh;
4119d9801cfSGerd Hoffmann     int ww, wh;
4129d9801cfSGerd Hoffmann 
41374444bc1SGerd Hoffmann     trace_gd_update(vc->label, x, y, w, h);
4149d9801cfSGerd Hoffmann 
4154cdfc935SHervé Poussineau     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
4164cdfc935SHervé Poussineau         return;
4174cdfc935SHervé Poussineau     }
4184cdfc935SHervé Poussineau 
419e3500d1fSGerd Hoffmann     if (vc->gfx.convert) {
420e3500d1fSGerd Hoffmann         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
421e3500d1fSGerd Hoffmann                                NULL, vc->gfx.convert,
422f0875536SGerd Hoffmann                                x, y, 0, 0, x, y, w, h);
423f0875536SGerd Hoffmann     }
424f0875536SGerd Hoffmann 
425e3500d1fSGerd Hoffmann     x1 = floor(x * vc->gfx.scale_x);
426e3500d1fSGerd Hoffmann     y1 = floor(y * vc->gfx.scale_y);
4279d9801cfSGerd Hoffmann 
428e3500d1fSGerd Hoffmann     x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
429e3500d1fSGerd Hoffmann     y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
4309d9801cfSGerd Hoffmann 
431e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
432e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
4339d9801cfSGerd Hoffmann 
434f8c223f6SGerd Hoffmann     win = gtk_widget_get_window(vc->gfx.drawing_area);
435f8c223f6SGerd Hoffmann     if (!win) {
436f8c223f6SGerd Hoffmann         return;
437f8c223f6SGerd Hoffmann     }
438f8c223f6SGerd Hoffmann     gdk_drawable_get_size(win, &ww, &wh);
4399d9801cfSGerd Hoffmann 
4409d9801cfSGerd Hoffmann     mx = my = 0;
4419d9801cfSGerd Hoffmann     if (ww > fbw) {
4429d9801cfSGerd Hoffmann         mx = (ww - fbw) / 2;
4439d9801cfSGerd Hoffmann     }
4449d9801cfSGerd Hoffmann     if (wh > fbh) {
4459d9801cfSGerd Hoffmann         my = (wh - fbh) / 2;
4469d9801cfSGerd Hoffmann     }
4479d9801cfSGerd Hoffmann 
448e3500d1fSGerd Hoffmann     gtk_widget_queue_draw_area(vc->gfx.drawing_area,
449e3500d1fSGerd Hoffmann                                mx + x1, my + y1, (x2 - x1), (y2 - y1));
4509d9801cfSGerd Hoffmann }
4519d9801cfSGerd Hoffmann 
452bc2ed970SGerd Hoffmann static void gd_refresh(DisplayChangeListener *dcl)
4539d9801cfSGerd Hoffmann {
454284d1c6bSGerd Hoffmann     graphic_hw_update(dcl->con);
4559d9801cfSGerd Hoffmann }
4569d9801cfSGerd Hoffmann 
457b087143bSIgor Mitsyanko #if GTK_CHECK_VERSION(3, 0, 0)
458b087143bSIgor Mitsyanko static void gd_mouse_set(DisplayChangeListener *dcl,
459b087143bSIgor Mitsyanko                          int x, int y, int visible)
460b087143bSIgor Mitsyanko {
461e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
462b087143bSIgor Mitsyanko     GdkDisplay *dpy;
463b087143bSIgor Mitsyanko     GdkDeviceManager *mgr;
464b087143bSIgor Mitsyanko     gint x_root, y_root;
465b087143bSIgor Mitsyanko 
4662bda6602SCole Robinson     if (qemu_input_is_absolute()) {
4672bda6602SCole Robinson         return;
4682bda6602SCole Robinson     }
4692bda6602SCole Robinson 
470e3500d1fSGerd Hoffmann     dpy = gtk_widget_get_display(vc->gfx.drawing_area);
471b087143bSIgor Mitsyanko     mgr = gdk_display_get_device_manager(dpy);
472e3500d1fSGerd Hoffmann     gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
473b087143bSIgor Mitsyanko                                x, y, &x_root, &y_root);
474b087143bSIgor Mitsyanko     gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
475e3500d1fSGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
476298526feSCole Robinson                     x_root, y_root);
4771271f7f7SGerd Hoffmann     vc->s->last_x = x;
4781271f7f7SGerd Hoffmann     vc->s->last_y = y;
479b087143bSIgor Mitsyanko }
480b087143bSIgor Mitsyanko #else
4819697f5d2SGerd Hoffmann static void gd_mouse_set(DisplayChangeListener *dcl,
4829697f5d2SGerd Hoffmann                          int x, int y, int visible)
4839697f5d2SGerd Hoffmann {
484e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
4859697f5d2SGerd Hoffmann     gint x_root, y_root;
4869697f5d2SGerd Hoffmann 
4872bda6602SCole Robinson     if (qemu_input_is_absolute()) {
4882bda6602SCole Robinson         return;
4892bda6602SCole Robinson     }
4902bda6602SCole Robinson 
491e3500d1fSGerd Hoffmann     gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
4929697f5d2SGerd Hoffmann                                x, y, &x_root, &y_root);
493e3500d1fSGerd Hoffmann     gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area),
494e3500d1fSGerd Hoffmann                              gtk_widget_get_screen(vc->gfx.drawing_area),
4959697f5d2SGerd Hoffmann                              x_root, y_root);
4969697f5d2SGerd Hoffmann }
497b087143bSIgor Mitsyanko #endif
4989697f5d2SGerd Hoffmann 
4999697f5d2SGerd Hoffmann static void gd_cursor_define(DisplayChangeListener *dcl,
5009697f5d2SGerd Hoffmann                              QEMUCursor *c)
5019697f5d2SGerd Hoffmann {
502e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
5039697f5d2SGerd Hoffmann     GdkPixbuf *pixbuf;
5049697f5d2SGerd Hoffmann     GdkCursor *cursor;
5059697f5d2SGerd Hoffmann 
5064cdfc935SHervé Poussineau     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
5074cdfc935SHervé Poussineau         return;
5084cdfc935SHervé Poussineau     }
5094cdfc935SHervé Poussineau 
5109697f5d2SGerd Hoffmann     pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
5119697f5d2SGerd Hoffmann                                       GDK_COLORSPACE_RGB, true, 8,
5129697f5d2SGerd Hoffmann                                       c->width, c->height, c->width * 4,
5139697f5d2SGerd Hoffmann                                       NULL, NULL);
514e3500d1fSGerd Hoffmann     cursor = gdk_cursor_new_from_pixbuf
515e3500d1fSGerd Hoffmann         (gtk_widget_get_display(vc->gfx.drawing_area),
5169697f5d2SGerd Hoffmann          pixbuf, c->hot_x, c->hot_y);
517e3500d1fSGerd Hoffmann     gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor);
5189697f5d2SGerd Hoffmann     g_object_unref(pixbuf);
519030b4b7dSStefan Weil #if !GTK_CHECK_VERSION(3, 0, 0)
52017139240SAnthony Liguori     gdk_cursor_unref(cursor);
521030b4b7dSStefan Weil #else
522030b4b7dSStefan Weil     g_object_unref(cursor);
523030b4b7dSStefan Weil #endif
5249697f5d2SGerd Hoffmann }
5259697f5d2SGerd Hoffmann 
5269d9801cfSGerd Hoffmann static void gd_switch(DisplayChangeListener *dcl,
5279d9801cfSGerd Hoffmann                       DisplaySurface *surface)
5289d9801cfSGerd Hoffmann {
529e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
5309d9801cfSGerd Hoffmann     bool resized = true;
5319d9801cfSGerd Hoffmann 
532f98f43eaSGerd Hoffmann     trace_gd_switch(vc->label,
533f98f43eaSGerd Hoffmann                     surface ? surface_width(surface)  : 0,
534f98f43eaSGerd Hoffmann                     surface ? surface_height(surface) : 0);
5359d9801cfSGerd Hoffmann 
536e3500d1fSGerd Hoffmann     if (vc->gfx.surface) {
537e3500d1fSGerd Hoffmann         cairo_surface_destroy(vc->gfx.surface);
538f98f43eaSGerd Hoffmann         vc->gfx.surface = NULL;
539f98f43eaSGerd Hoffmann     }
540f98f43eaSGerd Hoffmann     if (vc->gfx.convert) {
541f98f43eaSGerd Hoffmann         pixman_image_unref(vc->gfx.convert);
542f98f43eaSGerd Hoffmann         vc->gfx.convert = NULL;
5439d9801cfSGerd Hoffmann     }
5449d9801cfSGerd Hoffmann 
545f98f43eaSGerd Hoffmann     if (vc->gfx.ds && surface &&
546e3500d1fSGerd Hoffmann         surface_width(vc->gfx.ds) == surface_width(surface) &&
547e3500d1fSGerd Hoffmann         surface_height(vc->gfx.ds) == surface_height(surface)) {
5489d9801cfSGerd Hoffmann         resized = false;
5499d9801cfSGerd Hoffmann     }
550e3500d1fSGerd Hoffmann     vc->gfx.ds = surface;
551f0875536SGerd Hoffmann 
552f98f43eaSGerd Hoffmann     if (!surface) {
553f98f43eaSGerd Hoffmann         return;
5549d9801cfSGerd Hoffmann     }
5559d9801cfSGerd Hoffmann 
556f0875536SGerd Hoffmann     if (surface->format == PIXMAN_x8r8g8b8) {
557f0875536SGerd Hoffmann         /*
558f0875536SGerd Hoffmann          * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24
559f0875536SGerd Hoffmann          *
560f0875536SGerd Hoffmann          * No need to convert, use surface directly.  Should be the
561f0875536SGerd Hoffmann          * common case as this is qemu_default_pixelformat(32) too.
562f0875536SGerd Hoffmann          */
563e3500d1fSGerd Hoffmann         vc->gfx.surface = cairo_image_surface_create_for_data
564f0875536SGerd Hoffmann             (surface_data(surface),
565f0875536SGerd Hoffmann              CAIRO_FORMAT_RGB24,
5669d9801cfSGerd Hoffmann              surface_width(surface),
5679d9801cfSGerd Hoffmann              surface_height(surface),
5689d9801cfSGerd Hoffmann              surface_stride(surface));
569f0875536SGerd Hoffmann     } else {
570f0875536SGerd Hoffmann         /* Must convert surface, use pixman to do it. */
571e3500d1fSGerd Hoffmann         vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8,
572f0875536SGerd Hoffmann                                                    surface_width(surface),
573f0875536SGerd Hoffmann                                                    surface_height(surface),
574f0875536SGerd Hoffmann                                                    NULL, 0);
575e3500d1fSGerd Hoffmann         vc->gfx.surface = cairo_image_surface_create_for_data
576e3500d1fSGerd Hoffmann             ((void *)pixman_image_get_data(vc->gfx.convert),
577f0875536SGerd Hoffmann              CAIRO_FORMAT_RGB24,
578e3500d1fSGerd Hoffmann              pixman_image_get_width(vc->gfx.convert),
579e3500d1fSGerd Hoffmann              pixman_image_get_height(vc->gfx.convert),
580e3500d1fSGerd Hoffmann              pixman_image_get_stride(vc->gfx.convert));
581e3500d1fSGerd Hoffmann         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
582e3500d1fSGerd Hoffmann                                NULL, vc->gfx.convert,
583f0875536SGerd Hoffmann                                0, 0, 0, 0, 0, 0,
584e3500d1fSGerd Hoffmann                                pixman_image_get_width(vc->gfx.convert),
585e3500d1fSGerd Hoffmann                                pixman_image_get_height(vc->gfx.convert));
586f0875536SGerd Hoffmann     }
5879d9801cfSGerd Hoffmann 
5889d9801cfSGerd Hoffmann     if (resized) {
589e3500d1fSGerd Hoffmann         gd_update_windowsize(vc);
5909d9801cfSGerd Hoffmann     } else {
591e3500d1fSGerd Hoffmann         gd_update_full_redraw(vc);
5929d9801cfSGerd Hoffmann     }
5939d9801cfSGerd Hoffmann }
5949d9801cfSGerd Hoffmann 
59597edf3bdSGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = {
59697edf3bdSGerd Hoffmann     .dpy_name             = "gtk",
59797edf3bdSGerd Hoffmann     .dpy_gfx_update       = gd_update,
59897edf3bdSGerd Hoffmann     .dpy_gfx_switch       = gd_switch,
59997edf3bdSGerd Hoffmann     .dpy_gfx_check_format = qemu_pixman_check_format,
60097edf3bdSGerd Hoffmann     .dpy_refresh          = gd_refresh,
60197edf3bdSGerd Hoffmann     .dpy_mouse_set        = gd_mouse_set,
60297edf3bdSGerd Hoffmann     .dpy_cursor_define    = gd_cursor_define,
60397edf3bdSGerd Hoffmann };
60497edf3bdSGerd Hoffmann 
60597edf3bdSGerd Hoffmann 
60697edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
60797edf3bdSGerd Hoffmann 
60897edf3bdSGerd Hoffmann /** DisplayState Callbacks (opengl version) **/
60997edf3bdSGerd Hoffmann 
61097edf3bdSGerd Hoffmann static const DisplayChangeListenerOps dcl_egl_ops = {
61197edf3bdSGerd Hoffmann     .dpy_name             = "gtk-egl",
61297edf3bdSGerd Hoffmann     .dpy_gfx_update       = gd_egl_update,
61397edf3bdSGerd Hoffmann     .dpy_gfx_switch       = gd_egl_switch,
61497edf3bdSGerd Hoffmann     .dpy_gfx_check_format = console_gl_check_format,
61597edf3bdSGerd Hoffmann     .dpy_refresh          = gd_egl_refresh,
61697edf3bdSGerd Hoffmann     .dpy_mouse_set        = gd_mouse_set,
61797edf3bdSGerd Hoffmann     .dpy_cursor_define    = gd_cursor_define,
61897edf3bdSGerd Hoffmann };
61997edf3bdSGerd Hoffmann 
62097edf3bdSGerd Hoffmann #endif
62197edf3bdSGerd Hoffmann 
622a4ccabcfSAnthony Liguori /** QEMU Events **/
623a4ccabcfSAnthony Liguori 
624a4ccabcfSAnthony Liguori static void gd_change_runstate(void *opaque, int running, RunState state)
625a4ccabcfSAnthony Liguori {
626a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
627a4ccabcfSAnthony Liguori 
628a4ccabcfSAnthony Liguori     gd_update_caption(s);
629a4ccabcfSAnthony Liguori }
630a4ccabcfSAnthony Liguori 
631a4ccabcfSAnthony Liguori static void gd_mouse_mode_change(Notifier *notify, void *data)
632a4ccabcfSAnthony Liguori {
633800b0e81STakashi Iwai     GtkDisplayState *s;
63499623c90SGerd Hoffmann     int i;
635800b0e81STakashi Iwai 
636800b0e81STakashi Iwai     s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
637800b0e81STakashi Iwai     /* release the grab at switching to absolute mode */
638800b0e81STakashi Iwai     if (qemu_input_is_absolute() && gd_is_grab_active(s)) {
639800b0e81STakashi Iwai         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
640a4ccabcfSAnthony Liguori                                        FALSE);
641a4ccabcfSAnthony Liguori     }
64299623c90SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
64399623c90SGerd Hoffmann         VirtualConsole *vc = &s->vc[i];
64499623c90SGerd Hoffmann         gd_update_cursor(vc);
64599623c90SGerd Hoffmann     }
646800b0e81STakashi Iwai }
647a4ccabcfSAnthony Liguori 
648a4ccabcfSAnthony Liguori /** GTK Events **/
649a4ccabcfSAnthony Liguori 
650a4ccabcfSAnthony Liguori static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
651a4ccabcfSAnthony Liguori                                 void *opaque)
652a4ccabcfSAnthony Liguori {
653a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
654e3500d1fSGerd Hoffmann     int i;
655a4ccabcfSAnthony Liguori 
656a4ccabcfSAnthony Liguori     if (!no_quit) {
657e3500d1fSGerd Hoffmann         for (i = 0; i < s->nb_vcs; i++) {
658e3500d1fSGerd Hoffmann             if (s->vc[i].type != GD_VC_GFX) {
659e3500d1fSGerd Hoffmann                 continue;
660e3500d1fSGerd Hoffmann             }
661e3500d1fSGerd Hoffmann             unregister_displaychangelistener(&s->vc[i].gfx.dcl);
662e3500d1fSGerd Hoffmann         }
663a4ccabcfSAnthony Liguori         qmp_quit(NULL);
664a4ccabcfSAnthony Liguori         return FALSE;
665a4ccabcfSAnthony Liguori     }
666a4ccabcfSAnthony Liguori 
667a4ccabcfSAnthony Liguori     return TRUE;
668a4ccabcfSAnthony Liguori }
669a4ccabcfSAnthony Liguori 
670a4ccabcfSAnthony Liguori static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
671a4ccabcfSAnthony Liguori {
672e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
673e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
674c6158483SAnthony Liguori     int mx, my;
675a4ccabcfSAnthony Liguori     int ww, wh;
676a4ccabcfSAnthony Liguori     int fbw, fbh;
677a4ccabcfSAnthony Liguori 
67897edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
67997edf3bdSGerd Hoffmann     if (vc->gfx.gls) {
68097edf3bdSGerd Hoffmann         gd_egl_draw(vc);
68197edf3bdSGerd Hoffmann         return TRUE;
68297edf3bdSGerd Hoffmann     }
68397edf3bdSGerd Hoffmann #endif
68497edf3bdSGerd Hoffmann 
685c6158483SAnthony Liguori     if (!gtk_widget_get_realized(widget)) {
686c6158483SAnthony Liguori         return FALSE;
687c6158483SAnthony Liguori     }
688f98f43eaSGerd Hoffmann     if (!vc->gfx.ds) {
689f98f43eaSGerd Hoffmann         return FALSE;
690f98f43eaSGerd Hoffmann     }
691c6158483SAnthony Liguori 
692e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds);
693e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds);
694a4ccabcfSAnthony Liguori 
695a4ccabcfSAnthony Liguori     gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
696a4ccabcfSAnthony Liguori 
697c6158483SAnthony Liguori     if (s->full_screen) {
698e3500d1fSGerd Hoffmann         vc->gfx.scale_x = (double)ww / fbw;
699e3500d1fSGerd Hoffmann         vc->gfx.scale_y = (double)wh / fbh;
700c6158483SAnthony Liguori     } else if (s->free_scale) {
701c6158483SAnthony Liguori         double sx, sy;
702c6158483SAnthony Liguori 
703c6158483SAnthony Liguori         sx = (double)ww / fbw;
704c6158483SAnthony Liguori         sy = (double)wh / fbh;
705c6158483SAnthony Liguori 
706e3500d1fSGerd Hoffmann         vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
707a4ccabcfSAnthony Liguori     }
708a4ccabcfSAnthony Liguori 
709e3500d1fSGerd Hoffmann     fbw *= vc->gfx.scale_x;
710e3500d1fSGerd Hoffmann     fbh *= vc->gfx.scale_y;
7115104a1f6SAnthony Liguori 
712c6158483SAnthony Liguori     mx = my = 0;
713c6158483SAnthony Liguori     if (ww > fbw) {
714c6158483SAnthony Liguori         mx = (ww - fbw) / 2;
715c6158483SAnthony Liguori     }
716c6158483SAnthony Liguori     if (wh > fbh) {
717c6158483SAnthony Liguori         my = (wh - fbh) / 2;
718c6158483SAnthony Liguori     }
719c6158483SAnthony Liguori 
720c6158483SAnthony Liguori     cairo_rectangle(cr, 0, 0, ww, wh);
721c6158483SAnthony Liguori 
722c6158483SAnthony Liguori     /* Optionally cut out the inner area where the pixmap
723c6158483SAnthony Liguori        will be drawn. This avoids 'flashing' since we're
724c6158483SAnthony Liguori        not double-buffering. Note we're using the undocumented
725c6158483SAnthony Liguori        behaviour of drawing the rectangle from right to left
726c6158483SAnthony Liguori        to cut out the whole */
727c6158483SAnthony Liguori     cairo_rectangle(cr, mx + fbw, my,
728c6158483SAnthony Liguori                     -1 * fbw, fbh);
729c6158483SAnthony Liguori     cairo_fill(cr);
730c6158483SAnthony Liguori 
731e3500d1fSGerd Hoffmann     cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
732e3500d1fSGerd Hoffmann     cairo_set_source_surface(cr, vc->gfx.surface,
733e3500d1fSGerd Hoffmann                              mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
734a4ccabcfSAnthony Liguori     cairo_paint(cr);
735a4ccabcfSAnthony Liguori 
736a4ccabcfSAnthony Liguori     return TRUE;
737a4ccabcfSAnthony Liguori }
738a4ccabcfSAnthony Liguori 
739fe43bca8SDaniel P. Berrange #if !GTK_CHECK_VERSION(3, 0, 0)
740a4ccabcfSAnthony Liguori static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
741a4ccabcfSAnthony Liguori                                 void *opaque)
742a4ccabcfSAnthony Liguori {
743a4ccabcfSAnthony Liguori     cairo_t *cr;
744a4ccabcfSAnthony Liguori     gboolean ret;
745a4ccabcfSAnthony Liguori 
746a4ccabcfSAnthony Liguori     cr = gdk_cairo_create(gtk_widget_get_window(widget));
747a4ccabcfSAnthony Liguori     cairo_rectangle(cr,
748a4ccabcfSAnthony Liguori                     expose->area.x,
749a4ccabcfSAnthony Liguori                     expose->area.y,
750a4ccabcfSAnthony Liguori                     expose->area.width,
751a4ccabcfSAnthony Liguori                     expose->area.height);
752a4ccabcfSAnthony Liguori     cairo_clip(cr);
753a4ccabcfSAnthony Liguori 
754a4ccabcfSAnthony Liguori     ret = gd_draw_event(widget, cr, opaque);
755a4ccabcfSAnthony Liguori 
756a4ccabcfSAnthony Liguori     cairo_destroy(cr);
757a4ccabcfSAnthony Liguori 
758a4ccabcfSAnthony Liguori     return ret;
759a4ccabcfSAnthony Liguori }
760fe43bca8SDaniel P. Berrange #endif
761a4ccabcfSAnthony Liguori 
762a4ccabcfSAnthony Liguori static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
763a4ccabcfSAnthony Liguori                                 void *opaque)
764a4ccabcfSAnthony Liguori {
765e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
766e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
767a4ccabcfSAnthony Liguori     int x, y;
768c6158483SAnthony Liguori     int mx, my;
769c6158483SAnthony Liguori     int fbh, fbw;
770c6158483SAnthony Liguori     int ww, wh;
771a4ccabcfSAnthony Liguori 
772f98f43eaSGerd Hoffmann     if (!vc->gfx.ds) {
773f98f43eaSGerd Hoffmann         return TRUE;
774f98f43eaSGerd Hoffmann     }
775f98f43eaSGerd Hoffmann 
776e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
777e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
778c6158483SAnthony Liguori 
779e3500d1fSGerd Hoffmann     gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area),
780e3500d1fSGerd Hoffmann                           &ww, &wh);
781c6158483SAnthony Liguori 
782c6158483SAnthony Liguori     mx = my = 0;
783c6158483SAnthony Liguori     if (ww > fbw) {
784c6158483SAnthony Liguori         mx = (ww - fbw) / 2;
785c6158483SAnthony Liguori     }
786c6158483SAnthony Liguori     if (wh > fbh) {
787c6158483SAnthony Liguori         my = (wh - fbh) / 2;
788c6158483SAnthony Liguori     }
789c6158483SAnthony Liguori 
790e3500d1fSGerd Hoffmann     x = (motion->x - mx) / vc->gfx.scale_x;
791e3500d1fSGerd Hoffmann     y = (motion->y - my) / vc->gfx.scale_y;
792c6158483SAnthony Liguori 
793e61031cdSTakashi Iwai     if (qemu_input_is_absolute()) {
794c6158483SAnthony Liguori         if (x < 0 || y < 0 ||
795e3500d1fSGerd Hoffmann             x >= surface_width(vc->gfx.ds) ||
796e3500d1fSGerd Hoffmann             y >= surface_height(vc->gfx.ds)) {
797c6158483SAnthony Liguori             return TRUE;
798c6158483SAnthony Liguori         }
799e3500d1fSGerd Hoffmann         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
800e3500d1fSGerd Hoffmann                              surface_width(vc->gfx.ds));
801e3500d1fSGerd Hoffmann         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
802e3500d1fSGerd Hoffmann                              surface_height(vc->gfx.ds));
803192f81bfSGerd Hoffmann         qemu_input_event_sync();
8042884cf5bSGerd Hoffmann     } else if (s->last_set && s->ptr_owner == vc) {
805e3500d1fSGerd Hoffmann         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
806e3500d1fSGerd Hoffmann         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
807192f81bfSGerd Hoffmann         qemu_input_event_sync();
808a4ccabcfSAnthony Liguori     }
809a4ccabcfSAnthony Liguori     s->last_x = x;
810a4ccabcfSAnthony Liguori     s->last_y = y;
811e61031cdSTakashi Iwai     s->last_set = TRUE;
812a4ccabcfSAnthony Liguori 
8132884cf5bSGerd Hoffmann     if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
814e3500d1fSGerd Hoffmann         GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
8155104a1f6SAnthony Liguori         int x = (int)motion->x_root;
8165104a1f6SAnthony Liguori         int y = (int)motion->y_root;
8175104a1f6SAnthony Liguori 
8185104a1f6SAnthony Liguori         /* In relative mode check to see if client pointer hit
8195104a1f6SAnthony Liguori          * one of the screen edges, and if so move it back by
8205104a1f6SAnthony Liguori          * 200 pixels. This is important because the pointer
8215104a1f6SAnthony Liguori          * in the server doesn't correspond 1-for-1, and so
8225104a1f6SAnthony Liguori          * may still be only half way across the screen. Without
8235104a1f6SAnthony Liguori          * this warp, the server pointer would thus appear to hit
8245104a1f6SAnthony Liguori          * an invisible wall */
8255104a1f6SAnthony Liguori         if (x == 0) {
8265104a1f6SAnthony Liguori             x += 200;
8275104a1f6SAnthony Liguori         }
8285104a1f6SAnthony Liguori         if (y == 0) {
8295104a1f6SAnthony Liguori             y += 200;
8305104a1f6SAnthony Liguori         }
8315104a1f6SAnthony Liguori         if (x == (gdk_screen_get_width(screen) - 1)) {
8325104a1f6SAnthony Liguori             x -= 200;
8335104a1f6SAnthony Liguori         }
8345104a1f6SAnthony Liguori         if (y == (gdk_screen_get_height(screen) - 1)) {
8355104a1f6SAnthony Liguori             y -= 200;
8365104a1f6SAnthony Liguori         }
8375104a1f6SAnthony Liguori 
8385104a1f6SAnthony Liguori         if (x != (int)motion->x_root || y != (int)motion->y_root) {
8398906de76SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
8408906de76SDaniel P. Berrange             GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
8418906de76SDaniel P. Berrange             gdk_device_warp(dev, screen, x, y);
8428906de76SDaniel P. Berrange #else
8438906de76SDaniel P. Berrange             GdkDisplay *display = gtk_widget_get_display(widget);
8445104a1f6SAnthony Liguori             gdk_display_warp_pointer(display, screen, x, y);
8458906de76SDaniel P. Berrange #endif
846e61031cdSTakashi Iwai             s->last_set = FALSE;
8475104a1f6SAnthony Liguori             return FALSE;
8485104a1f6SAnthony Liguori         }
8495104a1f6SAnthony Liguori     }
850a4ccabcfSAnthony Liguori     return TRUE;
851a4ccabcfSAnthony Liguori }
852a4ccabcfSAnthony Liguori 
853a4ccabcfSAnthony Liguori static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
854a4ccabcfSAnthony Liguori                                 void *opaque)
855a4ccabcfSAnthony Liguori {
856e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
857e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
858192f81bfSGerd Hoffmann     InputButton btn;
859a4ccabcfSAnthony Liguori 
860800b0e81STakashi Iwai     /* implicitly grab the input at the first click in the relative mode */
861800b0e81STakashi Iwai     if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
8622884cf5bSGerd Hoffmann         !qemu_input_is_absolute() && s->ptr_owner != vc) {
8632884cf5bSGerd Hoffmann         if (!vc->window) {
864800b0e81STakashi Iwai             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
865800b0e81STakashi Iwai                                            TRUE);
8662884cf5bSGerd Hoffmann         } else {
867d531deefSGerd Hoffmann             gd_grab_pointer(vc, "relative-mode-click");
8682884cf5bSGerd Hoffmann         }
869800b0e81STakashi Iwai         return TRUE;
870800b0e81STakashi Iwai     }
871800b0e81STakashi Iwai 
872a4ccabcfSAnthony Liguori     if (button->button == 1) {
873192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_LEFT;
874a4ccabcfSAnthony Liguori     } else if (button->button == 2) {
875192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_MIDDLE;
876a4ccabcfSAnthony Liguori     } else if (button->button == 3) {
877192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_RIGHT;
878a4ccabcfSAnthony Liguori     } else {
879192f81bfSGerd Hoffmann         return TRUE;
880a4ccabcfSAnthony Liguori     }
881a4ccabcfSAnthony Liguori 
882e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn,
883e3500d1fSGerd Hoffmann                          button->type == GDK_BUTTON_PRESS);
884192f81bfSGerd Hoffmann     qemu_input_event_sync();
885a4ccabcfSAnthony Liguori     return TRUE;
886a4ccabcfSAnthony Liguori }
887a4ccabcfSAnthony Liguori 
888d58b9122SJan Kiszka static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
889d58b9122SJan Kiszka                                 void *opaque)
890d58b9122SJan Kiszka {
891e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
892d58b9122SJan Kiszka     InputButton btn;
893d58b9122SJan Kiszka 
894d58b9122SJan Kiszka     if (scroll->direction == GDK_SCROLL_UP) {
895d58b9122SJan Kiszka         btn = INPUT_BUTTON_WHEEL_UP;
896d58b9122SJan Kiszka     } else if (scroll->direction == GDK_SCROLL_DOWN) {
897d58b9122SJan Kiszka         btn = INPUT_BUTTON_WHEEL_DOWN;
898d58b9122SJan Kiszka     } else {
899d58b9122SJan Kiszka         return TRUE;
900d58b9122SJan Kiszka     }
901d58b9122SJan Kiszka 
902e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
903d58b9122SJan Kiszka     qemu_input_event_sync();
904e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
905d58b9122SJan Kiszka     qemu_input_event_sync();
906d58b9122SJan Kiszka     return TRUE;
907d58b9122SJan Kiszka }
908d58b9122SJan Kiszka 
9090a337ed0SGerd Hoffmann static int gd_map_keycode(GtkDisplayState *s, GdkDisplay *dpy, int gdk_keycode)
910a4ccabcfSAnthony Liguori {
911932f2d7eSGerd Hoffmann     int qemu_keycode;
912a4ccabcfSAnthony Liguori 
9130a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_WIN32
9140a337ed0SGerd Hoffmann     if (GDK_IS_WIN32_DISPLAY(dpy)) {
915932f2d7eSGerd Hoffmann         qemu_keycode = MapVirtualKey(gdk_keycode, MAPVK_VK_TO_VSC);
9162777ccc5SStefan Weil         switch (qemu_keycode) {
9172777ccc5SStefan Weil         case 103:   /* alt gr */
9182777ccc5SStefan Weil             qemu_keycode = 56 | SCANCODE_GREY;
9192777ccc5SStefan Weil             break;
9202777ccc5SStefan Weil         }
9210a337ed0SGerd Hoffmann         return qemu_keycode;
9220a337ed0SGerd Hoffmann     }
9230a337ed0SGerd Hoffmann #endif
924a4ccabcfSAnthony Liguori 
925a4ccabcfSAnthony Liguori     if (gdk_keycode < 9) {
926a4ccabcfSAnthony Liguori         qemu_keycode = 0;
927a4ccabcfSAnthony Liguori     } else if (gdk_keycode < 97) {
928a4ccabcfSAnthony Liguori         qemu_keycode = gdk_keycode - 8;
9290a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_X11
9300a337ed0SGerd Hoffmann     } else if (GDK_IS_X11_DISPLAY(dpy) && gdk_keycode < 158) {
9313158a348SBruce Rogers         if (s->has_evdev) {
932a4ccabcfSAnthony Liguori             qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
9333158a348SBruce Rogers         } else {
9343158a348SBruce Rogers             qemu_keycode = translate_xfree86_keycode(gdk_keycode - 97);
9353158a348SBruce Rogers         }
9360a337ed0SGerd Hoffmann #endif
937a4ccabcfSAnthony Liguori     } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
938a4ccabcfSAnthony Liguori         qemu_keycode = 0x70;
939a4ccabcfSAnthony Liguori     } else if (gdk_keycode == 211) { /* backslash */
940a4ccabcfSAnthony Liguori         qemu_keycode = 0x73;
941a4ccabcfSAnthony Liguori     } else {
942a4ccabcfSAnthony Liguori         qemu_keycode = 0;
943a4ccabcfSAnthony Liguori     }
944a4ccabcfSAnthony Liguori 
945932f2d7eSGerd Hoffmann     return qemu_keycode;
946932f2d7eSGerd Hoffmann }
947932f2d7eSGerd Hoffmann 
948f8c223f6SGerd Hoffmann static gboolean gd_text_key_down(GtkWidget *widget,
949f8c223f6SGerd Hoffmann                                  GdkEventKey *key, void *opaque)
950f8c223f6SGerd Hoffmann {
951f8c223f6SGerd Hoffmann     VirtualConsole *vc = opaque;
952f8c223f6SGerd Hoffmann     QemuConsole *con = vc->gfx.dcl.con;
953f8c223f6SGerd Hoffmann 
954f8c223f6SGerd Hoffmann     if (key->length) {
955f8c223f6SGerd Hoffmann         kbd_put_string_console(con, key->string, key->length);
956f8c223f6SGerd Hoffmann     } else {
957f8c223f6SGerd Hoffmann         int num = gd_map_keycode(vc->s, gtk_widget_get_display(widget),
958f8c223f6SGerd Hoffmann                                  key->hardware_keycode);
959f8c223f6SGerd Hoffmann         int qcode = qemu_input_key_number_to_qcode(num);
960f8c223f6SGerd Hoffmann         kbd_put_qcode_console(con, qcode);
961f8c223f6SGerd Hoffmann     }
962f8c223f6SGerd Hoffmann     return TRUE;
963f8c223f6SGerd Hoffmann }
964f8c223f6SGerd Hoffmann 
965932f2d7eSGerd Hoffmann static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
966932f2d7eSGerd Hoffmann {
967932f2d7eSGerd Hoffmann     VirtualConsole *vc = opaque;
968932f2d7eSGerd Hoffmann     GtkDisplayState *s = vc->s;
969932f2d7eSGerd Hoffmann     int gdk_keycode = key->hardware_keycode;
970932f2d7eSGerd Hoffmann     int qemu_keycode;
971932f2d7eSGerd Hoffmann     int i;
972932f2d7eSGerd Hoffmann 
9731a01716aSJan Kiszka     if (s->ignore_keys) {
9741a01716aSJan Kiszka         s->ignore_keys = (key->type == GDK_KEY_PRESS);
9751a01716aSJan Kiszka         return TRUE;
9761a01716aSJan Kiszka     }
9771a01716aSJan Kiszka 
9785c960521SMartin Decky     if (key->keyval == GDK_KEY_Pause) {
9795c960521SMartin Decky         qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE,
9805c960521SMartin Decky                                         key->type == GDK_KEY_PRESS);
9815c960521SMartin Decky         return TRUE;
9825c960521SMartin Decky     }
9835c960521SMartin Decky 
9840a337ed0SGerd Hoffmann     qemu_keycode = gd_map_keycode(s, gtk_widget_get_display(widget),
9850a337ed0SGerd Hoffmann                                   gdk_keycode);
986932f2d7eSGerd Hoffmann 
98774444bc1SGerd Hoffmann     trace_gd_key_event(vc->label, gdk_keycode, qemu_keycode,
988a4ccabcfSAnthony Liguori                        (key->type == GDK_KEY_PRESS) ? "down" : "up");
989a4ccabcfSAnthony Liguori 
9906db253caSJan Kiszka     for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
9916db253caSJan Kiszka         if (qemu_keycode == modifier_keycode[i]) {
9926db253caSJan Kiszka             s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
9936db253caSJan Kiszka         }
9946db253caSJan Kiszka     }
9956db253caSJan Kiszka 
996e3500d1fSGerd Hoffmann     qemu_input_event_send_key_number(vc->gfx.dcl.con, qemu_keycode,
997af98ba92SGerd Hoffmann                                      key->type == GDK_KEY_PRESS);
998a4ccabcfSAnthony Liguori 
999a4ccabcfSAnthony Liguori     return TRUE;
1000a4ccabcfSAnthony Liguori }
1001a4ccabcfSAnthony Liguori 
10020d0e044dSTakashi Iwai static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
10030d0e044dSTakashi Iwai {
10040d0e044dSTakashi Iwai     if (event->type == GDK_MOTION_NOTIFY) {
10050d0e044dSTakashi Iwai         return gd_motion_event(widget, &event->motion, opaque);
10060d0e044dSTakashi Iwai     }
10070d0e044dSTakashi Iwai     return FALSE;
10080d0e044dSTakashi Iwai }
10090d0e044dSTakashi Iwai 
1010a4ccabcfSAnthony Liguori /** Window Menu Actions **/
1011a4ccabcfSAnthony Liguori 
101230e8f22bSJan Kiszka static void gd_menu_pause(GtkMenuItem *item, void *opaque)
101330e8f22bSJan Kiszka {
101430e8f22bSJan Kiszka     GtkDisplayState *s = opaque;
101530e8f22bSJan Kiszka 
101630e8f22bSJan Kiszka     if (s->external_pause_update) {
101730e8f22bSJan Kiszka         return;
101830e8f22bSJan Kiszka     }
101930e8f22bSJan Kiszka     if (runstate_is_running()) {
102030e8f22bSJan Kiszka         qmp_stop(NULL);
102130e8f22bSJan Kiszka     } else {
102230e8f22bSJan Kiszka         qmp_cont(NULL);
102330e8f22bSJan Kiszka     }
102430e8f22bSJan Kiszka }
102530e8f22bSJan Kiszka 
102630e8f22bSJan Kiszka static void gd_menu_reset(GtkMenuItem *item, void *opaque)
102730e8f22bSJan Kiszka {
102830e8f22bSJan Kiszka     qmp_system_reset(NULL);
102930e8f22bSJan Kiszka }
103030e8f22bSJan Kiszka 
103130e8f22bSJan Kiszka static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
103230e8f22bSJan Kiszka {
103330e8f22bSJan Kiszka     qmp_system_powerdown(NULL);
103430e8f22bSJan Kiszka }
103530e8f22bSJan Kiszka 
1036a4ccabcfSAnthony Liguori static void gd_menu_quit(GtkMenuItem *item, void *opaque)
1037a4ccabcfSAnthony Liguori {
1038a4ccabcfSAnthony Liguori     qmp_quit(NULL);
1039a4ccabcfSAnthony Liguori }
1040a4ccabcfSAnthony Liguori 
1041a4ccabcfSAnthony Liguori static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
1042a4ccabcfSAnthony Liguori {
1043a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
1044e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_by_menu(s);
1045e72b59faSJohn Snow     GtkNotebook *nb = GTK_NOTEBOOK(s->notebook);
1046832189c9SGerd Hoffmann     gint page;
1047a4ccabcfSAnthony Liguori 
10486db253caSJan Kiszka     gtk_release_modifiers(s);
1049271a25c0SGerd Hoffmann     if (vc) {
1050e72b59faSJohn Snow         page = gtk_notebook_page_num(nb, vc->tab_item);
1051e72b59faSJohn Snow         gtk_notebook_set_current_page(nb, page);
10529d677e1cSJan Kiszka         gtk_widget_grab_focus(vc->focus);
1053d861def3SAnthony Liguori     }
10541a01716aSJan Kiszka     s->ignore_keys = false;
1055d861def3SAnthony Liguori }
1056a4ccabcfSAnthony Liguori 
1057277836c8SCole Robinson static void gd_accel_switch_vc(void *opaque)
1058277836c8SCole Robinson {
1059277836c8SCole Robinson     VirtualConsole *vc = opaque;
10601a01716aSJan Kiszka 
1061277836c8SCole Robinson     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
10621a01716aSJan Kiszka #if !GTK_CHECK_VERSION(3, 0, 0)
10631a01716aSJan Kiszka     /* GTK2 sends the accel key to the target console - ignore this until */
10641a01716aSJan Kiszka     vc->s->ignore_keys = true;
10651a01716aSJan Kiszka #endif
1066277836c8SCole Robinson }
1067277836c8SCole Robinson 
1068a4ccabcfSAnthony Liguori static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
1069a4ccabcfSAnthony Liguori {
1070a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
1071fa7a1e52SGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1072a4ccabcfSAnthony Liguori 
1073a4ccabcfSAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
1074a4ccabcfSAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
1075a4ccabcfSAnthony Liguori     } else {
1076a4ccabcfSAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1077a4ccabcfSAnthony Liguori     }
1078fa7a1e52SGerd Hoffmann     gd_update_windowsize(vc);
1079a4ccabcfSAnthony Liguori }
1080a4ccabcfSAnthony Liguori 
1081cdeb7090SGerd Hoffmann static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
1082cdeb7090SGerd Hoffmann                                     void *opaque)
1083cdeb7090SGerd Hoffmann {
1084cdeb7090SGerd Hoffmann     VirtualConsole *vc = opaque;
1085cdeb7090SGerd Hoffmann     GtkDisplayState *s = vc->s;
1086cdeb7090SGerd Hoffmann 
1087cdeb7090SGerd Hoffmann     gtk_widget_set_sensitive(vc->menu_item, true);
1088316cb068SGerd Hoffmann     gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
1089cdeb7090SGerd Hoffmann     gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
1090cdeb7090SGerd Hoffmann                                     vc->tab_item, vc->label);
1091cdeb7090SGerd Hoffmann     gtk_widget_destroy(vc->window);
1092cdeb7090SGerd Hoffmann     vc->window = NULL;
1093cdeb7090SGerd Hoffmann     return TRUE;
1094cdeb7090SGerd Hoffmann }
1095cdeb7090SGerd Hoffmann 
10960c77a37fSGerd Hoffmann static gboolean gd_win_grab(void *opaque)
10970c77a37fSGerd Hoffmann {
10980c77a37fSGerd Hoffmann     VirtualConsole *vc = opaque;
10990c77a37fSGerd Hoffmann 
11000c77a37fSGerd Hoffmann     fprintf(stderr, "%s: %s\n", __func__, vc->label);
11010c77a37fSGerd Hoffmann     if (vc->s->ptr_owner) {
11020c77a37fSGerd Hoffmann         gd_ungrab_pointer(vc->s);
11030c77a37fSGerd Hoffmann     } else {
1104d531deefSGerd Hoffmann         gd_grab_pointer(vc, "user-request-detached-tab");
11050c77a37fSGerd Hoffmann     }
11060c77a37fSGerd Hoffmann     return TRUE;
11070c77a37fSGerd Hoffmann }
11080c77a37fSGerd Hoffmann 
1109cdeb7090SGerd Hoffmann static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
1110cdeb7090SGerd Hoffmann {
1111cdeb7090SGerd Hoffmann     GtkDisplayState *s = opaque;
1112cdeb7090SGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1113cdeb7090SGerd Hoffmann 
1114f8c223f6SGerd Hoffmann     if (vc->type == GD_VC_GFX &&
1115f8c223f6SGerd Hoffmann         qemu_console_is_graphic(vc->gfx.dcl.con)) {
1116aa0a55d4SGerd Hoffmann         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1117aa0a55d4SGerd Hoffmann                                        FALSE);
1118cdeb7090SGerd Hoffmann     }
1119cdeb7090SGerd Hoffmann     if (!vc->window) {
1120cdeb7090SGerd Hoffmann         gtk_widget_set_sensitive(vc->menu_item, false);
1121cdeb7090SGerd Hoffmann         vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1122316cb068SGerd Hoffmann         gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
1123cdeb7090SGerd Hoffmann 
1124cdeb7090SGerd Hoffmann         g_signal_connect(vc->window, "delete-event",
1125cdeb7090SGerd Hoffmann                          G_CALLBACK(gd_tab_window_close), vc);
1126cdeb7090SGerd Hoffmann         gtk_widget_show_all(vc->window);
11274eeaa3a8SGerd Hoffmann 
1128f8c223f6SGerd Hoffmann         if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
11290c77a37fSGerd Hoffmann             GtkAccelGroup *ag = gtk_accel_group_new();
11300c77a37fSGerd Hoffmann             gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
11310c77a37fSGerd Hoffmann 
1132f8c223f6SGerd Hoffmann             GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
1133f8c223f6SGerd Hoffmann                                                vc, NULL);
11340c77a37fSGerd Hoffmann             gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
1135f8c223f6SGerd Hoffmann         }
11360c77a37fSGerd Hoffmann 
113782fc1809SGerd Hoffmann         gd_update_geometry_hints(vc);
11384eeaa3a8SGerd Hoffmann         gd_update_caption(s);
1139cdeb7090SGerd Hoffmann     }
1140cdeb7090SGerd Hoffmann }
1141cdeb7090SGerd Hoffmann 
1142c6158483SAnthony Liguori static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
1143c6158483SAnthony Liguori {
1144c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1145e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1146c6158483SAnthony Liguori 
114710409282SStefan Weil     if (!s->full_screen) {
1148c6158483SAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1149b0f31820SCole Robinson         gtk_widget_hide(s->menu_bar);
1150e3500d1fSGerd Hoffmann         if (vc->type == GD_VC_GFX) {
1151e3500d1fSGerd Hoffmann             gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
1152c6158483SAnthony Liguori         }
1153e3500d1fSGerd Hoffmann         gtk_window_fullscreen(GTK_WINDOW(s->window));
1154c6158483SAnthony Liguori         s->full_screen = TRUE;
1155c6158483SAnthony Liguori     } else {
1156c6158483SAnthony Liguori         gtk_window_unfullscreen(GTK_WINDOW(s->window));
1157c6158483SAnthony Liguori         gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
1158b0f31820SCole Robinson         gtk_widget_show(s->menu_bar);
1159c6158483SAnthony Liguori         s->full_screen = FALSE;
1160e3500d1fSGerd Hoffmann         if (vc->type == GD_VC_GFX) {
1161e3500d1fSGerd Hoffmann             vc->gfx.scale_x = 1.0;
1162e3500d1fSGerd Hoffmann             vc->gfx.scale_y = 1.0;
116382fc1809SGerd Hoffmann             gd_update_windowsize(vc);
1164e3500d1fSGerd Hoffmann         }
1165c6158483SAnthony Liguori     }
1166c6158483SAnthony Liguori 
1167e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
1168c6158483SAnthony Liguori }
1169c6158483SAnthony Liguori 
117095414914SCole Robinson static void gd_accel_full_screen(void *opaque)
117195414914SCole Robinson {
117295414914SCole Robinson     GtkDisplayState *s = opaque;
117395414914SCole Robinson     gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
117495414914SCole Robinson }
117595414914SCole Robinson 
1176c6158483SAnthony Liguori static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
1177c6158483SAnthony Liguori {
1178c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1179e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1180c6158483SAnthony Liguori 
1181c6158483SAnthony Liguori     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1182c6158483SAnthony Liguori                                    FALSE);
1183c6158483SAnthony Liguori 
118482fc1809SGerd Hoffmann     vc->gfx.scale_x += VC_SCALE_STEP;
118582fc1809SGerd Hoffmann     vc->gfx.scale_y += VC_SCALE_STEP;
1186c6158483SAnthony Liguori 
1187e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1188c6158483SAnthony Liguori }
1189c6158483SAnthony Liguori 
1190c6158483SAnthony Liguori static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
1191c6158483SAnthony Liguori {
1192c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1193e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1194c6158483SAnthony Liguori 
1195c6158483SAnthony Liguori     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1196c6158483SAnthony Liguori                                    FALSE);
1197c6158483SAnthony Liguori 
119882fc1809SGerd Hoffmann     vc->gfx.scale_x -= VC_SCALE_STEP;
119982fc1809SGerd Hoffmann     vc->gfx.scale_y -= VC_SCALE_STEP;
1200c6158483SAnthony Liguori 
120182fc1809SGerd Hoffmann     vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
120282fc1809SGerd Hoffmann     vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
1203c6158483SAnthony Liguori 
1204e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1205c6158483SAnthony Liguori }
1206c6158483SAnthony Liguori 
1207c6158483SAnthony Liguori static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
1208c6158483SAnthony Liguori {
1209c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1210e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1211c6158483SAnthony Liguori 
1212e3500d1fSGerd Hoffmann     vc->gfx.scale_x = 1.0;
1213e3500d1fSGerd Hoffmann     vc->gfx.scale_y = 1.0;
1214c6158483SAnthony Liguori 
1215e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1216c6158483SAnthony Liguori }
1217c6158483SAnthony Liguori 
1218c6158483SAnthony Liguori static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
1219c6158483SAnthony Liguori {
1220c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1221e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1222c6158483SAnthony Liguori 
1223c6158483SAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
1224c6158483SAnthony Liguori         s->free_scale = TRUE;
1225c6158483SAnthony Liguori     } else {
1226c6158483SAnthony Liguori         s->free_scale = FALSE;
1227e3500d1fSGerd Hoffmann         vc->gfx.scale_x = 1.0;
1228e3500d1fSGerd Hoffmann         vc->gfx.scale_y = 1.0;
1229c6158483SAnthony Liguori     }
1230c6158483SAnthony Liguori 
123182fc1809SGerd Hoffmann     gd_update_windowsize(vc);
1232e3500d1fSGerd Hoffmann     gd_update_full_redraw(vc);
1233c6158483SAnthony Liguori }
1234c6158483SAnthony Liguori 
1235f50def91SGerd Hoffmann #if GTK_CHECK_VERSION(3, 0, 0)
1236f50def91SGerd Hoffmann static void gd_grab_devices(VirtualConsole *vc, bool grab,
1237f50def91SGerd Hoffmann                             GdkInputSource source, GdkEventMask mask,
1238f50def91SGerd Hoffmann                             GdkCursor *cursor)
1239f50def91SGerd Hoffmann {
1240f50def91SGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1241f50def91SGerd Hoffmann     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1242f50def91SGerd Hoffmann     GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
1243f50def91SGerd Hoffmann     GList *tmp = devs;
1244f50def91SGerd Hoffmann 
1245f50def91SGerd Hoffmann     for (tmp = devs; tmp; tmp = tmp->next) {
1246f50def91SGerd Hoffmann         GdkDevice *dev = tmp->data;
1247f50def91SGerd Hoffmann         if (gdk_device_get_source(dev) != source) {
1248f50def91SGerd Hoffmann             continue;
1249f50def91SGerd Hoffmann         }
1250f50def91SGerd Hoffmann         if (grab) {
1251f50def91SGerd Hoffmann             GdkWindow *win = gtk_widget_get_window(vc->gfx.drawing_area);
1252f50def91SGerd Hoffmann             gdk_device_grab(dev, win, GDK_OWNERSHIP_NONE, FALSE,
1253f50def91SGerd Hoffmann                             mask, cursor, GDK_CURRENT_TIME);
1254f50def91SGerd Hoffmann         } else {
1255f50def91SGerd Hoffmann             gdk_device_ungrab(dev, GDK_CURRENT_TIME);
1256f50def91SGerd Hoffmann         }
1257f50def91SGerd Hoffmann     }
1258f50def91SGerd Hoffmann     g_list_free(devs);
1259f50def91SGerd Hoffmann }
1260f50def91SGerd Hoffmann #endif
1261f50def91SGerd Hoffmann 
1262d531deefSGerd Hoffmann static void gd_grab_keyboard(VirtualConsole *vc, const char *reason)
12635104a1f6SAnthony Liguori {
1264aa4f4058SGerd Hoffmann     if (vc->s->kbd_owner) {
1265aa4f4058SGerd Hoffmann         if (vc->s->kbd_owner == vc) {
1266aa4f4058SGerd Hoffmann             return;
1267aa4f4058SGerd Hoffmann         } else {
1268aa4f4058SGerd Hoffmann             gd_ungrab_keyboard(vc->s);
1269aa4f4058SGerd Hoffmann         }
1270aa4f4058SGerd Hoffmann     }
1271aa4f4058SGerd Hoffmann 
1272655199daSDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
1273f50def91SGerd Hoffmann     gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD,
1274655199daSDaniel P. Berrange                    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
1275f50def91SGerd Hoffmann                    NULL);
1276655199daSDaniel P. Berrange #else
1277e3500d1fSGerd Hoffmann     gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area),
12785104a1f6SAnthony Liguori                       FALSE,
12795104a1f6SAnthony Liguori                       GDK_CURRENT_TIME);
1280655199daSDaniel P. Berrange #endif
12814c638e2eSGerd Hoffmann     vc->s->kbd_owner = vc;
1282695cc59dSGerd Hoffmann     gd_update_caption(vc->s);
1283d531deefSGerd Hoffmann     trace_gd_grab(vc->label, "kbd", reason);
12845104a1f6SAnthony Liguori }
12855104a1f6SAnthony Liguori 
12864c638e2eSGerd Hoffmann static void gd_ungrab_keyboard(GtkDisplayState *s)
12875104a1f6SAnthony Liguori {
12884c638e2eSGerd Hoffmann     VirtualConsole *vc = s->kbd_owner;
12894c638e2eSGerd Hoffmann 
12904c638e2eSGerd Hoffmann     if (vc == NULL) {
12914c638e2eSGerd Hoffmann         return;
12924c638e2eSGerd Hoffmann     }
12934c638e2eSGerd Hoffmann     s->kbd_owner = NULL;
12944c638e2eSGerd Hoffmann 
1295655199daSDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
1296f50def91SGerd Hoffmann     gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL);
1297655199daSDaniel P. Berrange #else
12985104a1f6SAnthony Liguori     gdk_keyboard_ungrab(GDK_CURRENT_TIME);
1299655199daSDaniel P. Berrange #endif
1300695cc59dSGerd Hoffmann     gd_update_caption(s);
1301d531deefSGerd Hoffmann     trace_gd_ungrab(vc->label, "kbd");
13025104a1f6SAnthony Liguori }
13035104a1f6SAnthony Liguori 
1304d531deefSGerd Hoffmann static void gd_grab_pointer(VirtualConsole *vc, const char *reason)
13055104a1f6SAnthony Liguori {
1306e3500d1fSGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1307aa4f4058SGerd Hoffmann 
1308aa4f4058SGerd Hoffmann     if (vc->s->ptr_owner) {
1309aa4f4058SGerd Hoffmann         if (vc->s->ptr_owner == vc) {
1310aa4f4058SGerd Hoffmann             return;
1311aa4f4058SGerd Hoffmann         } else {
1312aa4f4058SGerd Hoffmann             gd_ungrab_pointer(vc->s);
1313aa4f4058SGerd Hoffmann         }
1314aa4f4058SGerd Hoffmann     }
1315aa4f4058SGerd Hoffmann 
1316ecce1929STakashi Iwai #if GTK_CHECK_VERSION(3, 0, 0)
13172a05485dSDaniel P. Berrange     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1318f50def91SGerd Hoffmann     gd_grab_devices(vc, true, GDK_SOURCE_MOUSE,
13192a05485dSDaniel P. Berrange                     GDK_POINTER_MOTION_MASK |
13202a05485dSDaniel P. Berrange                     GDK_BUTTON_PRESS_MASK |
13212a05485dSDaniel P. Berrange                     GDK_BUTTON_RELEASE_MASK |
13222a05485dSDaniel P. Berrange                     GDK_BUTTON_MOTION_MASK |
13232a05485dSDaniel P. Berrange                     GDK_SCROLL_MASK,
1324f50def91SGerd Hoffmann                     vc->s->null_cursor);
1325ecce1929STakashi Iwai     gdk_device_get_position(gdk_device_manager_get_client_pointer(mgr),
1326e3500d1fSGerd Hoffmann                             NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
13272a05485dSDaniel P. Berrange #else
1328e3500d1fSGerd Hoffmann     gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area),
13295104a1f6SAnthony Liguori                      FALSE, /* All events to come to our window directly */
13305104a1f6SAnthony Liguori                      GDK_POINTER_MOTION_MASK |
13315104a1f6SAnthony Liguori                      GDK_BUTTON_PRESS_MASK |
13325104a1f6SAnthony Liguori                      GDK_BUTTON_RELEASE_MASK |
13335104a1f6SAnthony Liguori                      GDK_BUTTON_MOTION_MASK |
13345104a1f6SAnthony Liguori                      GDK_SCROLL_MASK,
13355104a1f6SAnthony Liguori                      NULL, /* Allow cursor to move over entire desktop */
1336e3500d1fSGerd Hoffmann                      vc->s->null_cursor,
13375104a1f6SAnthony Liguori                      GDK_CURRENT_TIME);
1338ecce1929STakashi Iwai     gdk_display_get_pointer(display, NULL,
1339e3500d1fSGerd Hoffmann                             &vc->s->grab_x_root, &vc->s->grab_y_root, NULL);
13402a05485dSDaniel P. Berrange #endif
13414c638e2eSGerd Hoffmann     vc->s->ptr_owner = vc;
1342695cc59dSGerd Hoffmann     gd_update_caption(vc->s);
1343d531deefSGerd Hoffmann     trace_gd_grab(vc->label, "ptr", reason);
13442a05485dSDaniel P. Berrange }
13452a05485dSDaniel P. Berrange 
13464c638e2eSGerd Hoffmann static void gd_ungrab_pointer(GtkDisplayState *s)
13472a05485dSDaniel P. Berrange {
13484c638e2eSGerd Hoffmann     VirtualConsole *vc = s->ptr_owner;
13494c638e2eSGerd Hoffmann 
13504c638e2eSGerd Hoffmann     if (vc == NULL) {
13514c638e2eSGerd Hoffmann         return;
13524c638e2eSGerd Hoffmann     }
13534c638e2eSGerd Hoffmann     s->ptr_owner = NULL;
13544c638e2eSGerd Hoffmann 
1355e3500d1fSGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1356ecce1929STakashi Iwai #if GTK_CHECK_VERSION(3, 0, 0)
13572a05485dSDaniel P. Berrange     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1358f50def91SGerd Hoffmann     gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL);
1359ecce1929STakashi Iwai     gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
1360e3500d1fSGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
1361e3500d1fSGerd Hoffmann                     vc->s->grab_x_root, vc->s->grab_y_root);
13622a05485dSDaniel P. Berrange #else
13632a05485dSDaniel P. Berrange     gdk_pointer_ungrab(GDK_CURRENT_TIME);
1364ecce1929STakashi Iwai     gdk_display_warp_pointer(display,
1365e3500d1fSGerd Hoffmann                              gtk_widget_get_screen(vc->gfx.drawing_area),
1366e3500d1fSGerd Hoffmann                              vc->s->grab_x_root, vc->s->grab_y_root);
13672a05485dSDaniel P. Berrange #endif
1368695cc59dSGerd Hoffmann     gd_update_caption(s);
1369d531deefSGerd Hoffmann     trace_gd_ungrab(vc->label, "ptr");
13702a05485dSDaniel P. Berrange }
13712a05485dSDaniel P. Berrange 
13722a05485dSDaniel P. Berrange static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
13732a05485dSDaniel P. Berrange {
13742a05485dSDaniel P. Berrange     GtkDisplayState *s = opaque;
1375e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
13762a05485dSDaniel P. Berrange 
13772a05485dSDaniel P. Berrange     if (gd_is_grab_active(s)) {
1378d531deefSGerd Hoffmann         gd_grab_keyboard(vc, "user-request-main-window");
1379d531deefSGerd Hoffmann         gd_grab_pointer(vc, "user-request-main-window");
13805104a1f6SAnthony Liguori     } else {
13814c638e2eSGerd Hoffmann         gd_ungrab_keyboard(s);
13824c638e2eSGerd Hoffmann         gd_ungrab_pointer(s);
13835104a1f6SAnthony Liguori     }
13845104a1f6SAnthony Liguori 
1385e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
13865104a1f6SAnthony Liguori }
13875104a1f6SAnthony Liguori 
1388a4ccabcfSAnthony Liguori static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
1389a4ccabcfSAnthony Liguori                            gpointer data)
1390a4ccabcfSAnthony Liguori {
1391a4ccabcfSAnthony Liguori     GtkDisplayState *s = data;
1392e3500d1fSGerd Hoffmann     VirtualConsole *vc;
13935104a1f6SAnthony Liguori     gboolean on_vga;
1394a4ccabcfSAnthony Liguori 
1395a4ccabcfSAnthony Liguori     if (!gtk_widget_get_realized(s->notebook)) {
1396a4ccabcfSAnthony Liguori         return;
1397a4ccabcfSAnthony Liguori     }
1398a4ccabcfSAnthony Liguori 
13996fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK
14006fa27697SGerd Hoffmann     vc = gd_vc_find_current(s);
14016fa27697SGerd Hoffmann     if (vc && vc->type == GD_VC_VTE) {
14026fa27697SGerd Hoffmann         gtk_widget_hide(vc->vte.terminal);
14036fa27697SGerd Hoffmann     }
14046fa27697SGerd Hoffmann #endif
1405e3500d1fSGerd Hoffmann     vc = gd_vc_find_by_page(s, arg2);
1406e3500d1fSGerd Hoffmann     if (!vc) {
1407e3500d1fSGerd Hoffmann         return;
1408e3500d1fSGerd Hoffmann     }
14096fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK
14106fa27697SGerd Hoffmann     if (vc->type == GD_VC_VTE) {
14116fa27697SGerd Hoffmann         gtk_widget_show(vc->vte.terminal);
14126fa27697SGerd Hoffmann     }
14136fa27697SGerd Hoffmann #endif
1414e3500d1fSGerd Hoffmann     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
1415e3500d1fSGerd Hoffmann                                    TRUE);
1416f8c223f6SGerd Hoffmann     on_vga = (vc->type == GD_VC_GFX &&
1417f8c223f6SGerd Hoffmann               qemu_console_is_graphic(vc->gfx.dcl.con));
14185104a1f6SAnthony Liguori     if (!on_vga) {
14195104a1f6SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
14205104a1f6SAnthony Liguori                                        FALSE);
1421c6158483SAnthony Liguori     } else if (s->full_screen) {
1422c6158483SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1423c6158483SAnthony Liguori                                        TRUE);
14245104a1f6SAnthony Liguori     }
14255104a1f6SAnthony Liguori     gtk_widget_set_sensitive(s->grab_item, on_vga);
14265104a1f6SAnthony Liguori 
142782fc1809SGerd Hoffmann     gd_update_windowsize(vc);
1428e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
1429a4ccabcfSAnthony Liguori }
1430a4ccabcfSAnthony Liguori 
1431e3500d1fSGerd Hoffmann static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
1432e3500d1fSGerd Hoffmann                                gpointer opaque)
14335104a1f6SAnthony Liguori {
1434e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1435e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
14365104a1f6SAnthony Liguori 
14372884cf5bSGerd Hoffmann     if (gd_grab_on_hover(s)) {
1438d531deefSGerd Hoffmann         gd_grab_keyboard(vc, "grab-on-hover");
14395104a1f6SAnthony Liguori     }
14405104a1f6SAnthony Liguori     return TRUE;
14415104a1f6SAnthony Liguori }
14425104a1f6SAnthony Liguori 
1443e3500d1fSGerd Hoffmann static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
1444e3500d1fSGerd Hoffmann                                gpointer opaque)
14455104a1f6SAnthony Liguori {
1446e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1447e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
14485104a1f6SAnthony Liguori 
14492884cf5bSGerd Hoffmann     if (gd_grab_on_hover(s)) {
14504c638e2eSGerd Hoffmann         gd_ungrab_keyboard(s);
14515104a1f6SAnthony Liguori     }
14525104a1f6SAnthony Liguori     return TRUE;
14535104a1f6SAnthony Liguori }
14545104a1f6SAnthony Liguori 
14556db253caSJan Kiszka static gboolean gd_focus_out_event(GtkWidget *widget,
1456e3500d1fSGerd Hoffmann                                    GdkEventCrossing *crossing, gpointer opaque)
14576db253caSJan Kiszka {
1458e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1459e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
14606db253caSJan Kiszka 
14616db253caSJan Kiszka     gtk_release_modifiers(s);
14626db253caSJan Kiszka     return TRUE;
14636db253caSJan Kiszka }
14646db253caSJan Kiszka 
14651301e515SGerd Hoffmann static gboolean gd_configure(GtkWidget *widget,
14661301e515SGerd Hoffmann                              GdkEventConfigure *cfg, gpointer opaque)
14671301e515SGerd Hoffmann {
14681301e515SGerd Hoffmann     VirtualConsole *vc = opaque;
14691301e515SGerd Hoffmann     QemuUIInfo info;
14701301e515SGerd Hoffmann 
14711301e515SGerd Hoffmann     memset(&info, 0, sizeof(info));
14721301e515SGerd Hoffmann     info.width = cfg->width;
14731301e515SGerd Hoffmann     info.height = cfg->height;
14741301e515SGerd Hoffmann     dpy_set_ui_info(vc->gfx.dcl.con, &info);
14751301e515SGerd Hoffmann     return FALSE;
14761301e515SGerd Hoffmann }
14771301e515SGerd Hoffmann 
1478d861def3SAnthony Liguori /** Virtual Console Callbacks **/
1479d861def3SAnthony Liguori 
1480ed1132e4SGerd Hoffmann static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
1481cdeb7090SGerd Hoffmann                                int idx, GSList *group, GtkWidget *view_menu)
1482ed1132e4SGerd Hoffmann {
1483cdeb7090SGerd Hoffmann     vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
1484277836c8SCole Robinson     gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx,
1485277836c8SCole Robinson             HOTKEY_MODIFIERS, 0,
1486277836c8SCole Robinson             g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL));
1487277836c8SCole Robinson #if GTK_CHECK_VERSION(3, 8, 0)
1488277836c8SCole Robinson     gtk_accel_label_set_accel(
1489277836c8SCole Robinson             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))),
1490277836c8SCole Robinson             GDK_KEY_1 + idx, HOTKEY_MODIFIERS);
1491277836c8SCole Robinson #endif
1492ed1132e4SGerd Hoffmann 
1493ed1132e4SGerd Hoffmann     g_signal_connect(vc->menu_item, "activate",
1494ed1132e4SGerd Hoffmann                      G_CALLBACK(gd_menu_switch_vc), s);
1495ed1132e4SGerd Hoffmann     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
1496ed1132e4SGerd Hoffmann 
1497277836c8SCole Robinson     group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
1498ed1132e4SGerd Hoffmann     return group;
1499ed1132e4SGerd Hoffmann }
1500ed1132e4SGerd Hoffmann 
1501ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
15020fb20d1cSCole Robinson static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
15030fb20d1cSCole Robinson {
15040fb20d1cSCole Robinson     VirtualConsole *vc = opaque;
15050fb20d1cSCole Robinson 
15060fb20d1cSCole Robinson     if (gtk_adjustment_get_upper(adjustment) >
15070fb20d1cSCole Robinson         gtk_adjustment_get_page_size(adjustment)) {
1508271a25c0SGerd Hoffmann         gtk_widget_show(vc->vte.scrollbar);
15090fb20d1cSCole Robinson     } else {
1510271a25c0SGerd Hoffmann         gtk_widget_hide(vc->vte.scrollbar);
15110fb20d1cSCole Robinson     }
15120fb20d1cSCole Robinson }
15130fb20d1cSCole Robinson 
1514d861def3SAnthony Liguori static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
1515d861def3SAnthony Liguori {
1516d861def3SAnthony Liguori     VirtualConsole *vc = chr->opaque;
1517d861def3SAnthony Liguori 
1518271a25c0SGerd Hoffmann     vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
1519d4370741SCole Robinson     return len;
1520d861def3SAnthony Liguori }
1521d861def3SAnthony Liguori 
1522d861def3SAnthony Liguori static int nb_vcs;
1523d861def3SAnthony Liguori static CharDriverState *vcs[MAX_VCS];
1524d861def3SAnthony Liguori 
1525702ec69cSGerd Hoffmann static CharDriverState *gd_vc_handler(ChardevVC *unused)
1526d861def3SAnthony Liguori {
1527d861def3SAnthony Liguori     CharDriverState *chr;
1528d861def3SAnthony Liguori 
1529d861def3SAnthony Liguori     chr = g_malloc0(sizeof(*chr));
1530d861def3SAnthony Liguori     chr->chr_write = gd_vc_chr_write;
1531bd5c51eeSMichael Roth     /* defer OPENED events until our vc is fully initialized */
1532bd5c51eeSMichael Roth     chr->explicit_be_open = true;
1533d861def3SAnthony Liguori 
1534d861def3SAnthony Liguori     vcs[nb_vcs++] = chr;
1535d861def3SAnthony Liguori 
1536d861def3SAnthony Liguori     return chr;
1537d861def3SAnthony Liguori }
1538d861def3SAnthony Liguori 
1539d4370741SCole Robinson static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
1540d4370741SCole Robinson                          gpointer user_data)
1541d861def3SAnthony Liguori {
1542d4370741SCole Robinson     VirtualConsole *vc = user_data;
1543d861def3SAnthony Liguori 
1544271a25c0SGerd Hoffmann     qemu_chr_be_write(vc->vte.chr, (uint8_t  *)text, (unsigned int)size);
1545d861def3SAnthony Liguori     return TRUE;
1546d861def3SAnthony Liguori }
1547d861def3SAnthony Liguori 
1548ed1132e4SGerd Hoffmann static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
1549ed1132e4SGerd Hoffmann                               CharDriverState *chr, int idx,
1550e3500d1fSGerd Hoffmann                               GSList *group, GtkWidget *view_menu)
1551d861def3SAnthony Liguori {
1552d861def3SAnthony Liguori     char buffer[32];
15530fb20d1cSCole Robinson     GtkWidget *box;
15540fb20d1cSCole Robinson     GtkWidget *scrollbar;
15550fb20d1cSCole Robinson     GtkAdjustment *vadjustment;
1556d861def3SAnthony Liguori 
1557e3500d1fSGerd Hoffmann     vc->s = s;
1558ed1132e4SGerd Hoffmann     vc->vte.chr = chr;
1559d861def3SAnthony Liguori 
1560ed1132e4SGerd Hoffmann     snprintf(buffer, sizeof(buffer), "vc%d", idx);
1561cdeb7090SGerd Hoffmann     vc->label = g_strdup_printf("%s", vc->vte.chr->label
1562cdeb7090SGerd Hoffmann                                 ? vc->vte.chr->label : buffer);
1563cdeb7090SGerd Hoffmann     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1564d861def3SAnthony Liguori 
1565271a25c0SGerd Hoffmann     vc->vte.terminal = vte_terminal_new();
1566271a25c0SGerd Hoffmann     g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
1567d861def3SAnthony Liguori 
1568271a25c0SGerd Hoffmann     vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
156982fc1809SGerd Hoffmann     vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
157082fc1809SGerd Hoffmann                           VC_TERM_X_MIN, VC_TERM_Y_MIN);
1571d861def3SAnthony Liguori 
15720fb20d1cSCole Robinson #if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0)
1573271a25c0SGerd Hoffmann     vadjustment = gtk_scrollable_get_vadjustment
1574271a25c0SGerd Hoffmann         (GTK_SCROLLABLE(vc->vte.terminal));
15750fb20d1cSCole Robinson #else
1576271a25c0SGerd Hoffmann     vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
15770fb20d1cSCole Robinson #endif
1578d861def3SAnthony Liguori 
15790fb20d1cSCole Robinson #if GTK_CHECK_VERSION(3, 0, 0)
15800fb20d1cSCole Robinson     box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
15810fb20d1cSCole Robinson     scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
15820fb20d1cSCole Robinson #else
15830fb20d1cSCole Robinson     box = gtk_hbox_new(false, 2);
15840fb20d1cSCole Robinson     scrollbar = gtk_vscrollbar_new(vadjustment);
15850fb20d1cSCole Robinson #endif
15860fb20d1cSCole Robinson 
1587271a25c0SGerd Hoffmann     gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
15880fb20d1cSCole Robinson     gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);
15890fb20d1cSCole Robinson 
1590271a25c0SGerd Hoffmann     vc->vte.chr->opaque = vc;
1591271a25c0SGerd Hoffmann     vc->vte.box = box;
1592271a25c0SGerd Hoffmann     vc->vte.scrollbar = scrollbar;
15930fb20d1cSCole Robinson 
15940fb20d1cSCole Robinson     g_signal_connect(vadjustment, "changed",
15950fb20d1cSCole Robinson                      G_CALLBACK(gd_vc_adjustment_changed), vc);
15960fb20d1cSCole Robinson 
1597e3500d1fSGerd Hoffmann     vc->type = GD_VC_VTE;
1598271a25c0SGerd Hoffmann     vc->tab_item = box;
15999d677e1cSJan Kiszka     vc->focus = vc->vte.terminal;
1600271a25c0SGerd Hoffmann     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
1601cdeb7090SGerd Hoffmann                              gtk_label_new(vc->label));
1602d861def3SAnthony Liguori 
1603271a25c0SGerd Hoffmann     qemu_chr_be_generic_open(vc->vte.chr);
1604271a25c0SGerd Hoffmann     if (vc->vte.chr->init) {
1605271a25c0SGerd Hoffmann         vc->vte.chr->init(vc->vte.chr);
1606d861def3SAnthony Liguori     }
1607d861def3SAnthony Liguori 
1608d861def3SAnthony Liguori     return group;
1609a4ccabcfSAnthony Liguori }
1610a4ccabcfSAnthony Liguori 
1611ed1132e4SGerd Hoffmann static void gd_vcs_init(GtkDisplayState *s, GSList *group,
1612ee5f31e4SGerd Hoffmann                         GtkWidget *view_menu)
1613ee5f31e4SGerd Hoffmann {
1614ee5f31e4SGerd Hoffmann     int i;
1615ee5f31e4SGerd Hoffmann 
1616ee5f31e4SGerd Hoffmann     for (i = 0; i < nb_vcs; i++) {
1617ed1132e4SGerd Hoffmann         VirtualConsole *vc = &s->vc[s->nb_vcs];
1618ed1132e4SGerd Hoffmann         group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu);
1619ee5f31e4SGerd Hoffmann         s->nb_vcs++;
1620ee5f31e4SGerd Hoffmann     }
1621ee5f31e4SGerd Hoffmann }
1622ee5f31e4SGerd Hoffmann #endif /* CONFIG_VTE */
1623ee5f31e4SGerd Hoffmann 
1624a4ccabcfSAnthony Liguori /** Window Creation **/
1625a4ccabcfSAnthony Liguori 
1626e3500d1fSGerd Hoffmann static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
1627e3500d1fSGerd Hoffmann {
1628e3500d1fSGerd Hoffmann #if GTK_CHECK_VERSION(3, 0, 0)
1629e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "draw",
1630e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_draw_event), vc);
1631e3500d1fSGerd Hoffmann #else
1632e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "expose-event",
1633e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_expose_event), vc);
1634e3500d1fSGerd Hoffmann #endif
1635f8c223f6SGerd Hoffmann     if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
1636e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "event",
1637e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_event), vc);
1638e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "button-press-event",
1639e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_button_event), vc);
1640e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "button-release-event",
1641e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_button_event), vc);
1642e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "scroll-event",
1643e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_scroll_event), vc);
1644e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1645e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_key_event), vc);
1646e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "key-release-event",
1647e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_key_event), vc);
1648e3500d1fSGerd Hoffmann 
1649e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "enter-notify-event",
1650e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_enter_event), vc);
1651e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "leave-notify-event",
1652e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_leave_event), vc);
1653e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
1654e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_focus_out_event), vc);
16551301e515SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "configure-event",
16561301e515SGerd Hoffmann                          G_CALLBACK(gd_configure), vc);
1657f8c223f6SGerd Hoffmann     } else {
1658f8c223f6SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1659f8c223f6SGerd Hoffmann                          G_CALLBACK(gd_text_key_down), vc);
1660f8c223f6SGerd Hoffmann     }
1661e3500d1fSGerd Hoffmann }
1662e3500d1fSGerd Hoffmann 
1663a4ccabcfSAnthony Liguori static void gd_connect_signals(GtkDisplayState *s)
1664a4ccabcfSAnthony Liguori {
1665a4ccabcfSAnthony Liguori     g_signal_connect(s->show_tabs_item, "activate",
1666a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_show_tabs), s);
1667cdeb7090SGerd Hoffmann     g_signal_connect(s->untabify_item, "activate",
1668cdeb7090SGerd Hoffmann                      G_CALLBACK(gd_menu_untabify), s);
1669a4ccabcfSAnthony Liguori 
1670a4ccabcfSAnthony Liguori     g_signal_connect(s->window, "delete-event",
1671a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_window_close), s);
1672a4ccabcfSAnthony Liguori 
167330e8f22bSJan Kiszka     g_signal_connect(s->pause_item, "activate",
167430e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_pause), s);
167530e8f22bSJan Kiszka     g_signal_connect(s->reset_item, "activate",
167630e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_reset), s);
167730e8f22bSJan Kiszka     g_signal_connect(s->powerdown_item, "activate",
167830e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_powerdown), s);
1679a4ccabcfSAnthony Liguori     g_signal_connect(s->quit_item, "activate",
1680a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_quit), s);
1681c6158483SAnthony Liguori     g_signal_connect(s->full_screen_item, "activate",
1682c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_full_screen), s);
1683c6158483SAnthony Liguori     g_signal_connect(s->zoom_in_item, "activate",
1684c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_in), s);
1685c6158483SAnthony Liguori     g_signal_connect(s->zoom_out_item, "activate",
1686c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_out), s);
1687c6158483SAnthony Liguori     g_signal_connect(s->zoom_fixed_item, "activate",
1688c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_fixed), s);
1689c6158483SAnthony Liguori     g_signal_connect(s->zoom_fit_item, "activate",
1690c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_fit), s);
16915104a1f6SAnthony Liguori     g_signal_connect(s->grab_item, "activate",
16925104a1f6SAnthony Liguori                      G_CALLBACK(gd_menu_grab_input), s);
1693a4ccabcfSAnthony Liguori     g_signal_connect(s->notebook, "switch-page",
1694a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_change_page), s);
1695a4ccabcfSAnthony Liguori }
1696a4ccabcfSAnthony Liguori 
1697400519d2SCole Robinson static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
1698a4ccabcfSAnthony Liguori {
1699bf9b255fSAnthony Liguori     GtkWidget *machine_menu;
1700a4ccabcfSAnthony Liguori     GtkWidget *separator;
1701a4ccabcfSAnthony Liguori 
1702bf9b255fSAnthony Liguori     machine_menu = gtk_menu_new();
1703400519d2SCole Robinson     gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group);
170430e8f22bSJan Kiszka 
170530e8f22bSJan Kiszka     s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
1706bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
170730e8f22bSJan Kiszka 
170830e8f22bSJan Kiszka     separator = gtk_separator_menu_item_new();
1709bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
171030e8f22bSJan Kiszka 
17119068f20dSCole Robinson     s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset"));
1712bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
171330e8f22bSJan Kiszka 
17149068f20dSCole Robinson     s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down"));
1715bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
171630e8f22bSJan Kiszka 
171730e8f22bSJan Kiszka     separator = gtk_separator_menu_item_new();
1718bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1719a4ccabcfSAnthony Liguori 
17203d914488SCole Robinson     s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
1721a4ccabcfSAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
172230e8f22bSJan Kiszka                                  "<QEMU>/Machine/Quit");
17233d914488SCole Robinson     gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
1724db1da1f2SCole Robinson                             GDK_KEY_q, HOTKEY_MODIFIERS);
1725bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
1726a4ccabcfSAnthony Liguori 
1727bf9b255fSAnthony Liguori     return machine_menu;
1728bf9b255fSAnthony Liguori }
1729bf9b255fSAnthony Liguori 
1730e3500d1fSGerd Hoffmann static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
1731ed1132e4SGerd Hoffmann                               QemuConsole *con, int idx,
1732e3500d1fSGerd Hoffmann                               GSList *group, GtkWidget *view_menu)
1733e3500d1fSGerd Hoffmann {
1734779ce88fSGerd Hoffmann     vc->label = qemu_console_get_label(con);
1735e3500d1fSGerd Hoffmann     vc->s = s;
1736e3500d1fSGerd Hoffmann     vc->gfx.scale_x = 1.0;
1737e3500d1fSGerd Hoffmann     vc->gfx.scale_y = 1.0;
1738e3500d1fSGerd Hoffmann 
1739e3500d1fSGerd Hoffmann     vc->gfx.drawing_area = gtk_drawing_area_new();
1740e3500d1fSGerd Hoffmann     gtk_widget_add_events(vc->gfx.drawing_area,
1741e3500d1fSGerd Hoffmann                           GDK_POINTER_MOTION_MASK |
1742e3500d1fSGerd Hoffmann                           GDK_BUTTON_PRESS_MASK |
1743e3500d1fSGerd Hoffmann                           GDK_BUTTON_RELEASE_MASK |
1744e3500d1fSGerd Hoffmann                           GDK_BUTTON_MOTION_MASK |
1745e3500d1fSGerd Hoffmann                           GDK_ENTER_NOTIFY_MASK |
1746e3500d1fSGerd Hoffmann                           GDK_LEAVE_NOTIFY_MASK |
1747e3500d1fSGerd Hoffmann                           GDK_SCROLL_MASK |
1748e3500d1fSGerd Hoffmann                           GDK_KEY_PRESS_MASK);
1749e3500d1fSGerd Hoffmann     gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
1750e3500d1fSGerd Hoffmann 
1751e3500d1fSGerd Hoffmann     vc->type = GD_VC_GFX;
1752e3500d1fSGerd Hoffmann     vc->tab_item = vc->gfx.drawing_area;
17539d677e1cSJan Kiszka     vc->focus = vc->gfx.drawing_area;
1754e3500d1fSGerd Hoffmann     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
1755cdeb7090SGerd Hoffmann                              vc->tab_item, gtk_label_new(vc->label));
1756e3500d1fSGerd Hoffmann 
175797edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
175897edf3bdSGerd Hoffmann     if (display_opengl) {
175997edf3bdSGerd Hoffmann         /*
176097edf3bdSGerd Hoffmann          * gtk_widget_set_double_buffered() was deprecated in 3.14.
176197edf3bdSGerd Hoffmann          * It is required for opengl rendering on X11 though.  A
176297edf3bdSGerd Hoffmann          * proper replacement (native opengl support) is only
176397edf3bdSGerd Hoffmann          * available in 3.16+.  Silence the warning if possible.
176497edf3bdSGerd Hoffmann          */
176597edf3bdSGerd Hoffmann #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
176697edf3bdSGerd Hoffmann #pragma GCC diagnostic push
176797edf3bdSGerd Hoffmann #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
176897edf3bdSGerd Hoffmann #endif
176997edf3bdSGerd Hoffmann         gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
177097edf3bdSGerd Hoffmann #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
177197edf3bdSGerd Hoffmann #pragma GCC diagnostic pop
177297edf3bdSGerd Hoffmann #endif
177397edf3bdSGerd Hoffmann         vc->gfx.dcl.ops = &dcl_egl_ops;
177497edf3bdSGerd Hoffmann     } else
177597edf3bdSGerd Hoffmann #endif
177697edf3bdSGerd Hoffmann     {
1777e3500d1fSGerd Hoffmann         vc->gfx.dcl.ops = &dcl_ops;
177897edf3bdSGerd Hoffmann     }
177997edf3bdSGerd Hoffmann 
1780e3500d1fSGerd Hoffmann     vc->gfx.dcl.con = con;
1781e3500d1fSGerd Hoffmann     register_displaychangelistener(&vc->gfx.dcl);
1782e3500d1fSGerd Hoffmann 
1783f8c223f6SGerd Hoffmann     gd_connect_vc_gfx_signals(vc);
1784f8c223f6SGerd Hoffmann     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1785f8c223f6SGerd Hoffmann 
17861301e515SGerd Hoffmann     if (dpy_ui_info_supported(vc->gfx.dcl.con)) {
17871301e515SGerd Hoffmann         gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item));
17881d73cd78SGerd Hoffmann         s->free_scale = true;
17891301e515SGerd Hoffmann     }
17901301e515SGerd Hoffmann 
1791e3500d1fSGerd Hoffmann     return group;
1792e3500d1fSGerd Hoffmann }
1793e3500d1fSGerd Hoffmann 
1794400519d2SCole Robinson static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
1795bf9b255fSAnthony Liguori {
1796bf9b255fSAnthony Liguori     GSList *group = NULL;
1797bf9b255fSAnthony Liguori     GtkWidget *view_menu;
1798bf9b255fSAnthony Liguori     GtkWidget *separator;
1799ed1132e4SGerd Hoffmann     QemuConsole *con;
1800ed1132e4SGerd Hoffmann     int vc;
1801bf9b255fSAnthony Liguori 
1802bf9b255fSAnthony Liguori     view_menu = gtk_menu_new();
1803400519d2SCole Robinson     gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group);
1804a4ccabcfSAnthony Liguori 
18053d914488SCole Robinson     s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen"));
180695414914SCole Robinson 
180795414914SCole Robinson     gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0,
180895414914SCole Robinson             g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL));
180995414914SCole Robinson #if GTK_CHECK_VERSION(3, 8, 0)
181095414914SCole Robinson     gtk_accel_label_set_accel(
181195414914SCole Robinson             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))),
181295414914SCole Robinson             GDK_KEY_f, HOTKEY_MODIFIERS);
181395414914SCole Robinson #endif
1814bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
1815c6158483SAnthony Liguori 
1816c6158483SAnthony Liguori     separator = gtk_separator_menu_item_new();
1817bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1818c6158483SAnthony Liguori 
18193d914488SCole Robinson     s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In"));
1820c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
1821c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom In");
1822b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
1823b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
1824bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
1825c6158483SAnthony Liguori 
18263d914488SCole Robinson     s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
1827c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
1828c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom Out");
1829b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus,
1830b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
1831bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
1832c6158483SAnthony Liguori 
18333d914488SCole Robinson     s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit"));
1834c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
1835c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom Fixed");
1836b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0,
1837b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
1838bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
1839c6158483SAnthony Liguori 
1840834574eaSAnthony Liguori     s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
1841bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
1842c6158483SAnthony Liguori 
1843c6158483SAnthony Liguori     separator = gtk_separator_menu_item_new();
1844bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1845c6158483SAnthony Liguori 
1846834574eaSAnthony Liguori     s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
1847bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
18485104a1f6SAnthony Liguori 
1849834574eaSAnthony Liguori     s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
18505104a1f6SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
18515104a1f6SAnthony Liguori                                  "<QEMU>/View/Grab Input");
1852b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g,
1853b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
1854bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
18555104a1f6SAnthony Liguori 
1856a4ccabcfSAnthony Liguori     separator = gtk_separator_menu_item_new();
1857bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1858a4ccabcfSAnthony Liguori 
1859e3500d1fSGerd Hoffmann     /* gfx */
1860ed1132e4SGerd Hoffmann     for (vc = 0;; vc++) {
1861ed1132e4SGerd Hoffmann         con = qemu_console_lookup_by_index(vc);
1862f8c223f6SGerd Hoffmann         if (!con) {
1863ed1132e4SGerd Hoffmann             break;
1864ed1132e4SGerd Hoffmann         }
1865ed1132e4SGerd Hoffmann         group = gd_vc_gfx_init(s, &s->vc[vc], con,
1866ed1132e4SGerd Hoffmann                                vc, group, view_menu);
1867ed1132e4SGerd Hoffmann         s->nb_vcs++;
1868ed1132e4SGerd Hoffmann     }
1869a4ccabcfSAnthony Liguori 
1870ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
1871e3500d1fSGerd Hoffmann     /* vte */
1872ed1132e4SGerd Hoffmann     gd_vcs_init(s, group, view_menu);
1873ee5f31e4SGerd Hoffmann #endif
1874d861def3SAnthony Liguori 
1875a4ccabcfSAnthony Liguori     separator = gtk_separator_menu_item_new();
1876bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1877a4ccabcfSAnthony Liguori 
1878834574eaSAnthony Liguori     s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
1879bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
1880a4ccabcfSAnthony Liguori 
1881cdeb7090SGerd Hoffmann     s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
1882cdeb7090SGerd Hoffmann     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
1883cdeb7090SGerd Hoffmann 
1884bf9b255fSAnthony Liguori     return view_menu;
1885bf9b255fSAnthony Liguori }
1886a4ccabcfSAnthony Liguori 
1887bf9b255fSAnthony Liguori static void gd_create_menus(GtkDisplayState *s)
1888bf9b255fSAnthony Liguori {
1889400519d2SCole Robinson     s->accel_group = gtk_accel_group_new();
1890400519d2SCole Robinson     s->machine_menu = gd_create_menu_machine(s);
1891400519d2SCole Robinson     s->view_menu = gd_create_menu_view(s);
1892bf9b255fSAnthony Liguori 
1893bf9b255fSAnthony Liguori     s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
189430e8f22bSJan Kiszka     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
189530e8f22bSJan Kiszka                               s->machine_menu);
189630e8f22bSJan Kiszka     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
1897a4ccabcfSAnthony Liguori 
1898bf9b255fSAnthony Liguori     s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
1899a4ccabcfSAnthony Liguori     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
1900a4ccabcfSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
1901bf9b255fSAnthony Liguori 
1902400519d2SCole Robinson     g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group);
1903400519d2SCole Robinson     gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group);
1904a4ccabcfSAnthony Liguori }
1905a4ccabcfSAnthony Liguori 
19063158a348SBruce Rogers static void gd_set_keycode_type(GtkDisplayState *s)
19073158a348SBruce Rogers {
19080a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_X11
19093158a348SBruce Rogers     GdkDisplay *display = gtk_widget_get_display(s->window);
19100a337ed0SGerd Hoffmann     if (GDK_IS_X11_DISPLAY(display)) {
19113158a348SBruce Rogers         Display *x11_display = gdk_x11_display_get_xdisplay(display);
19123158a348SBruce Rogers         XkbDescPtr desc = XkbGetKeyboard(x11_display, XkbGBN_AllComponentsMask,
19133158a348SBruce Rogers                                          XkbUseCoreKbd);
19140a337ed0SGerd Hoffmann         char *keycodes = NULL;
19153158a348SBruce Rogers 
19163158a348SBruce Rogers         if (desc && desc->names) {
19173158a348SBruce Rogers             keycodes = XGetAtomName(x11_display, desc->names->keycodes);
19183158a348SBruce Rogers         }
19193158a348SBruce Rogers         if (keycodes == NULL) {
19203158a348SBruce Rogers             fprintf(stderr, "could not lookup keycode name\n");
19213158a348SBruce Rogers         } else if (strstart(keycodes, "evdev", NULL)) {
19223158a348SBruce Rogers             s->has_evdev = true;
19233158a348SBruce Rogers         } else if (!strstart(keycodes, "xfree86", NULL)) {
19243158a348SBruce Rogers             fprintf(stderr, "unknown keycodes `%s', please report to "
19253158a348SBruce Rogers                     "qemu-devel@nongnu.org\n", keycodes);
19263158a348SBruce Rogers         }
192784961407SChen Fan 
192884961407SChen Fan         if (desc) {
192984961407SChen Fan             XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
193084961407SChen Fan         }
193184961407SChen Fan         if (keycodes) {
193284961407SChen Fan             XFree(keycodes);
193384961407SChen Fan         }
19340a337ed0SGerd Hoffmann     }
19353158a348SBruce Rogers #endif
19363158a348SBruce Rogers }
19373158a348SBruce Rogers 
1938060ab763SGerd Hoffmann static gboolean gtkinit;
1939060ab763SGerd Hoffmann 
1940881249c7SJan Kiszka void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
1941a4ccabcfSAnthony Liguori {
1942a4ccabcfSAnthony Liguori     GtkDisplayState *s = g_malloc0(sizeof(*s));
1943d819cdccSStefan Weil     char *filename;
194463c67b6dSMax Reitz     GdkDisplay *window_display;
1945a4ccabcfSAnthony Liguori 
1946060ab763SGerd Hoffmann     if (!gtkinit) {
1947060ab763SGerd Hoffmann         fprintf(stderr, "gtk initialization failed\n");
1948060ab763SGerd Hoffmann         exit(1);
1949060ab763SGerd Hoffmann     }
1950060ab763SGerd Hoffmann 
1951a4ccabcfSAnthony Liguori     s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
195251572ab0SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 2, 0)
195351572ab0SDaniel P. Berrange     s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
195451572ab0SDaniel P. Berrange #else
1955a4ccabcfSAnthony Liguori     s->vbox = gtk_vbox_new(FALSE, 0);
195651572ab0SDaniel P. Berrange #endif
1957a4ccabcfSAnthony Liguori     s->notebook = gtk_notebook_new();
1958a4ccabcfSAnthony Liguori     s->menu_bar = gtk_menu_bar_new();
1959a4ccabcfSAnthony Liguori 
1960c6158483SAnthony Liguori     s->free_scale = FALSE;
1961a4ccabcfSAnthony Liguori 
19622cb5d2a4SAlberto Garcia     /* LC_MESSAGES only. See early_gtk_display_init() for details */
19632cb5d2a4SAlberto Garcia     setlocale(LC_MESSAGES, "");
1964834574eaSAnthony Liguori     bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
1965834574eaSAnthony Liguori     textdomain("qemu");
1966834574eaSAnthony Liguori 
196763c67b6dSMax Reitz     window_display = gtk_widget_get_display(s->window);
196863c67b6dSMax Reitz     s->null_cursor = gdk_cursor_new_for_display(window_display,
196963c67b6dSMax Reitz                                                 GDK_BLANK_CURSOR);
1970a4ccabcfSAnthony Liguori 
1971a4ccabcfSAnthony Liguori     s->mouse_mode_notifier.notify = gd_mouse_mode_change;
1972a4ccabcfSAnthony Liguori     qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
1973a4ccabcfSAnthony Liguori     qemu_add_vm_change_state_handler(gd_change_runstate, s);
1974a4ccabcfSAnthony Liguori 
1975f7da9c17SAnthony Liguori     filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg");
1976d819cdccSStefan Weil     if (filename) {
1977d819cdccSStefan Weil         GError *error = NULL;
1978d819cdccSStefan Weil         GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error);
1979d819cdccSStefan Weil         if (pixbuf) {
1980d819cdccSStefan Weil             gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf);
1981d819cdccSStefan Weil         } else {
1982d819cdccSStefan Weil             g_error_free(error);
1983d819cdccSStefan Weil         }
1984d819cdccSStefan Weil         g_free(filename);
1985d819cdccSStefan Weil     }
1986d819cdccSStefan Weil 
1987a4ccabcfSAnthony Liguori     gd_create_menus(s);
1988a4ccabcfSAnthony Liguori 
1989a4ccabcfSAnthony Liguori     gd_connect_signals(s);
1990a4ccabcfSAnthony Liguori 
1991a4ccabcfSAnthony Liguori     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1992a4ccabcfSAnthony Liguori     gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
1993a4ccabcfSAnthony Liguori 
1994a4ccabcfSAnthony Liguori     gd_update_caption(s);
1995a4ccabcfSAnthony Liguori 
1996a4ccabcfSAnthony Liguori     gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
1997a4ccabcfSAnthony Liguori     gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
1998a4ccabcfSAnthony Liguori 
1999a4ccabcfSAnthony Liguori     gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
2000a4ccabcfSAnthony Liguori 
2001a4ccabcfSAnthony Liguori     gtk_widget_show_all(s->window);
2002a4ccabcfSAnthony Liguori 
20036fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK
20046fa27697SGerd Hoffmann     {
20056fa27697SGerd Hoffmann         VirtualConsole *cur = gd_vc_find_current(s);
2006b310a2a6SFam Zheng         if (cur) {
20076fa27697SGerd Hoffmann             int i;
20086fa27697SGerd Hoffmann 
20096fa27697SGerd Hoffmann             for (i = 0; i < s->nb_vcs; i++) {
20106fa27697SGerd Hoffmann                 VirtualConsole *vc = &s->vc[i];
20116fa27697SGerd Hoffmann                 if (vc && vc->type == GD_VC_VTE && vc != cur) {
20126fa27697SGerd Hoffmann                     gtk_widget_hide(vc->vte.terminal);
20136fa27697SGerd Hoffmann                 }
20146fa27697SGerd Hoffmann             }
20156fa27697SGerd Hoffmann             gd_update_windowsize(cur);
20166fa27697SGerd Hoffmann         }
2017b310a2a6SFam Zheng     }
20186fa27697SGerd Hoffmann #endif
20196fa27697SGerd Hoffmann 
2020787ba4f0SPeter Wu     if (full_screen) {
2021787ba4f0SPeter Wu         gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
2022787ba4f0SPeter Wu     }
2023881249c7SJan Kiszka     if (grab_on_hover) {
2024881249c7SJan Kiszka         gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
2025881249c7SJan Kiszka     }
2026787ba4f0SPeter Wu 
20273158a348SBruce Rogers     gd_set_keycode_type(s);
2028a4ccabcfSAnthony Liguori }
2029ee5f31e4SGerd Hoffmann 
203097edf3bdSGerd Hoffmann void early_gtk_display_init(int opengl)
2031ee5f31e4SGerd Hoffmann {
20322cb5d2a4SAlberto Garcia     /* The QEMU code relies on the assumption that it's always run in
20332cb5d2a4SAlberto Garcia      * the C locale. Therefore it is not prepared to deal with
20342cb5d2a4SAlberto Garcia      * operations that produce different results depending on the
20352cb5d2a4SAlberto Garcia      * locale, such as printf's formatting of decimal numbers, and
20362cb5d2a4SAlberto Garcia      * possibly others.
20372cb5d2a4SAlberto Garcia      *
20382cb5d2a4SAlberto Garcia      * Since GTK+ calls setlocale() by default -importing the locale
20392cb5d2a4SAlberto Garcia      * settings from the environment- we must prevent it from doing so
20402cb5d2a4SAlberto Garcia      * using gtk_disable_setlocale().
20412cb5d2a4SAlberto Garcia      *
20422cb5d2a4SAlberto Garcia      * QEMU's GTK+ UI, however, _does_ have translations for some of
20432cb5d2a4SAlberto Garcia      * the menu items. As a trade-off between a functionally correct
20442cb5d2a4SAlberto Garcia      * QEMU and a fully internationalized UI we support importing
20452cb5d2a4SAlberto Garcia      * LC_MESSAGES from the environment (see the setlocale() call
20462cb5d2a4SAlberto Garcia      * earlier in this file). This allows us to display translated
20472cb5d2a4SAlberto Garcia      * messages leaving everything else untouched.
20482cb5d2a4SAlberto Garcia      */
20492cb5d2a4SAlberto Garcia     gtk_disable_setlocale();
2050060ab763SGerd Hoffmann     gtkinit = gtk_init_check(NULL, NULL);
2051060ab763SGerd Hoffmann     if (!gtkinit) {
2052060ab763SGerd Hoffmann         /* don't exit yet, that'll break -help */
2053060ab763SGerd Hoffmann         return;
2054060ab763SGerd Hoffmann     }
205597edf3bdSGerd Hoffmann 
205697edf3bdSGerd Hoffmann     switch (opengl) {
205797edf3bdSGerd Hoffmann     case -1: /* default */
205897edf3bdSGerd Hoffmann     case 0:  /* off */
205997edf3bdSGerd Hoffmann         break;
206097edf3bdSGerd Hoffmann     case 1: /* on */
206197edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
206297edf3bdSGerd Hoffmann         gtk_egl_init();
206397edf3bdSGerd Hoffmann #endif
206497edf3bdSGerd Hoffmann         break;
206597edf3bdSGerd Hoffmann     default:
206697edf3bdSGerd Hoffmann         g_assert_not_reached();
206797edf3bdSGerd Hoffmann         break;
206897edf3bdSGerd Hoffmann     }
206997edf3bdSGerd Hoffmann 
2070ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
2071ee5f31e4SGerd Hoffmann     register_vc_handler(gd_vc_handler);
2072ee5f31e4SGerd Hoffmann #endif
2073ee5f31e4SGerd Hoffmann }
2074