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