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