xref: /openbmc/qemu/ui/gtk.c (revision 63d2ada2f55a85c3143ecf69a5aeecf6b3f3ffc9)
1 /*
2  * GTK UI
3  *
4  * Copyright IBM, Corp. 2012
5  *
6  * Authors:
7  *  Anthony Liguori   <aliguori@us.ibm.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  *
12  * Portions from gtk-vnc:
13  *
14  * GTK VNC Widget
15  *
16  * Copyright (C) 2006  Anthony Liguori <anthony@codemonkey.ws>
17  * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
18  *
19  * This library is free software; you can redistribute it and/or
20  * modify it under the terms of the GNU Lesser General Public
21  * License as published by the Free Software Foundation; either
22  * version 2.0 of the License, or (at your option) any later version.
23  *
24  * This library is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
27  * Lesser General Public License for more details.
28  *
29  * You should have received a copy of the GNU Lesser General Public
30  * License along with this library; if not, write to the Free Software
31  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
32  */
33 
34 #define GETTEXT_PACKAGE "qemu"
35 #define LOCALEDIR "po"
36 
37 #include "qemu-common.h"
38 
39 #include "ui/console.h"
40 #include "ui/gtk.h"
41 
42 #include <glib/gi18n.h>
43 #include <locale.h>
44 #if defined(CONFIG_VTE)
45 #include <vte/vte.h>
46 #endif
47 #include <math.h>
48 
49 #include "trace.h"
50 #include "ui/input.h"
51 #include "sysemu/sysemu.h"
52 #include "qmp-commands.h"
53 #include "x_keymap.h"
54 #include "keymaps.h"
55 #include "sysemu/char.h"
56 #include "qom/object.h"
57 
58 #define MAX_VCS 10
59 #define VC_WINDOW_X_MIN  320
60 #define VC_WINDOW_Y_MIN  240
61 #define VC_TERM_X_MIN     80
62 #define VC_TERM_Y_MIN     25
63 #define VC_SCALE_MIN    0.25
64 #define VC_SCALE_STEP   0.25
65 
66 #if !defined(CONFIG_VTE)
67 # define VTE_CHECK_VERSION(a, b, c) 0
68 #endif
69 
70 #if defined(CONFIG_VTE) && !GTK_CHECK_VERSION(3, 0, 0)
71 /*
72  * The gtk2 vte terminal widget seriously messes up the window resize
73  * for some reason.  You basically can't make the qemu window smaller
74  * any more because the toplevel window geoemtry hints are overridden.
75  *
76  * Workaround that by hiding all vte widgets, except the one in the
77  * current tab.
78  *
79  * Luckily everything works smooth in gtk3.
80  */
81 # define VTE_RESIZE_HACK 1
82 #endif
83 
84 #if !GTK_CHECK_VERSION(2, 20, 0)
85 #define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget)
86 #endif
87 
88 #ifndef GDK_IS_X11_DISPLAY
89 #define GDK_IS_X11_DISPLAY(dpy) (dpy == dpy)
90 #endif
91 #ifndef GDK_IS_WIN32_DISPLAY
92 #define GDK_IS_WIN32_DISPLAY(dpy) (dpy == dpy)
93 #endif
94 
95 #ifndef GDK_KEY_0
96 #define GDK_KEY_0 GDK_0
97 #define GDK_KEY_1 GDK_1
98 #define GDK_KEY_2 GDK_2
99 #define GDK_KEY_f GDK_f
100 #define GDK_KEY_g GDK_g
101 #define GDK_KEY_q GDK_q
102 #define GDK_KEY_plus GDK_plus
103 #define GDK_KEY_minus GDK_minus
104 #define GDK_KEY_Pause GDK_Pause
105 #endif
106 
107 #define HOTKEY_MODIFIERS        (GDK_CONTROL_MASK | GDK_MOD1_MASK)
108 
109 static const int modifier_keycode[] = {
110     /* shift, control, alt keys, meta keys, both left & right */
111     0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd,
112 };
113 
114 struct GtkDisplayState {
115     GtkWidget *window;
116 
117     GtkWidget *menu_bar;
118 
119     GtkAccelGroup *accel_group;
120 
121     GtkWidget *machine_menu_item;
122     GtkWidget *machine_menu;
123     GtkWidget *pause_item;
124     GtkWidget *reset_item;
125     GtkWidget *powerdown_item;
126     GtkWidget *quit_item;
127 
128     GtkWidget *view_menu_item;
129     GtkWidget *view_menu;
130     GtkWidget *full_screen_item;
131     GtkWidget *zoom_in_item;
132     GtkWidget *zoom_out_item;
133     GtkWidget *zoom_fixed_item;
134     GtkWidget *zoom_fit_item;
135     GtkWidget *grab_item;
136     GtkWidget *grab_on_hover_item;
137 
138     int nb_vcs;
139     VirtualConsole vc[MAX_VCS];
140 
141     GtkWidget *show_tabs_item;
142     GtkWidget *untabify_item;
143 
144     GtkWidget *vbox;
145     GtkWidget *notebook;
146     int button_mask;
147     gboolean last_set;
148     int last_x;
149     int last_y;
150     int grab_x_root;
151     int grab_y_root;
152     VirtualConsole *kbd_owner;
153     VirtualConsole *ptr_owner;
154 
155     gboolean full_screen;
156 
157     GdkCursor *null_cursor;
158     Notifier mouse_mode_notifier;
159     gboolean free_scale;
160 
161     bool external_pause_update;
162 
163     bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
164     bool has_evdev;
165     bool ignore_keys;
166 };
167 
168 static void gd_grab_pointer(VirtualConsole *vc);
169 static void gd_ungrab_pointer(GtkDisplayState *s);
170 
171 /** Utility Functions **/
172 
173 static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s)
174 {
175     VirtualConsole *vc;
176     gint i;
177 
178     for (i = 0; i < s->nb_vcs; i++) {
179         vc = &s->vc[i];
180         if (gtk_check_menu_item_get_active
181             (GTK_CHECK_MENU_ITEM(vc->menu_item))) {
182             return vc;
183         }
184     }
185     return NULL;
186 }
187 
188 static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page)
189 {
190     VirtualConsole *vc;
191     gint i, p;
192 
193     for (i = 0; i < s->nb_vcs; i++) {
194         vc = &s->vc[i];
195         p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item);
196         if (p == page) {
197             return vc;
198         }
199     }
200     return NULL;
201 }
202 
203 static VirtualConsole *gd_vc_find_current(GtkDisplayState *s)
204 {
205     gint page;
206 
207     page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook));
208     return gd_vc_find_by_page(s, page);
209 }
210 
211 static bool gd_is_grab_active(GtkDisplayState *s)
212 {
213     return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
214 }
215 
216 static bool gd_grab_on_hover(GtkDisplayState *s)
217 {
218     return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
219 }
220 
221 static void gd_update_cursor(VirtualConsole *vc)
222 {
223     GtkDisplayState *s = vc->s;
224     GdkWindow *window;
225 
226     if (vc->type != GD_VC_GFX ||
227         !qemu_console_is_graphic(vc->gfx.dcl.con)) {
228         return;
229     }
230 
231     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
232         return;
233     }
234 
235     window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area));
236     if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) {
237         gdk_window_set_cursor(window, s->null_cursor);
238     } else {
239         gdk_window_set_cursor(window, NULL);
240     }
241 }
242 
243 static void gd_update_caption(GtkDisplayState *s)
244 {
245     const char *status = "";
246     gchar *prefix;
247     gchar *title;
248     const char *grab = "";
249     bool is_paused = !runstate_is_running();
250     int i;
251 
252     if (qemu_name) {
253         prefix = g_strdup_printf("QEMU (%s)", qemu_name);
254     } else {
255         prefix = g_strdup_printf("QEMU");
256     }
257 
258     if (s->ptr_owner != NULL &&
259         s->ptr_owner->window == NULL) {
260         grab = _(" - Press Ctrl+Alt+G to release grab");
261     }
262 
263     if (is_paused) {
264         status = _(" [Paused]");
265     }
266     s->external_pause_update = true;
267     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
268                                    is_paused);
269     s->external_pause_update = false;
270 
271     title = g_strdup_printf("%s%s%s", prefix, status, grab);
272     gtk_window_set_title(GTK_WINDOW(s->window), title);
273     g_free(title);
274 
275     for (i = 0; i < s->nb_vcs; i++) {
276         VirtualConsole *vc = &s->vc[i];
277 
278         if (!vc->window) {
279             continue;
280         }
281         title = g_strdup_printf("%s: %s%s%s", prefix, vc->label,
282                                 vc == s->kbd_owner ? " +kbd" : "",
283                                 vc == s->ptr_owner ? " +ptr" : "");
284         gtk_window_set_title(GTK_WINDOW(vc->window), title);
285         g_free(title);
286     }
287 
288     g_free(prefix);
289 }
290 
291 static void gd_update_geometry_hints(VirtualConsole *vc)
292 {
293     GtkDisplayState *s = vc->s;
294     GdkWindowHints mask = 0;
295     GdkGeometry geo = {};
296     GtkWidget *geo_widget = NULL;
297     GtkWindow *geo_window;
298 
299     if (vc->type == GD_VC_GFX) {
300         if (!vc->gfx.ds) {
301             return;
302         }
303         if (s->free_scale) {
304             geo.min_width  = surface_width(vc->gfx.ds) * VC_SCALE_MIN;
305             geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN;
306             mask |= GDK_HINT_MIN_SIZE;
307         } else {
308             geo.min_width  = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
309             geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
310             mask |= GDK_HINT_MIN_SIZE;
311         }
312         geo_widget = vc->gfx.drawing_area;
313         gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height);
314 
315 #if defined(CONFIG_VTE)
316     } else if (vc->type == GD_VC_VTE) {
317         VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
318         GtkBorder *ib;
319 
320         geo.width_inc  = vte_terminal_get_char_width(term);
321         geo.height_inc = vte_terminal_get_char_height(term);
322         mask |= GDK_HINT_RESIZE_INC;
323         geo.base_width  = geo.width_inc;
324         geo.base_height = geo.height_inc;
325         mask |= GDK_HINT_BASE_SIZE;
326         geo.min_width  = geo.width_inc * VC_TERM_X_MIN;
327         geo.min_height = geo.height_inc * VC_TERM_Y_MIN;
328         mask |= GDK_HINT_MIN_SIZE;
329         gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL);
330         geo.base_width  += ib->left + ib->right;
331         geo.base_height += ib->top + ib->bottom;
332         geo.min_width   += ib->left + ib->right;
333         geo.min_height  += ib->top + ib->bottom;
334         geo_widget = vc->vte.terminal;
335 #endif
336     }
337 
338     geo_window = GTK_WINDOW(vc->window ? vc->window : s->window);
339     gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
340 }
341 
342 static void gd_update_windowsize(VirtualConsole *vc)
343 {
344     GtkDisplayState *s = vc->s;
345 
346     gd_update_geometry_hints(vc);
347 
348     if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) {
349         gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window),
350                           VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN);
351     }
352 }
353 
354 static void gd_update_full_redraw(VirtualConsole *vc)
355 {
356     GtkWidget *area = vc->gfx.drawing_area;
357     int ww, wh;
358     gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
359     gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
360 }
361 
362 static void gtk_release_modifiers(GtkDisplayState *s)
363 {
364     VirtualConsole *vc = gd_vc_find_current(s);
365     int i, keycode;
366 
367     if (vc->type != GD_VC_GFX ||
368         !qemu_console_is_graphic(vc->gfx.dcl.con)) {
369         return;
370     }
371     for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
372         keycode = modifier_keycode[i];
373         if (!s->modifier_pressed[i]) {
374             continue;
375         }
376         qemu_input_event_send_key_number(vc->gfx.dcl.con, keycode, false);
377         s->modifier_pressed[i] = false;
378     }
379 }
380 
381 static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
382                                GtkWidget *widget)
383 {
384     g_object_ref(G_OBJECT(widget));
385     gtk_container_remove(GTK_CONTAINER(from), widget);
386     gtk_container_add(GTK_CONTAINER(to), widget);
387     g_object_unref(G_OBJECT(widget));
388 }
389 
390 /** DisplayState Callbacks **/
391 
392 static void gd_update(DisplayChangeListener *dcl,
393                       int x, int y, int w, int h)
394 {
395     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
396     GdkWindow *win;
397     int x1, x2, y1, y2;
398     int mx, my;
399     int fbw, fbh;
400     int ww, wh;
401 
402     trace_gd_update(vc->label, x, y, w, h);
403 
404     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
405         return;
406     }
407 
408     if (vc->gfx.convert) {
409         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
410                                NULL, vc->gfx.convert,
411                                x, y, 0, 0, x, y, w, h);
412     }
413 
414     x1 = floor(x * vc->gfx.scale_x);
415     y1 = floor(y * vc->gfx.scale_y);
416 
417     x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
418     y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
419 
420     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
421     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
422 
423     win = gtk_widget_get_window(vc->gfx.drawing_area);
424     if (!win) {
425         return;
426     }
427     gdk_drawable_get_size(win, &ww, &wh);
428 
429     mx = my = 0;
430     if (ww > fbw) {
431         mx = (ww - fbw) / 2;
432     }
433     if (wh > fbh) {
434         my = (wh - fbh) / 2;
435     }
436 
437     gtk_widget_queue_draw_area(vc->gfx.drawing_area,
438                                mx + x1, my + y1, (x2 - x1), (y2 - y1));
439 }
440 
441 static void gd_refresh(DisplayChangeListener *dcl)
442 {
443     graphic_hw_update(dcl->con);
444 }
445 
446 #if GTK_CHECK_VERSION(3, 0, 0)
447 static void gd_mouse_set(DisplayChangeListener *dcl,
448                          int x, int y, int visible)
449 {
450     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
451     GdkDisplay *dpy;
452     GdkDeviceManager *mgr;
453     gint x_root, y_root;
454 
455     if (qemu_input_is_absolute()) {
456         return;
457     }
458 
459     dpy = gtk_widget_get_display(vc->gfx.drawing_area);
460     mgr = gdk_display_get_device_manager(dpy);
461     gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
462                                x, y, &x_root, &y_root);
463     gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
464                     gtk_widget_get_screen(vc->gfx.drawing_area),
465                     x_root, y_root);
466     vc->s->last_x = x;
467     vc->s->last_y = y;
468 }
469 #else
470 static void gd_mouse_set(DisplayChangeListener *dcl,
471                          int x, int y, int visible)
472 {
473     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
474     gint x_root, y_root;
475 
476     if (qemu_input_is_absolute()) {
477         return;
478     }
479 
480     gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
481                                x, y, &x_root, &y_root);
482     gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area),
483                              gtk_widget_get_screen(vc->gfx.drawing_area),
484                              x_root, y_root);
485 }
486 #endif
487 
488 static void gd_cursor_define(DisplayChangeListener *dcl,
489                              QEMUCursor *c)
490 {
491     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
492     GdkPixbuf *pixbuf;
493     GdkCursor *cursor;
494 
495     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
496         return;
497     }
498 
499     pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
500                                       GDK_COLORSPACE_RGB, true, 8,
501                                       c->width, c->height, c->width * 4,
502                                       NULL, NULL);
503     cursor = gdk_cursor_new_from_pixbuf
504         (gtk_widget_get_display(vc->gfx.drawing_area),
505          pixbuf, c->hot_x, c->hot_y);
506     gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor);
507     g_object_unref(pixbuf);
508 #if !GTK_CHECK_VERSION(3, 0, 0)
509     gdk_cursor_unref(cursor);
510 #else
511     g_object_unref(cursor);
512 #endif
513 }
514 
515 static void gd_switch(DisplayChangeListener *dcl,
516                       DisplaySurface *surface)
517 {
518     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
519     bool resized = true;
520 
521     trace_gd_switch(vc->label,
522                     surface ? surface_width(surface)  : 0,
523                     surface ? surface_height(surface) : 0);
524 
525     if (vc->gfx.surface) {
526         cairo_surface_destroy(vc->gfx.surface);
527         vc->gfx.surface = NULL;
528     }
529     if (vc->gfx.convert) {
530         pixman_image_unref(vc->gfx.convert);
531         vc->gfx.convert = NULL;
532     }
533 
534     if (vc->gfx.ds && surface &&
535         surface_width(vc->gfx.ds) == surface_width(surface) &&
536         surface_height(vc->gfx.ds) == surface_height(surface)) {
537         resized = false;
538     }
539     vc->gfx.ds = surface;
540 
541     if (!surface) {
542         return;
543     }
544 
545     if (surface->format == PIXMAN_x8r8g8b8) {
546         /*
547          * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24
548          *
549          * No need to convert, use surface directly.  Should be the
550          * common case as this is qemu_default_pixelformat(32) too.
551          */
552         vc->gfx.surface = cairo_image_surface_create_for_data
553             (surface_data(surface),
554              CAIRO_FORMAT_RGB24,
555              surface_width(surface),
556              surface_height(surface),
557              surface_stride(surface));
558     } else {
559         /* Must convert surface, use pixman to do it. */
560         vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8,
561                                                    surface_width(surface),
562                                                    surface_height(surface),
563                                                    NULL, 0);
564         vc->gfx.surface = cairo_image_surface_create_for_data
565             ((void *)pixman_image_get_data(vc->gfx.convert),
566              CAIRO_FORMAT_RGB24,
567              pixman_image_get_width(vc->gfx.convert),
568              pixman_image_get_height(vc->gfx.convert),
569              pixman_image_get_stride(vc->gfx.convert));
570         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
571                                NULL, vc->gfx.convert,
572                                0, 0, 0, 0, 0, 0,
573                                pixman_image_get_width(vc->gfx.convert),
574                                pixman_image_get_height(vc->gfx.convert));
575     }
576 
577     if (resized) {
578         gd_update_windowsize(vc);
579     } else {
580         gd_update_full_redraw(vc);
581     }
582 }
583 
584 /** QEMU Events **/
585 
586 static void gd_change_runstate(void *opaque, int running, RunState state)
587 {
588     GtkDisplayState *s = opaque;
589 
590     gd_update_caption(s);
591 }
592 
593 static void gd_mouse_mode_change(Notifier *notify, void *data)
594 {
595     GtkDisplayState *s;
596     int i;
597 
598     s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
599     /* release the grab at switching to absolute mode */
600     if (qemu_input_is_absolute() && gd_is_grab_active(s)) {
601         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
602                                        FALSE);
603     }
604     for (i = 0; i < s->nb_vcs; i++) {
605         VirtualConsole *vc = &s->vc[i];
606         gd_update_cursor(vc);
607     }
608 }
609 
610 /** GTK Events **/
611 
612 static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
613                                 void *opaque)
614 {
615     GtkDisplayState *s = opaque;
616     int i;
617 
618     if (!no_quit) {
619         for (i = 0; i < s->nb_vcs; i++) {
620             if (s->vc[i].type != GD_VC_GFX) {
621                 continue;
622             }
623             unregister_displaychangelistener(&s->vc[i].gfx.dcl);
624         }
625         qmp_quit(NULL);
626         return FALSE;
627     }
628 
629     return TRUE;
630 }
631 
632 static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
633 {
634     VirtualConsole *vc = opaque;
635     GtkDisplayState *s = vc->s;
636     int mx, my;
637     int ww, wh;
638     int fbw, fbh;
639 
640     if (!gtk_widget_get_realized(widget)) {
641         return FALSE;
642     }
643     if (!vc->gfx.ds) {
644         return FALSE;
645     }
646 
647     fbw = surface_width(vc->gfx.ds);
648     fbh = surface_height(vc->gfx.ds);
649 
650     gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
651 
652     if (s->full_screen) {
653         vc->gfx.scale_x = (double)ww / fbw;
654         vc->gfx.scale_y = (double)wh / fbh;
655     } else if (s->free_scale) {
656         double sx, sy;
657 
658         sx = (double)ww / fbw;
659         sy = (double)wh / fbh;
660 
661         vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
662     }
663 
664     fbw *= vc->gfx.scale_x;
665     fbh *= vc->gfx.scale_y;
666 
667     mx = my = 0;
668     if (ww > fbw) {
669         mx = (ww - fbw) / 2;
670     }
671     if (wh > fbh) {
672         my = (wh - fbh) / 2;
673     }
674 
675     cairo_rectangle(cr, 0, 0, ww, wh);
676 
677     /* Optionally cut out the inner area where the pixmap
678        will be drawn. This avoids 'flashing' since we're
679        not double-buffering. Note we're using the undocumented
680        behaviour of drawing the rectangle from right to left
681        to cut out the whole */
682     cairo_rectangle(cr, mx + fbw, my,
683                     -1 * fbw, fbh);
684     cairo_fill(cr);
685 
686     cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
687     cairo_set_source_surface(cr, vc->gfx.surface,
688                              mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
689     cairo_paint(cr);
690 
691     return TRUE;
692 }
693 
694 #if !GTK_CHECK_VERSION(3, 0, 0)
695 static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
696                                 void *opaque)
697 {
698     cairo_t *cr;
699     gboolean ret;
700 
701     cr = gdk_cairo_create(gtk_widget_get_window(widget));
702     cairo_rectangle(cr,
703                     expose->area.x,
704                     expose->area.y,
705                     expose->area.width,
706                     expose->area.height);
707     cairo_clip(cr);
708 
709     ret = gd_draw_event(widget, cr, opaque);
710 
711     cairo_destroy(cr);
712 
713     return ret;
714 }
715 #endif
716 
717 static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
718                                 void *opaque)
719 {
720     VirtualConsole *vc = opaque;
721     GtkDisplayState *s = vc->s;
722     int x, y;
723     int mx, my;
724     int fbh, fbw;
725     int ww, wh;
726 
727     if (!vc->gfx.ds) {
728         return TRUE;
729     }
730 
731     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
732     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
733 
734     gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area),
735                           &ww, &wh);
736 
737     mx = my = 0;
738     if (ww > fbw) {
739         mx = (ww - fbw) / 2;
740     }
741     if (wh > fbh) {
742         my = (wh - fbh) / 2;
743     }
744 
745     x = (motion->x - mx) / vc->gfx.scale_x;
746     y = (motion->y - my) / vc->gfx.scale_y;
747 
748     if (qemu_input_is_absolute()) {
749         if (x < 0 || y < 0 ||
750             x >= surface_width(vc->gfx.ds) ||
751             y >= surface_height(vc->gfx.ds)) {
752             return TRUE;
753         }
754         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
755                              surface_width(vc->gfx.ds));
756         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
757                              surface_height(vc->gfx.ds));
758         qemu_input_event_sync();
759     } else if (s->last_set && s->ptr_owner == vc) {
760         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
761         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
762         qemu_input_event_sync();
763     }
764     s->last_x = x;
765     s->last_y = y;
766     s->last_set = TRUE;
767 
768     if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
769         GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
770         int x = (int)motion->x_root;
771         int y = (int)motion->y_root;
772 
773         /* In relative mode check to see if client pointer hit
774          * one of the screen edges, and if so move it back by
775          * 200 pixels. This is important because the pointer
776          * in the server doesn't correspond 1-for-1, and so
777          * may still be only half way across the screen. Without
778          * this warp, the server pointer would thus appear to hit
779          * an invisible wall */
780         if (x == 0) {
781             x += 200;
782         }
783         if (y == 0) {
784             y += 200;
785         }
786         if (x == (gdk_screen_get_width(screen) - 1)) {
787             x -= 200;
788         }
789         if (y == (gdk_screen_get_height(screen) - 1)) {
790             y -= 200;
791         }
792 
793         if (x != (int)motion->x_root || y != (int)motion->y_root) {
794 #if GTK_CHECK_VERSION(3, 0, 0)
795             GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
796             gdk_device_warp(dev, screen, x, y);
797 #else
798             GdkDisplay *display = gtk_widget_get_display(widget);
799             gdk_display_warp_pointer(display, screen, x, y);
800 #endif
801             s->last_set = FALSE;
802             return FALSE;
803         }
804     }
805     return TRUE;
806 }
807 
808 static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
809                                 void *opaque)
810 {
811     VirtualConsole *vc = opaque;
812     GtkDisplayState *s = vc->s;
813     InputButton btn;
814 
815     /* implicitly grab the input at the first click in the relative mode */
816     if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
817         !qemu_input_is_absolute() && s->ptr_owner != vc) {
818         gd_ungrab_pointer(s);
819         if (!vc->window) {
820             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
821                                            TRUE);
822         } else {
823             gd_grab_pointer(vc);
824             gd_update_caption(s);
825         }
826         return TRUE;
827     }
828 
829     if (button->button == 1) {
830         btn = INPUT_BUTTON_LEFT;
831     } else if (button->button == 2) {
832         btn = INPUT_BUTTON_MIDDLE;
833     } else if (button->button == 3) {
834         btn = INPUT_BUTTON_RIGHT;
835     } else {
836         return TRUE;
837     }
838 
839     qemu_input_queue_btn(vc->gfx.dcl.con, btn,
840                          button->type == GDK_BUTTON_PRESS);
841     qemu_input_event_sync();
842     return TRUE;
843 }
844 
845 static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
846                                 void *opaque)
847 {
848     VirtualConsole *vc = opaque;
849     InputButton btn;
850 
851     if (scroll->direction == GDK_SCROLL_UP) {
852         btn = INPUT_BUTTON_WHEEL_UP;
853     } else if (scroll->direction == GDK_SCROLL_DOWN) {
854         btn = INPUT_BUTTON_WHEEL_DOWN;
855     } else {
856         return TRUE;
857     }
858 
859     qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
860     qemu_input_event_sync();
861     qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
862     qemu_input_event_sync();
863     return TRUE;
864 }
865 
866 static int gd_map_keycode(GtkDisplayState *s, GdkDisplay *dpy, int gdk_keycode)
867 {
868     int qemu_keycode;
869 
870 #ifdef GDK_WINDOWING_WIN32
871     if (GDK_IS_WIN32_DISPLAY(dpy)) {
872         qemu_keycode = MapVirtualKey(gdk_keycode, MAPVK_VK_TO_VSC);
873         switch (qemu_keycode) {
874         case 103:   /* alt gr */
875             qemu_keycode = 56 | SCANCODE_GREY;
876             break;
877         }
878         return qemu_keycode;
879     }
880 #endif
881 
882     if (gdk_keycode < 9) {
883         qemu_keycode = 0;
884     } else if (gdk_keycode < 97) {
885         qemu_keycode = gdk_keycode - 8;
886 #ifdef GDK_WINDOWING_X11
887     } else if (GDK_IS_X11_DISPLAY(dpy) && gdk_keycode < 158) {
888         if (s->has_evdev) {
889             qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
890         } else {
891             qemu_keycode = translate_xfree86_keycode(gdk_keycode - 97);
892         }
893 #endif
894     } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
895         qemu_keycode = 0x70;
896     } else if (gdk_keycode == 211) { /* backslash */
897         qemu_keycode = 0x73;
898     } else {
899         qemu_keycode = 0;
900     }
901 
902     return qemu_keycode;
903 }
904 
905 static gboolean gd_text_key_down(GtkWidget *widget,
906                                  GdkEventKey *key, void *opaque)
907 {
908     VirtualConsole *vc = opaque;
909     QemuConsole *con = vc->gfx.dcl.con;
910 
911     if (key->length) {
912         kbd_put_string_console(con, key->string, key->length);
913     } else {
914         int num = gd_map_keycode(vc->s, gtk_widget_get_display(widget),
915                                  key->hardware_keycode);
916         int qcode = qemu_input_key_number_to_qcode(num);
917         kbd_put_qcode_console(con, qcode);
918     }
919     return TRUE;
920 }
921 
922 static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
923 {
924     VirtualConsole *vc = opaque;
925     GtkDisplayState *s = vc->s;
926     int gdk_keycode = key->hardware_keycode;
927     int qemu_keycode;
928     int i;
929 
930     if (s->ignore_keys) {
931         s->ignore_keys = (key->type == GDK_KEY_PRESS);
932         return TRUE;
933     }
934 
935     if (key->keyval == GDK_KEY_Pause) {
936         qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE,
937                                         key->type == GDK_KEY_PRESS);
938         return TRUE;
939     }
940 
941     qemu_keycode = gd_map_keycode(s, gtk_widget_get_display(widget),
942                                   gdk_keycode);
943 
944     trace_gd_key_event(vc->label, gdk_keycode, qemu_keycode,
945                        (key->type == GDK_KEY_PRESS) ? "down" : "up");
946 
947     for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
948         if (qemu_keycode == modifier_keycode[i]) {
949             s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
950         }
951     }
952 
953     qemu_input_event_send_key_number(vc->gfx.dcl.con, qemu_keycode,
954                                      key->type == GDK_KEY_PRESS);
955 
956     return TRUE;
957 }
958 
959 static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
960 {
961     if (event->type == GDK_MOTION_NOTIFY) {
962         return gd_motion_event(widget, &event->motion, opaque);
963     }
964     return FALSE;
965 }
966 
967 /** Window Menu Actions **/
968 
969 static void gd_menu_pause(GtkMenuItem *item, void *opaque)
970 {
971     GtkDisplayState *s = opaque;
972 
973     if (s->external_pause_update) {
974         return;
975     }
976     if (runstate_is_running()) {
977         qmp_stop(NULL);
978     } else {
979         qmp_cont(NULL);
980     }
981 }
982 
983 static void gd_menu_reset(GtkMenuItem *item, void *opaque)
984 {
985     qmp_system_reset(NULL);
986 }
987 
988 static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
989 {
990     qmp_system_powerdown(NULL);
991 }
992 
993 static void gd_menu_quit(GtkMenuItem *item, void *opaque)
994 {
995     qmp_quit(NULL);
996 }
997 
998 static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
999 {
1000     GtkDisplayState *s = opaque;
1001     VirtualConsole *vc = gd_vc_find_by_menu(s);
1002     GtkNotebook *nb = GTK_NOTEBOOK(s->notebook);
1003     gint page;
1004 
1005     gtk_release_modifiers(s);
1006     if (vc) {
1007         page = gtk_notebook_page_num(nb, vc->tab_item);
1008         gtk_notebook_set_current_page(nb, page);
1009         gtk_widget_grab_focus(vc->focus);
1010     }
1011     s->ignore_keys = false;
1012 }
1013 
1014 static void gd_accel_switch_vc(void *opaque)
1015 {
1016     VirtualConsole *vc = opaque;
1017 
1018     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
1019 #if !GTK_CHECK_VERSION(3, 0, 0)
1020     /* GTK2 sends the accel key to the target console - ignore this until */
1021     vc->s->ignore_keys = true;
1022 #endif
1023 }
1024 
1025 static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
1026 {
1027     GtkDisplayState *s = opaque;
1028     VirtualConsole *vc = gd_vc_find_current(s);
1029 
1030     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
1031         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
1032     } else {
1033         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1034     }
1035     gd_update_windowsize(vc);
1036 }
1037 
1038 static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
1039                                     void *opaque)
1040 {
1041     VirtualConsole *vc = opaque;
1042     GtkDisplayState *s = vc->s;
1043 
1044     gtk_widget_set_sensitive(vc->menu_item, true);
1045     gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
1046     gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
1047                                     vc->tab_item, vc->label);
1048     gtk_widget_destroy(vc->window);
1049     vc->window = NULL;
1050     return TRUE;
1051 }
1052 
1053 static gboolean gd_win_grab(void *opaque)
1054 {
1055     VirtualConsole *vc = opaque;
1056 
1057     fprintf(stderr, "%s: %s\n", __func__, vc->label);
1058     if (vc->s->ptr_owner) {
1059         gd_ungrab_pointer(vc->s);
1060     } else {
1061         gd_grab_pointer(vc);
1062     }
1063     gd_update_caption(vc->s);
1064     return TRUE;
1065 }
1066 
1067 static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
1068 {
1069     GtkDisplayState *s = opaque;
1070     VirtualConsole *vc = gd_vc_find_current(s);
1071 
1072     if (vc->type == GD_VC_GFX &&
1073         qemu_console_is_graphic(vc->gfx.dcl.con)) {
1074         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1075                                        FALSE);
1076     }
1077     if (!vc->window) {
1078         gtk_widget_set_sensitive(vc->menu_item, false);
1079         vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1080         gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
1081 
1082         g_signal_connect(vc->window, "delete-event",
1083                          G_CALLBACK(gd_tab_window_close), vc);
1084         gtk_widget_show_all(vc->window);
1085 
1086         if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
1087             GtkAccelGroup *ag = gtk_accel_group_new();
1088             gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
1089 
1090             GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
1091                                                vc, NULL);
1092             gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
1093         }
1094 
1095         gd_update_geometry_hints(vc);
1096         gd_update_caption(s);
1097     }
1098 }
1099 
1100 static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
1101 {
1102     GtkDisplayState *s = opaque;
1103     VirtualConsole *vc = gd_vc_find_current(s);
1104 
1105     if (!s->full_screen) {
1106         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1107         gtk_widget_hide(s->menu_bar);
1108         if (vc->type == GD_VC_GFX) {
1109             gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
1110             if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
1111                 gtk_check_menu_item_set_active
1112                     (GTK_CHECK_MENU_ITEM(s->grab_item), TRUE);
1113             }
1114         }
1115         gtk_window_fullscreen(GTK_WINDOW(s->window));
1116         s->full_screen = TRUE;
1117     } else {
1118         gtk_window_unfullscreen(GTK_WINDOW(s->window));
1119         gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
1120         gtk_widget_show(s->menu_bar);
1121         s->full_screen = FALSE;
1122         if (vc->type == GD_VC_GFX) {
1123             vc->gfx.scale_x = 1.0;
1124             vc->gfx.scale_y = 1.0;
1125             gd_update_windowsize(vc);
1126             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1127                                            FALSE);
1128         }
1129     }
1130 
1131     gd_update_cursor(vc);
1132 }
1133 
1134 static void gd_accel_full_screen(void *opaque)
1135 {
1136     GtkDisplayState *s = opaque;
1137     gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
1138 }
1139 
1140 static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
1141 {
1142     GtkDisplayState *s = opaque;
1143     VirtualConsole *vc = gd_vc_find_current(s);
1144 
1145     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1146                                    FALSE);
1147 
1148     vc->gfx.scale_x += VC_SCALE_STEP;
1149     vc->gfx.scale_y += VC_SCALE_STEP;
1150 
1151     gd_update_windowsize(vc);
1152 }
1153 
1154 static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
1155 {
1156     GtkDisplayState *s = opaque;
1157     VirtualConsole *vc = gd_vc_find_current(s);
1158 
1159     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1160                                    FALSE);
1161 
1162     vc->gfx.scale_x -= VC_SCALE_STEP;
1163     vc->gfx.scale_y -= VC_SCALE_STEP;
1164 
1165     vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
1166     vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
1167 
1168     gd_update_windowsize(vc);
1169 }
1170 
1171 static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
1172 {
1173     GtkDisplayState *s = opaque;
1174     VirtualConsole *vc = gd_vc_find_current(s);
1175 
1176     vc->gfx.scale_x = 1.0;
1177     vc->gfx.scale_y = 1.0;
1178 
1179     gd_update_windowsize(vc);
1180 }
1181 
1182 static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
1183 {
1184     GtkDisplayState *s = opaque;
1185     VirtualConsole *vc = gd_vc_find_current(s);
1186 
1187     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
1188         s->free_scale = TRUE;
1189     } else {
1190         s->free_scale = FALSE;
1191         vc->gfx.scale_x = 1.0;
1192         vc->gfx.scale_y = 1.0;
1193     }
1194 
1195     gd_update_windowsize(vc);
1196     gd_update_full_redraw(vc);
1197 }
1198 
1199 #if GTK_CHECK_VERSION(3, 0, 0)
1200 static void gd_grab_devices(VirtualConsole *vc, bool grab,
1201                             GdkInputSource source, GdkEventMask mask,
1202                             GdkCursor *cursor)
1203 {
1204     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1205     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1206     GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
1207     GList *tmp = devs;
1208 
1209     for (tmp = devs; tmp; tmp = tmp->next) {
1210         GdkDevice *dev = tmp->data;
1211         if (gdk_device_get_source(dev) != source) {
1212             continue;
1213         }
1214         if (grab) {
1215             GdkWindow *win = gtk_widget_get_window(vc->gfx.drawing_area);
1216             gdk_device_grab(dev, win, GDK_OWNERSHIP_NONE, FALSE,
1217                             mask, cursor, GDK_CURRENT_TIME);
1218         } else {
1219             gdk_device_ungrab(dev, GDK_CURRENT_TIME);
1220         }
1221     }
1222     g_list_free(devs);
1223 }
1224 #endif
1225 
1226 static void gd_grab_keyboard(VirtualConsole *vc)
1227 {
1228 #if GTK_CHECK_VERSION(3, 0, 0)
1229     gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD,
1230                    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
1231                    NULL);
1232 #else
1233     gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area),
1234                       FALSE,
1235                       GDK_CURRENT_TIME);
1236 #endif
1237     vc->s->kbd_owner = vc;
1238     trace_gd_grab(vc->label, "kbd", true);
1239 }
1240 
1241 static void gd_ungrab_keyboard(GtkDisplayState *s)
1242 {
1243     VirtualConsole *vc = s->kbd_owner;
1244 
1245     if (vc == NULL) {
1246         return;
1247     }
1248     s->kbd_owner = NULL;
1249 
1250 #if GTK_CHECK_VERSION(3, 0, 0)
1251     gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL);
1252 #else
1253     gdk_keyboard_ungrab(GDK_CURRENT_TIME);
1254 #endif
1255     trace_gd_grab(vc->label, "kbd", false);
1256 }
1257 
1258 static void gd_grab_pointer(VirtualConsole *vc)
1259 {
1260     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1261 #if GTK_CHECK_VERSION(3, 0, 0)
1262     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1263     gd_grab_devices(vc, true, GDK_SOURCE_MOUSE,
1264                     GDK_POINTER_MOTION_MASK |
1265                     GDK_BUTTON_PRESS_MASK |
1266                     GDK_BUTTON_RELEASE_MASK |
1267                     GDK_BUTTON_MOTION_MASK |
1268                     GDK_SCROLL_MASK,
1269                     vc->s->null_cursor);
1270     gdk_device_get_position(gdk_device_manager_get_client_pointer(mgr),
1271                             NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
1272 #else
1273     gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area),
1274                      FALSE, /* All events to come to our window directly */
1275                      GDK_POINTER_MOTION_MASK |
1276                      GDK_BUTTON_PRESS_MASK |
1277                      GDK_BUTTON_RELEASE_MASK |
1278                      GDK_BUTTON_MOTION_MASK |
1279                      GDK_SCROLL_MASK,
1280                      NULL, /* Allow cursor to move over entire desktop */
1281                      vc->s->null_cursor,
1282                      GDK_CURRENT_TIME);
1283     gdk_display_get_pointer(display, NULL,
1284                             &vc->s->grab_x_root, &vc->s->grab_y_root, NULL);
1285 #endif
1286     vc->s->ptr_owner = vc;
1287     trace_gd_grab(vc->label, "ptr", true);
1288 }
1289 
1290 static void gd_ungrab_pointer(GtkDisplayState *s)
1291 {
1292     VirtualConsole *vc = s->ptr_owner;
1293 
1294     if (vc == NULL) {
1295         return;
1296     }
1297     s->ptr_owner = NULL;
1298 
1299     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1300 #if GTK_CHECK_VERSION(3, 0, 0)
1301     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1302     gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL);
1303     gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
1304                     gtk_widget_get_screen(vc->gfx.drawing_area),
1305                     vc->s->grab_x_root, vc->s->grab_y_root);
1306 #else
1307     gdk_pointer_ungrab(GDK_CURRENT_TIME);
1308     gdk_display_warp_pointer(display,
1309                              gtk_widget_get_screen(vc->gfx.drawing_area),
1310                              vc->s->grab_x_root, vc->s->grab_y_root);
1311 #endif
1312     trace_gd_grab(vc->label, "ptr", false);
1313 }
1314 
1315 static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
1316 {
1317     GtkDisplayState *s = opaque;
1318     VirtualConsole *vc = gd_vc_find_current(s);
1319 
1320     if (gd_is_grab_active(s)) {
1321         if (!gd_grab_on_hover(s)) {
1322             gd_grab_keyboard(vc);
1323         }
1324         gd_grab_pointer(vc);
1325     } else {
1326         gd_ungrab_keyboard(s);
1327         gd_ungrab_pointer(s);
1328     }
1329 
1330     gd_update_caption(s);
1331     gd_update_cursor(vc);
1332 }
1333 
1334 static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
1335                            gpointer data)
1336 {
1337     GtkDisplayState *s = data;
1338     VirtualConsole *vc;
1339     gboolean on_vga;
1340 
1341     if (!gtk_widget_get_realized(s->notebook)) {
1342         return;
1343     }
1344 
1345 #ifdef VTE_RESIZE_HACK
1346     vc = gd_vc_find_current(s);
1347     if (vc && vc->type == GD_VC_VTE) {
1348         gtk_widget_hide(vc->vte.terminal);
1349     }
1350 #endif
1351     vc = gd_vc_find_by_page(s, arg2);
1352     if (!vc) {
1353         return;
1354     }
1355 #ifdef VTE_RESIZE_HACK
1356     if (vc->type == GD_VC_VTE) {
1357         gtk_widget_show(vc->vte.terminal);
1358     }
1359 #endif
1360     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
1361                                    TRUE);
1362     on_vga = (vc->type == GD_VC_GFX &&
1363               qemu_console_is_graphic(vc->gfx.dcl.con));
1364     if (!on_vga) {
1365         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1366                                        FALSE);
1367     } else if (s->full_screen) {
1368         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1369                                        TRUE);
1370     }
1371     gtk_widget_set_sensitive(s->grab_item, on_vga);
1372 
1373     gd_update_windowsize(vc);
1374     gd_update_cursor(vc);
1375 }
1376 
1377 static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
1378                                gpointer opaque)
1379 {
1380     VirtualConsole *vc = opaque;
1381     GtkDisplayState *s = vc->s;
1382 
1383     if (gd_grab_on_hover(s)) {
1384         gd_ungrab_keyboard(s);
1385         gd_grab_keyboard(vc);
1386         gd_update_caption(s);
1387     }
1388     return TRUE;
1389 }
1390 
1391 static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
1392                                gpointer opaque)
1393 {
1394     VirtualConsole *vc = opaque;
1395     GtkDisplayState *s = vc->s;
1396 
1397     if (gd_grab_on_hover(s)) {
1398         gd_ungrab_keyboard(s);
1399         gd_update_caption(s);
1400     }
1401     return TRUE;
1402 }
1403 
1404 static gboolean gd_focus_out_event(GtkWidget *widget,
1405                                    GdkEventCrossing *crossing, gpointer opaque)
1406 {
1407     VirtualConsole *vc = opaque;
1408     GtkDisplayState *s = vc->s;
1409 
1410     gtk_release_modifiers(s);
1411     return TRUE;
1412 }
1413 
1414 static gboolean gd_configure(GtkWidget *widget,
1415                              GdkEventConfigure *cfg, gpointer opaque)
1416 {
1417     VirtualConsole *vc = opaque;
1418     QemuUIInfo info;
1419 
1420     memset(&info, 0, sizeof(info));
1421     info.width = cfg->width;
1422     info.height = cfg->height;
1423     dpy_set_ui_info(vc->gfx.dcl.con, &info);
1424     return FALSE;
1425 }
1426 
1427 /** Virtual Console Callbacks **/
1428 
1429 static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
1430                                int idx, GSList *group, GtkWidget *view_menu)
1431 {
1432     vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
1433     gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx,
1434             HOTKEY_MODIFIERS, 0,
1435             g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL));
1436 #if GTK_CHECK_VERSION(3, 8, 0)
1437     gtk_accel_label_set_accel(
1438             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))),
1439             GDK_KEY_1 + idx, HOTKEY_MODIFIERS);
1440 #endif
1441 
1442     g_signal_connect(vc->menu_item, "activate",
1443                      G_CALLBACK(gd_menu_switch_vc), s);
1444     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
1445 
1446     group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
1447     return group;
1448 }
1449 
1450 #if defined(CONFIG_VTE)
1451 static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
1452 {
1453     VirtualConsole *vc = opaque;
1454 
1455     if (gtk_adjustment_get_upper(adjustment) >
1456         gtk_adjustment_get_page_size(adjustment)) {
1457         gtk_widget_show(vc->vte.scrollbar);
1458     } else {
1459         gtk_widget_hide(vc->vte.scrollbar);
1460     }
1461 }
1462 
1463 static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
1464 {
1465     VirtualConsole *vc = chr->opaque;
1466 
1467     vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
1468     return len;
1469 }
1470 
1471 static int nb_vcs;
1472 static CharDriverState *vcs[MAX_VCS];
1473 
1474 static CharDriverState *gd_vc_handler(ChardevVC *unused)
1475 {
1476     CharDriverState *chr;
1477 
1478     chr = g_malloc0(sizeof(*chr));
1479     chr->chr_write = gd_vc_chr_write;
1480     /* defer OPENED events until our vc is fully initialized */
1481     chr->explicit_be_open = true;
1482 
1483     vcs[nb_vcs++] = chr;
1484 
1485     return chr;
1486 }
1487 
1488 static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
1489                          gpointer user_data)
1490 {
1491     VirtualConsole *vc = user_data;
1492 
1493     qemu_chr_be_write(vc->vte.chr, (uint8_t  *)text, (unsigned int)size);
1494     return TRUE;
1495 }
1496 
1497 static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
1498                               CharDriverState *chr, int idx,
1499                               GSList *group, GtkWidget *view_menu)
1500 {
1501     char buffer[32];
1502     GtkWidget *box;
1503     GtkWidget *scrollbar;
1504     GtkAdjustment *vadjustment;
1505 
1506     vc->s = s;
1507     vc->vte.chr = chr;
1508 
1509     snprintf(buffer, sizeof(buffer), "vc%d", idx);
1510     vc->label = g_strdup_printf("%s", vc->vte.chr->label
1511                                 ? vc->vte.chr->label : buffer);
1512     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1513 
1514     vc->vte.terminal = vte_terminal_new();
1515     g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
1516 
1517     vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
1518     vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
1519                           VC_TERM_X_MIN, VC_TERM_Y_MIN);
1520 
1521 #if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0)
1522     vadjustment = gtk_scrollable_get_vadjustment
1523         (GTK_SCROLLABLE(vc->vte.terminal));
1524 #else
1525     vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
1526 #endif
1527 
1528 #if GTK_CHECK_VERSION(3, 0, 0)
1529     box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
1530     scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
1531 #else
1532     box = gtk_hbox_new(false, 2);
1533     scrollbar = gtk_vscrollbar_new(vadjustment);
1534 #endif
1535 
1536     gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
1537     gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);
1538 
1539     vc->vte.chr->opaque = vc;
1540     vc->vte.box = box;
1541     vc->vte.scrollbar = scrollbar;
1542 
1543     g_signal_connect(vadjustment, "changed",
1544                      G_CALLBACK(gd_vc_adjustment_changed), vc);
1545 
1546     vc->type = GD_VC_VTE;
1547     vc->tab_item = box;
1548     vc->focus = vc->vte.terminal;
1549     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
1550                              gtk_label_new(vc->label));
1551 
1552     qemu_chr_be_generic_open(vc->vte.chr);
1553     if (vc->vte.chr->init) {
1554         vc->vte.chr->init(vc->vte.chr);
1555     }
1556 
1557     return group;
1558 }
1559 
1560 static void gd_vcs_init(GtkDisplayState *s, GSList *group,
1561                         GtkWidget *view_menu)
1562 {
1563     int i;
1564 
1565     for (i = 0; i < nb_vcs; i++) {
1566         VirtualConsole *vc = &s->vc[s->nb_vcs];
1567         group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu);
1568         s->nb_vcs++;
1569     }
1570 }
1571 #endif /* CONFIG_VTE */
1572 
1573 /** Window Creation **/
1574 
1575 static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
1576 {
1577 #if GTK_CHECK_VERSION(3, 0, 0)
1578     g_signal_connect(vc->gfx.drawing_area, "draw",
1579                      G_CALLBACK(gd_draw_event), vc);
1580 #else
1581     g_signal_connect(vc->gfx.drawing_area, "expose-event",
1582                      G_CALLBACK(gd_expose_event), vc);
1583 #endif
1584     if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
1585         g_signal_connect(vc->gfx.drawing_area, "event",
1586                          G_CALLBACK(gd_event), vc);
1587         g_signal_connect(vc->gfx.drawing_area, "button-press-event",
1588                          G_CALLBACK(gd_button_event), vc);
1589         g_signal_connect(vc->gfx.drawing_area, "button-release-event",
1590                          G_CALLBACK(gd_button_event), vc);
1591         g_signal_connect(vc->gfx.drawing_area, "scroll-event",
1592                          G_CALLBACK(gd_scroll_event), vc);
1593         g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1594                          G_CALLBACK(gd_key_event), vc);
1595         g_signal_connect(vc->gfx.drawing_area, "key-release-event",
1596                          G_CALLBACK(gd_key_event), vc);
1597 
1598         g_signal_connect(vc->gfx.drawing_area, "enter-notify-event",
1599                          G_CALLBACK(gd_enter_event), vc);
1600         g_signal_connect(vc->gfx.drawing_area, "leave-notify-event",
1601                          G_CALLBACK(gd_leave_event), vc);
1602         g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
1603                          G_CALLBACK(gd_focus_out_event), vc);
1604         g_signal_connect(vc->gfx.drawing_area, "configure-event",
1605                          G_CALLBACK(gd_configure), vc);
1606     } else {
1607         g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1608                          G_CALLBACK(gd_text_key_down), vc);
1609     }
1610 }
1611 
1612 static void gd_connect_signals(GtkDisplayState *s)
1613 {
1614     g_signal_connect(s->show_tabs_item, "activate",
1615                      G_CALLBACK(gd_menu_show_tabs), s);
1616     g_signal_connect(s->untabify_item, "activate",
1617                      G_CALLBACK(gd_menu_untabify), s);
1618 
1619     g_signal_connect(s->window, "delete-event",
1620                      G_CALLBACK(gd_window_close), s);
1621 
1622     g_signal_connect(s->pause_item, "activate",
1623                      G_CALLBACK(gd_menu_pause), s);
1624     g_signal_connect(s->reset_item, "activate",
1625                      G_CALLBACK(gd_menu_reset), s);
1626     g_signal_connect(s->powerdown_item, "activate",
1627                      G_CALLBACK(gd_menu_powerdown), s);
1628     g_signal_connect(s->quit_item, "activate",
1629                      G_CALLBACK(gd_menu_quit), s);
1630     g_signal_connect(s->full_screen_item, "activate",
1631                      G_CALLBACK(gd_menu_full_screen), s);
1632     g_signal_connect(s->zoom_in_item, "activate",
1633                      G_CALLBACK(gd_menu_zoom_in), s);
1634     g_signal_connect(s->zoom_out_item, "activate",
1635                      G_CALLBACK(gd_menu_zoom_out), s);
1636     g_signal_connect(s->zoom_fixed_item, "activate",
1637                      G_CALLBACK(gd_menu_zoom_fixed), s);
1638     g_signal_connect(s->zoom_fit_item, "activate",
1639                      G_CALLBACK(gd_menu_zoom_fit), s);
1640     g_signal_connect(s->grab_item, "activate",
1641                      G_CALLBACK(gd_menu_grab_input), s);
1642     g_signal_connect(s->notebook, "switch-page",
1643                      G_CALLBACK(gd_change_page), s);
1644 }
1645 
1646 static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
1647 {
1648     GtkWidget *machine_menu;
1649     GtkWidget *separator;
1650 
1651     machine_menu = gtk_menu_new();
1652     gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group);
1653 
1654     s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
1655     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
1656 
1657     separator = gtk_separator_menu_item_new();
1658     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1659 
1660     s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset"));
1661     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
1662 
1663     s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down"));
1664     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
1665 
1666     separator = gtk_separator_menu_item_new();
1667     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1668 
1669     s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
1670     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
1671                                  "<QEMU>/Machine/Quit");
1672     gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
1673                             GDK_KEY_q, HOTKEY_MODIFIERS);
1674     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
1675 
1676     return machine_menu;
1677 }
1678 
1679 static const DisplayChangeListenerOps dcl_ops = {
1680     .dpy_name             = "gtk",
1681     .dpy_gfx_update       = gd_update,
1682     .dpy_gfx_switch       = gd_switch,
1683     .dpy_gfx_check_format = qemu_pixman_check_format,
1684     .dpy_refresh          = gd_refresh,
1685     .dpy_mouse_set        = gd_mouse_set,
1686     .dpy_cursor_define    = gd_cursor_define,
1687 };
1688 
1689 static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
1690                               QemuConsole *con, int idx,
1691                               GSList *group, GtkWidget *view_menu)
1692 {
1693     vc->label = qemu_console_get_label(con);
1694     vc->s = s;
1695     vc->gfx.scale_x = 1.0;
1696     vc->gfx.scale_y = 1.0;
1697 
1698     vc->gfx.drawing_area = gtk_drawing_area_new();
1699     gtk_widget_add_events(vc->gfx.drawing_area,
1700                           GDK_POINTER_MOTION_MASK |
1701                           GDK_BUTTON_PRESS_MASK |
1702                           GDK_BUTTON_RELEASE_MASK |
1703                           GDK_BUTTON_MOTION_MASK |
1704                           GDK_ENTER_NOTIFY_MASK |
1705                           GDK_LEAVE_NOTIFY_MASK |
1706                           GDK_SCROLL_MASK |
1707                           GDK_KEY_PRESS_MASK);
1708     gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
1709 
1710     vc->type = GD_VC_GFX;
1711     vc->tab_item = vc->gfx.drawing_area;
1712     vc->focus = vc->gfx.drawing_area;
1713     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
1714                              vc->tab_item, gtk_label_new(vc->label));
1715 
1716     vc->gfx.dcl.ops = &dcl_ops;
1717     vc->gfx.dcl.con = con;
1718     register_displaychangelistener(&vc->gfx.dcl);
1719 
1720     gd_connect_vc_gfx_signals(vc);
1721     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1722 
1723     if (dpy_ui_info_supported(vc->gfx.dcl.con)) {
1724         gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item));
1725     }
1726 
1727     return group;
1728 }
1729 
1730 static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
1731 {
1732     GSList *group = NULL;
1733     GtkWidget *view_menu;
1734     GtkWidget *separator;
1735     QemuConsole *con;
1736     int vc;
1737 
1738     view_menu = gtk_menu_new();
1739     gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group);
1740 
1741     s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen"));
1742 
1743     gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0,
1744             g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL));
1745 #if GTK_CHECK_VERSION(3, 8, 0)
1746     gtk_accel_label_set_accel(
1747             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))),
1748             GDK_KEY_f, HOTKEY_MODIFIERS);
1749 #endif
1750     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
1751 
1752     separator = gtk_separator_menu_item_new();
1753     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1754 
1755     s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In"));
1756     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
1757                                  "<QEMU>/View/Zoom In");
1758     gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
1759                             HOTKEY_MODIFIERS);
1760     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
1761 
1762     s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
1763     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
1764                                  "<QEMU>/View/Zoom Out");
1765     gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus,
1766                             HOTKEY_MODIFIERS);
1767     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
1768 
1769     s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit"));
1770     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
1771                                  "<QEMU>/View/Zoom Fixed");
1772     gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0,
1773                             HOTKEY_MODIFIERS);
1774     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
1775 
1776     s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
1777     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
1778 
1779     separator = gtk_separator_menu_item_new();
1780     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1781 
1782     s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
1783     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
1784 
1785     s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
1786     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
1787                                  "<QEMU>/View/Grab Input");
1788     gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g,
1789                             HOTKEY_MODIFIERS);
1790     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
1791 
1792     separator = gtk_separator_menu_item_new();
1793     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1794 
1795     /* gfx */
1796     for (vc = 0;; vc++) {
1797         con = qemu_console_lookup_by_index(vc);
1798         if (!con) {
1799             break;
1800         }
1801         group = gd_vc_gfx_init(s, &s->vc[vc], con,
1802                                vc, group, view_menu);
1803         s->nb_vcs++;
1804     }
1805 
1806 #if defined(CONFIG_VTE)
1807     /* vte */
1808     gd_vcs_init(s, group, view_menu);
1809 #endif
1810 
1811     separator = gtk_separator_menu_item_new();
1812     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1813 
1814     s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
1815     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
1816 
1817     s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
1818     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
1819 
1820     return view_menu;
1821 }
1822 
1823 static void gd_create_menus(GtkDisplayState *s)
1824 {
1825     s->accel_group = gtk_accel_group_new();
1826     s->machine_menu = gd_create_menu_machine(s);
1827     s->view_menu = gd_create_menu_view(s);
1828 
1829     s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
1830     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
1831                               s->machine_menu);
1832     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
1833 
1834     s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
1835     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
1836     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
1837 
1838     g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group);
1839     gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group);
1840 }
1841 
1842 static void gd_set_keycode_type(GtkDisplayState *s)
1843 {
1844 #ifdef GDK_WINDOWING_X11
1845     GdkDisplay *display = gtk_widget_get_display(s->window);
1846     if (GDK_IS_X11_DISPLAY(display)) {
1847         Display *x11_display = gdk_x11_display_get_xdisplay(display);
1848         XkbDescPtr desc = XkbGetKeyboard(x11_display, XkbGBN_AllComponentsMask,
1849                                          XkbUseCoreKbd);
1850         char *keycodes = NULL;
1851 
1852         if (desc && desc->names) {
1853             keycodes = XGetAtomName(x11_display, desc->names->keycodes);
1854         }
1855         if (keycodes == NULL) {
1856             fprintf(stderr, "could not lookup keycode name\n");
1857         } else if (strstart(keycodes, "evdev", NULL)) {
1858             s->has_evdev = true;
1859         } else if (!strstart(keycodes, "xfree86", NULL)) {
1860             fprintf(stderr, "unknown keycodes `%s', please report to "
1861                     "qemu-devel@nongnu.org\n", keycodes);
1862         }
1863 
1864         if (desc) {
1865             XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
1866         }
1867         if (keycodes) {
1868             XFree(keycodes);
1869         }
1870     }
1871 #endif
1872 }
1873 
1874 void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
1875 {
1876     GtkDisplayState *s = g_malloc0(sizeof(*s));
1877     char *filename;
1878 
1879     gtk_init(NULL, NULL);
1880 
1881     s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1882 #if GTK_CHECK_VERSION(3, 2, 0)
1883     s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1884 #else
1885     s->vbox = gtk_vbox_new(FALSE, 0);
1886 #endif
1887     s->notebook = gtk_notebook_new();
1888     s->menu_bar = gtk_menu_bar_new();
1889 
1890     s->free_scale = FALSE;
1891 
1892     setlocale(LC_ALL, "");
1893     bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
1894     textdomain("qemu");
1895 
1896     s->null_cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
1897 
1898     s->mouse_mode_notifier.notify = gd_mouse_mode_change;
1899     qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
1900     qemu_add_vm_change_state_handler(gd_change_runstate, s);
1901 
1902     filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg");
1903     if (filename) {
1904         GError *error = NULL;
1905         GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error);
1906         if (pixbuf) {
1907             gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf);
1908         } else {
1909             g_error_free(error);
1910         }
1911         g_free(filename);
1912     }
1913 
1914     gd_create_menus(s);
1915 
1916     gd_connect_signals(s);
1917 
1918     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1919     gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
1920 
1921     gd_update_caption(s);
1922 
1923     gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
1924     gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
1925 
1926     gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
1927 
1928     gtk_widget_show_all(s->window);
1929 
1930 #ifdef VTE_RESIZE_HACK
1931     {
1932         VirtualConsole *cur = gd_vc_find_current(s);
1933         if (cur) {
1934             int i;
1935 
1936             for (i = 0; i < s->nb_vcs; i++) {
1937                 VirtualConsole *vc = &s->vc[i];
1938                 if (vc && vc->type == GD_VC_VTE && vc != cur) {
1939                     gtk_widget_hide(vc->vte.terminal);
1940                 }
1941             }
1942             gd_update_windowsize(cur);
1943         }
1944     }
1945 #endif
1946 
1947     if (full_screen) {
1948         gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
1949     }
1950     if (grab_on_hover) {
1951         gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
1952     }
1953 
1954     gd_set_keycode_type(s);
1955 }
1956 
1957 void early_gtk_display_init(void)
1958 {
1959 #if defined(CONFIG_VTE)
1960     register_vc_handler(gd_vc_handler);
1961 #endif
1962 }
1963