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