xref: /qemu/ui/gtk.c (revision 89faed62)
1a4ccabcfSAnthony Liguori /*
2a4ccabcfSAnthony Liguori  * GTK UI
3a4ccabcfSAnthony Liguori  *
4a4ccabcfSAnthony Liguori  * Copyright IBM, Corp. 2012
5a4ccabcfSAnthony Liguori  *
6a4ccabcfSAnthony Liguori  * Authors:
7a4ccabcfSAnthony Liguori  *  Anthony Liguori   <aliguori@us.ibm.com>
8a4ccabcfSAnthony Liguori  *
9ac383789SThomas Huth  * This program is free software; you can redistribute it and/or modify
10ac383789SThomas Huth  * it under the terms of the GNU General Public License as published by
11ac383789SThomas Huth  * the Free Software Foundation; either version 2 of the License, or
12ac383789SThomas Huth  * (at your option) any later version.
13a4ccabcfSAnthony Liguori  *
14ac383789SThomas Huth  * This program is distributed in the hope that it will be useful,
15ac383789SThomas Huth  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16ac383789SThomas Huth  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17ac383789SThomas Huth  * General Public License for more details.
18ac383789SThomas Huth  *
19ac383789SThomas Huth  * You should have received a copy of the GNU General Public License
20ac383789SThomas Huth  * along with this program; if not, see <http://www.gnu.org/licenses/>.
21ac383789SThomas Huth  *
22ac383789SThomas Huth  * Portions from gtk-vnc (originally licensed under the LGPL v2+):
23a4ccabcfSAnthony Liguori  *
24a4ccabcfSAnthony Liguori  * GTK VNC Widget
25a4ccabcfSAnthony Liguori  *
26a4ccabcfSAnthony Liguori  * Copyright (C) 2006  Anthony Liguori <anthony@codemonkey.ws>
27a4ccabcfSAnthony Liguori  * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
28a4ccabcfSAnthony Liguori  */
29a4ccabcfSAnthony Liguori 
30834574eaSAnthony Liguori #define GETTEXT_PACKAGE "qemu"
31834574eaSAnthony Liguori #define LOCALEDIR "po"
32834574eaSAnthony Liguori 
33e16f4c87SPeter Maydell #include "qemu/osdep.h"
34e688df6bSMarkus Armbruster #include "qapi/error.h"
35fa4dcf57SKevin Wolf #include "qapi/qapi-commands-control.h"
3690f8c0f9SPhilippe Mathieu-Daudé #include "qapi/qapi-commands-machine.h"
37112ed241SMarkus Armbruster #include "qapi/qapi-commands-misc.h"
38f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
39c95e3080SKevin Wolf 
40dc7ff344SGerd Hoffmann #include "ui/console.h"
41dc7ff344SGerd Hoffmann #include "ui/gtk.h"
42bd593d2cSVolker Rümelin #ifdef G_OS_WIN32
43bd593d2cSVolker Rümelin #include <gdk/gdkwin32.h>
44bd593d2cSVolker Rümelin #endif
45bd593d2cSVolker Rümelin #include "ui/win32-kbd-hook.h"
46c95e3080SKevin Wolf 
47834574eaSAnthony Liguori #include <glib/gi18n.h>
483f58eadeSStefan Weil #include <locale.h>
49bbbf9bfbSStefan Weil #if defined(CONFIG_VTE)
50a4ccabcfSAnthony Liguori #include <vte/vte.h>
51bbbf9bfbSStefan Weil #endif
52a4ccabcfSAnthony Liguori #include <math.h>
53a4ccabcfSAnthony Liguori 
54ef0dd982SStefan Weil #include "trace.h"
5577d910fbSPaolo Bonzini #include "qemu/cutils.h"
56af98ba92SGerd Hoffmann #include "ui/input.h"
5754d31236SMarkus Armbruster #include "sysemu/runstate.h"
58a4ccabcfSAnthony Liguori #include "sysemu/sysemu.h"
59a4ccabcfSAnthony Liguori #include "keymaps.h"
608228e353SMarc-André Lureau #include "chardev/char.h"
616a24ced5SGerd Hoffmann #include "qom/object.h"
62a4ccabcfSAnthony Liguori 
6382fc1809SGerd Hoffmann #define VC_WINDOW_X_MIN  320
6482fc1809SGerd Hoffmann #define VC_WINDOW_Y_MIN  240
6582fc1809SGerd Hoffmann #define VC_TERM_X_MIN     80
6682fc1809SGerd Hoffmann #define VC_TERM_Y_MIN     25
6782fc1809SGerd Hoffmann #define VC_SCALE_MIN    0.25
6882fc1809SGerd Hoffmann #define VC_SCALE_STEP   0.25
69d861def3SAnthony Liguori 
702ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_X11
710041e9a0SMichael S. Tsirkin #include "x_keymap.h"
722ec78706SDaniel P. Berrange 
732ec78706SDaniel P. Berrange /* Gtk2 compat */
742ec78706SDaniel P. Berrange #ifndef GDK_IS_X11_DISPLAY
752ec78706SDaniel P. Berrange #define GDK_IS_X11_DISPLAY(dpy) (dpy != NULL)
762ec78706SDaniel P. Berrange #endif
772ec78706SDaniel P. Berrange #endif
782ec78706SDaniel P. Berrange 
792ec78706SDaniel P. Berrange 
802ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_WAYLAND
812ec78706SDaniel P. Berrange /* Gtk2 compat */
822ec78706SDaniel P. Berrange #ifndef GDK_IS_WAYLAND_DISPLAY
832ec78706SDaniel P. Berrange #define GDK_IS_WAYLAND_DISPLAY(dpy) (dpy != NULL)
842ec78706SDaniel P. Berrange #endif
852ec78706SDaniel P. Berrange #endif
862ec78706SDaniel P. Berrange 
872ec78706SDaniel P. Berrange 
882ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_WIN32
892ec78706SDaniel P. Berrange /* Gtk2 compat */
902ec78706SDaniel P. Berrange #ifndef GDK_IS_WIN32_DISPLAY
912ec78706SDaniel P. Berrange #define GDK_IS_WIN32_DISPLAY(dpy) (dpy != NULL)
922ec78706SDaniel P. Berrange #endif
932ec78706SDaniel P. Berrange #endif
942ec78706SDaniel P. Berrange 
952ec78706SDaniel P. Berrange 
962ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_BROADWAY
972ec78706SDaniel P. Berrange /* Gtk2 compat */
982ec78706SDaniel P. Berrange #ifndef GDK_IS_BROADWAY_DISPLAY
992ec78706SDaniel P. Berrange #define GDK_IS_BROADWAY_DISPLAY(dpy) (dpy != NULL)
1002ec78706SDaniel P. Berrange #endif
1012ec78706SDaniel P. Berrange #endif
1022ec78706SDaniel P. Berrange 
1032ec78706SDaniel P. Berrange 
1042ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_QUARTZ
1052ec78706SDaniel P. Berrange /* Gtk2 compat */
1062ec78706SDaniel P. Berrange #ifndef GDK_IS_QUARTZ_DISPLAY
1072ec78706SDaniel P. Berrange #define GDK_IS_QUARTZ_DISPLAY(dpy) (dpy != NULL)
1082ec78706SDaniel P. Berrange #endif
1092ec78706SDaniel P. Berrange #endif
1102ec78706SDaniel P. Berrange 
1112ec78706SDaniel P. Berrange 
112bbbf9bfbSStefan Weil #if !defined(CONFIG_VTE)
113bbbf9bfbSStefan Weil # define VTE_CHECK_VERSION(a, b, c) 0
114bbbf9bfbSStefan Weil #endif
115cba68834SDaniel P. Berrange 
116b1e749c0SJan Kiszka #define HOTKEY_MODIFIERS        (GDK_CONTROL_MASK | GDK_MOD1_MASK)
117b1e749c0SJan Kiszka 
1182ec78706SDaniel P. Berrange static const guint16 *keycode_map;
1192ec78706SDaniel P. Berrange static size_t keycode_maplen;
1202ec78706SDaniel P. Berrange 
121db1015e9SEduardo Habkost struct VCChardev {
1220ec7b3e7SMarc-André Lureau     Chardev parent;
12341ac54b2SMarc-André Lureau     VirtualConsole *console;
12441ac54b2SMarc-André Lureau     bool echo;
125db1015e9SEduardo Habkost };
126db1015e9SEduardo Habkost typedef struct VCChardev VCChardev;
12741ac54b2SMarc-André Lureau 
128777357d7SMarc-André Lureau #define TYPE_CHARDEV_VC "chardev-vc"
1298110fa1dSEduardo Habkost DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
1308110fa1dSEduardo Habkost                          TYPE_CHARDEV_VC)
131777357d7SMarc-André Lureau 
13211c82b58SGerd Hoffmann bool gtk_use_gl_area;
13311c82b58SGerd Hoffmann 
134d531deefSGerd Hoffmann static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
1352884cf5bSGerd Hoffmann static void gd_ungrab_pointer(GtkDisplayState *s);
136d531deefSGerd Hoffmann static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
137aa4f4058SGerd Hoffmann static void gd_ungrab_keyboard(GtkDisplayState *s);
1382884cf5bSGerd Hoffmann 
139a4ccabcfSAnthony Liguori /** Utility Functions **/
140a4ccabcfSAnthony Liguori 
141271a25c0SGerd Hoffmann static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s)
142271a25c0SGerd Hoffmann {
143271a25c0SGerd Hoffmann     VirtualConsole *vc;
144271a25c0SGerd Hoffmann     gint i;
145271a25c0SGerd Hoffmann 
146271a25c0SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
147271a25c0SGerd Hoffmann         vc = &s->vc[i];
148271a25c0SGerd Hoffmann         if (gtk_check_menu_item_get_active
149271a25c0SGerd Hoffmann             (GTK_CHECK_MENU_ITEM(vc->menu_item))) {
150271a25c0SGerd Hoffmann             return vc;
151271a25c0SGerd Hoffmann         }
152271a25c0SGerd Hoffmann     }
153271a25c0SGerd Hoffmann     return NULL;
154271a25c0SGerd Hoffmann }
155271a25c0SGerd Hoffmann 
156271a25c0SGerd Hoffmann static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page)
157271a25c0SGerd Hoffmann {
158271a25c0SGerd Hoffmann     VirtualConsole *vc;
159271a25c0SGerd Hoffmann     gint i, p;
160271a25c0SGerd Hoffmann 
161271a25c0SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
162271a25c0SGerd Hoffmann         vc = &s->vc[i];
163271a25c0SGerd Hoffmann         p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item);
164271a25c0SGerd Hoffmann         if (p == page) {
165271a25c0SGerd Hoffmann             return vc;
166271a25c0SGerd Hoffmann         }
167271a25c0SGerd Hoffmann     }
168271a25c0SGerd Hoffmann     return NULL;
169271a25c0SGerd Hoffmann }
170271a25c0SGerd Hoffmann 
171e3500d1fSGerd Hoffmann static VirtualConsole *gd_vc_find_current(GtkDisplayState *s)
172e3500d1fSGerd Hoffmann {
173e3500d1fSGerd Hoffmann     gint page;
174e3500d1fSGerd Hoffmann 
175e3500d1fSGerd Hoffmann     page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook));
176e3500d1fSGerd Hoffmann     return gd_vc_find_by_page(s, page);
177e3500d1fSGerd Hoffmann }
178e3500d1fSGerd Hoffmann 
1795104a1f6SAnthony Liguori static bool gd_is_grab_active(GtkDisplayState *s)
1805104a1f6SAnthony Liguori {
1815104a1f6SAnthony Liguori     return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
1825104a1f6SAnthony Liguori }
1835104a1f6SAnthony Liguori 
1845104a1f6SAnthony Liguori static bool gd_grab_on_hover(GtkDisplayState *s)
1855104a1f6SAnthony Liguori {
1865104a1f6SAnthony Liguori     return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
1875104a1f6SAnthony Liguori }
1885104a1f6SAnthony Liguori 
189e3500d1fSGerd Hoffmann static void gd_update_cursor(VirtualConsole *vc)
1905104a1f6SAnthony Liguori {
191e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
192e3500d1fSGerd Hoffmann     GdkWindow *window;
193832189c9SGerd Hoffmann 
194f8c223f6SGerd Hoffmann     if (vc->type != GD_VC_GFX ||
195f8c223f6SGerd Hoffmann         !qemu_console_is_graphic(vc->gfx.dcl.con)) {
196e3500d1fSGerd Hoffmann         return;
1975104a1f6SAnthony Liguori     }
1985104a1f6SAnthony Liguori 
1994cdfc935SHervé Poussineau     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
2004cdfc935SHervé Poussineau         return;
2014cdfc935SHervé Poussineau     }
2024cdfc935SHervé Poussineau 
203e3500d1fSGerd Hoffmann     window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area));
2042884cf5bSGerd Hoffmann     if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) {
205a4ccabcfSAnthony Liguori         gdk_window_set_cursor(window, s->null_cursor);
206a4ccabcfSAnthony Liguori     } else {
207a4ccabcfSAnthony Liguori         gdk_window_set_cursor(window, NULL);
208a4ccabcfSAnthony Liguori     }
209a4ccabcfSAnthony Liguori }
210a4ccabcfSAnthony Liguori 
211a4ccabcfSAnthony Liguori static void gd_update_caption(GtkDisplayState *s)
212a4ccabcfSAnthony Liguori {
213a4ccabcfSAnthony Liguori     const char *status = "";
2144eeaa3a8SGerd Hoffmann     gchar *prefix;
215a4ccabcfSAnthony Liguori     gchar *title;
2165104a1f6SAnthony Liguori     const char *grab = "";
21730e8f22bSJan Kiszka     bool is_paused = !runstate_is_running();
2184eeaa3a8SGerd Hoffmann     int i;
2195104a1f6SAnthony Liguori 
2204eeaa3a8SGerd Hoffmann     if (qemu_name) {
2214eeaa3a8SGerd Hoffmann         prefix = g_strdup_printf("QEMU (%s)", qemu_name);
2224eeaa3a8SGerd Hoffmann     } else {
2234eeaa3a8SGerd Hoffmann         prefix = g_strdup_printf("QEMU");
2244eeaa3a8SGerd Hoffmann     }
2254eeaa3a8SGerd Hoffmann 
2264eeaa3a8SGerd Hoffmann     if (s->ptr_owner != NULL &&
2274eeaa3a8SGerd Hoffmann         s->ptr_owner->window == NULL) {
228d8da9ee8SAurelien Jarno         grab = _(" - Press Ctrl+Alt+G to release grab");
2295104a1f6SAnthony Liguori     }
230a4ccabcfSAnthony Liguori 
23130e8f22bSJan Kiszka     if (is_paused) {
232d8da9ee8SAurelien Jarno         status = _(" [Paused]");
233a4ccabcfSAnthony Liguori     }
23430e8f22bSJan Kiszka     s->external_pause_update = true;
23530e8f22bSJan Kiszka     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
23630e8f22bSJan Kiszka                                    is_paused);
23730e8f22bSJan Kiszka     s->external_pause_update = false;
238a4ccabcfSAnthony Liguori 
2394eeaa3a8SGerd Hoffmann     title = g_strdup_printf("%s%s%s", prefix, status, grab);
2404eeaa3a8SGerd Hoffmann     gtk_window_set_title(GTK_WINDOW(s->window), title);
2414eeaa3a8SGerd Hoffmann     g_free(title);
2424eeaa3a8SGerd Hoffmann 
2434eeaa3a8SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
2444eeaa3a8SGerd Hoffmann         VirtualConsole *vc = &s->vc[i];
2454eeaa3a8SGerd Hoffmann 
2464eeaa3a8SGerd Hoffmann         if (!vc->window) {
2474eeaa3a8SGerd Hoffmann             continue;
2484eeaa3a8SGerd Hoffmann         }
2494eeaa3a8SGerd Hoffmann         title = g_strdup_printf("%s: %s%s%s", prefix, vc->label,
2504eeaa3a8SGerd Hoffmann                                 vc == s->kbd_owner ? " +kbd" : "",
2514eeaa3a8SGerd Hoffmann                                 vc == s->ptr_owner ? " +ptr" : "");
2524eeaa3a8SGerd Hoffmann         gtk_window_set_title(GTK_WINDOW(vc->window), title);
2534eeaa3a8SGerd Hoffmann         g_free(title);
254a4ccabcfSAnthony Liguori     }
255a4ccabcfSAnthony Liguori 
2564eeaa3a8SGerd Hoffmann     g_free(prefix);
257a4ccabcfSAnthony Liguori }
258a4ccabcfSAnthony Liguori 
25982fc1809SGerd Hoffmann static void gd_update_geometry_hints(VirtualConsole *vc)
26082fc1809SGerd Hoffmann {
26182fc1809SGerd Hoffmann     GtkDisplayState *s = vc->s;
26282fc1809SGerd Hoffmann     GdkWindowHints mask = 0;
26382fc1809SGerd Hoffmann     GdkGeometry geo = {};
26482fc1809SGerd Hoffmann     GtkWidget *geo_widget = NULL;
26582fc1809SGerd Hoffmann     GtkWindow *geo_window;
26682fc1809SGerd Hoffmann 
26782fc1809SGerd Hoffmann     if (vc->type == GD_VC_GFX) {
268f98f43eaSGerd Hoffmann         if (!vc->gfx.ds) {
269f98f43eaSGerd Hoffmann             return;
270f98f43eaSGerd Hoffmann         }
27182fc1809SGerd Hoffmann         if (s->free_scale) {
27282fc1809SGerd Hoffmann             geo.min_width  = surface_width(vc->gfx.ds) * VC_SCALE_MIN;
27382fc1809SGerd Hoffmann             geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN;
27482fc1809SGerd Hoffmann             mask |= GDK_HINT_MIN_SIZE;
27582fc1809SGerd Hoffmann         } else {
27682fc1809SGerd Hoffmann             geo.min_width  = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
27782fc1809SGerd Hoffmann             geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
27882fc1809SGerd Hoffmann             mask |= GDK_HINT_MIN_SIZE;
27982fc1809SGerd Hoffmann         }
28082fc1809SGerd Hoffmann         geo_widget = vc->gfx.drawing_area;
28182fc1809SGerd Hoffmann         gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height);
28282fc1809SGerd Hoffmann 
28382fc1809SGerd Hoffmann #if defined(CONFIG_VTE)
28482fc1809SGerd Hoffmann     } else if (vc->type == GD_VC_VTE) {
28582fc1809SGerd Hoffmann         VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
2866978dc4aSAlberto Garcia         GtkBorder padding = { 0 };
28782fc1809SGerd Hoffmann 
28884e2dc4bSCole Robinson #if VTE_CHECK_VERSION(0, 37, 0)
28984e2dc4bSCole Robinson         gtk_style_context_get_padding(
29084e2dc4bSCole Robinson                 gtk_widget_get_style_context(vc->vte.terminal),
29184e2dc4bSCole Robinson                 gtk_widget_get_state_flags(vc->vte.terminal),
29284e2dc4bSCole Robinson                 &padding);
29384e2dc4bSCole Robinson #else
2946978dc4aSAlberto Garcia         {
2956978dc4aSAlberto Garcia             GtkBorder *ib = NULL;
29684e2dc4bSCole Robinson             gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL);
2976978dc4aSAlberto Garcia             if (ib) {
2986978dc4aSAlberto Garcia                 padding = *ib;
2996978dc4aSAlberto Garcia                 gtk_border_free(ib);
3006978dc4aSAlberto Garcia             }
3016978dc4aSAlberto Garcia         }
30284e2dc4bSCole Robinson #endif
30384e2dc4bSCole Robinson 
30482fc1809SGerd Hoffmann         geo.width_inc  = vte_terminal_get_char_width(term);
30582fc1809SGerd Hoffmann         geo.height_inc = vte_terminal_get_char_height(term);
30682fc1809SGerd Hoffmann         mask |= GDK_HINT_RESIZE_INC;
30782fc1809SGerd Hoffmann         geo.base_width  = geo.width_inc;
30882fc1809SGerd Hoffmann         geo.base_height = geo.height_inc;
30982fc1809SGerd Hoffmann         mask |= GDK_HINT_BASE_SIZE;
31082fc1809SGerd Hoffmann         geo.min_width  = geo.width_inc * VC_TERM_X_MIN;
31182fc1809SGerd Hoffmann         geo.min_height = geo.height_inc * VC_TERM_Y_MIN;
31282fc1809SGerd Hoffmann         mask |= GDK_HINT_MIN_SIZE;
31384e2dc4bSCole Robinson 
3146978dc4aSAlberto Garcia         geo.base_width  += padding.left + padding.right;
3156978dc4aSAlberto Garcia         geo.base_height += padding.top + padding.bottom;
3166978dc4aSAlberto Garcia         geo.min_width   += padding.left + padding.right;
3176978dc4aSAlberto Garcia         geo.min_height  += padding.top + padding.bottom;
31882fc1809SGerd Hoffmann         geo_widget = vc->vte.terminal;
31982fc1809SGerd Hoffmann #endif
32082fc1809SGerd Hoffmann     }
32182fc1809SGerd Hoffmann 
32282fc1809SGerd Hoffmann     geo_window = GTK_WINDOW(vc->window ? vc->window : s->window);
32382fc1809SGerd Hoffmann     gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
32482fc1809SGerd Hoffmann }
32582fc1809SGerd Hoffmann 
32697edf3bdSGerd Hoffmann void gd_update_windowsize(VirtualConsole *vc)
327a4ccabcfSAnthony Liguori {
328e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
329c6158483SAnthony Liguori 
33082fc1809SGerd Hoffmann     gd_update_geometry_hints(vc);
331c6158483SAnthony Liguori 
33282fc1809SGerd Hoffmann     if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) {
33382fc1809SGerd Hoffmann         gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window),
33482fc1809SGerd Hoffmann                           VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN);
335a4ccabcfSAnthony Liguori     }
336aa0a55d4SGerd Hoffmann }
337a4ccabcfSAnthony Liguori 
338e3500d1fSGerd Hoffmann static void gd_update_full_redraw(VirtualConsole *vc)
3399d9801cfSGerd Hoffmann {
340e3500d1fSGerd Hoffmann     GtkWidget *area = vc->gfx.drawing_area;
3419d9801cfSGerd Hoffmann     int ww, wh;
34289d85cdeSDaniel P. Berrangé     ww = gdk_window_get_width(gtk_widget_get_window(area));
34389d85cdeSDaniel P. Berrangé     wh = gdk_window_get_height(gtk_widget_get_window(area));
3445cb69566SPaolo Bonzini #if defined(CONFIG_OPENGL)
34511c82b58SGerd Hoffmann     if (vc->gfx.gls && gtk_use_gl_area) {
346925a0400SGerd Hoffmann         gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
347925a0400SGerd Hoffmann         return;
348925a0400SGerd Hoffmann     }
349925a0400SGerd Hoffmann #endif
350e3500d1fSGerd Hoffmann     gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
3519d9801cfSGerd Hoffmann }
3529d9801cfSGerd Hoffmann 
3536db253caSJan Kiszka static void gtk_release_modifiers(GtkDisplayState *s)
3546db253caSJan Kiszka {
355e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
3566db253caSJan Kiszka 
357f8c223f6SGerd Hoffmann     if (vc->type != GD_VC_GFX ||
358f8c223f6SGerd Hoffmann         !qemu_console_is_graphic(vc->gfx.dcl.con)) {
3596db253caSJan Kiszka         return;
3606db253caSJan Kiszka     }
3610c0d4273SGerd Hoffmann     qkbd_state_lift_all_keys(vc->gfx.kbd);
3626db253caSJan Kiszka }
3636db253caSJan Kiszka 
364316cb068SGerd Hoffmann static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
365316cb068SGerd Hoffmann                                GtkWidget *widget)
366316cb068SGerd Hoffmann {
367316cb068SGerd Hoffmann     g_object_ref(G_OBJECT(widget));
368316cb068SGerd Hoffmann     gtk_container_remove(GTK_CONTAINER(from), widget);
369316cb068SGerd Hoffmann     gtk_container_add(GTK_CONTAINER(to), widget);
370316cb068SGerd Hoffmann     g_object_unref(G_OBJECT(widget));
371316cb068SGerd Hoffmann }
372316cb068SGerd Hoffmann 
373bd593d2cSVolker Rümelin static void *gd_win32_get_hwnd(VirtualConsole *vc)
374bd593d2cSVolker Rümelin {
375bd593d2cSVolker Rümelin #ifdef G_OS_WIN32
376bd593d2cSVolker Rümelin     return gdk_win32_window_get_impl_hwnd(
377bd593d2cSVolker Rümelin         gtk_widget_get_window(vc->window ? vc->window : vc->s->window));
378bd593d2cSVolker Rümelin #else
379bd593d2cSVolker Rümelin     return NULL;
380bd593d2cSVolker Rümelin #endif
381bd593d2cSVolker Rümelin }
382bd593d2cSVolker Rümelin 
3839d9801cfSGerd Hoffmann /** DisplayState Callbacks **/
3849d9801cfSGerd Hoffmann 
3859d9801cfSGerd Hoffmann static void gd_update(DisplayChangeListener *dcl,
386bc2ed970SGerd Hoffmann                       int x, int y, int w, int h)
3879d9801cfSGerd Hoffmann {
388e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
389f8c223f6SGerd Hoffmann     GdkWindow *win;
3909d9801cfSGerd Hoffmann     int x1, x2, y1, y2;
3919d9801cfSGerd Hoffmann     int mx, my;
3929d9801cfSGerd Hoffmann     int fbw, fbh;
3939d9801cfSGerd Hoffmann     int ww, wh;
3949d9801cfSGerd Hoffmann 
39574444bc1SGerd Hoffmann     trace_gd_update(vc->label, x, y, w, h);
3969d9801cfSGerd Hoffmann 
3974cdfc935SHervé Poussineau     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
3984cdfc935SHervé Poussineau         return;
3994cdfc935SHervé Poussineau     }
4004cdfc935SHervé Poussineau 
401e3500d1fSGerd Hoffmann     if (vc->gfx.convert) {
402e3500d1fSGerd Hoffmann         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
403e3500d1fSGerd Hoffmann                                NULL, vc->gfx.convert,
404f0875536SGerd Hoffmann                                x, y, 0, 0, x, y, w, h);
405f0875536SGerd Hoffmann     }
406f0875536SGerd Hoffmann 
407e3500d1fSGerd Hoffmann     x1 = floor(x * vc->gfx.scale_x);
408e3500d1fSGerd Hoffmann     y1 = floor(y * vc->gfx.scale_y);
4099d9801cfSGerd Hoffmann 
410e3500d1fSGerd Hoffmann     x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
411e3500d1fSGerd Hoffmann     y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
4129d9801cfSGerd Hoffmann 
413e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
414e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
4159d9801cfSGerd Hoffmann 
416f8c223f6SGerd Hoffmann     win = gtk_widget_get_window(vc->gfx.drawing_area);
417f8c223f6SGerd Hoffmann     if (!win) {
418f8c223f6SGerd Hoffmann         return;
419f8c223f6SGerd Hoffmann     }
42089d85cdeSDaniel P. Berrangé     ww = gdk_window_get_width(win);
42189d85cdeSDaniel P. Berrangé     wh = gdk_window_get_height(win);
4229d9801cfSGerd Hoffmann 
4239d9801cfSGerd Hoffmann     mx = my = 0;
4249d9801cfSGerd Hoffmann     if (ww > fbw) {
4259d9801cfSGerd Hoffmann         mx = (ww - fbw) / 2;
4269d9801cfSGerd Hoffmann     }
4279d9801cfSGerd Hoffmann     if (wh > fbh) {
4289d9801cfSGerd Hoffmann         my = (wh - fbh) / 2;
4299d9801cfSGerd Hoffmann     }
4309d9801cfSGerd Hoffmann 
431e3500d1fSGerd Hoffmann     gtk_widget_queue_draw_area(vc->gfx.drawing_area,
432e3500d1fSGerd Hoffmann                                mx + x1, my + y1, (x2 - x1), (y2 - y1));
4339d9801cfSGerd Hoffmann }
4349d9801cfSGerd Hoffmann 
435bc2ed970SGerd Hoffmann static void gd_refresh(DisplayChangeListener *dcl)
4369d9801cfSGerd Hoffmann {
437284d1c6bSGerd Hoffmann     graphic_hw_update(dcl->con);
4389d9801cfSGerd Hoffmann }
4399d9801cfSGerd Hoffmann 
440bb732ee7SCole Robinson static GdkDevice *gd_get_pointer(GdkDisplay *dpy)
441bb732ee7SCole Robinson {
442bb732ee7SCole Robinson     return gdk_seat_get_pointer(gdk_display_get_default_seat(dpy));
443bb732ee7SCole Robinson }
444bb732ee7SCole Robinson 
445b087143bSIgor Mitsyanko static void gd_mouse_set(DisplayChangeListener *dcl,
446b087143bSIgor Mitsyanko                          int x, int y, int visible)
447b087143bSIgor Mitsyanko {
448e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
449b087143bSIgor Mitsyanko     GdkDisplay *dpy;
450b087143bSIgor Mitsyanko     gint x_root, y_root;
451b087143bSIgor Mitsyanko 
4522bda6602SCole Robinson     if (qemu_input_is_absolute()) {
4532bda6602SCole Robinson         return;
4542bda6602SCole Robinson     }
4552bda6602SCole Robinson 
456e3500d1fSGerd Hoffmann     dpy = gtk_widget_get_display(vc->gfx.drawing_area);
457e3500d1fSGerd Hoffmann     gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
458b087143bSIgor Mitsyanko                                x, y, &x_root, &y_root);
459bb732ee7SCole Robinson     gdk_device_warp(gd_get_pointer(dpy),
460e3500d1fSGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
461298526feSCole Robinson                     x_root, y_root);
4621271f7f7SGerd Hoffmann     vc->s->last_x = x;
4631271f7f7SGerd Hoffmann     vc->s->last_y = y;
464b087143bSIgor Mitsyanko }
4659697f5d2SGerd Hoffmann 
4669697f5d2SGerd Hoffmann static void gd_cursor_define(DisplayChangeListener *dcl,
4679697f5d2SGerd Hoffmann                              QEMUCursor *c)
4689697f5d2SGerd Hoffmann {
469e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
4709697f5d2SGerd Hoffmann     GdkPixbuf *pixbuf;
4719697f5d2SGerd Hoffmann     GdkCursor *cursor;
4729697f5d2SGerd Hoffmann 
4734cdfc935SHervé Poussineau     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
4744cdfc935SHervé Poussineau         return;
4754cdfc935SHervé Poussineau     }
4764cdfc935SHervé Poussineau 
4779697f5d2SGerd Hoffmann     pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
4789697f5d2SGerd Hoffmann                                       GDK_COLORSPACE_RGB, true, 8,
4799697f5d2SGerd Hoffmann                                       c->width, c->height, c->width * 4,
4809697f5d2SGerd Hoffmann                                       NULL, NULL);
481e3500d1fSGerd Hoffmann     cursor = gdk_cursor_new_from_pixbuf
482e3500d1fSGerd Hoffmann         (gtk_widget_get_display(vc->gfx.drawing_area),
4839697f5d2SGerd Hoffmann          pixbuf, c->hot_x, c->hot_y);
484e3500d1fSGerd Hoffmann     gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor);
4859697f5d2SGerd Hoffmann     g_object_unref(pixbuf);
486030b4b7dSStefan Weil     g_object_unref(cursor);
4879697f5d2SGerd Hoffmann }
4889697f5d2SGerd Hoffmann 
4899d9801cfSGerd Hoffmann static void gd_switch(DisplayChangeListener *dcl,
4909d9801cfSGerd Hoffmann                       DisplaySurface *surface)
4919d9801cfSGerd Hoffmann {
492e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
4939d9801cfSGerd Hoffmann     bool resized = true;
4949d9801cfSGerd Hoffmann 
495e251b587SAkihiko Odaki     trace_gd_switch(vc->label, surface_width(surface), surface_height(surface));
4969d9801cfSGerd Hoffmann 
497e3500d1fSGerd Hoffmann     if (vc->gfx.surface) {
498e3500d1fSGerd Hoffmann         cairo_surface_destroy(vc->gfx.surface);
499f98f43eaSGerd Hoffmann         vc->gfx.surface = NULL;
500f98f43eaSGerd Hoffmann     }
501f98f43eaSGerd Hoffmann     if (vc->gfx.convert) {
502f98f43eaSGerd Hoffmann         pixman_image_unref(vc->gfx.convert);
503f98f43eaSGerd Hoffmann         vc->gfx.convert = NULL;
5049d9801cfSGerd Hoffmann     }
5059d9801cfSGerd Hoffmann 
506e251b587SAkihiko Odaki     if (vc->gfx.ds &&
507e3500d1fSGerd Hoffmann         surface_width(vc->gfx.ds) == surface_width(surface) &&
508e3500d1fSGerd Hoffmann         surface_height(vc->gfx.ds) == surface_height(surface)) {
5099d9801cfSGerd Hoffmann         resized = false;
5109d9801cfSGerd Hoffmann     }
511e3500d1fSGerd Hoffmann     vc->gfx.ds = surface;
512f0875536SGerd Hoffmann 
513f0875536SGerd Hoffmann     if (surface->format == PIXMAN_x8r8g8b8) {
514f0875536SGerd Hoffmann         /*
515f0875536SGerd Hoffmann          * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24
516f0875536SGerd Hoffmann          *
517f0875536SGerd Hoffmann          * No need to convert, use surface directly.  Should be the
518f0875536SGerd Hoffmann          * common case as this is qemu_default_pixelformat(32) too.
519f0875536SGerd Hoffmann          */
520e3500d1fSGerd Hoffmann         vc->gfx.surface = cairo_image_surface_create_for_data
521f0875536SGerd Hoffmann             (surface_data(surface),
522f0875536SGerd Hoffmann              CAIRO_FORMAT_RGB24,
5239d9801cfSGerd Hoffmann              surface_width(surface),
5249d9801cfSGerd Hoffmann              surface_height(surface),
5259d9801cfSGerd Hoffmann              surface_stride(surface));
526f0875536SGerd Hoffmann     } else {
527f0875536SGerd Hoffmann         /* Must convert surface, use pixman to do it. */
528e3500d1fSGerd Hoffmann         vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8,
529f0875536SGerd Hoffmann                                                    surface_width(surface),
530f0875536SGerd Hoffmann                                                    surface_height(surface),
531f0875536SGerd Hoffmann                                                    NULL, 0);
532e3500d1fSGerd Hoffmann         vc->gfx.surface = cairo_image_surface_create_for_data
533e3500d1fSGerd Hoffmann             ((void *)pixman_image_get_data(vc->gfx.convert),
534f0875536SGerd Hoffmann              CAIRO_FORMAT_RGB24,
535e3500d1fSGerd Hoffmann              pixman_image_get_width(vc->gfx.convert),
536e3500d1fSGerd Hoffmann              pixman_image_get_height(vc->gfx.convert),
537e3500d1fSGerd Hoffmann              pixman_image_get_stride(vc->gfx.convert));
538e3500d1fSGerd Hoffmann         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
539e3500d1fSGerd Hoffmann                                NULL, vc->gfx.convert,
540f0875536SGerd Hoffmann                                0, 0, 0, 0, 0, 0,
541e3500d1fSGerd Hoffmann                                pixman_image_get_width(vc->gfx.convert),
542e3500d1fSGerd Hoffmann                                pixman_image_get_height(vc->gfx.convert));
543f0875536SGerd Hoffmann     }
5449d9801cfSGerd Hoffmann 
5459d9801cfSGerd Hoffmann     if (resized) {
546e3500d1fSGerd Hoffmann         gd_update_windowsize(vc);
5479d9801cfSGerd Hoffmann     } else {
548e3500d1fSGerd Hoffmann         gd_update_full_redraw(vc);
5499d9801cfSGerd Hoffmann     }
5509d9801cfSGerd Hoffmann }
5519d9801cfSGerd Hoffmann 
55297edf3bdSGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = {
55397edf3bdSGerd Hoffmann     .dpy_name             = "gtk",
55497edf3bdSGerd Hoffmann     .dpy_gfx_update       = gd_update,
55597edf3bdSGerd Hoffmann     .dpy_gfx_switch       = gd_switch,
55697edf3bdSGerd Hoffmann     .dpy_gfx_check_format = qemu_pixman_check_format,
55797edf3bdSGerd Hoffmann     .dpy_refresh          = gd_refresh,
55897edf3bdSGerd Hoffmann     .dpy_mouse_set        = gd_mouse_set,
55997edf3bdSGerd Hoffmann     .dpy_cursor_define    = gd_cursor_define,
56097edf3bdSGerd Hoffmann };
56197edf3bdSGerd Hoffmann 
56297edf3bdSGerd Hoffmann 
56397edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
56497edf3bdSGerd Hoffmann 
56552a37e20SMarc-André Lureau static bool gd_has_dmabuf(DisplayChangeListener *dcl)
56652a37e20SMarc-André Lureau {
56752a37e20SMarc-André Lureau     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
56852a37e20SMarc-André Lureau 
5692606519bSMarc-André Lureau     if (gtk_use_gl_area && !gtk_widget_get_realized(vc->gfx.drawing_area)) {
5702606519bSMarc-André Lureau         /* FIXME: Assume it will work, actual check done after realize */
5712606519bSMarc-André Lureau         /* fixing this would require delaying listener registration */
5722606519bSMarc-André Lureau         return true;
5732606519bSMarc-André Lureau     }
5742606519bSMarc-André Lureau 
57552a37e20SMarc-André Lureau     return vc->gfx.has_dmabuf;
57652a37e20SMarc-André Lureau }
57752a37e20SMarc-André Lureau 
578*89faed62SVivek Kasireddy static void gd_gl_release_dmabuf(DisplayChangeListener *dcl,
579*89faed62SVivek Kasireddy                                  QemuDmaBuf *dmabuf)
580*89faed62SVivek Kasireddy {
581*89faed62SVivek Kasireddy #ifdef CONFIG_GBM
582*89faed62SVivek Kasireddy     egl_dmabuf_release_texture(dmabuf);
583*89faed62SVivek Kasireddy #endif
584*89faed62SVivek Kasireddy }
585*89faed62SVivek Kasireddy 
58697edf3bdSGerd Hoffmann /** DisplayState Callbacks (opengl version) **/
58797edf3bdSGerd Hoffmann 
588925a0400SGerd Hoffmann static const DisplayChangeListenerOps dcl_gl_area_ops = {
589925a0400SGerd Hoffmann     .dpy_name             = "gtk-egl",
590925a0400SGerd Hoffmann     .dpy_gfx_update       = gd_gl_area_update,
591925a0400SGerd Hoffmann     .dpy_gfx_switch       = gd_gl_area_switch,
592925a0400SGerd Hoffmann     .dpy_gfx_check_format = console_gl_check_format,
593925a0400SGerd Hoffmann     .dpy_refresh          = gd_gl_area_refresh,
594925a0400SGerd Hoffmann     .dpy_mouse_set        = gd_mouse_set,
595925a0400SGerd Hoffmann     .dpy_cursor_define    = gd_cursor_define,
596925a0400SGerd Hoffmann 
597925a0400SGerd Hoffmann     .dpy_gl_ctx_create       = gd_gl_area_create_context,
598925a0400SGerd Hoffmann     .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
599925a0400SGerd Hoffmann     .dpy_gl_ctx_make_current = gd_gl_area_make_current,
600f4c36bdaSGerd Hoffmann     .dpy_gl_scanout_texture  = gd_gl_area_scanout_texture,
601568b12fcSMarc-André Lureau     .dpy_gl_scanout_disable  = gd_gl_area_scanout_disable,
602925a0400SGerd Hoffmann     .dpy_gl_update           = gd_gl_area_scanout_flush,
6032606519bSMarc-André Lureau     .dpy_gl_scanout_dmabuf   = gd_gl_area_scanout_dmabuf,
604*89faed62SVivek Kasireddy     .dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
6052606519bSMarc-André Lureau     .dpy_has_dmabuf          = gd_has_dmabuf,
606925a0400SGerd Hoffmann };
607925a0400SGerd Hoffmann 
608bc6a3565SAkihiko Odaki #ifdef CONFIG_X11
609bc6a3565SAkihiko Odaki 
61097edf3bdSGerd Hoffmann static const DisplayChangeListenerOps dcl_egl_ops = {
61197edf3bdSGerd Hoffmann     .dpy_name             = "gtk-egl",
61297edf3bdSGerd Hoffmann     .dpy_gfx_update       = gd_egl_update,
61397edf3bdSGerd Hoffmann     .dpy_gfx_switch       = gd_egl_switch,
61497edf3bdSGerd Hoffmann     .dpy_gfx_check_format = console_gl_check_format,
61597edf3bdSGerd Hoffmann     .dpy_refresh          = gd_egl_refresh,
61697edf3bdSGerd Hoffmann     .dpy_mouse_set        = gd_mouse_set,
61797edf3bdSGerd Hoffmann     .dpy_cursor_define    = gd_cursor_define,
6184782aeb7SGerd Hoffmann 
6194782aeb7SGerd Hoffmann     .dpy_gl_ctx_create       = gd_egl_create_context,
6204782aeb7SGerd Hoffmann     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
6214782aeb7SGerd Hoffmann     .dpy_gl_ctx_make_current = gd_egl_make_current,
622543a7a16SGerd Hoffmann     .dpy_gl_scanout_disable  = gd_egl_scanout_disable,
623f4c36bdaSGerd Hoffmann     .dpy_gl_scanout_texture  = gd_egl_scanout_texture,
62470763feaSGerd Hoffmann     .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
625f1bd3132SGerd Hoffmann     .dpy_gl_cursor_dmabuf    = gd_egl_cursor_dmabuf,
626f1bd3132SGerd Hoffmann     .dpy_gl_cursor_position  = gd_egl_cursor_position,
6274782aeb7SGerd Hoffmann     .dpy_gl_update           = gd_egl_scanout_flush,
628*89faed62SVivek Kasireddy     .dpy_gl_release_dmabuf   = gd_gl_release_dmabuf,
62952a37e20SMarc-André Lureau     .dpy_has_dmabuf          = gd_has_dmabuf,
63097edf3bdSGerd Hoffmann };
63197edf3bdSGerd Hoffmann 
632bc6a3565SAkihiko Odaki #endif
633bc6a3565SAkihiko Odaki 
634925a0400SGerd Hoffmann #endif /* CONFIG_OPENGL */
63597edf3bdSGerd Hoffmann 
636a4ccabcfSAnthony Liguori /** QEMU Events **/
637a4ccabcfSAnthony Liguori 
638538f0497SPhilippe Mathieu-Daudé static void gd_change_runstate(void *opaque, bool running, RunState state)
639a4ccabcfSAnthony Liguori {
640a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
641a4ccabcfSAnthony Liguori 
642a4ccabcfSAnthony Liguori     gd_update_caption(s);
643a4ccabcfSAnthony Liguori }
644a4ccabcfSAnthony Liguori 
645a4ccabcfSAnthony Liguori static void gd_mouse_mode_change(Notifier *notify, void *data)
646a4ccabcfSAnthony Liguori {
647800b0e81STakashi Iwai     GtkDisplayState *s;
64899623c90SGerd Hoffmann     int i;
649800b0e81STakashi Iwai 
650800b0e81STakashi Iwai     s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
651800b0e81STakashi Iwai     /* release the grab at switching to absolute mode */
652800b0e81STakashi Iwai     if (qemu_input_is_absolute() && gd_is_grab_active(s)) {
653800b0e81STakashi Iwai         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
654a4ccabcfSAnthony Liguori                                        FALSE);
655a4ccabcfSAnthony Liguori     }
65699623c90SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
65799623c90SGerd Hoffmann         VirtualConsole *vc = &s->vc[i];
65899623c90SGerd Hoffmann         gd_update_cursor(vc);
65999623c90SGerd Hoffmann     }
660800b0e81STakashi Iwai }
661a4ccabcfSAnthony Liguori 
662a4ccabcfSAnthony Liguori /** GTK Events **/
663a4ccabcfSAnthony Liguori 
664a4ccabcfSAnthony Liguori static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
665a4ccabcfSAnthony Liguori                                 void *opaque)
666a4ccabcfSAnthony Liguori {
667a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
6680c8d7065SGerd Hoffmann     bool allow_close = true;
669a4ccabcfSAnthony Liguori 
6700c8d7065SGerd Hoffmann     if (s->opts->has_window_close && !s->opts->window_close) {
6710c8d7065SGerd Hoffmann         allow_close = false;
6720c8d7065SGerd Hoffmann     }
6730c8d7065SGerd Hoffmann 
6740c8d7065SGerd Hoffmann     if (allow_close) {
675a4ccabcfSAnthony Liguori         qmp_quit(NULL);
676a4ccabcfSAnthony Liguori     }
677a4ccabcfSAnthony Liguori 
678a4ccabcfSAnthony Liguori     return TRUE;
679a4ccabcfSAnthony Liguori }
680a4ccabcfSAnthony Liguori 
681925a0400SGerd Hoffmann static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height)
682925a0400SGerd Hoffmann {
683925a0400SGerd Hoffmann     QemuUIInfo info;
684925a0400SGerd Hoffmann 
685925a0400SGerd Hoffmann     memset(&info, 0, sizeof(info));
686925a0400SGerd Hoffmann     info.width = width;
687925a0400SGerd Hoffmann     info.height = height;
688925a0400SGerd Hoffmann     dpy_set_ui_info(vc->gfx.dcl.con, &info);
689925a0400SGerd Hoffmann }
690925a0400SGerd Hoffmann 
6915cb69566SPaolo Bonzini #if defined(CONFIG_OPENGL)
692925a0400SGerd Hoffmann 
693925a0400SGerd Hoffmann static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
694925a0400SGerd Hoffmann                                 void *opaque)
695925a0400SGerd Hoffmann {
696925a0400SGerd Hoffmann     VirtualConsole *vc = opaque;
697925a0400SGerd Hoffmann 
698925a0400SGerd Hoffmann     if (vc->gfx.gls) {
699925a0400SGerd Hoffmann         gd_gl_area_draw(vc);
700925a0400SGerd Hoffmann     }
701925a0400SGerd Hoffmann     return TRUE;
702925a0400SGerd Hoffmann }
703925a0400SGerd Hoffmann 
704925a0400SGerd Hoffmann static void gd_resize_event(GtkGLArea *area,
705925a0400SGerd Hoffmann                             gint width, gint height, gpointer *opaque)
706925a0400SGerd Hoffmann {
707925a0400SGerd Hoffmann     VirtualConsole *vc = (void *)opaque;
708925a0400SGerd Hoffmann 
709925a0400SGerd Hoffmann     gd_set_ui_info(vc, width, height);
710925a0400SGerd Hoffmann }
711925a0400SGerd Hoffmann 
712925a0400SGerd Hoffmann #endif
713925a0400SGerd Hoffmann 
714dc26435eSPhilippe Mathieu-Daudé /*
7153c4b8f83SVolker Rümelin  * If available, return the update interval of the monitor in ms,
7163c4b8f83SVolker Rümelin  * else return 0 (the default update interval).
717dc26435eSPhilippe Mathieu-Daudé  */
7180fdc9977SNikola Pavlica int gd_monitor_update_interval(GtkWidget *widget)
719dc26435eSPhilippe Mathieu-Daudé {
720dc26435eSPhilippe Mathieu-Daudé #ifdef GDK_VERSION_3_22
7210431e369SVolker Rümelin     GdkWindow *win = gtk_widget_get_window(widget);
722dc26435eSPhilippe Mathieu-Daudé 
723dc26435eSPhilippe Mathieu-Daudé     if (win) {
7240431e369SVolker Rümelin         GdkDisplay *dpy = gtk_widget_get_display(widget);
725dc26435eSPhilippe Mathieu-Daudé         GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
7263c4b8f83SVolker Rümelin         int refresh_rate = gdk_monitor_get_refresh_rate(monitor); /* [mHz] */
727dc26435eSPhilippe Mathieu-Daudé 
7283c4b8f83SVolker Rümelin         if (refresh_rate) {
7293c4b8f83SVolker Rümelin             /* T = 1 / f = 1 [s*Hz] / f = 1000*1000 [ms*mHz] / f */
7303c4b8f83SVolker Rümelin             return MIN(1000 * 1000 / refresh_rate,
7313c4b8f83SVolker Rümelin                        GUI_REFRESH_INTERVAL_DEFAULT);
7323c4b8f83SVolker Rümelin         }
733dc26435eSPhilippe Mathieu-Daudé     }
734dc26435eSPhilippe Mathieu-Daudé #endif
735dc26435eSPhilippe Mathieu-Daudé     return 0;
736dc26435eSPhilippe Mathieu-Daudé }
737dc26435eSPhilippe Mathieu-Daudé 
738a4ccabcfSAnthony Liguori static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
739a4ccabcfSAnthony Liguori {
740e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
741e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
742c6158483SAnthony Liguori     int mx, my;
743a4ccabcfSAnthony Liguori     int ww, wh;
744a4ccabcfSAnthony Liguori     int fbw, fbh;
745a4ccabcfSAnthony Liguori 
74697edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
74797edf3bdSGerd Hoffmann     if (vc->gfx.gls) {
74811c82b58SGerd Hoffmann         if (gtk_use_gl_area) {
749925a0400SGerd Hoffmann             /* invoke render callback please */
750925a0400SGerd Hoffmann             return FALSE;
75111c82b58SGerd Hoffmann         } else {
752bc6a3565SAkihiko Odaki #ifdef CONFIG_X11
75397edf3bdSGerd Hoffmann             gd_egl_draw(vc);
75497edf3bdSGerd Hoffmann             return TRUE;
755bc6a3565SAkihiko Odaki #else
756bc6a3565SAkihiko Odaki             abort();
757bc6a3565SAkihiko Odaki #endif
75811c82b58SGerd Hoffmann         }
75997edf3bdSGerd Hoffmann     }
76097edf3bdSGerd Hoffmann #endif
76197edf3bdSGerd Hoffmann 
762c6158483SAnthony Liguori     if (!gtk_widget_get_realized(widget)) {
763c6158483SAnthony Liguori         return FALSE;
764c6158483SAnthony Liguori     }
765f98f43eaSGerd Hoffmann     if (!vc->gfx.ds) {
766f98f43eaSGerd Hoffmann         return FALSE;
767f98f43eaSGerd Hoffmann     }
768c6158483SAnthony Liguori 
7693c4b8f83SVolker Rümelin     vc->gfx.dcl.update_interval =
7703c4b8f83SVolker Rümelin         gd_monitor_update_interval(vc->window ? vc->window : s->window);
771dc26435eSPhilippe Mathieu-Daudé 
772e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds);
773e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds);
774a4ccabcfSAnthony Liguori 
77589d85cdeSDaniel P. Berrangé     ww = gdk_window_get_width(gtk_widget_get_window(widget));
77689d85cdeSDaniel P. Berrangé     wh = gdk_window_get_height(gtk_widget_get_window(widget));
777a4ccabcfSAnthony Liguori 
778c6158483SAnthony Liguori     if (s->full_screen) {
779e3500d1fSGerd Hoffmann         vc->gfx.scale_x = (double)ww / fbw;
780e3500d1fSGerd Hoffmann         vc->gfx.scale_y = (double)wh / fbh;
781c6158483SAnthony Liguori     } else if (s->free_scale) {
782c6158483SAnthony Liguori         double sx, sy;
783c6158483SAnthony Liguori 
784c6158483SAnthony Liguori         sx = (double)ww / fbw;
785c6158483SAnthony Liguori         sy = (double)wh / fbh;
786c6158483SAnthony Liguori 
787e3500d1fSGerd Hoffmann         vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
788a4ccabcfSAnthony Liguori     }
789a4ccabcfSAnthony Liguori 
790e3500d1fSGerd Hoffmann     fbw *= vc->gfx.scale_x;
791e3500d1fSGerd Hoffmann     fbh *= vc->gfx.scale_y;
7925104a1f6SAnthony Liguori 
793c6158483SAnthony Liguori     mx = my = 0;
794c6158483SAnthony Liguori     if (ww > fbw) {
795c6158483SAnthony Liguori         mx = (ww - fbw) / 2;
796c6158483SAnthony Liguori     }
797c6158483SAnthony Liguori     if (wh > fbh) {
798c6158483SAnthony Liguori         my = (wh - fbh) / 2;
799c6158483SAnthony Liguori     }
800c6158483SAnthony Liguori 
801c6158483SAnthony Liguori     cairo_rectangle(cr, 0, 0, ww, wh);
802c6158483SAnthony Liguori 
803c6158483SAnthony Liguori     /* Optionally cut out the inner area where the pixmap
804c6158483SAnthony Liguori        will be drawn. This avoids 'flashing' since we're
805c6158483SAnthony Liguori        not double-buffering. Note we're using the undocumented
806c6158483SAnthony Liguori        behaviour of drawing the rectangle from right to left
807c6158483SAnthony Liguori        to cut out the whole */
808c6158483SAnthony Liguori     cairo_rectangle(cr, mx + fbw, my,
809c6158483SAnthony Liguori                     -1 * fbw, fbh);
810c6158483SAnthony Liguori     cairo_fill(cr);
811c6158483SAnthony Liguori 
812e3500d1fSGerd Hoffmann     cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
813e3500d1fSGerd Hoffmann     cairo_set_source_surface(cr, vc->gfx.surface,
814e3500d1fSGerd Hoffmann                              mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
815a4ccabcfSAnthony Liguori     cairo_paint(cr);
816a4ccabcfSAnthony Liguori 
817a4ccabcfSAnthony Liguori     return TRUE;
818a4ccabcfSAnthony Liguori }
819a4ccabcfSAnthony Liguori 
820a4ccabcfSAnthony Liguori static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
821a4ccabcfSAnthony Liguori                                 void *opaque)
822a4ccabcfSAnthony Liguori {
823e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
824e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
825a4ccabcfSAnthony Liguori     int x, y;
826c6158483SAnthony Liguori     int mx, my;
827c6158483SAnthony Liguori     int fbh, fbw;
828c6158483SAnthony Liguori     int ww, wh;
829a4ccabcfSAnthony Liguori 
830f98f43eaSGerd Hoffmann     if (!vc->gfx.ds) {
831f98f43eaSGerd Hoffmann         return TRUE;
832f98f43eaSGerd Hoffmann     }
833f98f43eaSGerd Hoffmann 
834e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
835e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
836c6158483SAnthony Liguori 
83789d85cdeSDaniel P. Berrangé     ww = gdk_window_get_width(gtk_widget_get_window(vc->gfx.drawing_area));
83889d85cdeSDaniel P. Berrangé     wh = gdk_window_get_height(gtk_widget_get_window(vc->gfx.drawing_area));
839c6158483SAnthony Liguori 
840c6158483SAnthony Liguori     mx = my = 0;
841c6158483SAnthony Liguori     if (ww > fbw) {
842c6158483SAnthony Liguori         mx = (ww - fbw) / 2;
843c6158483SAnthony Liguori     }
844c6158483SAnthony Liguori     if (wh > fbh) {
845c6158483SAnthony Liguori         my = (wh - fbh) / 2;
846c6158483SAnthony Liguori     }
847c6158483SAnthony Liguori 
848e3500d1fSGerd Hoffmann     x = (motion->x - mx) / vc->gfx.scale_x;
849e3500d1fSGerd Hoffmann     y = (motion->y - my) / vc->gfx.scale_y;
850c6158483SAnthony Liguori 
851e61031cdSTakashi Iwai     if (qemu_input_is_absolute()) {
852c6158483SAnthony Liguori         if (x < 0 || y < 0 ||
853e3500d1fSGerd Hoffmann             x >= surface_width(vc->gfx.ds) ||
854e3500d1fSGerd Hoffmann             y >= surface_height(vc->gfx.ds)) {
855c6158483SAnthony Liguori             return TRUE;
856c6158483SAnthony Liguori         }
857e3500d1fSGerd Hoffmann         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
8589cfa7ab9SPhilippe Voinov                              0, surface_width(vc->gfx.ds));
859e3500d1fSGerd Hoffmann         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
8609cfa7ab9SPhilippe Voinov                              0, surface_height(vc->gfx.ds));
861192f81bfSGerd Hoffmann         qemu_input_event_sync();
8622884cf5bSGerd Hoffmann     } else if (s->last_set && s->ptr_owner == vc) {
863e3500d1fSGerd Hoffmann         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
864e3500d1fSGerd Hoffmann         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
865192f81bfSGerd Hoffmann         qemu_input_event_sync();
866a4ccabcfSAnthony Liguori     }
867a4ccabcfSAnthony Liguori     s->last_x = x;
868a4ccabcfSAnthony Liguori     s->last_y = y;
869e61031cdSTakashi Iwai     s->last_set = TRUE;
870a4ccabcfSAnthony Liguori 
8712884cf5bSGerd Hoffmann     if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
872e3500d1fSGerd Hoffmann         GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
8737b23d121SVolker Rümelin         GdkDisplay *dpy = gtk_widget_get_display(widget);
8747b23d121SVolker Rümelin         GdkWindow *win = gtk_widget_get_window(widget);
8757b23d121SVolker Rümelin         GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
8767b23d121SVolker Rümelin         GdkRectangle geometry;
87776d8f93bSAlberto Garcia 
8785104a1f6SAnthony Liguori         int x = (int)motion->x_root;
8795104a1f6SAnthony Liguori         int y = (int)motion->y_root;
8805104a1f6SAnthony Liguori 
88176d8f93bSAlberto Garcia         gdk_monitor_get_geometry(monitor, &geometry);
88276d8f93bSAlberto Garcia 
8835104a1f6SAnthony Liguori         /* In relative mode check to see if client pointer hit
884cd6c768fSDennis Wölfing          * one of the monitor edges, and if so move it back to the
885cd6c768fSDennis Wölfing          * center of the monitor. This is important because the pointer
8865104a1f6SAnthony Liguori          * in the server doesn't correspond 1-for-1, and so
8875104a1f6SAnthony Liguori          * may still be only half way across the screen. Without
8885104a1f6SAnthony Liguori          * this warp, the server pointer would thus appear to hit
8895104a1f6SAnthony Liguori          * an invisible wall */
890cd6c768fSDennis Wölfing         if (x <= geometry.x || x - geometry.x >= geometry.width - 1 ||
891cd6c768fSDennis Wölfing             y <= geometry.y || y - geometry.y >= geometry.height - 1) {
8928906de76SDaniel P. Berrange             GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
893cd6c768fSDennis Wölfing             x = geometry.x + geometry.width / 2;
894cd6c768fSDennis Wölfing             y = geometry.y + geometry.height / 2;
895cd6c768fSDennis Wölfing 
8968906de76SDaniel P. Berrange             gdk_device_warp(dev, screen, x, y);
897e61031cdSTakashi Iwai             s->last_set = FALSE;
8985104a1f6SAnthony Liguori             return FALSE;
8995104a1f6SAnthony Liguori         }
9005104a1f6SAnthony Liguori     }
901a4ccabcfSAnthony Liguori     return TRUE;
902a4ccabcfSAnthony Liguori }
903a4ccabcfSAnthony Liguori 
904a4ccabcfSAnthony Liguori static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
905a4ccabcfSAnthony Liguori                                 void *opaque)
906a4ccabcfSAnthony Liguori {
907e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
908e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
909192f81bfSGerd Hoffmann     InputButton btn;
910a4ccabcfSAnthony Liguori 
911800b0e81STakashi Iwai     /* implicitly grab the input at the first click in the relative mode */
912800b0e81STakashi Iwai     if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
9132884cf5bSGerd Hoffmann         !qemu_input_is_absolute() && s->ptr_owner != vc) {
9142884cf5bSGerd Hoffmann         if (!vc->window) {
915800b0e81STakashi Iwai             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
916800b0e81STakashi Iwai                                            TRUE);
9172884cf5bSGerd Hoffmann         } else {
918d531deefSGerd Hoffmann             gd_grab_pointer(vc, "relative-mode-click");
9192884cf5bSGerd Hoffmann         }
920800b0e81STakashi Iwai         return TRUE;
921800b0e81STakashi Iwai     }
922800b0e81STakashi Iwai 
923a4ccabcfSAnthony Liguori     if (button->button == 1) {
924192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_LEFT;
925a4ccabcfSAnthony Liguori     } else if (button->button == 2) {
926192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_MIDDLE;
927a4ccabcfSAnthony Liguori     } else if (button->button == 3) {
928192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_RIGHT;
9291266b68cSFabian Lesniak     } else if (button->button == 8) {
9301266b68cSFabian Lesniak         btn = INPUT_BUTTON_SIDE;
9311266b68cSFabian Lesniak     } else if (button->button == 9) {
9321266b68cSFabian Lesniak         btn = INPUT_BUTTON_EXTRA;
933a4ccabcfSAnthony Liguori     } else {
934192f81bfSGerd Hoffmann         return TRUE;
935a4ccabcfSAnthony Liguori     }
936a4ccabcfSAnthony Liguori 
937e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn,
938e3500d1fSGerd Hoffmann                          button->type == GDK_BUTTON_PRESS);
939192f81bfSGerd Hoffmann     qemu_input_event_sync();
940a4ccabcfSAnthony Liguori     return TRUE;
941a4ccabcfSAnthony Liguori }
942a4ccabcfSAnthony Liguori 
943d58b9122SJan Kiszka static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
944d58b9122SJan Kiszka                                 void *opaque)
945d58b9122SJan Kiszka {
946e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
947d58b9122SJan Kiszka     InputButton btn;
948d58b9122SJan Kiszka 
949d58b9122SJan Kiszka     if (scroll->direction == GDK_SCROLL_UP) {
950f22d0af0SGerd Hoffmann         btn = INPUT_BUTTON_WHEEL_UP;
951d58b9122SJan Kiszka     } else if (scroll->direction == GDK_SCROLL_DOWN) {
952f22d0af0SGerd Hoffmann         btn = INPUT_BUTTON_WHEEL_DOWN;
953e229d1efSOGAWA Hirofumi     } else if (scroll->direction == GDK_SCROLL_SMOOTH) {
954e229d1efSOGAWA Hirofumi         gdouble delta_x, delta_y;
955e229d1efSOGAWA Hirofumi         if (!gdk_event_get_scroll_deltas((GdkEvent *)scroll,
956e229d1efSOGAWA Hirofumi                                          &delta_x, &delta_y)) {
957e229d1efSOGAWA Hirofumi             return TRUE;
958e229d1efSOGAWA Hirofumi         }
959a0fbb9a8SSergio Lopez         if (delta_y == 0) {
960a0fbb9a8SSergio Lopez             return TRUE;
961a0fbb9a8SSergio Lopez         } else if (delta_y > 0) {
962e229d1efSOGAWA Hirofumi             btn = INPUT_BUTTON_WHEEL_DOWN;
963e229d1efSOGAWA Hirofumi         } else {
964e229d1efSOGAWA Hirofumi             btn = INPUT_BUTTON_WHEEL_UP;
965e229d1efSOGAWA Hirofumi         }
966d58b9122SJan Kiszka     } else {
967d58b9122SJan Kiszka         return TRUE;
968d58b9122SJan Kiszka     }
969d58b9122SJan Kiszka 
970e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
971d58b9122SJan Kiszka     qemu_input_event_sync();
972e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
973d58b9122SJan Kiszka     qemu_input_event_sync();
974d58b9122SJan Kiszka     return TRUE;
975d58b9122SJan Kiszka }
976d58b9122SJan Kiszka 
9772ec78706SDaniel P. Berrange 
9782ec78706SDaniel P. Berrange static const guint16 *gd_get_keymap(size_t *maplen)
979a4ccabcfSAnthony Liguori {
9802ec78706SDaniel P. Berrange     GdkDisplay *dpy = gdk_display_get_default();
9812ec78706SDaniel P. Berrange 
9822ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_X11
9832ec78706SDaniel P. Berrange     if (GDK_IS_X11_DISPLAY(dpy)) {
9842ec78706SDaniel P. Berrange         trace_gd_keymap_windowing("x11");
9852ec78706SDaniel P. Berrange         return qemu_xkeymap_mapping_table(
9862ec78706SDaniel P. Berrange             gdk_x11_display_get_xdisplay(dpy), maplen);
9872ec78706SDaniel P. Berrange     }
9882ec78706SDaniel P. Berrange #endif
9892ec78706SDaniel P. Berrange 
9902ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_WAYLAND
9912ec78706SDaniel P. Berrange     if (GDK_IS_WAYLAND_DISPLAY(dpy)) {
9922ec78706SDaniel P. Berrange         trace_gd_keymap_windowing("wayland");
9932ec78706SDaniel P. Berrange         *maplen = qemu_input_map_xorgevdev_to_qcode_len;
9942ec78706SDaniel P. Berrange         return qemu_input_map_xorgevdev_to_qcode;
9952ec78706SDaniel P. Berrange     }
9962ec78706SDaniel P. Berrange #endif
997a4ccabcfSAnthony Liguori 
9980a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_WIN32
9990a337ed0SGerd Hoffmann     if (GDK_IS_WIN32_DISPLAY(dpy)) {
10002ec78706SDaniel P. Berrange         trace_gd_keymap_windowing("win32");
100114541927SVolker Rümelin         *maplen = qemu_input_map_atset1_to_qcode_len;
100214541927SVolker Rümelin         return qemu_input_map_atset1_to_qcode;
10030a337ed0SGerd Hoffmann     }
10040a337ed0SGerd Hoffmann #endif
1005a4ccabcfSAnthony Liguori 
10062ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_QUARTZ
10072ec78706SDaniel P. Berrange     if (GDK_IS_QUARTZ_DISPLAY(dpy)) {
10082ec78706SDaniel P. Berrange         trace_gd_keymap_windowing("quartz");
10092ec78706SDaniel P. Berrange         *maplen = qemu_input_map_osx_to_qcode_len;
10102ec78706SDaniel P. Berrange         return qemu_input_map_osx_to_qcode;
10113158a348SBruce Rogers     }
10120a337ed0SGerd Hoffmann #endif
10132ec78706SDaniel P. Berrange 
10142ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_BROADWAY
10152ec78706SDaniel P. Berrange     if (GDK_IS_BROADWAY_DISPLAY(dpy)) {
10162ec78706SDaniel P. Berrange         trace_gd_keymap_windowing("broadway");
10172ec78706SDaniel P. Berrange         g_warning("experimental: using broadway, x11 virtual keysym\n"
10182ec78706SDaniel P. Berrange                   "mapping - with very limited support. See also\n"
10192ec78706SDaniel P. Berrange                   "https://bugzilla.gnome.org/show_bug.cgi?id=700105");
10202ec78706SDaniel P. Berrange         *maplen = qemu_input_map_x11_to_qcode_len;
10212ec78706SDaniel P. Berrange         return qemu_input_map_x11_to_qcode;
10222ec78706SDaniel P. Berrange     }
1023a8ffb372SDaniel P. Berrange #endif
10242ec78706SDaniel P. Berrange 
10252ec78706SDaniel P. Berrange     g_warning("Unsupported GDK Windowing platform.\n"
10262ec78706SDaniel P. Berrange               "Disabling extended keycode tables.\n"
10272ec78706SDaniel P. Berrange               "Please report to qemu-devel@nongnu.org\n"
10282ec78706SDaniel P. Berrange               "including the following information:\n"
10292ec78706SDaniel P. Berrange               "\n"
10302ec78706SDaniel P. Berrange               "  - Operating system\n"
10312ec78706SDaniel P. Berrange               "  - GDK Windowing system build\n");
10322ec78706SDaniel P. Berrange     return NULL;
1033a4ccabcfSAnthony Liguori }
1034a4ccabcfSAnthony Liguori 
10352ec78706SDaniel P. Berrange 
10362ec78706SDaniel P. Berrange static int gd_map_keycode(int scancode)
10372ec78706SDaniel P. Berrange {
10382ec78706SDaniel P. Berrange     if (!keycode_map) {
10392ec78706SDaniel P. Berrange         return 0;
10402ec78706SDaniel P. Berrange     }
10412ec78706SDaniel P. Berrange     if (scancode > keycode_maplen) {
10422ec78706SDaniel P. Berrange         return 0;
10432ec78706SDaniel P. Berrange     }
10442ec78706SDaniel P. Berrange 
10452ec78706SDaniel P. Berrange     return keycode_map[scancode];
1046932f2d7eSGerd Hoffmann }
1047932f2d7eSGerd Hoffmann 
104814541927SVolker Rümelin static int gd_get_keycode(GdkEventKey *key)
104914541927SVolker Rümelin {
10507b23d121SVolker Rümelin #ifdef G_OS_WIN32
105114541927SVolker Rümelin     int scancode = gdk_event_get_scancode((GdkEvent *)key);
105214541927SVolker Rümelin 
105314541927SVolker Rümelin     /* translate Windows native scancodes to atset1 keycodes */
105414541927SVolker Rümelin     switch (scancode & (KF_EXTENDED | 0xff)) {
105514541927SVolker Rümelin     case 0x145:     /* NUMLOCK */
105614541927SVolker Rümelin         return scancode & 0xff;
105714541927SVolker Rümelin     }
105814541927SVolker Rümelin 
105914541927SVolker Rümelin     return scancode & KF_EXTENDED ?
106014541927SVolker Rümelin         0xe000 | (scancode & 0xff) : scancode & 0xff;
106114541927SVolker Rümelin 
106214541927SVolker Rümelin #else
106314541927SVolker Rümelin     return key->hardware_keycode;
106414541927SVolker Rümelin #endif
106514541927SVolker Rümelin }
106614541927SVolker Rümelin 
1067f8c223f6SGerd Hoffmann static gboolean gd_text_key_down(GtkWidget *widget,
1068f8c223f6SGerd Hoffmann                                  GdkEventKey *key, void *opaque)
1069f8c223f6SGerd Hoffmann {
1070f8c223f6SGerd Hoffmann     VirtualConsole *vc = opaque;
1071f8c223f6SGerd Hoffmann     QemuConsole *con = vc->gfx.dcl.con;
1072f8c223f6SGerd Hoffmann 
107385617885SThomas Huth     if (key->keyval == GDK_KEY_Delete) {
1074da024b1eSGerd Hoffmann         kbd_put_qcode_console(con, Q_KEY_CODE_DELETE, false);
107585617885SThomas Huth     } else if (key->length) {
1076f8c223f6SGerd Hoffmann         kbd_put_string_console(con, key->string, key->length);
1077f8c223f6SGerd Hoffmann     } else {
107814541927SVolker Rümelin         int qcode = gd_map_keycode(gd_get_keycode(key));
1079da024b1eSGerd Hoffmann         kbd_put_qcode_console(con, qcode, false);
1080f8c223f6SGerd Hoffmann     }
1081f8c223f6SGerd Hoffmann     return TRUE;
1082f8c223f6SGerd Hoffmann }
1083f8c223f6SGerd Hoffmann 
1084932f2d7eSGerd Hoffmann static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
1085932f2d7eSGerd Hoffmann {
1086932f2d7eSGerd Hoffmann     VirtualConsole *vc = opaque;
108714541927SVolker Rümelin     int keycode, qcode;
1088932f2d7eSGerd Hoffmann 
1089d3953bf7SVolker Rümelin #ifdef G_OS_WIN32
109008774f66SDaniel P. Berrange     /* on windows, we ought to ignore the reserved key event? */
109108774f66SDaniel P. Berrange     if (key->hardware_keycode == 0xff)
109208774f66SDaniel P. Berrange         return false;
1093d3953bf7SVolker Rümelin 
1094d3953bf7SVolker Rümelin     if (!vc->s->kbd_owner) {
1095d3953bf7SVolker Rümelin         if (key->hardware_keycode == VK_LWIN ||
1096d3953bf7SVolker Rümelin             key->hardware_keycode == VK_RWIN) {
1097d3953bf7SVolker Rümelin             return FALSE;
1098d3953bf7SVolker Rümelin         }
1099d3953bf7SVolker Rümelin     }
110008774f66SDaniel P. Berrange #endif
110108774f66SDaniel P. Berrange 
11028026a81aSDaniel P. Berrange     if (key->keyval == GDK_KEY_Pause
11038026a81aSDaniel P. Berrange #ifdef G_OS_WIN32
11048026a81aSDaniel P. Berrange         /* for some reason GDK does not fill keyval for VK_PAUSE
11058026a81aSDaniel P. Berrange          * See https://bugzilla.gnome.org/show_bug.cgi?id=769214
11068026a81aSDaniel P. Berrange          */
11078026a81aSDaniel P. Berrange         || key->hardware_keycode == VK_PAUSE
11088026a81aSDaniel P. Berrange #endif
11098026a81aSDaniel P. Berrange         ) {
11100c0d4273SGerd Hoffmann         qkbd_state_key_event(vc->gfx.kbd, Q_KEY_CODE_PAUSE,
11115c960521SMartin Decky                              key->type == GDK_KEY_PRESS);
11125c960521SMartin Decky         return TRUE;
11135c960521SMartin Decky     }
11145c960521SMartin Decky 
111514541927SVolker Rümelin     keycode = gd_get_keycode(key);
111614541927SVolker Rümelin     qcode = gd_map_keycode(keycode);
1117932f2d7eSGerd Hoffmann 
111814541927SVolker Rümelin     trace_gd_key_event(vc->label, keycode, qcode,
1119a4ccabcfSAnthony Liguori                        (key->type == GDK_KEY_PRESS) ? "down" : "up");
1120a4ccabcfSAnthony Liguori 
11210c0d4273SGerd Hoffmann     qkbd_state_key_event(vc->gfx.kbd, qcode,
1122af98ba92SGerd Hoffmann                          key->type == GDK_KEY_PRESS);
1123a4ccabcfSAnthony Liguori 
1124a4ccabcfSAnthony Liguori     return TRUE;
1125a4ccabcfSAnthony Liguori }
1126a4ccabcfSAnthony Liguori 
11270c4b1a7dSVolker Rümelin static gboolean gd_grab_broken_event(GtkWidget *widget,
11280c4b1a7dSVolker Rümelin                                      GdkEventGrabBroken *event, void *opaque)
11290c4b1a7dSVolker Rümelin {
11300c4b1a7dSVolker Rümelin #ifdef CONFIG_WIN32
11310c4b1a7dSVolker Rümelin     /*
11320c4b1a7dSVolker Rümelin      * On Windows the Ctrl-Alt-Del key combination can't be grabbed. This
11330c4b1a7dSVolker Rümelin      * key combination leaves all three keys in a stuck condition. We use
11340c4b1a7dSVolker Rümelin      * the grab-broken-event to release all keys.
11350c4b1a7dSVolker Rümelin      */
11360c4b1a7dSVolker Rümelin     if (event->keyboard) {
11370c4b1a7dSVolker Rümelin         VirtualConsole *vc = opaque;
11380c4b1a7dSVolker Rümelin         GtkDisplayState *s = vc->s;
11390c4b1a7dSVolker Rümelin 
11400c4b1a7dSVolker Rümelin         gtk_release_modifiers(s);
11410c4b1a7dSVolker Rümelin     }
11420c4b1a7dSVolker Rümelin #endif
11430c4b1a7dSVolker Rümelin     return TRUE;
11440c4b1a7dSVolker Rümelin }
11450c4b1a7dSVolker Rümelin 
11460d0e044dSTakashi Iwai static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
11470d0e044dSTakashi Iwai {
11480d0e044dSTakashi Iwai     if (event->type == GDK_MOTION_NOTIFY) {
11490d0e044dSTakashi Iwai         return gd_motion_event(widget, &event->motion, opaque);
11500d0e044dSTakashi Iwai     }
11510d0e044dSTakashi Iwai     return FALSE;
11520d0e044dSTakashi Iwai }
11530d0e044dSTakashi Iwai 
1154a4ccabcfSAnthony Liguori /** Window Menu Actions **/
1155a4ccabcfSAnthony Liguori 
115630e8f22bSJan Kiszka static void gd_menu_pause(GtkMenuItem *item, void *opaque)
115730e8f22bSJan Kiszka {
115830e8f22bSJan Kiszka     GtkDisplayState *s = opaque;
115930e8f22bSJan Kiszka 
116030e8f22bSJan Kiszka     if (s->external_pause_update) {
116130e8f22bSJan Kiszka         return;
116230e8f22bSJan Kiszka     }
116330e8f22bSJan Kiszka     if (runstate_is_running()) {
116430e8f22bSJan Kiszka         qmp_stop(NULL);
116530e8f22bSJan Kiszka     } else {
116630e8f22bSJan Kiszka         qmp_cont(NULL);
116730e8f22bSJan Kiszka     }
116830e8f22bSJan Kiszka }
116930e8f22bSJan Kiszka 
117030e8f22bSJan Kiszka static void gd_menu_reset(GtkMenuItem *item, void *opaque)
117130e8f22bSJan Kiszka {
117230e8f22bSJan Kiszka     qmp_system_reset(NULL);
117330e8f22bSJan Kiszka }
117430e8f22bSJan Kiszka 
117530e8f22bSJan Kiszka static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
117630e8f22bSJan Kiszka {
117730e8f22bSJan Kiszka     qmp_system_powerdown(NULL);
117830e8f22bSJan Kiszka }
117930e8f22bSJan Kiszka 
1180a4ccabcfSAnthony Liguori static void gd_menu_quit(GtkMenuItem *item, void *opaque)
1181a4ccabcfSAnthony Liguori {
1182a4ccabcfSAnthony Liguori     qmp_quit(NULL);
1183a4ccabcfSAnthony Liguori }
1184a4ccabcfSAnthony Liguori 
1185a4ccabcfSAnthony Liguori static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
1186a4ccabcfSAnthony Liguori {
1187a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
1188e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_by_menu(s);
1189e72b59faSJohn Snow     GtkNotebook *nb = GTK_NOTEBOOK(s->notebook);
1190832189c9SGerd Hoffmann     gint page;
1191a4ccabcfSAnthony Liguori 
11926db253caSJan Kiszka     gtk_release_modifiers(s);
1193271a25c0SGerd Hoffmann     if (vc) {
1194e72b59faSJohn Snow         page = gtk_notebook_page_num(nb, vc->tab_item);
1195e72b59faSJohn Snow         gtk_notebook_set_current_page(nb, page);
11969d677e1cSJan Kiszka         gtk_widget_grab_focus(vc->focus);
1197d861def3SAnthony Liguori     }
1198d861def3SAnthony Liguori }
1199a4ccabcfSAnthony Liguori 
1200277836c8SCole Robinson static void gd_accel_switch_vc(void *opaque)
1201277836c8SCole Robinson {
1202277836c8SCole Robinson     VirtualConsole *vc = opaque;
12031a01716aSJan Kiszka 
1204277836c8SCole Robinson     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
1205277836c8SCole Robinson }
1206277836c8SCole Robinson 
1207a4ccabcfSAnthony Liguori static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
1208a4ccabcfSAnthony Liguori {
1209a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
1210fa7a1e52SGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1211a4ccabcfSAnthony Liguori 
1212a4ccabcfSAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
1213a4ccabcfSAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
1214a4ccabcfSAnthony Liguori     } else {
1215a4ccabcfSAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1216a4ccabcfSAnthony Liguori     }
1217fa7a1e52SGerd Hoffmann     gd_update_windowsize(vc);
1218a4ccabcfSAnthony Liguori }
1219a4ccabcfSAnthony Liguori 
1220cdeb7090SGerd Hoffmann static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
1221cdeb7090SGerd Hoffmann                                     void *opaque)
1222cdeb7090SGerd Hoffmann {
1223cdeb7090SGerd Hoffmann     VirtualConsole *vc = opaque;
1224cdeb7090SGerd Hoffmann     GtkDisplayState *s = vc->s;
1225cdeb7090SGerd Hoffmann 
1226cdeb7090SGerd Hoffmann     gtk_widget_set_sensitive(vc->menu_item, true);
1227316cb068SGerd Hoffmann     gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
1228cdeb7090SGerd Hoffmann     gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
1229cdeb7090SGerd Hoffmann                                     vc->tab_item, vc->label);
1230cdeb7090SGerd Hoffmann     gtk_widget_destroy(vc->window);
1231cdeb7090SGerd Hoffmann     vc->window = NULL;
1232cdeb7090SGerd Hoffmann     return TRUE;
1233cdeb7090SGerd Hoffmann }
1234cdeb7090SGerd Hoffmann 
12350c77a37fSGerd Hoffmann static gboolean gd_win_grab(void *opaque)
12360c77a37fSGerd Hoffmann {
12370c77a37fSGerd Hoffmann     VirtualConsole *vc = opaque;
12380c77a37fSGerd Hoffmann 
12390c77a37fSGerd Hoffmann     fprintf(stderr, "%s: %s\n", __func__, vc->label);
12400c77a37fSGerd Hoffmann     if (vc->s->ptr_owner) {
12410c77a37fSGerd Hoffmann         gd_ungrab_pointer(vc->s);
12420c77a37fSGerd Hoffmann     } else {
1243d531deefSGerd Hoffmann         gd_grab_pointer(vc, "user-request-detached-tab");
12440c77a37fSGerd Hoffmann     }
12450c77a37fSGerd Hoffmann     return TRUE;
12460c77a37fSGerd Hoffmann }
12470c77a37fSGerd Hoffmann 
1248cdeb7090SGerd Hoffmann static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
1249cdeb7090SGerd Hoffmann {
1250cdeb7090SGerd Hoffmann     GtkDisplayState *s = opaque;
1251cdeb7090SGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1252cdeb7090SGerd Hoffmann 
1253f8c223f6SGerd Hoffmann     if (vc->type == GD_VC_GFX &&
1254f8c223f6SGerd Hoffmann         qemu_console_is_graphic(vc->gfx.dcl.con)) {
1255aa0a55d4SGerd Hoffmann         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1256aa0a55d4SGerd Hoffmann                                        FALSE);
1257cdeb7090SGerd Hoffmann     }
1258cdeb7090SGerd Hoffmann     if (!vc->window) {
1259cdeb7090SGerd Hoffmann         gtk_widget_set_sensitive(vc->menu_item, false);
1260cdeb7090SGerd Hoffmann         vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1261316cb068SGerd Hoffmann         gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
1262cdeb7090SGerd Hoffmann 
1263cdeb7090SGerd Hoffmann         g_signal_connect(vc->window, "delete-event",
1264cdeb7090SGerd Hoffmann                          G_CALLBACK(gd_tab_window_close), vc);
1265cdeb7090SGerd Hoffmann         gtk_widget_show_all(vc->window);
12664eeaa3a8SGerd Hoffmann 
1267f8c223f6SGerd Hoffmann         if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
12680c77a37fSGerd Hoffmann             GtkAccelGroup *ag = gtk_accel_group_new();
12690c77a37fSGerd Hoffmann             gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
12700c77a37fSGerd Hoffmann 
1271f8c223f6SGerd Hoffmann             GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
1272f8c223f6SGerd Hoffmann                                                vc, NULL);
12730c77a37fSGerd Hoffmann             gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
1274f8c223f6SGerd Hoffmann         }
12750c77a37fSGerd Hoffmann 
127682fc1809SGerd Hoffmann         gd_update_geometry_hints(vc);
12774eeaa3a8SGerd Hoffmann         gd_update_caption(s);
1278cdeb7090SGerd Hoffmann     }
1279cdeb7090SGerd Hoffmann }
1280cdeb7090SGerd Hoffmann 
12811d187745SPeter Wu static void gd_menu_show_menubar(GtkMenuItem *item, void *opaque)
12821d187745SPeter Wu {
12831d187745SPeter Wu     GtkDisplayState *s = opaque;
12841d187745SPeter Wu     VirtualConsole *vc = gd_vc_find_current(s);
12851d187745SPeter Wu 
12861d187745SPeter Wu     if (s->full_screen) {
12871d187745SPeter Wu         return;
12881d187745SPeter Wu     }
12891d187745SPeter Wu 
12901d187745SPeter Wu     if (gtk_check_menu_item_get_active(
12911d187745SPeter Wu                 GTK_CHECK_MENU_ITEM(s->show_menubar_item))) {
12921d187745SPeter Wu         gtk_widget_show(s->menu_bar);
12931d187745SPeter Wu     } else {
12941d187745SPeter Wu         gtk_widget_hide(s->menu_bar);
12951d187745SPeter Wu     }
12961d187745SPeter Wu     gd_update_windowsize(vc);
12971d187745SPeter Wu }
12981d187745SPeter Wu 
12991d187745SPeter Wu static void gd_accel_show_menubar(void *opaque)
13001d187745SPeter Wu {
13011d187745SPeter Wu     GtkDisplayState *s = opaque;
13021d187745SPeter Wu     gtk_menu_item_activate(GTK_MENU_ITEM(s->show_menubar_item));
13031d187745SPeter Wu }
13041d187745SPeter Wu 
1305c6158483SAnthony Liguori static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
1306c6158483SAnthony Liguori {
1307c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1308e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1309c6158483SAnthony Liguori 
131010409282SStefan Weil     if (!s->full_screen) {
1311c6158483SAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1312b0f31820SCole Robinson         gtk_widget_hide(s->menu_bar);
1313e3500d1fSGerd Hoffmann         if (vc->type == GD_VC_GFX) {
1314e3500d1fSGerd Hoffmann             gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
1315c6158483SAnthony Liguori         }
1316e3500d1fSGerd Hoffmann         gtk_window_fullscreen(GTK_WINDOW(s->window));
1317c6158483SAnthony Liguori         s->full_screen = TRUE;
1318c6158483SAnthony Liguori     } else {
1319c6158483SAnthony Liguori         gtk_window_unfullscreen(GTK_WINDOW(s->window));
1320c6158483SAnthony Liguori         gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
13211d187745SPeter Wu         if (gtk_check_menu_item_get_active(
13221d187745SPeter Wu                     GTK_CHECK_MENU_ITEM(s->show_menubar_item))) {
1323b0f31820SCole Robinson             gtk_widget_show(s->menu_bar);
13241d187745SPeter Wu         }
1325c6158483SAnthony Liguori         s->full_screen = FALSE;
1326e3500d1fSGerd Hoffmann         if (vc->type == GD_VC_GFX) {
1327e3500d1fSGerd Hoffmann             vc->gfx.scale_x = 1.0;
1328e3500d1fSGerd Hoffmann             vc->gfx.scale_y = 1.0;
132982fc1809SGerd Hoffmann             gd_update_windowsize(vc);
1330e3500d1fSGerd Hoffmann         }
1331c6158483SAnthony Liguori     }
1332c6158483SAnthony Liguori 
1333e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
1334c6158483SAnthony Liguori }
1335c6158483SAnthony Liguori 
133695414914SCole Robinson static void gd_accel_full_screen(void *opaque)
133795414914SCole Robinson {
133895414914SCole Robinson     GtkDisplayState *s = opaque;
133995414914SCole Robinson     gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
134095414914SCole Robinson }
134195414914SCole Robinson 
1342c6158483SAnthony Liguori static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
1343c6158483SAnthony Liguori {
1344c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1345e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1346c6158483SAnthony Liguori 
1347c6158483SAnthony Liguori     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1348c6158483SAnthony Liguori                                    FALSE);
1349c6158483SAnthony Liguori 
135082fc1809SGerd Hoffmann     vc->gfx.scale_x += VC_SCALE_STEP;
135182fc1809SGerd Hoffmann     vc->gfx.scale_y += VC_SCALE_STEP;
1352c6158483SAnthony Liguori 
1353e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1354c6158483SAnthony Liguori }
1355c6158483SAnthony Liguori 
135666f6b82bSZiyue Yang static void gd_accel_zoom_in(void *opaque)
135766f6b82bSZiyue Yang {
135866f6b82bSZiyue Yang     GtkDisplayState *s = opaque;
135966f6b82bSZiyue Yang     gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_in_item));
136066f6b82bSZiyue Yang }
136166f6b82bSZiyue Yang 
1362c6158483SAnthony Liguori static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
1363c6158483SAnthony Liguori {
1364c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1365e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1366c6158483SAnthony Liguori 
1367c6158483SAnthony Liguori     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1368c6158483SAnthony Liguori                                    FALSE);
1369c6158483SAnthony Liguori 
137082fc1809SGerd Hoffmann     vc->gfx.scale_x -= VC_SCALE_STEP;
137182fc1809SGerd Hoffmann     vc->gfx.scale_y -= VC_SCALE_STEP;
1372c6158483SAnthony Liguori 
137382fc1809SGerd Hoffmann     vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
137482fc1809SGerd Hoffmann     vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
1375c6158483SAnthony Liguori 
1376e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1377c6158483SAnthony Liguori }
1378c6158483SAnthony Liguori 
1379c6158483SAnthony Liguori static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
1380c6158483SAnthony Liguori {
1381c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1382e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1383c6158483SAnthony Liguori 
1384e3500d1fSGerd Hoffmann     vc->gfx.scale_x = 1.0;
1385e3500d1fSGerd Hoffmann     vc->gfx.scale_y = 1.0;
1386c6158483SAnthony Liguori 
1387e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1388c6158483SAnthony Liguori }
1389c6158483SAnthony Liguori 
1390c6158483SAnthony Liguori static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
1391c6158483SAnthony Liguori {
1392c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1393e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1394c6158483SAnthony Liguori 
1395c6158483SAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
1396c6158483SAnthony Liguori         s->free_scale = TRUE;
1397c6158483SAnthony Liguori     } else {
1398c6158483SAnthony Liguori         s->free_scale = FALSE;
1399e3500d1fSGerd Hoffmann         vc->gfx.scale_x = 1.0;
1400e3500d1fSGerd Hoffmann         vc->gfx.scale_y = 1.0;
1401c6158483SAnthony Liguori     }
1402c6158483SAnthony Liguori 
140382fc1809SGerd Hoffmann     gd_update_windowsize(vc);
1404e3500d1fSGerd Hoffmann     gd_update_full_redraw(vc);
1405c6158483SAnthony Liguori }
1406c6158483SAnthony Liguori 
1407a69fc693SGerd Hoffmann static void gd_grab_update(VirtualConsole *vc, bool kbd, bool ptr)
1408a69fc693SGerd Hoffmann {
1409a69fc693SGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1410a69fc693SGerd Hoffmann     GdkSeat *seat = gdk_display_get_default_seat(display);
1411a69fc693SGerd Hoffmann     GdkWindow *window = gtk_widget_get_window(vc->gfx.drawing_area);
1412a69fc693SGerd Hoffmann     GdkSeatCapabilities caps = 0;
1413a69fc693SGerd Hoffmann     GdkCursor *cursor = NULL;
1414a69fc693SGerd Hoffmann 
1415a69fc693SGerd Hoffmann     if (kbd) {
1416a69fc693SGerd Hoffmann         caps |= GDK_SEAT_CAPABILITY_KEYBOARD;
1417a69fc693SGerd Hoffmann     }
1418a69fc693SGerd Hoffmann     if (ptr) {
1419a69fc693SGerd Hoffmann         caps |= GDK_SEAT_CAPABILITY_ALL_POINTING;
1420a69fc693SGerd Hoffmann         cursor = vc->s->null_cursor;
1421a69fc693SGerd Hoffmann     }
1422a69fc693SGerd Hoffmann 
1423a69fc693SGerd Hoffmann     if (caps) {
1424a69fc693SGerd Hoffmann         gdk_seat_grab(seat, window, caps, false, cursor,
1425a69fc693SGerd Hoffmann                       NULL, NULL, NULL);
1426a69fc693SGerd Hoffmann     } else {
1427a69fc693SGerd Hoffmann         gdk_seat_ungrab(seat);
1428a69fc693SGerd Hoffmann     }
1429a69fc693SGerd Hoffmann }
1430f50def91SGerd Hoffmann 
1431d531deefSGerd Hoffmann static void gd_grab_keyboard(VirtualConsole *vc, const char *reason)
14325104a1f6SAnthony Liguori {
1433aa4f4058SGerd Hoffmann     if (vc->s->kbd_owner) {
1434aa4f4058SGerd Hoffmann         if (vc->s->kbd_owner == vc) {
1435aa4f4058SGerd Hoffmann             return;
1436aa4f4058SGerd Hoffmann         } else {
1437aa4f4058SGerd Hoffmann             gd_ungrab_keyboard(vc->s);
1438aa4f4058SGerd Hoffmann         }
1439aa4f4058SGerd Hoffmann     }
1440aa4f4058SGerd Hoffmann 
1441bd593d2cSVolker Rümelin     win32_kbd_set_grab(true);
1442a69fc693SGerd Hoffmann     gd_grab_update(vc, true, vc->s->ptr_owner == vc);
14434c638e2eSGerd Hoffmann     vc->s->kbd_owner = vc;
1444695cc59dSGerd Hoffmann     gd_update_caption(vc->s);
1445d531deefSGerd Hoffmann     trace_gd_grab(vc->label, "kbd", reason);
14465104a1f6SAnthony Liguori }
14475104a1f6SAnthony Liguori 
14484c638e2eSGerd Hoffmann static void gd_ungrab_keyboard(GtkDisplayState *s)
14495104a1f6SAnthony Liguori {
14504c638e2eSGerd Hoffmann     VirtualConsole *vc = s->kbd_owner;
14514c638e2eSGerd Hoffmann 
14524c638e2eSGerd Hoffmann     if (vc == NULL) {
14534c638e2eSGerd Hoffmann         return;
14544c638e2eSGerd Hoffmann     }
14554c638e2eSGerd Hoffmann     s->kbd_owner = NULL;
14564c638e2eSGerd Hoffmann 
1457bd593d2cSVolker Rümelin     win32_kbd_set_grab(false);
1458a69fc693SGerd Hoffmann     gd_grab_update(vc, false, vc->s->ptr_owner == vc);
1459695cc59dSGerd Hoffmann     gd_update_caption(s);
1460d531deefSGerd Hoffmann     trace_gd_ungrab(vc->label, "kbd");
14615104a1f6SAnthony Liguori }
14625104a1f6SAnthony Liguori 
1463d531deefSGerd Hoffmann static void gd_grab_pointer(VirtualConsole *vc, const char *reason)
14645104a1f6SAnthony Liguori {
1465e3500d1fSGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1466aa4f4058SGerd Hoffmann 
1467aa4f4058SGerd Hoffmann     if (vc->s->ptr_owner) {
1468aa4f4058SGerd Hoffmann         if (vc->s->ptr_owner == vc) {
1469aa4f4058SGerd Hoffmann             return;
1470aa4f4058SGerd Hoffmann         } else {
1471aa4f4058SGerd Hoffmann             gd_ungrab_pointer(vc->s);
1472aa4f4058SGerd Hoffmann         }
1473aa4f4058SGerd Hoffmann     }
1474aa4f4058SGerd Hoffmann 
1475a69fc693SGerd Hoffmann     gd_grab_update(vc, vc->s->kbd_owner == vc, true);
1476a69fc693SGerd Hoffmann     gdk_device_get_position(gd_get_pointer(display),
1477a69fc693SGerd Hoffmann                             NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
14784c638e2eSGerd Hoffmann     vc->s->ptr_owner = vc;
1479695cc59dSGerd Hoffmann     gd_update_caption(vc->s);
1480d531deefSGerd Hoffmann     trace_gd_grab(vc->label, "ptr", reason);
14812a05485dSDaniel P. Berrange }
14822a05485dSDaniel P. Berrange 
14834c638e2eSGerd Hoffmann static void gd_ungrab_pointer(GtkDisplayState *s)
14842a05485dSDaniel P. Berrange {
14854c638e2eSGerd Hoffmann     VirtualConsole *vc = s->ptr_owner;
148641cc5239SGerd Hoffmann     GdkDisplay *display;
14874c638e2eSGerd Hoffmann 
14884c638e2eSGerd Hoffmann     if (vc == NULL) {
14894c638e2eSGerd Hoffmann         return;
14904c638e2eSGerd Hoffmann     }
14914c638e2eSGerd Hoffmann     s->ptr_owner = NULL;
14924c638e2eSGerd Hoffmann 
149341cc5239SGerd Hoffmann     display = gtk_widget_get_display(vc->gfx.drawing_area);
1494a69fc693SGerd Hoffmann     gd_grab_update(vc, vc->s->kbd_owner == vc, false);
1495a69fc693SGerd Hoffmann     gdk_device_warp(gd_get_pointer(display),
1496a69fc693SGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
1497a69fc693SGerd Hoffmann                     vc->s->grab_x_root, vc->s->grab_y_root);
1498695cc59dSGerd Hoffmann     gd_update_caption(s);
1499d531deefSGerd Hoffmann     trace_gd_ungrab(vc->label, "ptr");
15002a05485dSDaniel P. Berrange }
15012a05485dSDaniel P. Berrange 
15022a05485dSDaniel P. Berrange static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
15032a05485dSDaniel P. Berrange {
15042a05485dSDaniel P. Berrange     GtkDisplayState *s = opaque;
1505e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
15062a05485dSDaniel P. Berrange 
15072a05485dSDaniel P. Berrange     if (gd_is_grab_active(s)) {
1508d531deefSGerd Hoffmann         gd_grab_keyboard(vc, "user-request-main-window");
1509d531deefSGerd Hoffmann         gd_grab_pointer(vc, "user-request-main-window");
15105104a1f6SAnthony Liguori     } else {
15114c638e2eSGerd Hoffmann         gd_ungrab_keyboard(s);
15124c638e2eSGerd Hoffmann         gd_ungrab_pointer(s);
15135104a1f6SAnthony Liguori     }
15145104a1f6SAnthony Liguori 
1515e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
15165104a1f6SAnthony Liguori }
15175104a1f6SAnthony Liguori 
1518a4ccabcfSAnthony Liguori static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
1519a4ccabcfSAnthony Liguori                            gpointer data)
1520a4ccabcfSAnthony Liguori {
1521a4ccabcfSAnthony Liguori     GtkDisplayState *s = data;
1522e3500d1fSGerd Hoffmann     VirtualConsole *vc;
15235104a1f6SAnthony Liguori     gboolean on_vga;
1524a4ccabcfSAnthony Liguori 
1525a4ccabcfSAnthony Liguori     if (!gtk_widget_get_realized(s->notebook)) {
1526a4ccabcfSAnthony Liguori         return;
1527a4ccabcfSAnthony Liguori     }
1528a4ccabcfSAnthony Liguori 
1529e3500d1fSGerd Hoffmann     vc = gd_vc_find_by_page(s, arg2);
1530e3500d1fSGerd Hoffmann     if (!vc) {
1531e3500d1fSGerd Hoffmann         return;
1532e3500d1fSGerd Hoffmann     }
1533e3500d1fSGerd Hoffmann     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
1534e3500d1fSGerd Hoffmann                                    TRUE);
1535f8c223f6SGerd Hoffmann     on_vga = (vc->type == GD_VC_GFX &&
1536f8c223f6SGerd Hoffmann               qemu_console_is_graphic(vc->gfx.dcl.con));
15375104a1f6SAnthony Liguori     if (!on_vga) {
15385104a1f6SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
15395104a1f6SAnthony Liguori                                        FALSE);
1540c6158483SAnthony Liguori     } else if (s->full_screen) {
1541c6158483SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1542c6158483SAnthony Liguori                                        TRUE);
15435104a1f6SAnthony Liguori     }
15445104a1f6SAnthony Liguori     gtk_widget_set_sensitive(s->grab_item, on_vga);
1545a0815632SStefan Hajnoczi #ifdef CONFIG_VTE
1546a0815632SStefan Hajnoczi     gtk_widget_set_sensitive(s->copy_item, vc->type == GD_VC_VTE);
1547a0815632SStefan Hajnoczi #endif
15485104a1f6SAnthony Liguori 
154982fc1809SGerd Hoffmann     gd_update_windowsize(vc);
1550e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
1551a4ccabcfSAnthony Liguori }
1552a4ccabcfSAnthony Liguori 
1553e3500d1fSGerd Hoffmann static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
1554e3500d1fSGerd Hoffmann                                gpointer opaque)
15555104a1f6SAnthony Liguori {
1556e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1557e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
15585104a1f6SAnthony Liguori 
15592884cf5bSGerd Hoffmann     if (gd_grab_on_hover(s)) {
1560d531deefSGerd Hoffmann         gd_grab_keyboard(vc, "grab-on-hover");
15615104a1f6SAnthony Liguori     }
15625104a1f6SAnthony Liguori     return TRUE;
15635104a1f6SAnthony Liguori }
15645104a1f6SAnthony Liguori 
1565e3500d1fSGerd Hoffmann static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
1566e3500d1fSGerd Hoffmann                                gpointer opaque)
15675104a1f6SAnthony Liguori {
1568e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1569e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
15705104a1f6SAnthony Liguori 
15712884cf5bSGerd Hoffmann     if (gd_grab_on_hover(s)) {
15724c638e2eSGerd Hoffmann         gd_ungrab_keyboard(s);
15735104a1f6SAnthony Liguori     }
15745104a1f6SAnthony Liguori     return TRUE;
15755104a1f6SAnthony Liguori }
15765104a1f6SAnthony Liguori 
1577bd593d2cSVolker Rümelin static gboolean gd_focus_in_event(GtkWidget *widget,
1578bd593d2cSVolker Rümelin                                   GdkEventFocus *event, gpointer opaque)
1579bd593d2cSVolker Rümelin {
1580bd593d2cSVolker Rümelin     VirtualConsole *vc = opaque;
1581bd593d2cSVolker Rümelin 
1582bd593d2cSVolker Rümelin     win32_kbd_set_window(gd_win32_get_hwnd(vc));
1583bd593d2cSVolker Rümelin     return TRUE;
1584bd593d2cSVolker Rümelin }
1585bd593d2cSVolker Rümelin 
15866db253caSJan Kiszka static gboolean gd_focus_out_event(GtkWidget *widget,
1587bd593d2cSVolker Rümelin                                    GdkEventFocus *event, gpointer opaque)
15886db253caSJan Kiszka {
1589e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1590e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
15916db253caSJan Kiszka 
1592bd593d2cSVolker Rümelin     win32_kbd_set_window(NULL);
15936db253caSJan Kiszka     gtk_release_modifiers(s);
15946db253caSJan Kiszka     return TRUE;
15956db253caSJan Kiszka }
15966db253caSJan Kiszka 
15971301e515SGerd Hoffmann static gboolean gd_configure(GtkWidget *widget,
15981301e515SGerd Hoffmann                              GdkEventConfigure *cfg, gpointer opaque)
15991301e515SGerd Hoffmann {
16001301e515SGerd Hoffmann     VirtualConsole *vc = opaque;
16011301e515SGerd Hoffmann 
1602925a0400SGerd Hoffmann     gd_set_ui_info(vc, cfg->width, cfg->height);
16031301e515SGerd Hoffmann     return FALSE;
16041301e515SGerd Hoffmann }
16051301e515SGerd Hoffmann 
1606d861def3SAnthony Liguori /** Virtual Console Callbacks **/
1607d861def3SAnthony Liguori 
1608ed1132e4SGerd Hoffmann static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
1609cdeb7090SGerd Hoffmann                                int idx, GSList *group, GtkWidget *view_menu)
1610ed1132e4SGerd Hoffmann {
1611cdeb7090SGerd Hoffmann     vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
1612277836c8SCole Robinson     gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx,
1613277836c8SCole Robinson             HOTKEY_MODIFIERS, 0,
1614277836c8SCole Robinson             g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL));
1615277836c8SCole Robinson     gtk_accel_label_set_accel(
1616277836c8SCole Robinson             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))),
1617277836c8SCole Robinson             GDK_KEY_1 + idx, HOTKEY_MODIFIERS);
1618ed1132e4SGerd Hoffmann 
1619ed1132e4SGerd Hoffmann     g_signal_connect(vc->menu_item, "activate",
1620ed1132e4SGerd Hoffmann                      G_CALLBACK(gd_menu_switch_vc), s);
1621ed1132e4SGerd Hoffmann     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
1622ed1132e4SGerd Hoffmann 
1623b3ac2b94SSimran Singhal     return gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
1624ed1132e4SGerd Hoffmann }
1625ed1132e4SGerd Hoffmann 
1626ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
162744b31e0bSMichael S. Tsirkin static void gd_menu_copy(GtkMenuItem *item, void *opaque)
162844b31e0bSMichael S. Tsirkin {
162944b31e0bSMichael S. Tsirkin     GtkDisplayState *s = opaque;
163044b31e0bSMichael S. Tsirkin     VirtualConsole *vc = gd_vc_find_current(s);
163144b31e0bSMichael S. Tsirkin 
163282a4f1a9SAnthony PERARD #if VTE_CHECK_VERSION(0, 50, 0)
163382a4f1a9SAnthony PERARD     vte_terminal_copy_clipboard_format(VTE_TERMINAL(vc->vte.terminal),
163482a4f1a9SAnthony PERARD                                        VTE_FORMAT_TEXT);
163582a4f1a9SAnthony PERARD #else
163644b31e0bSMichael S. Tsirkin     vte_terminal_copy_clipboard(VTE_TERMINAL(vc->vte.terminal));
163782a4f1a9SAnthony PERARD #endif
163844b31e0bSMichael S. Tsirkin }
163944b31e0bSMichael S. Tsirkin 
16400fb20d1cSCole Robinson static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
16410fb20d1cSCole Robinson {
16420fb20d1cSCole Robinson     VirtualConsole *vc = opaque;
16430fb20d1cSCole Robinson 
16440fb20d1cSCole Robinson     if (gtk_adjustment_get_upper(adjustment) >
16450fb20d1cSCole Robinson         gtk_adjustment_get_page_size(adjustment)) {
1646271a25c0SGerd Hoffmann         gtk_widget_show(vc->vte.scrollbar);
16470fb20d1cSCole Robinson     } else {
1648271a25c0SGerd Hoffmann         gtk_widget_hide(vc->vte.scrollbar);
16490fb20d1cSCole Robinson     }
16500fb20d1cSCole Robinson }
16510fb20d1cSCole Robinson 
1652584af1f1SVolker Rümelin static void gd_vc_send_chars(VirtualConsole *vc)
1653584af1f1SVolker Rümelin {
1654584af1f1SVolker Rümelin     uint32_t len, avail;
1655584af1f1SVolker Rümelin 
1656584af1f1SVolker Rümelin     len = qemu_chr_be_can_write(vc->vte.chr);
1657584af1f1SVolker Rümelin     avail = fifo8_num_used(&vc->vte.out_fifo);
16587bce330aSVolker Rümelin     while (len > 0 && avail > 0) {
1659584af1f1SVolker Rümelin         const uint8_t *buf;
1660584af1f1SVolker Rümelin         uint32_t size;
1661584af1f1SVolker Rümelin 
16627bce330aSVolker Rümelin         buf = fifo8_pop_buf(&vc->vte.out_fifo, MIN(len, avail), &size);
1663584af1f1SVolker Rümelin         qemu_chr_be_write(vc->vte.chr, (uint8_t *)buf, size);
16647bce330aSVolker Rümelin         len = qemu_chr_be_can_write(vc->vte.chr);
16657bce330aSVolker Rümelin         avail -= size;
1666584af1f1SVolker Rümelin     }
1667584af1f1SVolker Rümelin }
1668584af1f1SVolker Rümelin 
16690ec7b3e7SMarc-André Lureau static int gd_vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
1670d861def3SAnthony Liguori {
1671777357d7SMarc-André Lureau     VCChardev *vcd = VC_CHARDEV(chr);
167241ac54b2SMarc-André Lureau     VirtualConsole *vc = vcd->console;
1673d861def3SAnthony Liguori 
1674271a25c0SGerd Hoffmann     vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
1675d4370741SCole Robinson     return len;
1676d861def3SAnthony Liguori }
1677d861def3SAnthony Liguori 
1678584af1f1SVolker Rümelin static void gd_vc_chr_accept_input(Chardev *chr)
1679584af1f1SVolker Rümelin {
1680584af1f1SVolker Rümelin     VCChardev *vcd = VC_CHARDEV(chr);
1681584af1f1SVolker Rümelin     VirtualConsole *vc = vcd->console;
1682584af1f1SVolker Rümelin 
1683584af1f1SVolker Rümelin     gd_vc_send_chars(vc);
1684584af1f1SVolker Rümelin }
1685584af1f1SVolker Rümelin 
16860ec7b3e7SMarc-André Lureau static void gd_vc_chr_set_echo(Chardev *chr, bool echo)
1687fba958c6SPaolo Bonzini {
1688777357d7SMarc-André Lureau     VCChardev *vcd = VC_CHARDEV(chr);
168941ac54b2SMarc-André Lureau     VirtualConsole *vc = vcd->console;
1690fba958c6SPaolo Bonzini 
169141ac54b2SMarc-André Lureau     if (vc) {
1692fba958c6SPaolo Bonzini         vc->vte.echo = echo;
169341ac54b2SMarc-André Lureau     } else {
169441ac54b2SMarc-André Lureau         vcd->echo = echo;
169541ac54b2SMarc-André Lureau     }
1696fba958c6SPaolo Bonzini }
1697fba958c6SPaolo Bonzini 
1698d861def3SAnthony Liguori static int nb_vcs;
16990ec7b3e7SMarc-André Lureau static Chardev *vcs[MAX_VCS];
1700777357d7SMarc-André Lureau static void gd_vc_open(Chardev *chr,
1701777357d7SMarc-André Lureau                        ChardevBackend *backend,
1702777357d7SMarc-André Lureau                        bool *be_opened,
17036f974c84SMarc-André Lureau                        Error **errp)
1704d861def3SAnthony Liguori {
1705c952b715SMarc-André Lureau     if (nb_vcs == MAX_VCS) {
1706c952b715SMarc-André Lureau         error_setg(errp, "Maximum number of consoles reached");
1707777357d7SMarc-André Lureau         return;
1708919e11f3SDaniel P. Berrange     }
1709919e11f3SDaniel P. Berrange 
1710d861def3SAnthony Liguori     vcs[nb_vcs++] = chr;
1711d861def3SAnthony Liguori 
17126f974c84SMarc-André Lureau     /* console/chardev init sometimes completes elsewhere in a 2nd
17136f974c84SMarc-André Lureau      * stage, so defer OPENED events until they are fully initialized
17146f974c84SMarc-André Lureau      */
17156f974c84SMarc-André Lureau     *be_opened = false;
1716d861def3SAnthony Liguori }
1717d861def3SAnthony Liguori 
1718777357d7SMarc-André Lureau static void char_gd_vc_class_init(ObjectClass *oc, void *data)
1719777357d7SMarc-André Lureau {
1720777357d7SMarc-André Lureau     ChardevClass *cc = CHARDEV_CLASS(oc);
1721777357d7SMarc-André Lureau 
172288cace9fSMarc-André Lureau     cc->parse = qemu_chr_parse_vc;
1723777357d7SMarc-André Lureau     cc->open = gd_vc_open;
1724777357d7SMarc-André Lureau     cc->chr_write = gd_vc_chr_write;
1725584af1f1SVolker Rümelin     cc->chr_accept_input = gd_vc_chr_accept_input;
1726777357d7SMarc-André Lureau     cc->chr_set_echo = gd_vc_chr_set_echo;
1727777357d7SMarc-André Lureau }
1728777357d7SMarc-André Lureau 
1729777357d7SMarc-André Lureau static const TypeInfo char_gd_vc_type_info = {
1730777357d7SMarc-André Lureau     .name = TYPE_CHARDEV_VC,
1731777357d7SMarc-André Lureau     .parent = TYPE_CHARDEV,
17326f974c84SMarc-André Lureau     .instance_size = sizeof(VCChardev),
1733777357d7SMarc-André Lureau     .class_init = char_gd_vc_class_init,
1734777357d7SMarc-André Lureau };
1735777357d7SMarc-André Lureau 
1736d4370741SCole Robinson static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
1737d4370741SCole Robinson                          gpointer user_data)
1738d861def3SAnthony Liguori {
1739d4370741SCole Robinson     VirtualConsole *vc = user_data;
1740584af1f1SVolker Rümelin     uint32_t free;
1741d861def3SAnthony Liguori 
1742fba958c6SPaolo Bonzini     if (vc->vte.echo) {
1743fba958c6SPaolo Bonzini         VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
1744fba958c6SPaolo Bonzini         int i;
1745fba958c6SPaolo Bonzini         for (i = 0; i < size; i++) {
1746fba958c6SPaolo Bonzini             uint8_t c = text[i];
1747fba958c6SPaolo Bonzini             if (c >= 128 || isprint(c)) {
1748fba958c6SPaolo Bonzini                 /* 8-bit characters are considered printable.  */
1749fba958c6SPaolo Bonzini                 vte_terminal_feed(term, &text[i], 1);
1750fba958c6SPaolo Bonzini             } else if (c == '\r' || c == '\n') {
1751fba958c6SPaolo Bonzini                 vte_terminal_feed(term, "\r\n", 2);
1752fba958c6SPaolo Bonzini             } else {
1753fba958c6SPaolo Bonzini                 char ctrl[2] = { '^', 0};
1754fba958c6SPaolo Bonzini                 ctrl[1] = text[i] ^ 64;
1755fba958c6SPaolo Bonzini                 vte_terminal_feed(term, ctrl, 2);
1756fba958c6SPaolo Bonzini             }
1757fba958c6SPaolo Bonzini         }
1758fba958c6SPaolo Bonzini     }
1759fba958c6SPaolo Bonzini 
1760584af1f1SVolker Rümelin     free = fifo8_num_free(&vc->vte.out_fifo);
1761584af1f1SVolker Rümelin     fifo8_push_all(&vc->vte.out_fifo, (uint8_t *)text, MIN(free, size));
1762584af1f1SVolker Rümelin     gd_vc_send_chars(vc);
17638eb13bbbSZack Marvel 
1764d861def3SAnthony Liguori     return TRUE;
1765d861def3SAnthony Liguori }
1766d861def3SAnthony Liguori 
1767ed1132e4SGerd Hoffmann static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
17680ec7b3e7SMarc-André Lureau                               Chardev *chr, int idx,
1769e3500d1fSGerd Hoffmann                               GSList *group, GtkWidget *view_menu)
1770d861def3SAnthony Liguori {
1771d861def3SAnthony Liguori     char buffer[32];
17720fb20d1cSCole Robinson     GtkWidget *box;
17730fb20d1cSCole Robinson     GtkWidget *scrollbar;
17740fb20d1cSCole Robinson     GtkAdjustment *vadjustment;
1775777357d7SMarc-André Lureau     VCChardev *vcd = VC_CHARDEV(chr);
1776d861def3SAnthony Liguori 
1777e3500d1fSGerd Hoffmann     vc->s = s;
177841ac54b2SMarc-André Lureau     vc->vte.echo = vcd->echo;
1779ed1132e4SGerd Hoffmann     vc->vte.chr = chr;
1780584af1f1SVolker Rümelin     fifo8_create(&vc->vte.out_fifo, 4096);
178141ac54b2SMarc-André Lureau     vcd->console = vc;
1782d861def3SAnthony Liguori 
1783ed1132e4SGerd Hoffmann     snprintf(buffer, sizeof(buffer), "vc%d", idx);
1784cdeb7090SGerd Hoffmann     vc->label = g_strdup_printf("%s", vc->vte.chr->label
1785cdeb7090SGerd Hoffmann                                 ? vc->vte.chr->label : buffer);
1786cdeb7090SGerd Hoffmann     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1787d861def3SAnthony Liguori 
1788271a25c0SGerd Hoffmann     vc->vte.terminal = vte_terminal_new();
1789271a25c0SGerd Hoffmann     g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
1790d861def3SAnthony Liguori 
1791fba958c6SPaolo Bonzini     /* The documentation says that the default is UTF-8, but actually it is
17926415994fSKevin Wolf      * 7-bit ASCII at least in VTE 0.38. The function is deprecated since
17936415994fSKevin Wolf      * VTE 0.54 (only UTF-8 is supported now). */
17946415994fSKevin Wolf #if !VTE_CHECK_VERSION(0, 54, 0)
17954d594233SOlaf Hering #if VTE_CHECK_VERSION(0, 38, 0)
1796fba958c6SPaolo Bonzini     vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8", NULL);
1797fba958c6SPaolo Bonzini #else
1798fba958c6SPaolo Bonzini     vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8");
1799fba958c6SPaolo Bonzini #endif
18006415994fSKevin Wolf #endif
1801fba958c6SPaolo Bonzini 
1802271a25c0SGerd Hoffmann     vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
180382fc1809SGerd Hoffmann     vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
180482fc1809SGerd Hoffmann                           VC_TERM_X_MIN, VC_TERM_Y_MIN);
1805d861def3SAnthony Liguori 
180689d85cdeSDaniel P. Berrangé #if VTE_CHECK_VERSION(0, 28, 0)
1807271a25c0SGerd Hoffmann     vadjustment = gtk_scrollable_get_vadjustment
1808271a25c0SGerd Hoffmann         (GTK_SCROLLABLE(vc->vte.terminal));
18090fb20d1cSCole Robinson #else
1810271a25c0SGerd Hoffmann     vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
18110fb20d1cSCole Robinson #endif
1812d861def3SAnthony Liguori 
18130fb20d1cSCole Robinson     box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
18140fb20d1cSCole Robinson     scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
18150fb20d1cSCole Robinson 
181663ad9325SJan Kiszka     gtk_box_pack_end(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);
181763ad9325SJan Kiszka     gtk_box_pack_end(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
18180fb20d1cSCole Robinson 
1819271a25c0SGerd Hoffmann     vc->vte.box = box;
1820271a25c0SGerd Hoffmann     vc->vte.scrollbar = scrollbar;
18210fb20d1cSCole Robinson 
18220fb20d1cSCole Robinson     g_signal_connect(vadjustment, "changed",
18230fb20d1cSCole Robinson                      G_CALLBACK(gd_vc_adjustment_changed), vc);
18240fb20d1cSCole Robinson 
1825e3500d1fSGerd Hoffmann     vc->type = GD_VC_VTE;
1826271a25c0SGerd Hoffmann     vc->tab_item = box;
18279d677e1cSJan Kiszka     vc->focus = vc->vte.terminal;
1828271a25c0SGerd Hoffmann     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
1829cdeb7090SGerd Hoffmann                              gtk_label_new(vc->label));
1830d861def3SAnthony Liguori 
183163618135SMarc-André Lureau     qemu_chr_be_event(vc->vte.chr, CHR_EVENT_OPENED);
1832d861def3SAnthony Liguori 
1833d861def3SAnthony Liguori     return group;
1834a4ccabcfSAnthony Liguori }
1835a4ccabcfSAnthony Liguori 
1836ed1132e4SGerd Hoffmann static void gd_vcs_init(GtkDisplayState *s, GSList *group,
1837ee5f31e4SGerd Hoffmann                         GtkWidget *view_menu)
1838ee5f31e4SGerd Hoffmann {
1839ee5f31e4SGerd Hoffmann     int i;
1840ee5f31e4SGerd Hoffmann 
1841ee5f31e4SGerd Hoffmann     for (i = 0; i < nb_vcs; i++) {
1842ed1132e4SGerd Hoffmann         VirtualConsole *vc = &s->vc[s->nb_vcs];
1843ed1132e4SGerd Hoffmann         group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu);
1844ee5f31e4SGerd Hoffmann         s->nb_vcs++;
1845ee5f31e4SGerd Hoffmann     }
1846ee5f31e4SGerd Hoffmann }
1847ee5f31e4SGerd Hoffmann #endif /* CONFIG_VTE */
1848ee5f31e4SGerd Hoffmann 
1849a4ccabcfSAnthony Liguori /** Window Creation **/
1850a4ccabcfSAnthony Liguori 
1851e3500d1fSGerd Hoffmann static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
1852e3500d1fSGerd Hoffmann {
1853e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "draw",
1854e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_draw_event), vc);
18555cb69566SPaolo Bonzini #if defined(CONFIG_OPENGL)
185611c82b58SGerd Hoffmann     if (gtk_use_gl_area) {
1857925a0400SGerd Hoffmann         /* wire up GtkGlArea events */
1858925a0400SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "render",
1859925a0400SGerd Hoffmann                          G_CALLBACK(gd_render_event), vc);
1860925a0400SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "resize",
1861925a0400SGerd Hoffmann                          G_CALLBACK(gd_resize_event), vc);
1862925a0400SGerd Hoffmann     }
1863925a0400SGerd Hoffmann #endif
1864f8c223f6SGerd Hoffmann     if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
1865e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "event",
1866e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_event), vc);
1867e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "button-press-event",
1868e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_button_event), vc);
1869e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "button-release-event",
1870e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_button_event), vc);
1871e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "scroll-event",
1872e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_scroll_event), vc);
1873e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1874e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_key_event), vc);
1875e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "key-release-event",
1876e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_key_event), vc);
1877e3500d1fSGerd Hoffmann 
1878e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "enter-notify-event",
1879e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_enter_event), vc);
1880e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "leave-notify-event",
1881e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_leave_event), vc);
1882bd593d2cSVolker Rümelin         g_signal_connect(vc->gfx.drawing_area, "focus-in-event",
1883bd593d2cSVolker Rümelin                          G_CALLBACK(gd_focus_in_event), vc);
1884e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
1885e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_focus_out_event), vc);
18861301e515SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "configure-event",
18871301e515SGerd Hoffmann                          G_CALLBACK(gd_configure), vc);
18880c4b1a7dSVolker Rümelin         g_signal_connect(vc->gfx.drawing_area, "grab-broken-event",
18890c4b1a7dSVolker Rümelin                          G_CALLBACK(gd_grab_broken_event), vc);
1890f8c223f6SGerd Hoffmann     } else {
1891f8c223f6SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1892f8c223f6SGerd Hoffmann                          G_CALLBACK(gd_text_key_down), vc);
1893f8c223f6SGerd Hoffmann     }
1894e3500d1fSGerd Hoffmann }
1895e3500d1fSGerd Hoffmann 
1896a4ccabcfSAnthony Liguori static void gd_connect_signals(GtkDisplayState *s)
1897a4ccabcfSAnthony Liguori {
1898a4ccabcfSAnthony Liguori     g_signal_connect(s->show_tabs_item, "activate",
1899a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_show_tabs), s);
1900cdeb7090SGerd Hoffmann     g_signal_connect(s->untabify_item, "activate",
1901cdeb7090SGerd Hoffmann                      G_CALLBACK(gd_menu_untabify), s);
19021d187745SPeter Wu     g_signal_connect(s->show_menubar_item, "activate",
19031d187745SPeter Wu                      G_CALLBACK(gd_menu_show_menubar), s);
1904a4ccabcfSAnthony Liguori 
1905a4ccabcfSAnthony Liguori     g_signal_connect(s->window, "delete-event",
1906a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_window_close), s);
1907a4ccabcfSAnthony Liguori 
190830e8f22bSJan Kiszka     g_signal_connect(s->pause_item, "activate",
190930e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_pause), s);
191030e8f22bSJan Kiszka     g_signal_connect(s->reset_item, "activate",
191130e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_reset), s);
191230e8f22bSJan Kiszka     g_signal_connect(s->powerdown_item, "activate",
191330e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_powerdown), s);
1914a4ccabcfSAnthony Liguori     g_signal_connect(s->quit_item, "activate",
1915a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_quit), s);
191644b31e0bSMichael S. Tsirkin #if defined(CONFIG_VTE)
191744b31e0bSMichael S. Tsirkin     g_signal_connect(s->copy_item, "activate",
191844b31e0bSMichael S. Tsirkin                      G_CALLBACK(gd_menu_copy), s);
191944b31e0bSMichael S. Tsirkin #endif
1920c6158483SAnthony Liguori     g_signal_connect(s->full_screen_item, "activate",
1921c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_full_screen), s);
1922c6158483SAnthony Liguori     g_signal_connect(s->zoom_in_item, "activate",
1923c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_in), s);
1924c6158483SAnthony Liguori     g_signal_connect(s->zoom_out_item, "activate",
1925c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_out), s);
1926c6158483SAnthony Liguori     g_signal_connect(s->zoom_fixed_item, "activate",
1927c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_fixed), s);
1928c6158483SAnthony Liguori     g_signal_connect(s->zoom_fit_item, "activate",
1929c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_fit), s);
19305104a1f6SAnthony Liguori     g_signal_connect(s->grab_item, "activate",
19315104a1f6SAnthony Liguori                      G_CALLBACK(gd_menu_grab_input), s);
1932a4ccabcfSAnthony Liguori     g_signal_connect(s->notebook, "switch-page",
1933a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_change_page), s);
1934a4ccabcfSAnthony Liguori }
1935a4ccabcfSAnthony Liguori 
1936400519d2SCole Robinson static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
1937a4ccabcfSAnthony Liguori {
1938bf9b255fSAnthony Liguori     GtkWidget *machine_menu;
1939a4ccabcfSAnthony Liguori     GtkWidget *separator;
1940a4ccabcfSAnthony Liguori 
1941bf9b255fSAnthony Liguori     machine_menu = gtk_menu_new();
1942400519d2SCole Robinson     gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group);
194330e8f22bSJan Kiszka 
194430e8f22bSJan Kiszka     s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
1945bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
194630e8f22bSJan Kiszka 
194730e8f22bSJan Kiszka     separator = gtk_separator_menu_item_new();
1948bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
194930e8f22bSJan Kiszka 
19509068f20dSCole Robinson     s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset"));
1951bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
195230e8f22bSJan Kiszka 
19539068f20dSCole Robinson     s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down"));
1954bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
195530e8f22bSJan Kiszka 
195630e8f22bSJan Kiszka     separator = gtk_separator_menu_item_new();
1957bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1958a4ccabcfSAnthony Liguori 
19593d914488SCole Robinson     s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
1960a4ccabcfSAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
196130e8f22bSJan Kiszka                                  "<QEMU>/Machine/Quit");
19623d914488SCole Robinson     gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
1963db1da1f2SCole Robinson                             GDK_KEY_q, HOTKEY_MODIFIERS);
1964bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
1965a4ccabcfSAnthony Liguori 
1966bf9b255fSAnthony Liguori     return machine_menu;
1967bf9b255fSAnthony Liguori }
1968bf9b255fSAnthony Liguori 
19692606519bSMarc-André Lureau #if defined(CONFIG_OPENGL)
19702606519bSMarc-André Lureau static void gl_area_realize(GtkGLArea *area, VirtualConsole *vc)
19712606519bSMarc-André Lureau {
19722606519bSMarc-André Lureau     gtk_gl_area_make_current(area);
19732606519bSMarc-André Lureau     qemu_egl_display = eglGetCurrentDisplay();
19742606519bSMarc-André Lureau     vc->gfx.has_dmabuf = qemu_egl_has_dmabuf();
19752606519bSMarc-André Lureau     if (!vc->gfx.has_dmabuf) {
19762606519bSMarc-André Lureau         error_report("GtkGLArea console lacks DMABUF support.");
19772606519bSMarc-André Lureau     }
19782606519bSMarc-André Lureau }
19792606519bSMarc-André Lureau #endif
19802606519bSMarc-André Lureau 
1981e3500d1fSGerd Hoffmann static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
1982ed1132e4SGerd Hoffmann                               QemuConsole *con, int idx,
1983e3500d1fSGerd Hoffmann                               GSList *group, GtkWidget *view_menu)
1984e3500d1fSGerd Hoffmann {
19851d454c3fSPaolo Bonzini     bool zoom_to_fit = false;
1986c4c00922SNikola Pavlica 
1987779ce88fSGerd Hoffmann     vc->label = qemu_console_get_label(con);
1988e3500d1fSGerd Hoffmann     vc->s = s;
1989e3500d1fSGerd Hoffmann     vc->gfx.scale_x = 1.0;
1990e3500d1fSGerd Hoffmann     vc->gfx.scale_y = 1.0;
1991e3500d1fSGerd Hoffmann 
1992925a0400SGerd Hoffmann #if defined(CONFIG_OPENGL)
1993925a0400SGerd Hoffmann     if (display_opengl) {
199411c82b58SGerd Hoffmann         if (gtk_use_gl_area) {
1995925a0400SGerd Hoffmann             vc->gfx.drawing_area = gtk_gl_area_new();
19962606519bSMarc-André Lureau             g_signal_connect(vc->gfx.drawing_area, "realize",
19972606519bSMarc-André Lureau                              G_CALLBACK(gl_area_realize), vc);
1998925a0400SGerd Hoffmann             vc->gfx.dcl.ops = &dcl_gl_area_ops;
1999f988e3c0SMarc-André Lureau         } else {
2000bc6a3565SAkihiko Odaki #ifdef CONFIG_X11
2001e3500d1fSGerd Hoffmann             vc->gfx.drawing_area = gtk_drawing_area_new();
2002925a0400SGerd Hoffmann             /*
2003925a0400SGerd Hoffmann              * gtk_widget_set_double_buffered() was deprecated in 3.14.
2004925a0400SGerd Hoffmann              * It is required for opengl rendering on X11 though.  A
2005925a0400SGerd Hoffmann              * proper replacement (native opengl support) is only
2006925a0400SGerd Hoffmann              * available in 3.16+.  Silence the warning if possible.
2007925a0400SGerd Hoffmann              */
2008925a0400SGerd Hoffmann #pragma GCC diagnostic push
2009925a0400SGerd Hoffmann #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2010925a0400SGerd Hoffmann             gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
2011925a0400SGerd Hoffmann #pragma GCC diagnostic pop
2012925a0400SGerd Hoffmann             vc->gfx.dcl.ops = &dcl_egl_ops;
201352a37e20SMarc-André Lureau             vc->gfx.has_dmabuf = qemu_egl_has_dmabuf();
2014bc6a3565SAkihiko Odaki #else
2015bc6a3565SAkihiko Odaki             abort();
2016bc6a3565SAkihiko Odaki #endif
201711c82b58SGerd Hoffmann         }
2018925a0400SGerd Hoffmann     } else
2019925a0400SGerd Hoffmann #endif
2020925a0400SGerd Hoffmann     {
2021925a0400SGerd Hoffmann         vc->gfx.drawing_area = gtk_drawing_area_new();
2022925a0400SGerd Hoffmann         vc->gfx.dcl.ops = &dcl_ops;
2023925a0400SGerd Hoffmann     }
2024925a0400SGerd Hoffmann 
2025925a0400SGerd Hoffmann 
2026e3500d1fSGerd Hoffmann     gtk_widget_add_events(vc->gfx.drawing_area,
2027e3500d1fSGerd Hoffmann                           GDK_POINTER_MOTION_MASK |
2028e3500d1fSGerd Hoffmann                           GDK_BUTTON_PRESS_MASK |
2029e3500d1fSGerd Hoffmann                           GDK_BUTTON_RELEASE_MASK |
2030e3500d1fSGerd Hoffmann                           GDK_BUTTON_MOTION_MASK |
2031e3500d1fSGerd Hoffmann                           GDK_ENTER_NOTIFY_MASK |
2032e3500d1fSGerd Hoffmann                           GDK_LEAVE_NOTIFY_MASK |
2033e3500d1fSGerd Hoffmann                           GDK_SCROLL_MASK |
2034d89bf1d4SSergio Lopez                           GDK_SMOOTH_SCROLL_MASK |
2035e3500d1fSGerd Hoffmann                           GDK_KEY_PRESS_MASK);
2036e3500d1fSGerd Hoffmann     gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
2037e3500d1fSGerd Hoffmann 
2038e3500d1fSGerd Hoffmann     vc->type = GD_VC_GFX;
2039e3500d1fSGerd Hoffmann     vc->tab_item = vc->gfx.drawing_area;
20409d677e1cSJan Kiszka     vc->focus = vc->gfx.drawing_area;
2041e3500d1fSGerd Hoffmann     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
2042cdeb7090SGerd Hoffmann                              vc->tab_item, gtk_label_new(vc->label));
2043e3500d1fSGerd Hoffmann 
20440c0d4273SGerd Hoffmann     vc->gfx.kbd = qkbd_state_init(con);
2045e3500d1fSGerd Hoffmann     vc->gfx.dcl.con = con;
2046c4c00922SNikola Pavlica 
2047e3500d1fSGerd Hoffmann     register_displaychangelistener(&vc->gfx.dcl);
2048e3500d1fSGerd Hoffmann 
2049f8c223f6SGerd Hoffmann     gd_connect_vc_gfx_signals(vc);
2050f8c223f6SGerd Hoffmann     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
2051f8c223f6SGerd Hoffmann 
20521301e515SGerd Hoffmann     if (dpy_ui_info_supported(vc->gfx.dcl.con)) {
2053e8b1386eSGerd Hoffmann         zoom_to_fit = true;
2054e8b1386eSGerd Hoffmann     }
2055e8b1386eSGerd Hoffmann     if (s->opts->u.gtk.has_zoom_to_fit) {
2056e8b1386eSGerd Hoffmann         zoom_to_fit = s->opts->u.gtk.zoom_to_fit;
2057e8b1386eSGerd Hoffmann     }
2058e8b1386eSGerd Hoffmann     if (zoom_to_fit) {
20591301e515SGerd Hoffmann         gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item));
20601d73cd78SGerd Hoffmann         s->free_scale = true;
20611301e515SGerd Hoffmann     }
20621301e515SGerd Hoffmann 
2063e3500d1fSGerd Hoffmann     return group;
2064e3500d1fSGerd Hoffmann }
2065e3500d1fSGerd Hoffmann 
2066400519d2SCole Robinson static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
2067bf9b255fSAnthony Liguori {
2068bf9b255fSAnthony Liguori     GSList *group = NULL;
2069bf9b255fSAnthony Liguori     GtkWidget *view_menu;
2070bf9b255fSAnthony Liguori     GtkWidget *separator;
2071ed1132e4SGerd Hoffmann     QemuConsole *con;
2072ed1132e4SGerd Hoffmann     int vc;
2073bf9b255fSAnthony Liguori 
2074bf9b255fSAnthony Liguori     view_menu = gtk_menu_new();
2075400519d2SCole Robinson     gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group);
2076a4ccabcfSAnthony Liguori 
20773d914488SCole Robinson     s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen"));
207895414914SCole Robinson 
207944b31e0bSMichael S. Tsirkin #if defined(CONFIG_VTE)
208044b31e0bSMichael S. Tsirkin     s->copy_item = gtk_menu_item_new_with_mnemonic(_("_Copy"));
208144b31e0bSMichael S. Tsirkin     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->copy_item);
208244b31e0bSMichael S. Tsirkin #endif
208344b31e0bSMichael S. Tsirkin 
208495414914SCole Robinson     gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0,
208595414914SCole Robinson             g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL));
208695414914SCole Robinson     gtk_accel_label_set_accel(
208795414914SCole Robinson             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))),
208895414914SCole Robinson             GDK_KEY_f, HOTKEY_MODIFIERS);
2089bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
2090c6158483SAnthony Liguori 
2091c6158483SAnthony Liguori     separator = gtk_separator_menu_item_new();
2092bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2093c6158483SAnthony Liguori 
20943d914488SCole Robinson     s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In"));
2095c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
2096c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom In");
2097b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
2098b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
209966f6b82bSZiyue Yang     gtk_accel_group_connect(s->accel_group, GDK_KEY_equal, HOTKEY_MODIFIERS, 0,
210066f6b82bSZiyue Yang             g_cclosure_new_swap(G_CALLBACK(gd_accel_zoom_in), s, NULL));
2101bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
2102c6158483SAnthony Liguori 
21033d914488SCole Robinson     s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
2104c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
2105c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom Out");
2106b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus,
2107b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
2108bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
2109c6158483SAnthony Liguori 
21103d914488SCole Robinson     s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit"));
2111c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
2112c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom Fixed");
2113b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0,
2114b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
2115bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
2116c6158483SAnthony Liguori 
2117834574eaSAnthony Liguori     s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
2118bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
2119c6158483SAnthony Liguori 
2120c6158483SAnthony Liguori     separator = gtk_separator_menu_item_new();
2121bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2122c6158483SAnthony Liguori 
2123834574eaSAnthony Liguori     s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
2124bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
21255104a1f6SAnthony Liguori 
2126834574eaSAnthony Liguori     s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
21275104a1f6SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
21285104a1f6SAnthony Liguori                                  "<QEMU>/View/Grab Input");
2129b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g,
2130b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
2131bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
21325104a1f6SAnthony Liguori 
2133a4ccabcfSAnthony Liguori     separator = gtk_separator_menu_item_new();
2134bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2135a4ccabcfSAnthony Liguori 
2136e3500d1fSGerd Hoffmann     /* gfx */
2137ed1132e4SGerd Hoffmann     for (vc = 0;; vc++) {
2138ed1132e4SGerd Hoffmann         con = qemu_console_lookup_by_index(vc);
2139f8c223f6SGerd Hoffmann         if (!con) {
2140ed1132e4SGerd Hoffmann             break;
2141ed1132e4SGerd Hoffmann         }
2142ed1132e4SGerd Hoffmann         group = gd_vc_gfx_init(s, &s->vc[vc], con,
2143ed1132e4SGerd Hoffmann                                vc, group, view_menu);
2144ed1132e4SGerd Hoffmann         s->nb_vcs++;
2145ed1132e4SGerd Hoffmann     }
2146a4ccabcfSAnthony Liguori 
2147ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
2148e3500d1fSGerd Hoffmann     /* vte */
2149ed1132e4SGerd Hoffmann     gd_vcs_init(s, group, view_menu);
2150ee5f31e4SGerd Hoffmann #endif
2151d861def3SAnthony Liguori 
2152a4ccabcfSAnthony Liguori     separator = gtk_separator_menu_item_new();
2153bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2154a4ccabcfSAnthony Liguori 
2155834574eaSAnthony Liguori     s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
2156bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
2157a4ccabcfSAnthony Liguori 
2158cdeb7090SGerd Hoffmann     s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
2159cdeb7090SGerd Hoffmann     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
2160cdeb7090SGerd Hoffmann 
21611d187745SPeter Wu     s->show_menubar_item = gtk_check_menu_item_new_with_mnemonic(
21621d187745SPeter Wu             _("Show Menubar"));
21631d187745SPeter Wu     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->show_menubar_item),
21641d187745SPeter Wu                                    TRUE);
21651d187745SPeter Wu     gtk_accel_group_connect(s->accel_group, GDK_KEY_m, HOTKEY_MODIFIERS, 0,
21661d187745SPeter Wu             g_cclosure_new_swap(G_CALLBACK(gd_accel_show_menubar), s, NULL));
21671d187745SPeter Wu     gtk_accel_label_set_accel(
21681d187745SPeter Wu             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->show_menubar_item))),
21691d187745SPeter Wu             GDK_KEY_m, HOTKEY_MODIFIERS);
21701d187745SPeter Wu     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_menubar_item);
21711d187745SPeter Wu 
2172bf9b255fSAnthony Liguori     return view_menu;
2173bf9b255fSAnthony Liguori }
2174a4ccabcfSAnthony Liguori 
2175bf9b255fSAnthony Liguori static void gd_create_menus(GtkDisplayState *s)
2176bf9b255fSAnthony Liguori {
2177677b4905SPeter Wu     GtkSettings *settings;
2178677b4905SPeter Wu 
2179400519d2SCole Robinson     s->accel_group = gtk_accel_group_new();
2180400519d2SCole Robinson     s->machine_menu = gd_create_menu_machine(s);
2181400519d2SCole Robinson     s->view_menu = gd_create_menu_view(s);
2182bf9b255fSAnthony Liguori 
2183bf9b255fSAnthony Liguori     s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
218430e8f22bSJan Kiszka     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
218530e8f22bSJan Kiszka                               s->machine_menu);
218630e8f22bSJan Kiszka     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
2187a4ccabcfSAnthony Liguori 
2188bf9b255fSAnthony Liguori     s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
2189a4ccabcfSAnthony Liguori     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
2190a4ccabcfSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
2191bf9b255fSAnthony Liguori 
2192400519d2SCole Robinson     g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group);
2193400519d2SCole Robinson     gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group);
2194677b4905SPeter Wu 
2195677b4905SPeter Wu     /* Disable the default "F10" menu shortcut. */
2196677b4905SPeter Wu     settings = gtk_widget_get_settings(s->window);
2197677b4905SPeter Wu     g_object_set(G_OBJECT(settings), "gtk-menu-bar-accel", "", NULL);
2198a4ccabcfSAnthony Liguori }
2199a4ccabcfSAnthony Liguori 
22003158a348SBruce Rogers 
2201060ab763SGerd Hoffmann static gboolean gtkinit;
2202060ab763SGerd Hoffmann 
2203db71589fSGerd Hoffmann static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
2204a4ccabcfSAnthony Liguori {
22053d4da9d6SHervé Poussineau     VirtualConsole *vc;
22063d4da9d6SHervé Poussineau 
2207a4ccabcfSAnthony Liguori     GtkDisplayState *s = g_malloc0(sizeof(*s));
220863c67b6dSMax Reitz     GdkDisplay *window_display;
2209a8260d38SDaniel P. Berrangé     GtkIconTheme *theme;
221077d910fbSPaolo Bonzini     char *dir;
2211a4ccabcfSAnthony Liguori 
2212060ab763SGerd Hoffmann     if (!gtkinit) {
2213060ab763SGerd Hoffmann         fprintf(stderr, "gtk initialization failed\n");
2214060ab763SGerd Hoffmann         exit(1);
2215060ab763SGerd Hoffmann     }
22160c8d7065SGerd Hoffmann     assert(opts->type == DISPLAY_TYPE_GTK);
22170c8d7065SGerd Hoffmann     s->opts = opts;
2218060ab763SGerd Hoffmann 
2219a8260d38SDaniel P. Berrangé     theme = gtk_icon_theme_get_default();
222077d910fbSPaolo Bonzini     dir = get_relocated_path(CONFIG_QEMU_ICONDIR);
222177d910fbSPaolo Bonzini     gtk_icon_theme_prepend_search_path(theme, dir);
222277d910fbSPaolo Bonzini     g_free(dir);
222367ea9546SDaniel P. Berrangé     g_set_prgname("qemu");
2224a8260d38SDaniel P. Berrangé 
2225a4ccabcfSAnthony Liguori     s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
222651572ab0SDaniel P. Berrange     s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2227a4ccabcfSAnthony Liguori     s->notebook = gtk_notebook_new();
2228a4ccabcfSAnthony Liguori     s->menu_bar = gtk_menu_bar_new();
2229a4ccabcfSAnthony Liguori 
2230c6158483SAnthony Liguori     s->free_scale = FALSE;
2231a4ccabcfSAnthony Liguori 
223227b224a6SKevin Wolf     /* Mostly LC_MESSAGES only. See early_gtk_display_init() for details. For
223327b224a6SKevin Wolf      * LC_CTYPE, we need to make sure that non-ASCII characters are considered
223427b224a6SKevin Wolf      * printable, but without changing any of the character classes to make
223527b224a6SKevin Wolf      * sure that we don't accidentally break implicit assumptions.  */
22362cb5d2a4SAlberto Garcia     setlocale(LC_MESSAGES, "");
223727b224a6SKevin Wolf     setlocale(LC_CTYPE, "C.UTF-8");
223877d910fbSPaolo Bonzini     dir = get_relocated_path(CONFIG_QEMU_LOCALEDIR);
223977d910fbSPaolo Bonzini     bindtextdomain("qemu", dir);
224077d910fbSPaolo Bonzini     g_free(dir);
2241c55c9744Syanminhui     bind_textdomain_codeset("qemu", "UTF-8");
2242834574eaSAnthony Liguori     textdomain("qemu");
2243834574eaSAnthony Liguori 
224463c67b6dSMax Reitz     window_display = gtk_widget_get_display(s->window);
22459cfca0b9SGerd Hoffmann     if (s->opts->has_show_cursor && s->opts->show_cursor) {
22469cfca0b9SGerd Hoffmann         s->null_cursor = NULL; /* default pointer */
22479cfca0b9SGerd Hoffmann     } else {
224863c67b6dSMax Reitz         s->null_cursor = gdk_cursor_new_for_display(window_display,
224963c67b6dSMax Reitz                                                     GDK_BLANK_CURSOR);
22509cfca0b9SGerd Hoffmann     }
2251a4ccabcfSAnthony Liguori 
2252a4ccabcfSAnthony Liguori     s->mouse_mode_notifier.notify = gd_mouse_mode_change;
2253a4ccabcfSAnthony Liguori     qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
2254a4ccabcfSAnthony Liguori     qemu_add_vm_change_state_handler(gd_change_runstate, s);
2255a4ccabcfSAnthony Liguori 
2256a8260d38SDaniel P. Berrangé     gtk_window_set_icon_name(GTK_WINDOW(s->window), "qemu");
2257d819cdccSStefan Weil 
2258a4ccabcfSAnthony Liguori     gd_create_menus(s);
2259a4ccabcfSAnthony Liguori 
2260a4ccabcfSAnthony Liguori     gd_connect_signals(s);
2261a4ccabcfSAnthony Liguori 
2262a4ccabcfSAnthony Liguori     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
2263a4ccabcfSAnthony Liguori     gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
2264a4ccabcfSAnthony Liguori 
2265a4ccabcfSAnthony Liguori     gd_update_caption(s);
2266a4ccabcfSAnthony Liguori 
2267a4ccabcfSAnthony Liguori     gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
2268a4ccabcfSAnthony Liguori     gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
2269a4ccabcfSAnthony Liguori 
2270a4ccabcfSAnthony Liguori     gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
2271a4ccabcfSAnthony Liguori 
2272a4ccabcfSAnthony Liguori     gtk_widget_show_all(s->window);
2273a4ccabcfSAnthony Liguori 
22743d4da9d6SHervé Poussineau     vc = gd_vc_find_current(s);
22753d4da9d6SHervé Poussineau     gtk_widget_set_sensitive(s->view_menu, vc != NULL);
2276a0815632SStefan Hajnoczi #ifdef CONFIG_VTE
2277a0815632SStefan Hajnoczi     gtk_widget_set_sensitive(s->copy_item,
22783d4da9d6SHervé Poussineau                              vc && vc->type == GD_VC_VTE);
2279a0815632SStefan Hajnoczi #endif
2280a0815632SStefan Hajnoczi 
22810c8d7065SGerd Hoffmann     if (opts->has_full_screen &&
22820c8d7065SGerd Hoffmann         opts->full_screen) {
2283787ba4f0SPeter Wu         gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
2284787ba4f0SPeter Wu     }
22850c8d7065SGerd Hoffmann     if (opts->u.gtk.has_grab_on_hover &&
22860c8d7065SGerd Hoffmann         opts->u.gtk.grab_on_hover) {
2287881249c7SJan Kiszka         gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
2288881249c7SJan Kiszka     }
2289d11ebe2cSGerd Hoffmann     gd_clipboard_init(s);
2290a4ccabcfSAnthony Liguori }
2291ee5f31e4SGerd Hoffmann 
2292db71589fSGerd Hoffmann static void early_gtk_display_init(DisplayOptions *opts)
2293ee5f31e4SGerd Hoffmann {
22942cb5d2a4SAlberto Garcia     /* The QEMU code relies on the assumption that it's always run in
22952cb5d2a4SAlberto Garcia      * the C locale. Therefore it is not prepared to deal with
22962cb5d2a4SAlberto Garcia      * operations that produce different results depending on the
22972cb5d2a4SAlberto Garcia      * locale, such as printf's formatting of decimal numbers, and
22982cb5d2a4SAlberto Garcia      * possibly others.
22992cb5d2a4SAlberto Garcia      *
23002cb5d2a4SAlberto Garcia      * Since GTK+ calls setlocale() by default -importing the locale
23012cb5d2a4SAlberto Garcia      * settings from the environment- we must prevent it from doing so
23022cb5d2a4SAlberto Garcia      * using gtk_disable_setlocale().
23032cb5d2a4SAlberto Garcia      *
23042cb5d2a4SAlberto Garcia      * QEMU's GTK+ UI, however, _does_ have translations for some of
23052cb5d2a4SAlberto Garcia      * the menu items. As a trade-off between a functionally correct
23062cb5d2a4SAlberto Garcia      * QEMU and a fully internationalized UI we support importing
23072cb5d2a4SAlberto Garcia      * LC_MESSAGES from the environment (see the setlocale() call
23082cb5d2a4SAlberto Garcia      * earlier in this file). This allows us to display translated
23092cb5d2a4SAlberto Garcia      * messages leaving everything else untouched.
23102cb5d2a4SAlberto Garcia      */
23112cb5d2a4SAlberto Garcia     gtk_disable_setlocale();
2312060ab763SGerd Hoffmann     gtkinit = gtk_init_check(NULL, NULL);
2313060ab763SGerd Hoffmann     if (!gtkinit) {
2314060ab763SGerd Hoffmann         /* don't exit yet, that'll break -help */
2315060ab763SGerd Hoffmann         return;
2316060ab763SGerd Hoffmann     }
231797edf3bdSGerd Hoffmann 
23180c8d7065SGerd Hoffmann     assert(opts->type == DISPLAY_TYPE_GTK);
2319002b2902SGerd Hoffmann     if (opts->has_gl && opts->gl != DISPLAYGL_MODE_OFF) {
232097edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
23215cb69566SPaolo Bonzini #if defined(GDK_WINDOWING_WAYLAND)
23224c702805SGerd Hoffmann         if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
232311c82b58SGerd Hoffmann             gtk_use_gl_area = true;
2324925a0400SGerd Hoffmann             gtk_gl_area_init();
23254f4cb828STomeu Vizoso         } else
232697edf3bdSGerd Hoffmann #endif
23274c702805SGerd Hoffmann         {
2328bc6a3565SAkihiko Odaki #ifdef CONFIG_X11
232954d208ffSGerd Hoffmann             DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON;
233054d208ffSGerd Hoffmann             gtk_egl_init(mode);
2331bc6a3565SAkihiko Odaki #endif
23324c702805SGerd Hoffmann         }
2333925a0400SGerd Hoffmann #endif
233497edf3bdSGerd Hoffmann     }
233597edf3bdSGerd Hoffmann 
23362ec78706SDaniel P. Berrange     keycode_map = gd_get_keymap(&keycode_maplen);
23372ec78706SDaniel P. Berrange 
2338ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
2339777357d7SMarc-André Lureau     type_register(&char_gd_vc_type_info);
2340ee5f31e4SGerd Hoffmann #endif
2341ee5f31e4SGerd Hoffmann }
2342db71589fSGerd Hoffmann 
2343db71589fSGerd Hoffmann static QemuDisplay qemu_display_gtk = {
2344db71589fSGerd Hoffmann     .type       = DISPLAY_TYPE_GTK,
2345db71589fSGerd Hoffmann     .early_init = early_gtk_display_init,
2346db71589fSGerd Hoffmann     .init       = gtk_display_init,
2347db71589fSGerd Hoffmann };
2348db71589fSGerd Hoffmann 
2349db71589fSGerd Hoffmann static void register_gtk(void)
2350db71589fSGerd Hoffmann {
2351db71589fSGerd Hoffmann     qemu_display_register(&qemu_display_gtk);
2352db71589fSGerd Hoffmann }
2353db71589fSGerd Hoffmann 
2354db71589fSGerd Hoffmann type_init(register_gtk);
2355b36ae1c1SGerd Hoffmann 
2356b36ae1c1SGerd Hoffmann #ifdef CONFIG_OPENGL
2357b36ae1c1SGerd Hoffmann module_dep("ui-opengl");
2358b36ae1c1SGerd Hoffmann #endif
2359