xref: /qemu/ui/gtk.c (revision fa4dcf577ea61758bf55b64c8f9590c29897fb82)
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"
35*fa4dcf57SKevin Wolf #include "qapi/qapi-commands-control.h"
36112ed241SMarkus Armbruster #include "qapi/qapi-commands-misc.h"
37f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
38c95e3080SKevin Wolf 
39dc7ff344SGerd Hoffmann #include "ui/console.h"
40dc7ff344SGerd Hoffmann #include "ui/gtk.h"
41c95e3080SKevin Wolf 
42834574eaSAnthony Liguori #include <glib/gi18n.h>
433f58eadeSStefan Weil #include <locale.h>
44bbbf9bfbSStefan Weil #if defined(CONFIG_VTE)
45a4ccabcfSAnthony Liguori #include <vte/vte.h>
46bbbf9bfbSStefan Weil #endif
47a4ccabcfSAnthony Liguori #include <math.h>
48a4ccabcfSAnthony Liguori 
49ef0dd982SStefan Weil #include "trace.h"
50af98ba92SGerd Hoffmann #include "ui/input.h"
5154d31236SMarkus Armbruster #include "sysemu/runstate.h"
52a4ccabcfSAnthony Liguori #include "sysemu/sysemu.h"
53a4ccabcfSAnthony Liguori #include "keymaps.h"
548228e353SMarc-André Lureau #include "chardev/char.h"
556a24ced5SGerd Hoffmann #include "qom/object.h"
56a4ccabcfSAnthony Liguori 
57d861def3SAnthony Liguori #define MAX_VCS 10
5882fc1809SGerd Hoffmann #define VC_WINDOW_X_MIN  320
5982fc1809SGerd Hoffmann #define VC_WINDOW_Y_MIN  240
6082fc1809SGerd Hoffmann #define VC_TERM_X_MIN     80
6182fc1809SGerd Hoffmann #define VC_TERM_Y_MIN     25
6282fc1809SGerd Hoffmann #define VC_SCALE_MIN    0.25
6382fc1809SGerd Hoffmann #define VC_SCALE_STEP   0.25
64d861def3SAnthony Liguori 
652ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_X11
660041e9a0SMichael S. Tsirkin #include "x_keymap.h"
672ec78706SDaniel P. Berrange 
682ec78706SDaniel P. Berrange /* Gtk2 compat */
692ec78706SDaniel P. Berrange #ifndef GDK_IS_X11_DISPLAY
702ec78706SDaniel P. Berrange #define GDK_IS_X11_DISPLAY(dpy) (dpy != NULL)
712ec78706SDaniel P. Berrange #endif
722ec78706SDaniel P. Berrange #endif
732ec78706SDaniel P. Berrange 
742ec78706SDaniel P. Berrange 
752ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_WAYLAND
762ec78706SDaniel P. Berrange /* Gtk2 compat */
772ec78706SDaniel P. Berrange #ifndef GDK_IS_WAYLAND_DISPLAY
782ec78706SDaniel P. Berrange #define GDK_IS_WAYLAND_DISPLAY(dpy) (dpy != NULL)
792ec78706SDaniel P. Berrange #endif
802ec78706SDaniel P. Berrange #endif
812ec78706SDaniel P. Berrange 
822ec78706SDaniel P. Berrange 
832ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_WIN32
842ec78706SDaniel P. Berrange /* Gtk2 compat */
852ec78706SDaniel P. Berrange #ifndef GDK_IS_WIN32_DISPLAY
862ec78706SDaniel P. Berrange #define GDK_IS_WIN32_DISPLAY(dpy) (dpy != NULL)
872ec78706SDaniel P. Berrange #endif
882ec78706SDaniel P. Berrange #endif
892ec78706SDaniel P. Berrange 
902ec78706SDaniel P. Berrange 
912ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_BROADWAY
922ec78706SDaniel P. Berrange /* Gtk2 compat */
932ec78706SDaniel P. Berrange #ifndef GDK_IS_BROADWAY_DISPLAY
942ec78706SDaniel P. Berrange #define GDK_IS_BROADWAY_DISPLAY(dpy) (dpy != NULL)
952ec78706SDaniel P. Berrange #endif
962ec78706SDaniel P. Berrange #endif
972ec78706SDaniel P. Berrange 
982ec78706SDaniel P. Berrange 
992ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_QUARTZ
1002ec78706SDaniel P. Berrange /* Gtk2 compat */
1012ec78706SDaniel P. Berrange #ifndef GDK_IS_QUARTZ_DISPLAY
1022ec78706SDaniel P. Berrange #define GDK_IS_QUARTZ_DISPLAY(dpy) (dpy != NULL)
1032ec78706SDaniel P. Berrange #endif
1042ec78706SDaniel P. Berrange #endif
1052ec78706SDaniel P. Berrange 
1062ec78706SDaniel P. Berrange 
107bbbf9bfbSStefan Weil #if !defined(CONFIG_VTE)
108bbbf9bfbSStefan Weil # define VTE_CHECK_VERSION(a, b, c) 0
109bbbf9bfbSStefan Weil #endif
110cba68834SDaniel P. Berrange 
111c8f3f17cSDaniel P. Berrange /* Some older mingw versions lack this constant or have
112c8f3f17cSDaniel P. Berrange  * it conditionally defined */
113c8f3f17cSDaniel P. Berrange #ifdef _WIN32
114c8f3f17cSDaniel P. Berrange # ifndef MAPVK_VK_TO_VSC
115c8f3f17cSDaniel P. Berrange #  define MAPVK_VK_TO_VSC 0
116c8f3f17cSDaniel P. Berrange # endif
117c8f3f17cSDaniel P. Berrange #endif
118c8f3f17cSDaniel P. Berrange 
119c8f3f17cSDaniel P. Berrange 
120b1e749c0SJan Kiszka #define HOTKEY_MODIFIERS        (GDK_CONTROL_MASK | GDK_MOD1_MASK)
121b1e749c0SJan Kiszka 
1222ec78706SDaniel P. Berrange static const guint16 *keycode_map;
1232ec78706SDaniel P. Berrange static size_t keycode_maplen;
1242ec78706SDaniel P. Berrange 
125e3500d1fSGerd Hoffmann struct GtkDisplayState {
126a4ccabcfSAnthony Liguori     GtkWidget *window;
127a4ccabcfSAnthony Liguori 
128a4ccabcfSAnthony Liguori     GtkWidget *menu_bar;
129a4ccabcfSAnthony Liguori 
13073d4dc71SAnthony Liguori     GtkAccelGroup *accel_group;
13173d4dc71SAnthony Liguori 
13230e8f22bSJan Kiszka     GtkWidget *machine_menu_item;
13330e8f22bSJan Kiszka     GtkWidget *machine_menu;
13430e8f22bSJan Kiszka     GtkWidget *pause_item;
13530e8f22bSJan Kiszka     GtkWidget *reset_item;
13630e8f22bSJan Kiszka     GtkWidget *powerdown_item;
137a4ccabcfSAnthony Liguori     GtkWidget *quit_item;
138a4ccabcfSAnthony Liguori 
139a4ccabcfSAnthony Liguori     GtkWidget *view_menu_item;
140a4ccabcfSAnthony Liguori     GtkWidget *view_menu;
141c6158483SAnthony Liguori     GtkWidget *full_screen_item;
14244b31e0bSMichael S. Tsirkin     GtkWidget *copy_item;
143c6158483SAnthony Liguori     GtkWidget *zoom_in_item;
144c6158483SAnthony Liguori     GtkWidget *zoom_out_item;
145c6158483SAnthony Liguori     GtkWidget *zoom_fixed_item;
146c6158483SAnthony Liguori     GtkWidget *zoom_fit_item;
1475104a1f6SAnthony Liguori     GtkWidget *grab_item;
1485104a1f6SAnthony Liguori     GtkWidget *grab_on_hover_item;
149a4ccabcfSAnthony Liguori 
150d861def3SAnthony Liguori     int nb_vcs;
151d861def3SAnthony Liguori     VirtualConsole vc[MAX_VCS];
152d861def3SAnthony Liguori 
153a4ccabcfSAnthony Liguori     GtkWidget *show_tabs_item;
154cdeb7090SGerd Hoffmann     GtkWidget *untabify_item;
1551d187745SPeter Wu     GtkWidget *show_menubar_item;
156a4ccabcfSAnthony Liguori 
157a4ccabcfSAnthony Liguori     GtkWidget *vbox;
158a4ccabcfSAnthony Liguori     GtkWidget *notebook;
159a4ccabcfSAnthony Liguori     int button_mask;
160e61031cdSTakashi Iwai     gboolean last_set;
161a4ccabcfSAnthony Liguori     int last_x;
162a4ccabcfSAnthony Liguori     int last_y;
163ecce1929STakashi Iwai     int grab_x_root;
164ecce1929STakashi Iwai     int grab_y_root;
1654c638e2eSGerd Hoffmann     VirtualConsole *kbd_owner;
1664c638e2eSGerd Hoffmann     VirtualConsole *ptr_owner;
167a4ccabcfSAnthony Liguori 
168c6158483SAnthony Liguori     gboolean full_screen;
169a4ccabcfSAnthony Liguori 
170a4ccabcfSAnthony Liguori     GdkCursor *null_cursor;
171a4ccabcfSAnthony Liguori     Notifier mouse_mode_notifier;
172c6158483SAnthony Liguori     gboolean free_scale;
17330e8f22bSJan Kiszka 
17430e8f22bSJan Kiszka     bool external_pause_update;
1756db253caSJan Kiszka 
1761a01716aSJan Kiszka     bool ignore_keys;
1770c8d7065SGerd Hoffmann 
1780c8d7065SGerd Hoffmann     DisplayOptions *opts;
179e3500d1fSGerd Hoffmann };
180a4ccabcfSAnthony Liguori 
1810ec7b3e7SMarc-André Lureau typedef struct VCChardev {
1820ec7b3e7SMarc-André Lureau     Chardev parent;
18341ac54b2SMarc-André Lureau     VirtualConsole *console;
18441ac54b2SMarc-André Lureau     bool echo;
1850ec7b3e7SMarc-André Lureau } VCChardev;
18641ac54b2SMarc-André Lureau 
187777357d7SMarc-André Lureau #define TYPE_CHARDEV_VC "chardev-vc"
188777357d7SMarc-André Lureau #define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
189777357d7SMarc-André Lureau 
19011c82b58SGerd Hoffmann bool gtk_use_gl_area;
19111c82b58SGerd Hoffmann 
192d531deefSGerd Hoffmann static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
1932884cf5bSGerd Hoffmann static void gd_ungrab_pointer(GtkDisplayState *s);
194d531deefSGerd Hoffmann static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
195aa4f4058SGerd Hoffmann static void gd_ungrab_keyboard(GtkDisplayState *s);
1962884cf5bSGerd Hoffmann 
197a4ccabcfSAnthony Liguori /** Utility Functions **/
198a4ccabcfSAnthony Liguori 
199271a25c0SGerd Hoffmann static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s)
200271a25c0SGerd Hoffmann {
201271a25c0SGerd Hoffmann     VirtualConsole *vc;
202271a25c0SGerd Hoffmann     gint i;
203271a25c0SGerd Hoffmann 
204271a25c0SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
205271a25c0SGerd Hoffmann         vc = &s->vc[i];
206271a25c0SGerd Hoffmann         if (gtk_check_menu_item_get_active
207271a25c0SGerd Hoffmann             (GTK_CHECK_MENU_ITEM(vc->menu_item))) {
208271a25c0SGerd Hoffmann             return vc;
209271a25c0SGerd Hoffmann         }
210271a25c0SGerd Hoffmann     }
211271a25c0SGerd Hoffmann     return NULL;
212271a25c0SGerd Hoffmann }
213271a25c0SGerd Hoffmann 
214271a25c0SGerd Hoffmann static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page)
215271a25c0SGerd Hoffmann {
216271a25c0SGerd Hoffmann     VirtualConsole *vc;
217271a25c0SGerd Hoffmann     gint i, p;
218271a25c0SGerd Hoffmann 
219271a25c0SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
220271a25c0SGerd Hoffmann         vc = &s->vc[i];
221271a25c0SGerd Hoffmann         p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item);
222271a25c0SGerd Hoffmann         if (p == page) {
223271a25c0SGerd Hoffmann             return vc;
224271a25c0SGerd Hoffmann         }
225271a25c0SGerd Hoffmann     }
226271a25c0SGerd Hoffmann     return NULL;
227271a25c0SGerd Hoffmann }
228271a25c0SGerd Hoffmann 
229e3500d1fSGerd Hoffmann static VirtualConsole *gd_vc_find_current(GtkDisplayState *s)
230e3500d1fSGerd Hoffmann {
231e3500d1fSGerd Hoffmann     gint page;
232e3500d1fSGerd Hoffmann 
233e3500d1fSGerd Hoffmann     page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook));
234e3500d1fSGerd Hoffmann     return gd_vc_find_by_page(s, page);
235e3500d1fSGerd Hoffmann }
236e3500d1fSGerd Hoffmann 
2375104a1f6SAnthony Liguori static bool gd_is_grab_active(GtkDisplayState *s)
2385104a1f6SAnthony Liguori {
2395104a1f6SAnthony Liguori     return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
2405104a1f6SAnthony Liguori }
2415104a1f6SAnthony Liguori 
2425104a1f6SAnthony Liguori static bool gd_grab_on_hover(GtkDisplayState *s)
2435104a1f6SAnthony Liguori {
2445104a1f6SAnthony Liguori     return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
2455104a1f6SAnthony Liguori }
2465104a1f6SAnthony Liguori 
247e3500d1fSGerd Hoffmann static void gd_update_cursor(VirtualConsole *vc)
2485104a1f6SAnthony Liguori {
249e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
250e3500d1fSGerd Hoffmann     GdkWindow *window;
251832189c9SGerd Hoffmann 
252f8c223f6SGerd Hoffmann     if (vc->type != GD_VC_GFX ||
253f8c223f6SGerd Hoffmann         !qemu_console_is_graphic(vc->gfx.dcl.con)) {
254e3500d1fSGerd Hoffmann         return;
2555104a1f6SAnthony Liguori     }
2565104a1f6SAnthony Liguori 
2574cdfc935SHervé Poussineau     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
2584cdfc935SHervé Poussineau         return;
2594cdfc935SHervé Poussineau     }
2604cdfc935SHervé Poussineau 
261e3500d1fSGerd Hoffmann     window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area));
2622884cf5bSGerd Hoffmann     if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) {
263a4ccabcfSAnthony Liguori         gdk_window_set_cursor(window, s->null_cursor);
264a4ccabcfSAnthony Liguori     } else {
265a4ccabcfSAnthony Liguori         gdk_window_set_cursor(window, NULL);
266a4ccabcfSAnthony Liguori     }
267a4ccabcfSAnthony Liguori }
268a4ccabcfSAnthony Liguori 
269a4ccabcfSAnthony Liguori static void gd_update_caption(GtkDisplayState *s)
270a4ccabcfSAnthony Liguori {
271a4ccabcfSAnthony Liguori     const char *status = "";
2724eeaa3a8SGerd Hoffmann     gchar *prefix;
273a4ccabcfSAnthony Liguori     gchar *title;
2745104a1f6SAnthony Liguori     const char *grab = "";
27530e8f22bSJan Kiszka     bool is_paused = !runstate_is_running();
2764eeaa3a8SGerd Hoffmann     int i;
2775104a1f6SAnthony Liguori 
2784eeaa3a8SGerd Hoffmann     if (qemu_name) {
2794eeaa3a8SGerd Hoffmann         prefix = g_strdup_printf("QEMU (%s)", qemu_name);
2804eeaa3a8SGerd Hoffmann     } else {
2814eeaa3a8SGerd Hoffmann         prefix = g_strdup_printf("QEMU");
2824eeaa3a8SGerd Hoffmann     }
2834eeaa3a8SGerd Hoffmann 
2844eeaa3a8SGerd Hoffmann     if (s->ptr_owner != NULL &&
2854eeaa3a8SGerd Hoffmann         s->ptr_owner->window == NULL) {
286d8da9ee8SAurelien Jarno         grab = _(" - Press Ctrl+Alt+G to release grab");
2875104a1f6SAnthony Liguori     }
288a4ccabcfSAnthony Liguori 
28930e8f22bSJan Kiszka     if (is_paused) {
290d8da9ee8SAurelien Jarno         status = _(" [Paused]");
291a4ccabcfSAnthony Liguori     }
29230e8f22bSJan Kiszka     s->external_pause_update = true;
29330e8f22bSJan Kiszka     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
29430e8f22bSJan Kiszka                                    is_paused);
29530e8f22bSJan Kiszka     s->external_pause_update = false;
296a4ccabcfSAnthony Liguori 
2974eeaa3a8SGerd Hoffmann     title = g_strdup_printf("%s%s%s", prefix, status, grab);
2984eeaa3a8SGerd Hoffmann     gtk_window_set_title(GTK_WINDOW(s->window), title);
2994eeaa3a8SGerd Hoffmann     g_free(title);
3004eeaa3a8SGerd Hoffmann 
3014eeaa3a8SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
3024eeaa3a8SGerd Hoffmann         VirtualConsole *vc = &s->vc[i];
3034eeaa3a8SGerd Hoffmann 
3044eeaa3a8SGerd Hoffmann         if (!vc->window) {
3054eeaa3a8SGerd Hoffmann             continue;
3064eeaa3a8SGerd Hoffmann         }
3074eeaa3a8SGerd Hoffmann         title = g_strdup_printf("%s: %s%s%s", prefix, vc->label,
3084eeaa3a8SGerd Hoffmann                                 vc == s->kbd_owner ? " +kbd" : "",
3094eeaa3a8SGerd Hoffmann                                 vc == s->ptr_owner ? " +ptr" : "");
3104eeaa3a8SGerd Hoffmann         gtk_window_set_title(GTK_WINDOW(vc->window), title);
3114eeaa3a8SGerd Hoffmann         g_free(title);
312a4ccabcfSAnthony Liguori     }
313a4ccabcfSAnthony Liguori 
3144eeaa3a8SGerd Hoffmann     g_free(prefix);
315a4ccabcfSAnthony Liguori }
316a4ccabcfSAnthony Liguori 
31782fc1809SGerd Hoffmann static void gd_update_geometry_hints(VirtualConsole *vc)
31882fc1809SGerd Hoffmann {
31982fc1809SGerd Hoffmann     GtkDisplayState *s = vc->s;
32082fc1809SGerd Hoffmann     GdkWindowHints mask = 0;
32182fc1809SGerd Hoffmann     GdkGeometry geo = {};
32282fc1809SGerd Hoffmann     GtkWidget *geo_widget = NULL;
32382fc1809SGerd Hoffmann     GtkWindow *geo_window;
32482fc1809SGerd Hoffmann 
32582fc1809SGerd Hoffmann     if (vc->type == GD_VC_GFX) {
326f98f43eaSGerd Hoffmann         if (!vc->gfx.ds) {
327f98f43eaSGerd Hoffmann             return;
328f98f43eaSGerd Hoffmann         }
32982fc1809SGerd Hoffmann         if (s->free_scale) {
33082fc1809SGerd Hoffmann             geo.min_width  = surface_width(vc->gfx.ds) * VC_SCALE_MIN;
33182fc1809SGerd Hoffmann             geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN;
33282fc1809SGerd Hoffmann             mask |= GDK_HINT_MIN_SIZE;
33382fc1809SGerd Hoffmann         } else {
33482fc1809SGerd Hoffmann             geo.min_width  = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
33582fc1809SGerd Hoffmann             geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
33682fc1809SGerd Hoffmann             mask |= GDK_HINT_MIN_SIZE;
33782fc1809SGerd Hoffmann         }
33882fc1809SGerd Hoffmann         geo_widget = vc->gfx.drawing_area;
33982fc1809SGerd Hoffmann         gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height);
34082fc1809SGerd Hoffmann 
34182fc1809SGerd Hoffmann #if defined(CONFIG_VTE)
34282fc1809SGerd Hoffmann     } else if (vc->type == GD_VC_VTE) {
34382fc1809SGerd Hoffmann         VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
3446978dc4aSAlberto Garcia         GtkBorder padding = { 0 };
34582fc1809SGerd Hoffmann 
34684e2dc4bSCole Robinson #if VTE_CHECK_VERSION(0, 37, 0)
34784e2dc4bSCole Robinson         gtk_style_context_get_padding(
34884e2dc4bSCole Robinson                 gtk_widget_get_style_context(vc->vte.terminal),
34984e2dc4bSCole Robinson                 gtk_widget_get_state_flags(vc->vte.terminal),
35084e2dc4bSCole Robinson                 &padding);
35184e2dc4bSCole Robinson #else
3526978dc4aSAlberto Garcia         {
3536978dc4aSAlberto Garcia             GtkBorder *ib = NULL;
35484e2dc4bSCole Robinson             gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL);
3556978dc4aSAlberto Garcia             if (ib) {
3566978dc4aSAlberto Garcia                 padding = *ib;
3576978dc4aSAlberto Garcia                 gtk_border_free(ib);
3586978dc4aSAlberto Garcia             }
3596978dc4aSAlberto Garcia         }
36084e2dc4bSCole Robinson #endif
36184e2dc4bSCole Robinson 
36282fc1809SGerd Hoffmann         geo.width_inc  = vte_terminal_get_char_width(term);
36382fc1809SGerd Hoffmann         geo.height_inc = vte_terminal_get_char_height(term);
36482fc1809SGerd Hoffmann         mask |= GDK_HINT_RESIZE_INC;
36582fc1809SGerd Hoffmann         geo.base_width  = geo.width_inc;
36682fc1809SGerd Hoffmann         geo.base_height = geo.height_inc;
36782fc1809SGerd Hoffmann         mask |= GDK_HINT_BASE_SIZE;
36882fc1809SGerd Hoffmann         geo.min_width  = geo.width_inc * VC_TERM_X_MIN;
36982fc1809SGerd Hoffmann         geo.min_height = geo.height_inc * VC_TERM_Y_MIN;
37082fc1809SGerd Hoffmann         mask |= GDK_HINT_MIN_SIZE;
37184e2dc4bSCole Robinson 
3726978dc4aSAlberto Garcia         geo.base_width  += padding.left + padding.right;
3736978dc4aSAlberto Garcia         geo.base_height += padding.top + padding.bottom;
3746978dc4aSAlberto Garcia         geo.min_width   += padding.left + padding.right;
3756978dc4aSAlberto Garcia         geo.min_height  += padding.top + padding.bottom;
37682fc1809SGerd Hoffmann         geo_widget = vc->vte.terminal;
37782fc1809SGerd Hoffmann #endif
37882fc1809SGerd Hoffmann     }
37982fc1809SGerd Hoffmann 
38082fc1809SGerd Hoffmann     geo_window = GTK_WINDOW(vc->window ? vc->window : s->window);
38182fc1809SGerd Hoffmann     gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
38282fc1809SGerd Hoffmann }
38382fc1809SGerd Hoffmann 
38497edf3bdSGerd Hoffmann void gd_update_windowsize(VirtualConsole *vc)
385a4ccabcfSAnthony Liguori {
386e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
387c6158483SAnthony Liguori 
38882fc1809SGerd Hoffmann     gd_update_geometry_hints(vc);
389c6158483SAnthony Liguori 
39082fc1809SGerd Hoffmann     if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) {
39182fc1809SGerd Hoffmann         gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window),
39282fc1809SGerd Hoffmann                           VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN);
393a4ccabcfSAnthony Liguori     }
394aa0a55d4SGerd Hoffmann }
395a4ccabcfSAnthony Liguori 
396e3500d1fSGerd Hoffmann static void gd_update_full_redraw(VirtualConsole *vc)
3979d9801cfSGerd Hoffmann {
398e3500d1fSGerd Hoffmann     GtkWidget *area = vc->gfx.drawing_area;
3999d9801cfSGerd Hoffmann     int ww, wh;
40089d85cdeSDaniel P. Berrangé     ww = gdk_window_get_width(gtk_widget_get_window(area));
40189d85cdeSDaniel P. Berrangé     wh = gdk_window_get_height(gtk_widget_get_window(area));
402925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
40311c82b58SGerd Hoffmann     if (vc->gfx.gls && gtk_use_gl_area) {
404925a0400SGerd Hoffmann         gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
405925a0400SGerd Hoffmann         return;
406925a0400SGerd Hoffmann     }
407925a0400SGerd Hoffmann #endif
408e3500d1fSGerd Hoffmann     gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
4099d9801cfSGerd Hoffmann }
4109d9801cfSGerd Hoffmann 
4116db253caSJan Kiszka static void gtk_release_modifiers(GtkDisplayState *s)
4126db253caSJan Kiszka {
413e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
4146db253caSJan Kiszka 
415f8c223f6SGerd Hoffmann     if (vc->type != GD_VC_GFX ||
416f8c223f6SGerd Hoffmann         !qemu_console_is_graphic(vc->gfx.dcl.con)) {
4176db253caSJan Kiszka         return;
4186db253caSJan Kiszka     }
4190c0d4273SGerd Hoffmann     qkbd_state_lift_all_keys(vc->gfx.kbd);
4206db253caSJan Kiszka }
4216db253caSJan Kiszka 
422316cb068SGerd Hoffmann static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
423316cb068SGerd Hoffmann                                GtkWidget *widget)
424316cb068SGerd Hoffmann {
425316cb068SGerd Hoffmann     g_object_ref(G_OBJECT(widget));
426316cb068SGerd Hoffmann     gtk_container_remove(GTK_CONTAINER(from), widget);
427316cb068SGerd Hoffmann     gtk_container_add(GTK_CONTAINER(to), widget);
428316cb068SGerd Hoffmann     g_object_unref(G_OBJECT(widget));
429316cb068SGerd Hoffmann }
430316cb068SGerd Hoffmann 
4319d9801cfSGerd Hoffmann /** DisplayState Callbacks **/
4329d9801cfSGerd Hoffmann 
4339d9801cfSGerd Hoffmann static void gd_update(DisplayChangeListener *dcl,
434bc2ed970SGerd Hoffmann                       int x, int y, int w, int h)
4359d9801cfSGerd Hoffmann {
436e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
437f8c223f6SGerd Hoffmann     GdkWindow *win;
4389d9801cfSGerd Hoffmann     int x1, x2, y1, y2;
4399d9801cfSGerd Hoffmann     int mx, my;
4409d9801cfSGerd Hoffmann     int fbw, fbh;
4419d9801cfSGerd Hoffmann     int ww, wh;
4429d9801cfSGerd Hoffmann 
44374444bc1SGerd Hoffmann     trace_gd_update(vc->label, x, y, w, h);
4449d9801cfSGerd Hoffmann 
4454cdfc935SHervé Poussineau     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
4464cdfc935SHervé Poussineau         return;
4474cdfc935SHervé Poussineau     }
4484cdfc935SHervé Poussineau 
449e3500d1fSGerd Hoffmann     if (vc->gfx.convert) {
450e3500d1fSGerd Hoffmann         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
451e3500d1fSGerd Hoffmann                                NULL, vc->gfx.convert,
452f0875536SGerd Hoffmann                                x, y, 0, 0, x, y, w, h);
453f0875536SGerd Hoffmann     }
454f0875536SGerd Hoffmann 
455e3500d1fSGerd Hoffmann     x1 = floor(x * vc->gfx.scale_x);
456e3500d1fSGerd Hoffmann     y1 = floor(y * vc->gfx.scale_y);
4579d9801cfSGerd Hoffmann 
458e3500d1fSGerd Hoffmann     x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
459e3500d1fSGerd Hoffmann     y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
4609d9801cfSGerd Hoffmann 
461e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
462e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
4639d9801cfSGerd Hoffmann 
464f8c223f6SGerd Hoffmann     win = gtk_widget_get_window(vc->gfx.drawing_area);
465f8c223f6SGerd Hoffmann     if (!win) {
466f8c223f6SGerd Hoffmann         return;
467f8c223f6SGerd Hoffmann     }
46889d85cdeSDaniel P. Berrangé     ww = gdk_window_get_width(win);
46989d85cdeSDaniel P. Berrangé     wh = gdk_window_get_height(win);
4709d9801cfSGerd Hoffmann 
4719d9801cfSGerd Hoffmann     mx = my = 0;
4729d9801cfSGerd Hoffmann     if (ww > fbw) {
4739d9801cfSGerd Hoffmann         mx = (ww - fbw) / 2;
4749d9801cfSGerd Hoffmann     }
4759d9801cfSGerd Hoffmann     if (wh > fbh) {
4769d9801cfSGerd Hoffmann         my = (wh - fbh) / 2;
4779d9801cfSGerd Hoffmann     }
4789d9801cfSGerd Hoffmann 
479e3500d1fSGerd Hoffmann     gtk_widget_queue_draw_area(vc->gfx.drawing_area,
480e3500d1fSGerd Hoffmann                                mx + x1, my + y1, (x2 - x1), (y2 - y1));
4819d9801cfSGerd Hoffmann }
4829d9801cfSGerd Hoffmann 
483bc2ed970SGerd Hoffmann static void gd_refresh(DisplayChangeListener *dcl)
4849d9801cfSGerd Hoffmann {
485284d1c6bSGerd Hoffmann     graphic_hw_update(dcl->con);
4869d9801cfSGerd Hoffmann }
4879d9801cfSGerd Hoffmann 
488bb732ee7SCole Robinson static GdkDevice *gd_get_pointer(GdkDisplay *dpy)
489bb732ee7SCole Robinson {
490bb732ee7SCole Robinson #if GTK_CHECK_VERSION(3, 20, 0)
491bb732ee7SCole Robinson     return gdk_seat_get_pointer(gdk_display_get_default_seat(dpy));
492bb732ee7SCole Robinson #else
493bb732ee7SCole Robinson     return gdk_device_manager_get_client_pointer(
494bb732ee7SCole Robinson         gdk_display_get_device_manager(dpy));
495bb732ee7SCole Robinson #endif
496bb732ee7SCole Robinson }
497bb732ee7SCole Robinson 
498b087143bSIgor Mitsyanko static void gd_mouse_set(DisplayChangeListener *dcl,
499b087143bSIgor Mitsyanko                          int x, int y, int visible)
500b087143bSIgor Mitsyanko {
501e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
502b087143bSIgor Mitsyanko     GdkDisplay *dpy;
503b087143bSIgor Mitsyanko     gint x_root, y_root;
504b087143bSIgor Mitsyanko 
5052bda6602SCole Robinson     if (qemu_input_is_absolute()) {
5062bda6602SCole Robinson         return;
5072bda6602SCole Robinson     }
5082bda6602SCole Robinson 
509e3500d1fSGerd Hoffmann     dpy = gtk_widget_get_display(vc->gfx.drawing_area);
510e3500d1fSGerd Hoffmann     gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
511b087143bSIgor Mitsyanko                                x, y, &x_root, &y_root);
512bb732ee7SCole Robinson     gdk_device_warp(gd_get_pointer(dpy),
513e3500d1fSGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
514298526feSCole Robinson                     x_root, y_root);
5151271f7f7SGerd Hoffmann     vc->s->last_x = x;
5161271f7f7SGerd Hoffmann     vc->s->last_y = y;
517b087143bSIgor Mitsyanko }
5189697f5d2SGerd Hoffmann 
5199697f5d2SGerd Hoffmann static void gd_cursor_define(DisplayChangeListener *dcl,
5209697f5d2SGerd Hoffmann                              QEMUCursor *c)
5219697f5d2SGerd Hoffmann {
522e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
5239697f5d2SGerd Hoffmann     GdkPixbuf *pixbuf;
5249697f5d2SGerd Hoffmann     GdkCursor *cursor;
5259697f5d2SGerd Hoffmann 
5264cdfc935SHervé Poussineau     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
5274cdfc935SHervé Poussineau         return;
5284cdfc935SHervé Poussineau     }
5294cdfc935SHervé Poussineau 
5309697f5d2SGerd Hoffmann     pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
5319697f5d2SGerd Hoffmann                                       GDK_COLORSPACE_RGB, true, 8,
5329697f5d2SGerd Hoffmann                                       c->width, c->height, c->width * 4,
5339697f5d2SGerd Hoffmann                                       NULL, NULL);
534e3500d1fSGerd Hoffmann     cursor = gdk_cursor_new_from_pixbuf
535e3500d1fSGerd Hoffmann         (gtk_widget_get_display(vc->gfx.drawing_area),
5369697f5d2SGerd Hoffmann          pixbuf, c->hot_x, c->hot_y);
537e3500d1fSGerd Hoffmann     gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor);
5389697f5d2SGerd Hoffmann     g_object_unref(pixbuf);
539030b4b7dSStefan Weil     g_object_unref(cursor);
5409697f5d2SGerd Hoffmann }
5419697f5d2SGerd Hoffmann 
5429d9801cfSGerd Hoffmann static void gd_switch(DisplayChangeListener *dcl,
5439d9801cfSGerd Hoffmann                       DisplaySurface *surface)
5449d9801cfSGerd Hoffmann {
545e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
5469d9801cfSGerd Hoffmann     bool resized = true;
5479d9801cfSGerd Hoffmann 
548f98f43eaSGerd Hoffmann     trace_gd_switch(vc->label,
549f98f43eaSGerd Hoffmann                     surface ? surface_width(surface)  : 0,
550f98f43eaSGerd Hoffmann                     surface ? surface_height(surface) : 0);
5519d9801cfSGerd Hoffmann 
552e3500d1fSGerd Hoffmann     if (vc->gfx.surface) {
553e3500d1fSGerd Hoffmann         cairo_surface_destroy(vc->gfx.surface);
554f98f43eaSGerd Hoffmann         vc->gfx.surface = NULL;
555f98f43eaSGerd Hoffmann     }
556f98f43eaSGerd Hoffmann     if (vc->gfx.convert) {
557f98f43eaSGerd Hoffmann         pixman_image_unref(vc->gfx.convert);
558f98f43eaSGerd Hoffmann         vc->gfx.convert = NULL;
5599d9801cfSGerd Hoffmann     }
5609d9801cfSGerd Hoffmann 
561f98f43eaSGerd Hoffmann     if (vc->gfx.ds && surface &&
562e3500d1fSGerd Hoffmann         surface_width(vc->gfx.ds) == surface_width(surface) &&
563e3500d1fSGerd Hoffmann         surface_height(vc->gfx.ds) == surface_height(surface)) {
5649d9801cfSGerd Hoffmann         resized = false;
5659d9801cfSGerd Hoffmann     }
566e3500d1fSGerd Hoffmann     vc->gfx.ds = surface;
567f0875536SGerd Hoffmann 
568f98f43eaSGerd Hoffmann     if (!surface) {
569f98f43eaSGerd Hoffmann         return;
5709d9801cfSGerd Hoffmann     }
5719d9801cfSGerd Hoffmann 
572f0875536SGerd Hoffmann     if (surface->format == PIXMAN_x8r8g8b8) {
573f0875536SGerd Hoffmann         /*
574f0875536SGerd Hoffmann          * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24
575f0875536SGerd Hoffmann          *
576f0875536SGerd Hoffmann          * No need to convert, use surface directly.  Should be the
577f0875536SGerd Hoffmann          * common case as this is qemu_default_pixelformat(32) too.
578f0875536SGerd Hoffmann          */
579e3500d1fSGerd Hoffmann         vc->gfx.surface = cairo_image_surface_create_for_data
580f0875536SGerd Hoffmann             (surface_data(surface),
581f0875536SGerd Hoffmann              CAIRO_FORMAT_RGB24,
5829d9801cfSGerd Hoffmann              surface_width(surface),
5839d9801cfSGerd Hoffmann              surface_height(surface),
5849d9801cfSGerd Hoffmann              surface_stride(surface));
585f0875536SGerd Hoffmann     } else {
586f0875536SGerd Hoffmann         /* Must convert surface, use pixman to do it. */
587e3500d1fSGerd Hoffmann         vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8,
588f0875536SGerd Hoffmann                                                    surface_width(surface),
589f0875536SGerd Hoffmann                                                    surface_height(surface),
590f0875536SGerd Hoffmann                                                    NULL, 0);
591e3500d1fSGerd Hoffmann         vc->gfx.surface = cairo_image_surface_create_for_data
592e3500d1fSGerd Hoffmann             ((void *)pixman_image_get_data(vc->gfx.convert),
593f0875536SGerd Hoffmann              CAIRO_FORMAT_RGB24,
594e3500d1fSGerd Hoffmann              pixman_image_get_width(vc->gfx.convert),
595e3500d1fSGerd Hoffmann              pixman_image_get_height(vc->gfx.convert),
596e3500d1fSGerd Hoffmann              pixman_image_get_stride(vc->gfx.convert));
597e3500d1fSGerd Hoffmann         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
598e3500d1fSGerd Hoffmann                                NULL, vc->gfx.convert,
599f0875536SGerd Hoffmann                                0, 0, 0, 0, 0, 0,
600e3500d1fSGerd Hoffmann                                pixman_image_get_width(vc->gfx.convert),
601e3500d1fSGerd Hoffmann                                pixman_image_get_height(vc->gfx.convert));
602f0875536SGerd Hoffmann     }
6039d9801cfSGerd Hoffmann 
6049d9801cfSGerd Hoffmann     if (resized) {
605e3500d1fSGerd Hoffmann         gd_update_windowsize(vc);
6069d9801cfSGerd Hoffmann     } else {
607e3500d1fSGerd Hoffmann         gd_update_full_redraw(vc);
6089d9801cfSGerd Hoffmann     }
6099d9801cfSGerd Hoffmann }
6109d9801cfSGerd Hoffmann 
61197edf3bdSGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = {
61297edf3bdSGerd Hoffmann     .dpy_name             = "gtk",
61397edf3bdSGerd Hoffmann     .dpy_gfx_update       = gd_update,
61497edf3bdSGerd Hoffmann     .dpy_gfx_switch       = gd_switch,
61597edf3bdSGerd Hoffmann     .dpy_gfx_check_format = qemu_pixman_check_format,
61697edf3bdSGerd Hoffmann     .dpy_refresh          = gd_refresh,
61797edf3bdSGerd Hoffmann     .dpy_mouse_set        = gd_mouse_set,
61897edf3bdSGerd Hoffmann     .dpy_cursor_define    = gd_cursor_define,
61997edf3bdSGerd Hoffmann };
62097edf3bdSGerd Hoffmann 
62197edf3bdSGerd Hoffmann 
62297edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
62397edf3bdSGerd Hoffmann 
62497edf3bdSGerd Hoffmann /** DisplayState Callbacks (opengl version) **/
62597edf3bdSGerd Hoffmann 
626925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
627925a0400SGerd Hoffmann 
628925a0400SGerd Hoffmann static const DisplayChangeListenerOps dcl_gl_area_ops = {
629925a0400SGerd Hoffmann     .dpy_name             = "gtk-egl",
630925a0400SGerd Hoffmann     .dpy_gfx_update       = gd_gl_area_update,
631925a0400SGerd Hoffmann     .dpy_gfx_switch       = gd_gl_area_switch,
632925a0400SGerd Hoffmann     .dpy_gfx_check_format = console_gl_check_format,
633925a0400SGerd Hoffmann     .dpy_refresh          = gd_gl_area_refresh,
634925a0400SGerd Hoffmann     .dpy_mouse_set        = gd_mouse_set,
635925a0400SGerd Hoffmann     .dpy_cursor_define    = gd_cursor_define,
636925a0400SGerd Hoffmann 
637925a0400SGerd Hoffmann     .dpy_gl_ctx_create       = gd_gl_area_create_context,
638925a0400SGerd Hoffmann     .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
639925a0400SGerd Hoffmann     .dpy_gl_ctx_make_current = gd_gl_area_make_current,
640925a0400SGerd Hoffmann     .dpy_gl_ctx_get_current  = gd_gl_area_get_current_context,
641f4c36bdaSGerd Hoffmann     .dpy_gl_scanout_texture  = gd_gl_area_scanout_texture,
642925a0400SGerd Hoffmann     .dpy_gl_update           = gd_gl_area_scanout_flush,
643925a0400SGerd Hoffmann };
644925a0400SGerd Hoffmann 
64511c82b58SGerd Hoffmann #endif /* CONFIG_GTK_GL */
646925a0400SGerd Hoffmann 
64797edf3bdSGerd Hoffmann static const DisplayChangeListenerOps dcl_egl_ops = {
64897edf3bdSGerd Hoffmann     .dpy_name             = "gtk-egl",
64997edf3bdSGerd Hoffmann     .dpy_gfx_update       = gd_egl_update,
65097edf3bdSGerd Hoffmann     .dpy_gfx_switch       = gd_egl_switch,
65197edf3bdSGerd Hoffmann     .dpy_gfx_check_format = console_gl_check_format,
65297edf3bdSGerd Hoffmann     .dpy_refresh          = gd_egl_refresh,
65397edf3bdSGerd Hoffmann     .dpy_mouse_set        = gd_mouse_set,
65497edf3bdSGerd Hoffmann     .dpy_cursor_define    = gd_cursor_define,
6554782aeb7SGerd Hoffmann 
6564782aeb7SGerd Hoffmann     .dpy_gl_ctx_create       = gd_egl_create_context,
6574782aeb7SGerd Hoffmann     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
6584782aeb7SGerd Hoffmann     .dpy_gl_ctx_make_current = gd_egl_make_current,
6594782aeb7SGerd Hoffmann     .dpy_gl_ctx_get_current  = qemu_egl_get_current_context,
660543a7a16SGerd Hoffmann     .dpy_gl_scanout_disable  = gd_egl_scanout_disable,
661f4c36bdaSGerd Hoffmann     .dpy_gl_scanout_texture  = gd_egl_scanout_texture,
66270763feaSGerd Hoffmann     .dpy_gl_scanout_dmabuf   = gd_egl_scanout_dmabuf,
663f1bd3132SGerd Hoffmann     .dpy_gl_cursor_dmabuf    = gd_egl_cursor_dmabuf,
664f1bd3132SGerd Hoffmann     .dpy_gl_cursor_position  = gd_egl_cursor_position,
66570763feaSGerd Hoffmann     .dpy_gl_release_dmabuf   = gd_egl_release_dmabuf,
6664782aeb7SGerd Hoffmann     .dpy_gl_update           = gd_egl_scanout_flush,
66797edf3bdSGerd Hoffmann };
66897edf3bdSGerd Hoffmann 
669925a0400SGerd Hoffmann #endif /* CONFIG_OPENGL */
67097edf3bdSGerd Hoffmann 
671a4ccabcfSAnthony Liguori /** QEMU Events **/
672a4ccabcfSAnthony Liguori 
673a4ccabcfSAnthony Liguori static void gd_change_runstate(void *opaque, int running, RunState state)
674a4ccabcfSAnthony Liguori {
675a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
676a4ccabcfSAnthony Liguori 
677a4ccabcfSAnthony Liguori     gd_update_caption(s);
678a4ccabcfSAnthony Liguori }
679a4ccabcfSAnthony Liguori 
680a4ccabcfSAnthony Liguori static void gd_mouse_mode_change(Notifier *notify, void *data)
681a4ccabcfSAnthony Liguori {
682800b0e81STakashi Iwai     GtkDisplayState *s;
68399623c90SGerd Hoffmann     int i;
684800b0e81STakashi Iwai 
685800b0e81STakashi Iwai     s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
686800b0e81STakashi Iwai     /* release the grab at switching to absolute mode */
687800b0e81STakashi Iwai     if (qemu_input_is_absolute() && gd_is_grab_active(s)) {
688800b0e81STakashi Iwai         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
689a4ccabcfSAnthony Liguori                                        FALSE);
690a4ccabcfSAnthony Liguori     }
69199623c90SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
69299623c90SGerd Hoffmann         VirtualConsole *vc = &s->vc[i];
69399623c90SGerd Hoffmann         gd_update_cursor(vc);
69499623c90SGerd Hoffmann     }
695800b0e81STakashi Iwai }
696a4ccabcfSAnthony Liguori 
697a4ccabcfSAnthony Liguori /** GTK Events **/
698a4ccabcfSAnthony Liguori 
699a4ccabcfSAnthony Liguori static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
700a4ccabcfSAnthony Liguori                                 void *opaque)
701a4ccabcfSAnthony Liguori {
702a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
7030c8d7065SGerd Hoffmann     bool allow_close = true;
704a4ccabcfSAnthony Liguori 
7050c8d7065SGerd Hoffmann     if (s->opts->has_window_close && !s->opts->window_close) {
7060c8d7065SGerd Hoffmann         allow_close = false;
7070c8d7065SGerd Hoffmann     }
7080c8d7065SGerd Hoffmann 
7090c8d7065SGerd Hoffmann     if (allow_close) {
710a4ccabcfSAnthony Liguori         qmp_quit(NULL);
711a4ccabcfSAnthony Liguori     }
712a4ccabcfSAnthony Liguori 
713a4ccabcfSAnthony Liguori     return TRUE;
714a4ccabcfSAnthony Liguori }
715a4ccabcfSAnthony Liguori 
716925a0400SGerd Hoffmann static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height)
717925a0400SGerd Hoffmann {
718925a0400SGerd Hoffmann     QemuUIInfo info;
719925a0400SGerd Hoffmann 
720925a0400SGerd Hoffmann     memset(&info, 0, sizeof(info));
721925a0400SGerd Hoffmann     info.width = width;
722925a0400SGerd Hoffmann     info.height = height;
723925a0400SGerd Hoffmann     dpy_set_ui_info(vc->gfx.dcl.con, &info);
724925a0400SGerd Hoffmann }
725925a0400SGerd Hoffmann 
726925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
727925a0400SGerd Hoffmann 
728925a0400SGerd Hoffmann static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
729925a0400SGerd Hoffmann                                 void *opaque)
730925a0400SGerd Hoffmann {
731925a0400SGerd Hoffmann     VirtualConsole *vc = opaque;
732925a0400SGerd Hoffmann 
733925a0400SGerd Hoffmann     if (vc->gfx.gls) {
734925a0400SGerd Hoffmann         gd_gl_area_draw(vc);
735925a0400SGerd Hoffmann     }
736925a0400SGerd Hoffmann     return TRUE;
737925a0400SGerd Hoffmann }
738925a0400SGerd Hoffmann 
739925a0400SGerd Hoffmann static void gd_resize_event(GtkGLArea *area,
740925a0400SGerd Hoffmann                             gint width, gint height, gpointer *opaque)
741925a0400SGerd Hoffmann {
742925a0400SGerd Hoffmann     VirtualConsole *vc = (void *)opaque;
743925a0400SGerd Hoffmann 
744925a0400SGerd Hoffmann     gd_set_ui_info(vc, width, height);
745925a0400SGerd Hoffmann }
746925a0400SGerd Hoffmann 
747925a0400SGerd Hoffmann #endif
748925a0400SGerd Hoffmann 
749a4ccabcfSAnthony Liguori static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
750a4ccabcfSAnthony Liguori {
751e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
752e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
753c6158483SAnthony Liguori     int mx, my;
754a4ccabcfSAnthony Liguori     int ww, wh;
755a4ccabcfSAnthony Liguori     int fbw, fbh;
756a4ccabcfSAnthony Liguori 
75797edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
75897edf3bdSGerd Hoffmann     if (vc->gfx.gls) {
75911c82b58SGerd Hoffmann         if (gtk_use_gl_area) {
760925a0400SGerd Hoffmann             /* invoke render callback please */
761925a0400SGerd Hoffmann             return FALSE;
76211c82b58SGerd Hoffmann         } else {
76397edf3bdSGerd Hoffmann             gd_egl_draw(vc);
76497edf3bdSGerd Hoffmann             return TRUE;
76511c82b58SGerd Hoffmann         }
76697edf3bdSGerd Hoffmann     }
76797edf3bdSGerd Hoffmann #endif
76897edf3bdSGerd Hoffmann 
769c6158483SAnthony Liguori     if (!gtk_widget_get_realized(widget)) {
770c6158483SAnthony Liguori         return FALSE;
771c6158483SAnthony Liguori     }
772f98f43eaSGerd Hoffmann     if (!vc->gfx.ds) {
773f98f43eaSGerd Hoffmann         return FALSE;
774f98f43eaSGerd Hoffmann     }
775c6158483SAnthony Liguori 
776e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds);
777e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds);
778a4ccabcfSAnthony Liguori 
77989d85cdeSDaniel P. Berrangé     ww = gdk_window_get_width(gtk_widget_get_window(widget));
78089d85cdeSDaniel P. Berrangé     wh = gdk_window_get_height(gtk_widget_get_window(widget));
781a4ccabcfSAnthony Liguori 
782c6158483SAnthony Liguori     if (s->full_screen) {
783e3500d1fSGerd Hoffmann         vc->gfx.scale_x = (double)ww / fbw;
784e3500d1fSGerd Hoffmann         vc->gfx.scale_y = (double)wh / fbh;
785c6158483SAnthony Liguori     } else if (s->free_scale) {
786c6158483SAnthony Liguori         double sx, sy;
787c6158483SAnthony Liguori 
788c6158483SAnthony Liguori         sx = (double)ww / fbw;
789c6158483SAnthony Liguori         sy = (double)wh / fbh;
790c6158483SAnthony Liguori 
791e3500d1fSGerd Hoffmann         vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
792a4ccabcfSAnthony Liguori     }
793a4ccabcfSAnthony Liguori 
794e3500d1fSGerd Hoffmann     fbw *= vc->gfx.scale_x;
795e3500d1fSGerd Hoffmann     fbh *= vc->gfx.scale_y;
7965104a1f6SAnthony Liguori 
797c6158483SAnthony Liguori     mx = my = 0;
798c6158483SAnthony Liguori     if (ww > fbw) {
799c6158483SAnthony Liguori         mx = (ww - fbw) / 2;
800c6158483SAnthony Liguori     }
801c6158483SAnthony Liguori     if (wh > fbh) {
802c6158483SAnthony Liguori         my = (wh - fbh) / 2;
803c6158483SAnthony Liguori     }
804c6158483SAnthony Liguori 
805c6158483SAnthony Liguori     cairo_rectangle(cr, 0, 0, ww, wh);
806c6158483SAnthony Liguori 
807c6158483SAnthony Liguori     /* Optionally cut out the inner area where the pixmap
808c6158483SAnthony Liguori        will be drawn. This avoids 'flashing' since we're
809c6158483SAnthony Liguori        not double-buffering. Note we're using the undocumented
810c6158483SAnthony Liguori        behaviour of drawing the rectangle from right to left
811c6158483SAnthony Liguori        to cut out the whole */
812c6158483SAnthony Liguori     cairo_rectangle(cr, mx + fbw, my,
813c6158483SAnthony Liguori                     -1 * fbw, fbh);
814c6158483SAnthony Liguori     cairo_fill(cr);
815c6158483SAnthony Liguori 
816e3500d1fSGerd Hoffmann     cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
817e3500d1fSGerd Hoffmann     cairo_set_source_surface(cr, vc->gfx.surface,
818e3500d1fSGerd Hoffmann                              mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
819a4ccabcfSAnthony Liguori     cairo_paint(cr);
820a4ccabcfSAnthony Liguori 
821a4ccabcfSAnthony Liguori     return TRUE;
822a4ccabcfSAnthony Liguori }
823a4ccabcfSAnthony Liguori 
824a4ccabcfSAnthony Liguori static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
825a4ccabcfSAnthony Liguori                                 void *opaque)
826a4ccabcfSAnthony Liguori {
827e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
828e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
829a4ccabcfSAnthony Liguori     int x, y;
830c6158483SAnthony Liguori     int mx, my;
831c6158483SAnthony Liguori     int fbh, fbw;
832c6158483SAnthony Liguori     int ww, wh;
833a4ccabcfSAnthony Liguori 
834f98f43eaSGerd Hoffmann     if (!vc->gfx.ds) {
835f98f43eaSGerd Hoffmann         return TRUE;
836f98f43eaSGerd Hoffmann     }
837f98f43eaSGerd Hoffmann 
838e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
839e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
840c6158483SAnthony Liguori 
84189d85cdeSDaniel P. Berrangé     ww = gdk_window_get_width(gtk_widget_get_window(vc->gfx.drawing_area));
84289d85cdeSDaniel P. Berrangé     wh = gdk_window_get_height(gtk_widget_get_window(vc->gfx.drawing_area));
843c6158483SAnthony Liguori 
844c6158483SAnthony Liguori     mx = my = 0;
845c6158483SAnthony Liguori     if (ww > fbw) {
846c6158483SAnthony Liguori         mx = (ww - fbw) / 2;
847c6158483SAnthony Liguori     }
848c6158483SAnthony Liguori     if (wh > fbh) {
849c6158483SAnthony Liguori         my = (wh - fbh) / 2;
850c6158483SAnthony Liguori     }
851c6158483SAnthony Liguori 
852e3500d1fSGerd Hoffmann     x = (motion->x - mx) / vc->gfx.scale_x;
853e3500d1fSGerd Hoffmann     y = (motion->y - my) / vc->gfx.scale_y;
854c6158483SAnthony Liguori 
855e61031cdSTakashi Iwai     if (qemu_input_is_absolute()) {
856c6158483SAnthony Liguori         if (x < 0 || y < 0 ||
857e3500d1fSGerd Hoffmann             x >= surface_width(vc->gfx.ds) ||
858e3500d1fSGerd Hoffmann             y >= surface_height(vc->gfx.ds)) {
859c6158483SAnthony Liguori             return TRUE;
860c6158483SAnthony Liguori         }
861e3500d1fSGerd Hoffmann         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
8629cfa7ab9SPhilippe Voinov                              0, surface_width(vc->gfx.ds));
863e3500d1fSGerd Hoffmann         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
8649cfa7ab9SPhilippe Voinov                              0, surface_height(vc->gfx.ds));
865192f81bfSGerd Hoffmann         qemu_input_event_sync();
8662884cf5bSGerd Hoffmann     } else if (s->last_set && s->ptr_owner == vc) {
867e3500d1fSGerd Hoffmann         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
868e3500d1fSGerd Hoffmann         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
869192f81bfSGerd Hoffmann         qemu_input_event_sync();
870a4ccabcfSAnthony Liguori     }
871a4ccabcfSAnthony Liguori     s->last_x = x;
872a4ccabcfSAnthony Liguori     s->last_y = y;
873e61031cdSTakashi Iwai     s->last_set = TRUE;
874a4ccabcfSAnthony Liguori 
8752884cf5bSGerd Hoffmann     if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
876e3500d1fSGerd Hoffmann         GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
87776d8f93bSAlberto Garcia         int screen_width, screen_height;
87876d8f93bSAlberto Garcia 
8795104a1f6SAnthony Liguori         int x = (int)motion->x_root;
8805104a1f6SAnthony Liguori         int y = (int)motion->y_root;
8815104a1f6SAnthony Liguori 
88276d8f93bSAlberto Garcia #if GTK_CHECK_VERSION(3, 22, 0)
88376d8f93bSAlberto Garcia         {
88476d8f93bSAlberto Garcia             GdkDisplay *dpy = gtk_widget_get_display(widget);
88576d8f93bSAlberto Garcia             GdkWindow *win = gtk_widget_get_window(widget);
88676d8f93bSAlberto Garcia             GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
88776d8f93bSAlberto Garcia             GdkRectangle geometry;
88876d8f93bSAlberto Garcia             gdk_monitor_get_geometry(monitor, &geometry);
88976d8f93bSAlberto Garcia             screen_width = geometry.width;
89076d8f93bSAlberto Garcia             screen_height = geometry.height;
89176d8f93bSAlberto Garcia         }
89276d8f93bSAlberto Garcia #else
89376d8f93bSAlberto Garcia         {
89476d8f93bSAlberto Garcia             screen_width = gdk_screen_get_width(screen);
89576d8f93bSAlberto Garcia             screen_height = gdk_screen_get_height(screen);
89676d8f93bSAlberto Garcia         }
89776d8f93bSAlberto Garcia #endif
89876d8f93bSAlberto Garcia 
8995104a1f6SAnthony Liguori         /* In relative mode check to see if client pointer hit
9005104a1f6SAnthony Liguori          * one of the screen edges, and if so move it back by
9015104a1f6SAnthony Liguori          * 200 pixels. This is important because the pointer
9025104a1f6SAnthony Liguori          * in the server doesn't correspond 1-for-1, and so
9035104a1f6SAnthony Liguori          * may still be only half way across the screen. Without
9045104a1f6SAnthony Liguori          * this warp, the server pointer would thus appear to hit
9055104a1f6SAnthony Liguori          * an invisible wall */
9065104a1f6SAnthony Liguori         if (x == 0) {
9075104a1f6SAnthony Liguori             x += 200;
9085104a1f6SAnthony Liguori         }
9095104a1f6SAnthony Liguori         if (y == 0) {
9105104a1f6SAnthony Liguori             y += 200;
9115104a1f6SAnthony Liguori         }
91276d8f93bSAlberto Garcia         if (x == (screen_width - 1)) {
9135104a1f6SAnthony Liguori             x -= 200;
9145104a1f6SAnthony Liguori         }
91576d8f93bSAlberto Garcia         if (y == (screen_height - 1)) {
9165104a1f6SAnthony Liguori             y -= 200;
9175104a1f6SAnthony Liguori         }
9185104a1f6SAnthony Liguori 
9195104a1f6SAnthony Liguori         if (x != (int)motion->x_root || y != (int)motion->y_root) {
9208906de76SDaniel P. Berrange             GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
9218906de76SDaniel P. Berrange             gdk_device_warp(dev, screen, x, y);
922e61031cdSTakashi Iwai             s->last_set = FALSE;
9235104a1f6SAnthony Liguori             return FALSE;
9245104a1f6SAnthony Liguori         }
9255104a1f6SAnthony Liguori     }
926a4ccabcfSAnthony Liguori     return TRUE;
927a4ccabcfSAnthony Liguori }
928a4ccabcfSAnthony Liguori 
929a4ccabcfSAnthony Liguori static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
930a4ccabcfSAnthony Liguori                                 void *opaque)
931a4ccabcfSAnthony Liguori {
932e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
933e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
934192f81bfSGerd Hoffmann     InputButton btn;
935a4ccabcfSAnthony Liguori 
936800b0e81STakashi Iwai     /* implicitly grab the input at the first click in the relative mode */
937800b0e81STakashi Iwai     if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
9382884cf5bSGerd Hoffmann         !qemu_input_is_absolute() && s->ptr_owner != vc) {
9392884cf5bSGerd Hoffmann         if (!vc->window) {
940800b0e81STakashi Iwai             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
941800b0e81STakashi Iwai                                            TRUE);
9422884cf5bSGerd Hoffmann         } else {
943d531deefSGerd Hoffmann             gd_grab_pointer(vc, "relative-mode-click");
9442884cf5bSGerd Hoffmann         }
945800b0e81STakashi Iwai         return TRUE;
946800b0e81STakashi Iwai     }
947800b0e81STakashi Iwai 
948a4ccabcfSAnthony Liguori     if (button->button == 1) {
949192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_LEFT;
950a4ccabcfSAnthony Liguori     } else if (button->button == 2) {
951192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_MIDDLE;
952a4ccabcfSAnthony Liguori     } else if (button->button == 3) {
953192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_RIGHT;
9541266b68cSFabian Lesniak     } else if (button->button == 8) {
9551266b68cSFabian Lesniak         btn = INPUT_BUTTON_SIDE;
9561266b68cSFabian Lesniak     } else if (button->button == 9) {
9571266b68cSFabian Lesniak         btn = INPUT_BUTTON_EXTRA;
958a4ccabcfSAnthony Liguori     } else {
959192f81bfSGerd Hoffmann         return TRUE;
960a4ccabcfSAnthony Liguori     }
961a4ccabcfSAnthony Liguori 
962e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn,
963e3500d1fSGerd Hoffmann                          button->type == GDK_BUTTON_PRESS);
964192f81bfSGerd Hoffmann     qemu_input_event_sync();
965a4ccabcfSAnthony Liguori     return TRUE;
966a4ccabcfSAnthony Liguori }
967a4ccabcfSAnthony Liguori 
968d58b9122SJan Kiszka static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
969d58b9122SJan Kiszka                                 void *opaque)
970d58b9122SJan Kiszka {
971e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
972d58b9122SJan Kiszka     InputButton btn;
973d58b9122SJan Kiszka 
974d58b9122SJan Kiszka     if (scroll->direction == GDK_SCROLL_UP) {
975f22d0af0SGerd Hoffmann         btn = INPUT_BUTTON_WHEEL_UP;
976d58b9122SJan Kiszka     } else if (scroll->direction == GDK_SCROLL_DOWN) {
977f22d0af0SGerd Hoffmann         btn = INPUT_BUTTON_WHEEL_DOWN;
978e229d1efSOGAWA Hirofumi     } else if (scroll->direction == GDK_SCROLL_SMOOTH) {
979e229d1efSOGAWA Hirofumi         gdouble delta_x, delta_y;
980e229d1efSOGAWA Hirofumi         if (!gdk_event_get_scroll_deltas((GdkEvent *)scroll,
981e229d1efSOGAWA Hirofumi                                          &delta_x, &delta_y)) {
982e229d1efSOGAWA Hirofumi             return TRUE;
983e229d1efSOGAWA Hirofumi         }
984a0fbb9a8SSergio Lopez         if (delta_y == 0) {
985a0fbb9a8SSergio Lopez             return TRUE;
986a0fbb9a8SSergio Lopez         } else if (delta_y > 0) {
987e229d1efSOGAWA Hirofumi             btn = INPUT_BUTTON_WHEEL_DOWN;
988e229d1efSOGAWA Hirofumi         } else {
989e229d1efSOGAWA Hirofumi             btn = INPUT_BUTTON_WHEEL_UP;
990e229d1efSOGAWA Hirofumi         }
991d58b9122SJan Kiszka     } else {
992d58b9122SJan Kiszka         return TRUE;
993d58b9122SJan Kiszka     }
994d58b9122SJan Kiszka 
995e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
996d58b9122SJan Kiszka     qemu_input_event_sync();
997e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
998d58b9122SJan Kiszka     qemu_input_event_sync();
999d58b9122SJan Kiszka     return TRUE;
1000d58b9122SJan Kiszka }
1001d58b9122SJan Kiszka 
10022ec78706SDaniel P. Berrange 
10032ec78706SDaniel P. Berrange static const guint16 *gd_get_keymap(size_t *maplen)
1004a4ccabcfSAnthony Liguori {
10052ec78706SDaniel P. Berrange     GdkDisplay *dpy = gdk_display_get_default();
10062ec78706SDaniel P. Berrange 
10072ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_X11
10082ec78706SDaniel P. Berrange     if (GDK_IS_X11_DISPLAY(dpy)) {
10092ec78706SDaniel P. Berrange         trace_gd_keymap_windowing("x11");
10102ec78706SDaniel P. Berrange         return qemu_xkeymap_mapping_table(
10112ec78706SDaniel P. Berrange             gdk_x11_display_get_xdisplay(dpy), maplen);
10122ec78706SDaniel P. Berrange     }
10132ec78706SDaniel P. Berrange #endif
10142ec78706SDaniel P. Berrange 
10152ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_WAYLAND
10162ec78706SDaniel P. Berrange     if (GDK_IS_WAYLAND_DISPLAY(dpy)) {
10172ec78706SDaniel P. Berrange         trace_gd_keymap_windowing("wayland");
10182ec78706SDaniel P. Berrange         *maplen = qemu_input_map_xorgevdev_to_qcode_len;
10192ec78706SDaniel P. Berrange         return qemu_input_map_xorgevdev_to_qcode;
10202ec78706SDaniel P. Berrange     }
10212ec78706SDaniel P. Berrange #endif
1022a4ccabcfSAnthony Liguori 
10230a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_WIN32
10240a337ed0SGerd Hoffmann     if (GDK_IS_WIN32_DISPLAY(dpy)) {
10252ec78706SDaniel P. Berrange         trace_gd_keymap_windowing("win32");
10262ec78706SDaniel P. Berrange         *maplen = qemu_input_map_win32_to_qcode_len;
10272ec78706SDaniel P. Berrange         return qemu_input_map_win32_to_qcode;
10280a337ed0SGerd Hoffmann     }
10290a337ed0SGerd Hoffmann #endif
1030a4ccabcfSAnthony Liguori 
10312ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_QUARTZ
10322ec78706SDaniel P. Berrange     if (GDK_IS_QUARTZ_DISPLAY(dpy)) {
10332ec78706SDaniel P. Berrange         trace_gd_keymap_windowing("quartz");
10342ec78706SDaniel P. Berrange         *maplen = qemu_input_map_osx_to_qcode_len;
10352ec78706SDaniel P. Berrange         return qemu_input_map_osx_to_qcode;
10363158a348SBruce Rogers     }
10370a337ed0SGerd Hoffmann #endif
10382ec78706SDaniel P. Berrange 
10392ec78706SDaniel P. Berrange #ifdef GDK_WINDOWING_BROADWAY
10402ec78706SDaniel P. Berrange     if (GDK_IS_BROADWAY_DISPLAY(dpy)) {
10412ec78706SDaniel P. Berrange         trace_gd_keymap_windowing("broadway");
10422ec78706SDaniel P. Berrange         g_warning("experimental: using broadway, x11 virtual keysym\n"
10432ec78706SDaniel P. Berrange                   "mapping - with very limited support. See also\n"
10442ec78706SDaniel P. Berrange                   "https://bugzilla.gnome.org/show_bug.cgi?id=700105");
10452ec78706SDaniel P. Berrange         *maplen = qemu_input_map_x11_to_qcode_len;
10462ec78706SDaniel P. Berrange         return qemu_input_map_x11_to_qcode;
10472ec78706SDaniel P. Berrange     }
1048a8ffb372SDaniel P. Berrange #endif
10492ec78706SDaniel P. Berrange 
10502ec78706SDaniel P. Berrange     g_warning("Unsupported GDK Windowing platform.\n"
10512ec78706SDaniel P. Berrange               "Disabling extended keycode tables.\n"
10522ec78706SDaniel P. Berrange               "Please report to qemu-devel@nongnu.org\n"
10532ec78706SDaniel P. Berrange               "including the following information:\n"
10542ec78706SDaniel P. Berrange               "\n"
10552ec78706SDaniel P. Berrange               "  - Operating system\n"
10562ec78706SDaniel P. Berrange               "  - GDK Windowing system build\n");
10572ec78706SDaniel P. Berrange     return NULL;
1058a4ccabcfSAnthony Liguori }
1059a4ccabcfSAnthony Liguori 
10602ec78706SDaniel P. Berrange 
10612ec78706SDaniel P. Berrange static int gd_map_keycode(int scancode)
10622ec78706SDaniel P. Berrange {
10632ec78706SDaniel P. Berrange     if (!keycode_map) {
10642ec78706SDaniel P. Berrange         return 0;
10652ec78706SDaniel P. Berrange     }
10662ec78706SDaniel P. Berrange     if (scancode > keycode_maplen) {
10672ec78706SDaniel P. Berrange         return 0;
10682ec78706SDaniel P. Berrange     }
10692ec78706SDaniel P. Berrange 
10702ec78706SDaniel P. Berrange     return keycode_map[scancode];
1071932f2d7eSGerd Hoffmann }
1072932f2d7eSGerd Hoffmann 
1073f8c223f6SGerd Hoffmann static gboolean gd_text_key_down(GtkWidget *widget,
1074f8c223f6SGerd Hoffmann                                  GdkEventKey *key, void *opaque)
1075f8c223f6SGerd Hoffmann {
1076f8c223f6SGerd Hoffmann     VirtualConsole *vc = opaque;
1077f8c223f6SGerd Hoffmann     QemuConsole *con = vc->gfx.dcl.con;
1078f8c223f6SGerd Hoffmann 
107985617885SThomas Huth     if (key->keyval == GDK_KEY_Delete) {
1080da024b1eSGerd Hoffmann         kbd_put_qcode_console(con, Q_KEY_CODE_DELETE, false);
108185617885SThomas Huth     } else if (key->length) {
1082f8c223f6SGerd Hoffmann         kbd_put_string_console(con, key->string, key->length);
1083f8c223f6SGerd Hoffmann     } else {
10842ec78706SDaniel P. Berrange         int qcode = gd_map_keycode(key->hardware_keycode);
1085da024b1eSGerd Hoffmann         kbd_put_qcode_console(con, qcode, false);
1086f8c223f6SGerd Hoffmann     }
1087f8c223f6SGerd Hoffmann     return TRUE;
1088f8c223f6SGerd Hoffmann }
1089f8c223f6SGerd Hoffmann 
1090932f2d7eSGerd Hoffmann static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
1091932f2d7eSGerd Hoffmann {
1092932f2d7eSGerd Hoffmann     VirtualConsole *vc = opaque;
1093932f2d7eSGerd Hoffmann     GtkDisplayState *s = vc->s;
10942ec78706SDaniel P. Berrange     int qcode;
1095932f2d7eSGerd Hoffmann 
10961a01716aSJan Kiszka     if (s->ignore_keys) {
10971a01716aSJan Kiszka         s->ignore_keys = (key->type == GDK_KEY_PRESS);
10981a01716aSJan Kiszka         return TRUE;
10991a01716aSJan Kiszka     }
11001a01716aSJan Kiszka 
110108774f66SDaniel P. Berrange #ifdef WIN32
110208774f66SDaniel P. Berrange     /* on windows, we ought to ignore the reserved key event? */
110308774f66SDaniel P. Berrange     if (key->hardware_keycode == 0xff)
110408774f66SDaniel P. Berrange         return false;
110508774f66SDaniel P. Berrange #endif
110608774f66SDaniel P. Berrange 
11078026a81aSDaniel P. Berrange     if (key->keyval == GDK_KEY_Pause
11088026a81aSDaniel P. Berrange #ifdef G_OS_WIN32
11098026a81aSDaniel P. Berrange         /* for some reason GDK does not fill keyval for VK_PAUSE
11108026a81aSDaniel P. Berrange          * See https://bugzilla.gnome.org/show_bug.cgi?id=769214
11118026a81aSDaniel P. Berrange          */
11128026a81aSDaniel P. Berrange         || key->hardware_keycode == VK_PAUSE
11138026a81aSDaniel P. Berrange #endif
11148026a81aSDaniel P. Berrange         ) {
11150c0d4273SGerd Hoffmann         qkbd_state_key_event(vc->gfx.kbd, Q_KEY_CODE_PAUSE,
11165c960521SMartin Decky                              key->type == GDK_KEY_PRESS);
11175c960521SMartin Decky         return TRUE;
11185c960521SMartin Decky     }
11195c960521SMartin Decky 
11202ec78706SDaniel P. Berrange     qcode = gd_map_keycode(key->hardware_keycode);
1121932f2d7eSGerd Hoffmann 
11222ec78706SDaniel P. Berrange     trace_gd_key_event(vc->label, key->hardware_keycode, qcode,
1123a4ccabcfSAnthony Liguori                        (key->type == GDK_KEY_PRESS) ? "down" : "up");
1124a4ccabcfSAnthony Liguori 
11250c0d4273SGerd Hoffmann     qkbd_state_key_event(vc->gfx.kbd, qcode,
1126af98ba92SGerd Hoffmann                          key->type == GDK_KEY_PRESS);
1127a4ccabcfSAnthony Liguori 
1128a4ccabcfSAnthony Liguori     return TRUE;
1129a4ccabcfSAnthony Liguori }
1130a4ccabcfSAnthony Liguori 
11310d0e044dSTakashi Iwai static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
11320d0e044dSTakashi Iwai {
11330d0e044dSTakashi Iwai     if (event->type == GDK_MOTION_NOTIFY) {
11340d0e044dSTakashi Iwai         return gd_motion_event(widget, &event->motion, opaque);
11350d0e044dSTakashi Iwai     }
11360d0e044dSTakashi Iwai     return FALSE;
11370d0e044dSTakashi Iwai }
11380d0e044dSTakashi Iwai 
1139a4ccabcfSAnthony Liguori /** Window Menu Actions **/
1140a4ccabcfSAnthony Liguori 
114130e8f22bSJan Kiszka static void gd_menu_pause(GtkMenuItem *item, void *opaque)
114230e8f22bSJan Kiszka {
114330e8f22bSJan Kiszka     GtkDisplayState *s = opaque;
114430e8f22bSJan Kiszka 
114530e8f22bSJan Kiszka     if (s->external_pause_update) {
114630e8f22bSJan Kiszka         return;
114730e8f22bSJan Kiszka     }
114830e8f22bSJan Kiszka     if (runstate_is_running()) {
114930e8f22bSJan Kiszka         qmp_stop(NULL);
115030e8f22bSJan Kiszka     } else {
115130e8f22bSJan Kiszka         qmp_cont(NULL);
115230e8f22bSJan Kiszka     }
115330e8f22bSJan Kiszka }
115430e8f22bSJan Kiszka 
115530e8f22bSJan Kiszka static void gd_menu_reset(GtkMenuItem *item, void *opaque)
115630e8f22bSJan Kiszka {
115730e8f22bSJan Kiszka     qmp_system_reset(NULL);
115830e8f22bSJan Kiszka }
115930e8f22bSJan Kiszka 
116030e8f22bSJan Kiszka static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
116130e8f22bSJan Kiszka {
116230e8f22bSJan Kiszka     qmp_system_powerdown(NULL);
116330e8f22bSJan Kiszka }
116430e8f22bSJan Kiszka 
1165a4ccabcfSAnthony Liguori static void gd_menu_quit(GtkMenuItem *item, void *opaque)
1166a4ccabcfSAnthony Liguori {
1167a4ccabcfSAnthony Liguori     qmp_quit(NULL);
1168a4ccabcfSAnthony Liguori }
1169a4ccabcfSAnthony Liguori 
1170a4ccabcfSAnthony Liguori static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
1171a4ccabcfSAnthony Liguori {
1172a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
1173e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_by_menu(s);
1174e72b59faSJohn Snow     GtkNotebook *nb = GTK_NOTEBOOK(s->notebook);
1175832189c9SGerd Hoffmann     gint page;
1176a4ccabcfSAnthony Liguori 
11776db253caSJan Kiszka     gtk_release_modifiers(s);
1178271a25c0SGerd Hoffmann     if (vc) {
1179e72b59faSJohn Snow         page = gtk_notebook_page_num(nb, vc->tab_item);
1180e72b59faSJohn Snow         gtk_notebook_set_current_page(nb, page);
11819d677e1cSJan Kiszka         gtk_widget_grab_focus(vc->focus);
1182d861def3SAnthony Liguori     }
11831a01716aSJan Kiszka     s->ignore_keys = false;
1184d861def3SAnthony Liguori }
1185a4ccabcfSAnthony Liguori 
1186277836c8SCole Robinson static void gd_accel_switch_vc(void *opaque)
1187277836c8SCole Robinson {
1188277836c8SCole Robinson     VirtualConsole *vc = opaque;
11891a01716aSJan Kiszka 
1190277836c8SCole Robinson     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
1191277836c8SCole Robinson }
1192277836c8SCole Robinson 
1193a4ccabcfSAnthony Liguori static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
1194a4ccabcfSAnthony Liguori {
1195a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
1196fa7a1e52SGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1197a4ccabcfSAnthony Liguori 
1198a4ccabcfSAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
1199a4ccabcfSAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
1200a4ccabcfSAnthony Liguori     } else {
1201a4ccabcfSAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1202a4ccabcfSAnthony Liguori     }
1203fa7a1e52SGerd Hoffmann     gd_update_windowsize(vc);
1204a4ccabcfSAnthony Liguori }
1205a4ccabcfSAnthony Liguori 
1206cdeb7090SGerd Hoffmann static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
1207cdeb7090SGerd Hoffmann                                     void *opaque)
1208cdeb7090SGerd Hoffmann {
1209cdeb7090SGerd Hoffmann     VirtualConsole *vc = opaque;
1210cdeb7090SGerd Hoffmann     GtkDisplayState *s = vc->s;
1211cdeb7090SGerd Hoffmann 
1212cdeb7090SGerd Hoffmann     gtk_widget_set_sensitive(vc->menu_item, true);
1213316cb068SGerd Hoffmann     gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
1214cdeb7090SGerd Hoffmann     gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
1215cdeb7090SGerd Hoffmann                                     vc->tab_item, vc->label);
1216cdeb7090SGerd Hoffmann     gtk_widget_destroy(vc->window);
1217cdeb7090SGerd Hoffmann     vc->window = NULL;
1218cdeb7090SGerd Hoffmann     return TRUE;
1219cdeb7090SGerd Hoffmann }
1220cdeb7090SGerd Hoffmann 
12210c77a37fSGerd Hoffmann static gboolean gd_win_grab(void *opaque)
12220c77a37fSGerd Hoffmann {
12230c77a37fSGerd Hoffmann     VirtualConsole *vc = opaque;
12240c77a37fSGerd Hoffmann 
12250c77a37fSGerd Hoffmann     fprintf(stderr, "%s: %s\n", __func__, vc->label);
12260c77a37fSGerd Hoffmann     if (vc->s->ptr_owner) {
12270c77a37fSGerd Hoffmann         gd_ungrab_pointer(vc->s);
12280c77a37fSGerd Hoffmann     } else {
1229d531deefSGerd Hoffmann         gd_grab_pointer(vc, "user-request-detached-tab");
12300c77a37fSGerd Hoffmann     }
12310c77a37fSGerd Hoffmann     return TRUE;
12320c77a37fSGerd Hoffmann }
12330c77a37fSGerd Hoffmann 
1234cdeb7090SGerd Hoffmann static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
1235cdeb7090SGerd Hoffmann {
1236cdeb7090SGerd Hoffmann     GtkDisplayState *s = opaque;
1237cdeb7090SGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1238cdeb7090SGerd Hoffmann 
1239f8c223f6SGerd Hoffmann     if (vc->type == GD_VC_GFX &&
1240f8c223f6SGerd Hoffmann         qemu_console_is_graphic(vc->gfx.dcl.con)) {
1241aa0a55d4SGerd Hoffmann         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1242aa0a55d4SGerd Hoffmann                                        FALSE);
1243cdeb7090SGerd Hoffmann     }
1244cdeb7090SGerd Hoffmann     if (!vc->window) {
1245cdeb7090SGerd Hoffmann         gtk_widget_set_sensitive(vc->menu_item, false);
1246cdeb7090SGerd Hoffmann         vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1247316cb068SGerd Hoffmann         gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
1248cdeb7090SGerd Hoffmann 
1249cdeb7090SGerd Hoffmann         g_signal_connect(vc->window, "delete-event",
1250cdeb7090SGerd Hoffmann                          G_CALLBACK(gd_tab_window_close), vc);
1251cdeb7090SGerd Hoffmann         gtk_widget_show_all(vc->window);
12524eeaa3a8SGerd Hoffmann 
1253f8c223f6SGerd Hoffmann         if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
12540c77a37fSGerd Hoffmann             GtkAccelGroup *ag = gtk_accel_group_new();
12550c77a37fSGerd Hoffmann             gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
12560c77a37fSGerd Hoffmann 
1257f8c223f6SGerd Hoffmann             GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
1258f8c223f6SGerd Hoffmann                                                vc, NULL);
12590c77a37fSGerd Hoffmann             gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
1260f8c223f6SGerd Hoffmann         }
12610c77a37fSGerd Hoffmann 
126282fc1809SGerd Hoffmann         gd_update_geometry_hints(vc);
12634eeaa3a8SGerd Hoffmann         gd_update_caption(s);
1264cdeb7090SGerd Hoffmann     }
1265cdeb7090SGerd Hoffmann }
1266cdeb7090SGerd Hoffmann 
12671d187745SPeter Wu static void gd_menu_show_menubar(GtkMenuItem *item, void *opaque)
12681d187745SPeter Wu {
12691d187745SPeter Wu     GtkDisplayState *s = opaque;
12701d187745SPeter Wu     VirtualConsole *vc = gd_vc_find_current(s);
12711d187745SPeter Wu 
12721d187745SPeter Wu     if (s->full_screen) {
12731d187745SPeter Wu         return;
12741d187745SPeter Wu     }
12751d187745SPeter Wu 
12761d187745SPeter Wu     if (gtk_check_menu_item_get_active(
12771d187745SPeter Wu                 GTK_CHECK_MENU_ITEM(s->show_menubar_item))) {
12781d187745SPeter Wu         gtk_widget_show(s->menu_bar);
12791d187745SPeter Wu     } else {
12801d187745SPeter Wu         gtk_widget_hide(s->menu_bar);
12811d187745SPeter Wu     }
12821d187745SPeter Wu     gd_update_windowsize(vc);
12831d187745SPeter Wu }
12841d187745SPeter Wu 
12851d187745SPeter Wu static void gd_accel_show_menubar(void *opaque)
12861d187745SPeter Wu {
12871d187745SPeter Wu     GtkDisplayState *s = opaque;
12881d187745SPeter Wu     gtk_menu_item_activate(GTK_MENU_ITEM(s->show_menubar_item));
12891d187745SPeter Wu }
12901d187745SPeter Wu 
1291c6158483SAnthony Liguori static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
1292c6158483SAnthony Liguori {
1293c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1294e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1295c6158483SAnthony Liguori 
129610409282SStefan Weil     if (!s->full_screen) {
1297c6158483SAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1298b0f31820SCole Robinson         gtk_widget_hide(s->menu_bar);
1299e3500d1fSGerd Hoffmann         if (vc->type == GD_VC_GFX) {
1300e3500d1fSGerd Hoffmann             gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
1301c6158483SAnthony Liguori         }
1302e3500d1fSGerd Hoffmann         gtk_window_fullscreen(GTK_WINDOW(s->window));
1303c6158483SAnthony Liguori         s->full_screen = TRUE;
1304c6158483SAnthony Liguori     } else {
1305c6158483SAnthony Liguori         gtk_window_unfullscreen(GTK_WINDOW(s->window));
1306c6158483SAnthony Liguori         gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
13071d187745SPeter Wu         if (gtk_check_menu_item_get_active(
13081d187745SPeter Wu                     GTK_CHECK_MENU_ITEM(s->show_menubar_item))) {
1309b0f31820SCole Robinson             gtk_widget_show(s->menu_bar);
13101d187745SPeter Wu         }
1311c6158483SAnthony Liguori         s->full_screen = FALSE;
1312e3500d1fSGerd Hoffmann         if (vc->type == GD_VC_GFX) {
1313e3500d1fSGerd Hoffmann             vc->gfx.scale_x = 1.0;
1314e3500d1fSGerd Hoffmann             vc->gfx.scale_y = 1.0;
131582fc1809SGerd Hoffmann             gd_update_windowsize(vc);
1316e3500d1fSGerd Hoffmann         }
1317c6158483SAnthony Liguori     }
1318c6158483SAnthony Liguori 
1319e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
1320c6158483SAnthony Liguori }
1321c6158483SAnthony Liguori 
132295414914SCole Robinson static void gd_accel_full_screen(void *opaque)
132395414914SCole Robinson {
132495414914SCole Robinson     GtkDisplayState *s = opaque;
132595414914SCole Robinson     gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
132695414914SCole Robinson }
132795414914SCole Robinson 
1328c6158483SAnthony Liguori static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
1329c6158483SAnthony Liguori {
1330c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1331e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1332c6158483SAnthony Liguori 
1333c6158483SAnthony Liguori     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1334c6158483SAnthony Liguori                                    FALSE);
1335c6158483SAnthony Liguori 
133682fc1809SGerd Hoffmann     vc->gfx.scale_x += VC_SCALE_STEP;
133782fc1809SGerd Hoffmann     vc->gfx.scale_y += VC_SCALE_STEP;
1338c6158483SAnthony Liguori 
1339e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1340c6158483SAnthony Liguori }
1341c6158483SAnthony Liguori 
134266f6b82bSZiyue Yang static void gd_accel_zoom_in(void *opaque)
134366f6b82bSZiyue Yang {
134466f6b82bSZiyue Yang     GtkDisplayState *s = opaque;
134566f6b82bSZiyue Yang     gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_in_item));
134666f6b82bSZiyue Yang }
134766f6b82bSZiyue Yang 
1348c6158483SAnthony Liguori static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
1349c6158483SAnthony Liguori {
1350c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1351e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1352c6158483SAnthony Liguori 
1353c6158483SAnthony Liguori     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1354c6158483SAnthony Liguori                                    FALSE);
1355c6158483SAnthony Liguori 
135682fc1809SGerd Hoffmann     vc->gfx.scale_x -= VC_SCALE_STEP;
135782fc1809SGerd Hoffmann     vc->gfx.scale_y -= VC_SCALE_STEP;
1358c6158483SAnthony Liguori 
135982fc1809SGerd Hoffmann     vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
136082fc1809SGerd Hoffmann     vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
1361c6158483SAnthony Liguori 
1362e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1363c6158483SAnthony Liguori }
1364c6158483SAnthony Liguori 
1365c6158483SAnthony Liguori static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
1366c6158483SAnthony Liguori {
1367c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1368e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1369c6158483SAnthony Liguori 
1370e3500d1fSGerd Hoffmann     vc->gfx.scale_x = 1.0;
1371e3500d1fSGerd Hoffmann     vc->gfx.scale_y = 1.0;
1372c6158483SAnthony Liguori 
1373e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1374c6158483SAnthony Liguori }
1375c6158483SAnthony Liguori 
1376c6158483SAnthony Liguori static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
1377c6158483SAnthony Liguori {
1378c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1379e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1380c6158483SAnthony Liguori 
1381c6158483SAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
1382c6158483SAnthony Liguori         s->free_scale = TRUE;
1383c6158483SAnthony Liguori     } else {
1384c6158483SAnthony Liguori         s->free_scale = FALSE;
1385e3500d1fSGerd Hoffmann         vc->gfx.scale_x = 1.0;
1386e3500d1fSGerd Hoffmann         vc->gfx.scale_y = 1.0;
1387c6158483SAnthony Liguori     }
1388c6158483SAnthony Liguori 
138982fc1809SGerd Hoffmann     gd_update_windowsize(vc);
1390e3500d1fSGerd Hoffmann     gd_update_full_redraw(vc);
1391c6158483SAnthony Liguori }
1392c6158483SAnthony Liguori 
1393a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1394a69fc693SGerd Hoffmann static void gd_grab_update(VirtualConsole *vc, bool kbd, bool ptr)
1395a69fc693SGerd Hoffmann {
1396a69fc693SGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1397a69fc693SGerd Hoffmann     GdkSeat *seat = gdk_display_get_default_seat(display);
1398a69fc693SGerd Hoffmann     GdkWindow *window = gtk_widget_get_window(vc->gfx.drawing_area);
1399a69fc693SGerd Hoffmann     GdkSeatCapabilities caps = 0;
1400a69fc693SGerd Hoffmann     GdkCursor *cursor = NULL;
1401a69fc693SGerd Hoffmann 
1402a69fc693SGerd Hoffmann     if (kbd) {
1403a69fc693SGerd Hoffmann         caps |= GDK_SEAT_CAPABILITY_KEYBOARD;
1404a69fc693SGerd Hoffmann     }
1405a69fc693SGerd Hoffmann     if (ptr) {
1406a69fc693SGerd Hoffmann         caps |= GDK_SEAT_CAPABILITY_ALL_POINTING;
1407a69fc693SGerd Hoffmann         cursor = vc->s->null_cursor;
1408a69fc693SGerd Hoffmann     }
1409a69fc693SGerd Hoffmann 
1410a69fc693SGerd Hoffmann     if (caps) {
1411a69fc693SGerd Hoffmann         gdk_seat_grab(seat, window, caps, false, cursor,
1412a69fc693SGerd Hoffmann                       NULL, NULL, NULL);
1413a69fc693SGerd Hoffmann     } else {
1414a69fc693SGerd Hoffmann         gdk_seat_ungrab(seat);
1415a69fc693SGerd Hoffmann     }
1416a69fc693SGerd Hoffmann }
141789d85cdeSDaniel P. Berrangé #else
1418f50def91SGerd Hoffmann static void gd_grab_devices(VirtualConsole *vc, bool grab,
1419f50def91SGerd Hoffmann                             GdkInputSource source, GdkEventMask mask,
1420f50def91SGerd Hoffmann                             GdkCursor *cursor)
1421f50def91SGerd Hoffmann {
1422f50def91SGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1423f50def91SGerd Hoffmann     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1424f50def91SGerd Hoffmann     GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
1425f50def91SGerd Hoffmann     GList *tmp = devs;
1426f50def91SGerd Hoffmann 
1427f50def91SGerd Hoffmann     for (tmp = devs; tmp; tmp = tmp->next) {
1428f50def91SGerd Hoffmann         GdkDevice *dev = tmp->data;
1429f50def91SGerd Hoffmann         if (gdk_device_get_source(dev) != source) {
1430f50def91SGerd Hoffmann             continue;
1431f50def91SGerd Hoffmann         }
1432f50def91SGerd Hoffmann         if (grab) {
1433f50def91SGerd Hoffmann             GdkWindow *win = gtk_widget_get_window(vc->gfx.drawing_area);
1434f50def91SGerd Hoffmann             gdk_device_grab(dev, win, GDK_OWNERSHIP_NONE, FALSE,
1435f50def91SGerd Hoffmann                             mask, cursor, GDK_CURRENT_TIME);
1436f50def91SGerd Hoffmann         } else {
1437f50def91SGerd Hoffmann             gdk_device_ungrab(dev, GDK_CURRENT_TIME);
1438f50def91SGerd Hoffmann         }
1439f50def91SGerd Hoffmann     }
1440f50def91SGerd Hoffmann     g_list_free(devs);
1441f50def91SGerd Hoffmann }
1442f50def91SGerd Hoffmann #endif
1443f50def91SGerd Hoffmann 
1444d531deefSGerd Hoffmann static void gd_grab_keyboard(VirtualConsole *vc, const char *reason)
14455104a1f6SAnthony Liguori {
1446aa4f4058SGerd Hoffmann     if (vc->s->kbd_owner) {
1447aa4f4058SGerd Hoffmann         if (vc->s->kbd_owner == vc) {
1448aa4f4058SGerd Hoffmann             return;
1449aa4f4058SGerd Hoffmann         } else {
1450aa4f4058SGerd Hoffmann             gd_ungrab_keyboard(vc->s);
1451aa4f4058SGerd Hoffmann         }
1452aa4f4058SGerd Hoffmann     }
1453aa4f4058SGerd Hoffmann 
1454a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1455a69fc693SGerd Hoffmann     gd_grab_update(vc, true, vc->s->ptr_owner == vc);
145689d85cdeSDaniel P. Berrangé #else
1457f50def91SGerd Hoffmann     gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD,
1458655199daSDaniel P. Berrange                    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
1459f50def91SGerd Hoffmann                    NULL);
1460655199daSDaniel P. Berrange #endif
14614c638e2eSGerd Hoffmann     vc->s->kbd_owner = vc;
1462695cc59dSGerd Hoffmann     gd_update_caption(vc->s);
1463d531deefSGerd Hoffmann     trace_gd_grab(vc->label, "kbd", reason);
14645104a1f6SAnthony Liguori }
14655104a1f6SAnthony Liguori 
14664c638e2eSGerd Hoffmann static void gd_ungrab_keyboard(GtkDisplayState *s)
14675104a1f6SAnthony Liguori {
14684c638e2eSGerd Hoffmann     VirtualConsole *vc = s->kbd_owner;
14694c638e2eSGerd Hoffmann 
14704c638e2eSGerd Hoffmann     if (vc == NULL) {
14714c638e2eSGerd Hoffmann         return;
14724c638e2eSGerd Hoffmann     }
14734c638e2eSGerd Hoffmann     s->kbd_owner = NULL;
14744c638e2eSGerd Hoffmann 
1475a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1476a69fc693SGerd Hoffmann     gd_grab_update(vc, false, vc->s->ptr_owner == vc);
1477655199daSDaniel P. Berrange #else
147889d85cdeSDaniel P. Berrangé     gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL);
1479655199daSDaniel P. Berrange #endif
1480695cc59dSGerd Hoffmann     gd_update_caption(s);
1481d531deefSGerd Hoffmann     trace_gd_ungrab(vc->label, "kbd");
14825104a1f6SAnthony Liguori }
14835104a1f6SAnthony Liguori 
1484d531deefSGerd Hoffmann static void gd_grab_pointer(VirtualConsole *vc, const char *reason)
14855104a1f6SAnthony Liguori {
1486e3500d1fSGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1487aa4f4058SGerd Hoffmann 
1488aa4f4058SGerd Hoffmann     if (vc->s->ptr_owner) {
1489aa4f4058SGerd Hoffmann         if (vc->s->ptr_owner == vc) {
1490aa4f4058SGerd Hoffmann             return;
1491aa4f4058SGerd Hoffmann         } else {
1492aa4f4058SGerd Hoffmann             gd_ungrab_pointer(vc->s);
1493aa4f4058SGerd Hoffmann         }
1494aa4f4058SGerd Hoffmann     }
1495aa4f4058SGerd Hoffmann 
1496a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1497a69fc693SGerd Hoffmann     gd_grab_update(vc, vc->s->kbd_owner == vc, true);
1498a69fc693SGerd Hoffmann     gdk_device_get_position(gd_get_pointer(display),
1499a69fc693SGerd Hoffmann                             NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
150089d85cdeSDaniel P. Berrangé #else
1501f50def91SGerd Hoffmann     gd_grab_devices(vc, true, GDK_SOURCE_MOUSE,
15022a05485dSDaniel P. Berrange                     GDK_POINTER_MOTION_MASK |
15032a05485dSDaniel P. Berrange                     GDK_BUTTON_PRESS_MASK |
15042a05485dSDaniel P. Berrange                     GDK_BUTTON_RELEASE_MASK |
15052a05485dSDaniel P. Berrange                     GDK_BUTTON_MOTION_MASK |
15062a05485dSDaniel P. Berrange                     GDK_SCROLL_MASK,
1507f50def91SGerd Hoffmann                     vc->s->null_cursor);
1508bb732ee7SCole Robinson     gdk_device_get_position(gd_get_pointer(display),
1509e3500d1fSGerd Hoffmann                             NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
15102a05485dSDaniel P. Berrange #endif
15114c638e2eSGerd Hoffmann     vc->s->ptr_owner = vc;
1512695cc59dSGerd Hoffmann     gd_update_caption(vc->s);
1513d531deefSGerd Hoffmann     trace_gd_grab(vc->label, "ptr", reason);
15142a05485dSDaniel P. Berrange }
15152a05485dSDaniel P. Berrange 
15164c638e2eSGerd Hoffmann static void gd_ungrab_pointer(GtkDisplayState *s)
15172a05485dSDaniel P. Berrange {
15184c638e2eSGerd Hoffmann     VirtualConsole *vc = s->ptr_owner;
151941cc5239SGerd Hoffmann     GdkDisplay *display;
15204c638e2eSGerd Hoffmann 
15214c638e2eSGerd Hoffmann     if (vc == NULL) {
15224c638e2eSGerd Hoffmann         return;
15234c638e2eSGerd Hoffmann     }
15244c638e2eSGerd Hoffmann     s->ptr_owner = NULL;
15254c638e2eSGerd Hoffmann 
152641cc5239SGerd Hoffmann     display = gtk_widget_get_display(vc->gfx.drawing_area);
1527a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1528a69fc693SGerd Hoffmann     gd_grab_update(vc, vc->s->kbd_owner == vc, false);
1529a69fc693SGerd Hoffmann     gdk_device_warp(gd_get_pointer(display),
1530a69fc693SGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
1531a69fc693SGerd Hoffmann                     vc->s->grab_x_root, vc->s->grab_y_root);
153289d85cdeSDaniel P. Berrangé #else
1533f50def91SGerd Hoffmann     gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL);
1534bb732ee7SCole Robinson     gdk_device_warp(gd_get_pointer(display),
1535e3500d1fSGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
1536e3500d1fSGerd Hoffmann                     vc->s->grab_x_root, vc->s->grab_y_root);
15372a05485dSDaniel P. Berrange #endif
1538695cc59dSGerd Hoffmann     gd_update_caption(s);
1539d531deefSGerd Hoffmann     trace_gd_ungrab(vc->label, "ptr");
15402a05485dSDaniel P. Berrange }
15412a05485dSDaniel P. Berrange 
15422a05485dSDaniel P. Berrange static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
15432a05485dSDaniel P. Berrange {
15442a05485dSDaniel P. Berrange     GtkDisplayState *s = opaque;
1545e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
15462a05485dSDaniel P. Berrange 
15472a05485dSDaniel P. Berrange     if (gd_is_grab_active(s)) {
1548d531deefSGerd Hoffmann         gd_grab_keyboard(vc, "user-request-main-window");
1549d531deefSGerd Hoffmann         gd_grab_pointer(vc, "user-request-main-window");
15505104a1f6SAnthony Liguori     } else {
15514c638e2eSGerd Hoffmann         gd_ungrab_keyboard(s);
15524c638e2eSGerd Hoffmann         gd_ungrab_pointer(s);
15535104a1f6SAnthony Liguori     }
15545104a1f6SAnthony Liguori 
1555e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
15565104a1f6SAnthony Liguori }
15575104a1f6SAnthony Liguori 
1558a4ccabcfSAnthony Liguori static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
1559a4ccabcfSAnthony Liguori                            gpointer data)
1560a4ccabcfSAnthony Liguori {
1561a4ccabcfSAnthony Liguori     GtkDisplayState *s = data;
1562e3500d1fSGerd Hoffmann     VirtualConsole *vc;
15635104a1f6SAnthony Liguori     gboolean on_vga;
1564a4ccabcfSAnthony Liguori 
1565a4ccabcfSAnthony Liguori     if (!gtk_widget_get_realized(s->notebook)) {
1566a4ccabcfSAnthony Liguori         return;
1567a4ccabcfSAnthony Liguori     }
1568a4ccabcfSAnthony Liguori 
1569e3500d1fSGerd Hoffmann     vc = gd_vc_find_by_page(s, arg2);
1570e3500d1fSGerd Hoffmann     if (!vc) {
1571e3500d1fSGerd Hoffmann         return;
1572e3500d1fSGerd Hoffmann     }
1573e3500d1fSGerd Hoffmann     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
1574e3500d1fSGerd Hoffmann                                    TRUE);
1575f8c223f6SGerd Hoffmann     on_vga = (vc->type == GD_VC_GFX &&
1576f8c223f6SGerd Hoffmann               qemu_console_is_graphic(vc->gfx.dcl.con));
15775104a1f6SAnthony Liguori     if (!on_vga) {
15785104a1f6SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
15795104a1f6SAnthony Liguori                                        FALSE);
1580c6158483SAnthony Liguori     } else if (s->full_screen) {
1581c6158483SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1582c6158483SAnthony Liguori                                        TRUE);
15835104a1f6SAnthony Liguori     }
15845104a1f6SAnthony Liguori     gtk_widget_set_sensitive(s->grab_item, on_vga);
1585a0815632SStefan Hajnoczi #ifdef CONFIG_VTE
1586a0815632SStefan Hajnoczi     gtk_widget_set_sensitive(s->copy_item, vc->type == GD_VC_VTE);
1587a0815632SStefan Hajnoczi #endif
15885104a1f6SAnthony Liguori 
158982fc1809SGerd Hoffmann     gd_update_windowsize(vc);
1590e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
1591a4ccabcfSAnthony Liguori }
1592a4ccabcfSAnthony Liguori 
1593e3500d1fSGerd Hoffmann static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
1594e3500d1fSGerd Hoffmann                                gpointer opaque)
15955104a1f6SAnthony Liguori {
1596e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1597e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
15985104a1f6SAnthony Liguori 
15992884cf5bSGerd Hoffmann     if (gd_grab_on_hover(s)) {
1600d531deefSGerd Hoffmann         gd_grab_keyboard(vc, "grab-on-hover");
16015104a1f6SAnthony Liguori     }
16025104a1f6SAnthony Liguori     return TRUE;
16035104a1f6SAnthony Liguori }
16045104a1f6SAnthony Liguori 
1605e3500d1fSGerd Hoffmann static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
1606e3500d1fSGerd Hoffmann                                gpointer opaque)
16075104a1f6SAnthony Liguori {
1608e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1609e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
16105104a1f6SAnthony Liguori 
16112884cf5bSGerd Hoffmann     if (gd_grab_on_hover(s)) {
16124c638e2eSGerd Hoffmann         gd_ungrab_keyboard(s);
16135104a1f6SAnthony Liguori     }
16145104a1f6SAnthony Liguori     return TRUE;
16155104a1f6SAnthony Liguori }
16165104a1f6SAnthony Liguori 
16176db253caSJan Kiszka static gboolean gd_focus_out_event(GtkWidget *widget,
1618e3500d1fSGerd Hoffmann                                    GdkEventCrossing *crossing, gpointer opaque)
16196db253caSJan Kiszka {
1620e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1621e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
16226db253caSJan Kiszka 
16236db253caSJan Kiszka     gtk_release_modifiers(s);
16246db253caSJan Kiszka     return TRUE;
16256db253caSJan Kiszka }
16266db253caSJan Kiszka 
16271301e515SGerd Hoffmann static gboolean gd_configure(GtkWidget *widget,
16281301e515SGerd Hoffmann                              GdkEventConfigure *cfg, gpointer opaque)
16291301e515SGerd Hoffmann {
16301301e515SGerd Hoffmann     VirtualConsole *vc = opaque;
16311301e515SGerd Hoffmann 
1632925a0400SGerd Hoffmann     gd_set_ui_info(vc, cfg->width, cfg->height);
16331301e515SGerd Hoffmann     return FALSE;
16341301e515SGerd Hoffmann }
16351301e515SGerd Hoffmann 
1636d861def3SAnthony Liguori /** Virtual Console Callbacks **/
1637d861def3SAnthony Liguori 
1638ed1132e4SGerd Hoffmann static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
1639cdeb7090SGerd Hoffmann                                int idx, GSList *group, GtkWidget *view_menu)
1640ed1132e4SGerd Hoffmann {
1641cdeb7090SGerd Hoffmann     vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
1642277836c8SCole Robinson     gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx,
1643277836c8SCole Robinson             HOTKEY_MODIFIERS, 0,
1644277836c8SCole Robinson             g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL));
1645277836c8SCole Robinson     gtk_accel_label_set_accel(
1646277836c8SCole Robinson             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))),
1647277836c8SCole Robinson             GDK_KEY_1 + idx, HOTKEY_MODIFIERS);
1648ed1132e4SGerd Hoffmann 
1649ed1132e4SGerd Hoffmann     g_signal_connect(vc->menu_item, "activate",
1650ed1132e4SGerd Hoffmann                      G_CALLBACK(gd_menu_switch_vc), s);
1651ed1132e4SGerd Hoffmann     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
1652ed1132e4SGerd Hoffmann 
1653277836c8SCole Robinson     group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
1654ed1132e4SGerd Hoffmann     return group;
1655ed1132e4SGerd Hoffmann }
1656ed1132e4SGerd Hoffmann 
1657ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
165844b31e0bSMichael S. Tsirkin static void gd_menu_copy(GtkMenuItem *item, void *opaque)
165944b31e0bSMichael S. Tsirkin {
166044b31e0bSMichael S. Tsirkin     GtkDisplayState *s = opaque;
166144b31e0bSMichael S. Tsirkin     VirtualConsole *vc = gd_vc_find_current(s);
166244b31e0bSMichael S. Tsirkin 
166382a4f1a9SAnthony PERARD #if VTE_CHECK_VERSION(0, 50, 0)
166482a4f1a9SAnthony PERARD     vte_terminal_copy_clipboard_format(VTE_TERMINAL(vc->vte.terminal),
166582a4f1a9SAnthony PERARD                                        VTE_FORMAT_TEXT);
166682a4f1a9SAnthony PERARD #else
166744b31e0bSMichael S. Tsirkin     vte_terminal_copy_clipboard(VTE_TERMINAL(vc->vte.terminal));
166882a4f1a9SAnthony PERARD #endif
166944b31e0bSMichael S. Tsirkin }
167044b31e0bSMichael S. Tsirkin 
16710fb20d1cSCole Robinson static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
16720fb20d1cSCole Robinson {
16730fb20d1cSCole Robinson     VirtualConsole *vc = opaque;
16740fb20d1cSCole Robinson 
16750fb20d1cSCole Robinson     if (gtk_adjustment_get_upper(adjustment) >
16760fb20d1cSCole Robinson         gtk_adjustment_get_page_size(adjustment)) {
1677271a25c0SGerd Hoffmann         gtk_widget_show(vc->vte.scrollbar);
16780fb20d1cSCole Robinson     } else {
1679271a25c0SGerd Hoffmann         gtk_widget_hide(vc->vte.scrollbar);
16800fb20d1cSCole Robinson     }
16810fb20d1cSCole Robinson }
16820fb20d1cSCole Robinson 
16830ec7b3e7SMarc-André Lureau static int gd_vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
1684d861def3SAnthony Liguori {
1685777357d7SMarc-André Lureau     VCChardev *vcd = VC_CHARDEV(chr);
168641ac54b2SMarc-André Lureau     VirtualConsole *vc = vcd->console;
1687d861def3SAnthony Liguori 
1688271a25c0SGerd Hoffmann     vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
1689d4370741SCole Robinson     return len;
1690d861def3SAnthony Liguori }
1691d861def3SAnthony Liguori 
16920ec7b3e7SMarc-André Lureau static void gd_vc_chr_set_echo(Chardev *chr, bool echo)
1693fba958c6SPaolo Bonzini {
1694777357d7SMarc-André Lureau     VCChardev *vcd = VC_CHARDEV(chr);
169541ac54b2SMarc-André Lureau     VirtualConsole *vc = vcd->console;
1696fba958c6SPaolo Bonzini 
169741ac54b2SMarc-André Lureau     if (vc) {
1698fba958c6SPaolo Bonzini         vc->vte.echo = echo;
169941ac54b2SMarc-André Lureau     } else {
170041ac54b2SMarc-André Lureau         vcd->echo = echo;
170141ac54b2SMarc-André Lureau     }
1702fba958c6SPaolo Bonzini }
1703fba958c6SPaolo Bonzini 
1704d861def3SAnthony Liguori static int nb_vcs;
17050ec7b3e7SMarc-André Lureau static Chardev *vcs[MAX_VCS];
1706777357d7SMarc-André Lureau static void gd_vc_open(Chardev *chr,
1707777357d7SMarc-André Lureau                        ChardevBackend *backend,
1708777357d7SMarc-André Lureau                        bool *be_opened,
17096f974c84SMarc-André Lureau                        Error **errp)
1710d861def3SAnthony Liguori {
1711c952b715SMarc-André Lureau     if (nb_vcs == MAX_VCS) {
1712c952b715SMarc-André Lureau         error_setg(errp, "Maximum number of consoles reached");
1713777357d7SMarc-André Lureau         return;
1714919e11f3SDaniel P. Berrange     }
1715919e11f3SDaniel P. Berrange 
1716d861def3SAnthony Liguori     vcs[nb_vcs++] = chr;
1717d861def3SAnthony Liguori 
17186f974c84SMarc-André Lureau     /* console/chardev init sometimes completes elsewhere in a 2nd
17196f974c84SMarc-André Lureau      * stage, so defer OPENED events until they are fully initialized
17206f974c84SMarc-André Lureau      */
17216f974c84SMarc-André Lureau     *be_opened = false;
1722d861def3SAnthony Liguori }
1723d861def3SAnthony Liguori 
1724777357d7SMarc-André Lureau static void char_gd_vc_class_init(ObjectClass *oc, void *data)
1725777357d7SMarc-André Lureau {
1726777357d7SMarc-André Lureau     ChardevClass *cc = CHARDEV_CLASS(oc);
1727777357d7SMarc-André Lureau 
172888cace9fSMarc-André Lureau     cc->parse = qemu_chr_parse_vc;
1729777357d7SMarc-André Lureau     cc->open = gd_vc_open;
1730777357d7SMarc-André Lureau     cc->chr_write = gd_vc_chr_write;
1731777357d7SMarc-André Lureau     cc->chr_set_echo = gd_vc_chr_set_echo;
1732777357d7SMarc-André Lureau }
1733777357d7SMarc-André Lureau 
1734777357d7SMarc-André Lureau static const TypeInfo char_gd_vc_type_info = {
1735777357d7SMarc-André Lureau     .name = TYPE_CHARDEV_VC,
1736777357d7SMarc-André Lureau     .parent = TYPE_CHARDEV,
17376f974c84SMarc-André Lureau     .instance_size = sizeof(VCChardev),
1738777357d7SMarc-André Lureau     .class_init = char_gd_vc_class_init,
1739777357d7SMarc-André Lureau };
1740777357d7SMarc-André Lureau 
1741d4370741SCole Robinson static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
1742d4370741SCole Robinson                          gpointer user_data)
1743d861def3SAnthony Liguori {
1744d4370741SCole Robinson     VirtualConsole *vc = user_data;
1745d861def3SAnthony Liguori 
1746fba958c6SPaolo Bonzini     if (vc->vte.echo) {
1747fba958c6SPaolo Bonzini         VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
1748fba958c6SPaolo Bonzini         int i;
1749fba958c6SPaolo Bonzini         for (i = 0; i < size; i++) {
1750fba958c6SPaolo Bonzini             uint8_t c = text[i];
1751fba958c6SPaolo Bonzini             if (c >= 128 || isprint(c)) {
1752fba958c6SPaolo Bonzini                 /* 8-bit characters are considered printable.  */
1753fba958c6SPaolo Bonzini                 vte_terminal_feed(term, &text[i], 1);
1754fba958c6SPaolo Bonzini             } else if (c == '\r' || c == '\n') {
1755fba958c6SPaolo Bonzini                 vte_terminal_feed(term, "\r\n", 2);
1756fba958c6SPaolo Bonzini             } else {
1757fba958c6SPaolo Bonzini                 char ctrl[2] = { '^', 0};
1758fba958c6SPaolo Bonzini                 ctrl[1] = text[i] ^ 64;
1759fba958c6SPaolo Bonzini                 vte_terminal_feed(term, ctrl, 2);
1760fba958c6SPaolo Bonzini             }
1761fba958c6SPaolo Bonzini         }
1762fba958c6SPaolo Bonzini     }
1763fba958c6SPaolo Bonzini 
1764271a25c0SGerd Hoffmann     qemu_chr_be_write(vc->vte.chr, (uint8_t  *)text, (unsigned int)size);
1765d861def3SAnthony Liguori     return TRUE;
1766d861def3SAnthony Liguori }
1767d861def3SAnthony Liguori 
1768ed1132e4SGerd Hoffmann static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
17690ec7b3e7SMarc-André Lureau                               Chardev *chr, int idx,
1770e3500d1fSGerd Hoffmann                               GSList *group, GtkWidget *view_menu)
1771d861def3SAnthony Liguori {
1772d861def3SAnthony Liguori     char buffer[32];
17730fb20d1cSCole Robinson     GtkWidget *box;
17740fb20d1cSCole Robinson     GtkWidget *scrollbar;
17750fb20d1cSCole Robinson     GtkAdjustment *vadjustment;
1776777357d7SMarc-André Lureau     VCChardev *vcd = VC_CHARDEV(chr);
1777d861def3SAnthony Liguori 
1778e3500d1fSGerd Hoffmann     vc->s = s;
177941ac54b2SMarc-André Lureau     vc->vte.echo = vcd->echo;
1780ed1132e4SGerd Hoffmann     vc->vte.chr = chr;
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);
1855925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
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);
1882e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
1883e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_focus_out_event), vc);
18841301e515SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "configure-event",
18851301e515SGerd Hoffmann                          G_CALLBACK(gd_configure), vc);
1886f8c223f6SGerd Hoffmann     } else {
1887f8c223f6SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1888f8c223f6SGerd Hoffmann                          G_CALLBACK(gd_text_key_down), vc);
1889f8c223f6SGerd Hoffmann     }
1890e3500d1fSGerd Hoffmann }
1891e3500d1fSGerd Hoffmann 
1892a4ccabcfSAnthony Liguori static void gd_connect_signals(GtkDisplayState *s)
1893a4ccabcfSAnthony Liguori {
1894a4ccabcfSAnthony Liguori     g_signal_connect(s->show_tabs_item, "activate",
1895a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_show_tabs), s);
1896cdeb7090SGerd Hoffmann     g_signal_connect(s->untabify_item, "activate",
1897cdeb7090SGerd Hoffmann                      G_CALLBACK(gd_menu_untabify), s);
18981d187745SPeter Wu     g_signal_connect(s->show_menubar_item, "activate",
18991d187745SPeter Wu                      G_CALLBACK(gd_menu_show_menubar), s);
1900a4ccabcfSAnthony Liguori 
1901a4ccabcfSAnthony Liguori     g_signal_connect(s->window, "delete-event",
1902a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_window_close), s);
1903a4ccabcfSAnthony Liguori 
190430e8f22bSJan Kiszka     g_signal_connect(s->pause_item, "activate",
190530e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_pause), s);
190630e8f22bSJan Kiszka     g_signal_connect(s->reset_item, "activate",
190730e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_reset), s);
190830e8f22bSJan Kiszka     g_signal_connect(s->powerdown_item, "activate",
190930e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_powerdown), s);
1910a4ccabcfSAnthony Liguori     g_signal_connect(s->quit_item, "activate",
1911a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_quit), s);
191244b31e0bSMichael S. Tsirkin #if defined(CONFIG_VTE)
191344b31e0bSMichael S. Tsirkin     g_signal_connect(s->copy_item, "activate",
191444b31e0bSMichael S. Tsirkin                      G_CALLBACK(gd_menu_copy), s);
191544b31e0bSMichael S. Tsirkin #endif
1916c6158483SAnthony Liguori     g_signal_connect(s->full_screen_item, "activate",
1917c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_full_screen), s);
1918c6158483SAnthony Liguori     g_signal_connect(s->zoom_in_item, "activate",
1919c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_in), s);
1920c6158483SAnthony Liguori     g_signal_connect(s->zoom_out_item, "activate",
1921c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_out), s);
1922c6158483SAnthony Liguori     g_signal_connect(s->zoom_fixed_item, "activate",
1923c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_fixed), s);
1924c6158483SAnthony Liguori     g_signal_connect(s->zoom_fit_item, "activate",
1925c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_fit), s);
19265104a1f6SAnthony Liguori     g_signal_connect(s->grab_item, "activate",
19275104a1f6SAnthony Liguori                      G_CALLBACK(gd_menu_grab_input), s);
1928a4ccabcfSAnthony Liguori     g_signal_connect(s->notebook, "switch-page",
1929a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_change_page), s);
1930a4ccabcfSAnthony Liguori }
1931a4ccabcfSAnthony Liguori 
1932400519d2SCole Robinson static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
1933a4ccabcfSAnthony Liguori {
1934bf9b255fSAnthony Liguori     GtkWidget *machine_menu;
1935a4ccabcfSAnthony Liguori     GtkWidget *separator;
1936a4ccabcfSAnthony Liguori 
1937bf9b255fSAnthony Liguori     machine_menu = gtk_menu_new();
1938400519d2SCole Robinson     gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group);
193930e8f22bSJan Kiszka 
194030e8f22bSJan Kiszka     s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
1941bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
194230e8f22bSJan Kiszka 
194330e8f22bSJan Kiszka     separator = gtk_separator_menu_item_new();
1944bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
194530e8f22bSJan Kiszka 
19469068f20dSCole Robinson     s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset"));
1947bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
194830e8f22bSJan Kiszka 
19499068f20dSCole Robinson     s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down"));
1950bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
195130e8f22bSJan Kiszka 
195230e8f22bSJan Kiszka     separator = gtk_separator_menu_item_new();
1953bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1954a4ccabcfSAnthony Liguori 
19553d914488SCole Robinson     s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
1956a4ccabcfSAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
195730e8f22bSJan Kiszka                                  "<QEMU>/Machine/Quit");
19583d914488SCole Robinson     gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
1959db1da1f2SCole Robinson                             GDK_KEY_q, HOTKEY_MODIFIERS);
1960bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
1961a4ccabcfSAnthony Liguori 
1962bf9b255fSAnthony Liguori     return machine_menu;
1963bf9b255fSAnthony Liguori }
1964bf9b255fSAnthony Liguori 
196528b58f19SPhilippe Mathieu-Daudé /*
196628b58f19SPhilippe Mathieu-Daudé  * If available, return the refresh rate of the display in milli-Hertz,
196728b58f19SPhilippe Mathieu-Daudé  * else return 0.
196828b58f19SPhilippe Mathieu-Daudé  */
196931ab416dSPhilippe Mathieu-Daudé static int gd_refresh_rate_millihz(GtkWidget *window)
197028b58f19SPhilippe Mathieu-Daudé {
197128b58f19SPhilippe Mathieu-Daudé #ifdef GDK_VERSION_3_22
197231ab416dSPhilippe Mathieu-Daudé     GdkWindow *win = gtk_widget_get_window(window);
19737f4d96f9SPhilippe Mathieu-Daudé 
19747f4d96f9SPhilippe Mathieu-Daudé     if (win) {
19757f4d96f9SPhilippe Mathieu-Daudé         GdkDisplay *dpy = gtk_widget_get_display(window);
197628b58f19SPhilippe Mathieu-Daudé         GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
197728b58f19SPhilippe Mathieu-Daudé 
197828b58f19SPhilippe Mathieu-Daudé         return gdk_monitor_get_refresh_rate(monitor);
19797f4d96f9SPhilippe Mathieu-Daudé     }
198028b58f19SPhilippe Mathieu-Daudé #endif
19817f4d96f9SPhilippe Mathieu-Daudé     return 0;
198228b58f19SPhilippe Mathieu-Daudé }
198328b58f19SPhilippe Mathieu-Daudé 
1984e3500d1fSGerd Hoffmann static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
1985ed1132e4SGerd Hoffmann                               QemuConsole *con, int idx,
1986e3500d1fSGerd Hoffmann                               GSList *group, GtkWidget *view_menu)
1987e3500d1fSGerd Hoffmann {
19881d454c3fSPaolo Bonzini     bool zoom_to_fit = false;
1989c4c00922SNikola Pavlica     int refresh_rate_millihz;
1990c4c00922SNikola Pavlica 
1991779ce88fSGerd Hoffmann     vc->label = qemu_console_get_label(con);
1992e3500d1fSGerd Hoffmann     vc->s = s;
1993e3500d1fSGerd Hoffmann     vc->gfx.scale_x = 1.0;
1994e3500d1fSGerd Hoffmann     vc->gfx.scale_y = 1.0;
1995e3500d1fSGerd Hoffmann 
1996925a0400SGerd Hoffmann #if defined(CONFIG_OPENGL)
1997925a0400SGerd Hoffmann     if (display_opengl) {
1998925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
199911c82b58SGerd Hoffmann         if (gtk_use_gl_area) {
2000925a0400SGerd Hoffmann             vc->gfx.drawing_area = gtk_gl_area_new();
2001925a0400SGerd Hoffmann             vc->gfx.dcl.ops = &dcl_gl_area_ops;
200211c82b58SGerd Hoffmann         } else
200311c82b58SGerd Hoffmann #endif /* CONFIG_GTK_GL */
200411c82b58SGerd Hoffmann         {
2005e3500d1fSGerd Hoffmann             vc->gfx.drawing_area = gtk_drawing_area_new();
2006925a0400SGerd Hoffmann             /*
2007925a0400SGerd Hoffmann              * gtk_widget_set_double_buffered() was deprecated in 3.14.
2008925a0400SGerd Hoffmann              * It is required for opengl rendering on X11 though.  A
2009925a0400SGerd Hoffmann              * proper replacement (native opengl support) is only
2010925a0400SGerd Hoffmann              * available in 3.16+.  Silence the warning if possible.
2011925a0400SGerd Hoffmann              */
2012925a0400SGerd Hoffmann #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
2013925a0400SGerd Hoffmann #pragma GCC diagnostic push
2014925a0400SGerd Hoffmann #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2015925a0400SGerd Hoffmann #endif
2016925a0400SGerd Hoffmann             gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
2017925a0400SGerd Hoffmann #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
2018925a0400SGerd Hoffmann #pragma GCC diagnostic pop
2019925a0400SGerd Hoffmann #endif
2020925a0400SGerd Hoffmann             vc->gfx.dcl.ops = &dcl_egl_ops;
202111c82b58SGerd Hoffmann         }
2022925a0400SGerd Hoffmann     } else
2023925a0400SGerd Hoffmann #endif
2024925a0400SGerd Hoffmann     {
2025925a0400SGerd Hoffmann         vc->gfx.drawing_area = gtk_drawing_area_new();
2026925a0400SGerd Hoffmann         vc->gfx.dcl.ops = &dcl_ops;
2027925a0400SGerd Hoffmann     }
2028925a0400SGerd Hoffmann 
2029925a0400SGerd Hoffmann 
2030e3500d1fSGerd Hoffmann     gtk_widget_add_events(vc->gfx.drawing_area,
2031e3500d1fSGerd Hoffmann                           GDK_POINTER_MOTION_MASK |
2032e3500d1fSGerd Hoffmann                           GDK_BUTTON_PRESS_MASK |
2033e3500d1fSGerd Hoffmann                           GDK_BUTTON_RELEASE_MASK |
2034e3500d1fSGerd Hoffmann                           GDK_BUTTON_MOTION_MASK |
2035e3500d1fSGerd Hoffmann                           GDK_ENTER_NOTIFY_MASK |
2036e3500d1fSGerd Hoffmann                           GDK_LEAVE_NOTIFY_MASK |
2037e3500d1fSGerd Hoffmann                           GDK_SCROLL_MASK |
2038d89bf1d4SSergio Lopez                           GDK_SMOOTH_SCROLL_MASK |
2039e3500d1fSGerd Hoffmann                           GDK_KEY_PRESS_MASK);
2040e3500d1fSGerd Hoffmann     gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
2041e3500d1fSGerd Hoffmann 
2042e3500d1fSGerd Hoffmann     vc->type = GD_VC_GFX;
2043e3500d1fSGerd Hoffmann     vc->tab_item = vc->gfx.drawing_area;
20449d677e1cSJan Kiszka     vc->focus = vc->gfx.drawing_area;
2045e3500d1fSGerd Hoffmann     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
2046cdeb7090SGerd Hoffmann                              vc->tab_item, gtk_label_new(vc->label));
2047e3500d1fSGerd Hoffmann 
20480c0d4273SGerd Hoffmann     vc->gfx.kbd = qkbd_state_init(con);
2049e3500d1fSGerd Hoffmann     vc->gfx.dcl.con = con;
2050c4c00922SNikola Pavlica 
205131ab416dSPhilippe Mathieu-Daudé     refresh_rate_millihz = gd_refresh_rate_millihz(vc->window ?
205231ab416dSPhilippe Mathieu-Daudé                                                    vc->window : s->window);
2053c4c00922SNikola Pavlica     if (refresh_rate_millihz) {
2054c4c00922SNikola Pavlica         vc->gfx.dcl.update_interval = MILLISEC_PER_SEC / refresh_rate_millihz;
2055c4c00922SNikola Pavlica     }
2056c4c00922SNikola Pavlica 
2057e3500d1fSGerd Hoffmann     register_displaychangelistener(&vc->gfx.dcl);
2058e3500d1fSGerd Hoffmann 
2059f8c223f6SGerd Hoffmann     gd_connect_vc_gfx_signals(vc);
2060f8c223f6SGerd Hoffmann     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
2061f8c223f6SGerd Hoffmann 
20621301e515SGerd Hoffmann     if (dpy_ui_info_supported(vc->gfx.dcl.con)) {
2063e8b1386eSGerd Hoffmann         zoom_to_fit = true;
2064e8b1386eSGerd Hoffmann     }
2065e8b1386eSGerd Hoffmann     if (s->opts->u.gtk.has_zoom_to_fit) {
2066e8b1386eSGerd Hoffmann         zoom_to_fit = s->opts->u.gtk.zoom_to_fit;
2067e8b1386eSGerd Hoffmann     }
2068e8b1386eSGerd Hoffmann     if (zoom_to_fit) {
20691301e515SGerd Hoffmann         gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item));
20701d73cd78SGerd Hoffmann         s->free_scale = true;
20711301e515SGerd Hoffmann     }
20721301e515SGerd Hoffmann 
2073e3500d1fSGerd Hoffmann     return group;
2074e3500d1fSGerd Hoffmann }
2075e3500d1fSGerd Hoffmann 
2076400519d2SCole Robinson static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
2077bf9b255fSAnthony Liguori {
2078bf9b255fSAnthony Liguori     GSList *group = NULL;
2079bf9b255fSAnthony Liguori     GtkWidget *view_menu;
2080bf9b255fSAnthony Liguori     GtkWidget *separator;
2081ed1132e4SGerd Hoffmann     QemuConsole *con;
2082ed1132e4SGerd Hoffmann     int vc;
2083bf9b255fSAnthony Liguori 
2084bf9b255fSAnthony Liguori     view_menu = gtk_menu_new();
2085400519d2SCole Robinson     gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group);
2086a4ccabcfSAnthony Liguori 
20873d914488SCole Robinson     s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen"));
208895414914SCole Robinson 
208944b31e0bSMichael S. Tsirkin #if defined(CONFIG_VTE)
209044b31e0bSMichael S. Tsirkin     s->copy_item = gtk_menu_item_new_with_mnemonic(_("_Copy"));
209144b31e0bSMichael S. Tsirkin     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->copy_item);
209244b31e0bSMichael S. Tsirkin #endif
209344b31e0bSMichael S. Tsirkin 
209495414914SCole Robinson     gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0,
209595414914SCole Robinson             g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL));
209695414914SCole Robinson     gtk_accel_label_set_accel(
209795414914SCole Robinson             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))),
209895414914SCole Robinson             GDK_KEY_f, HOTKEY_MODIFIERS);
2099bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
2100c6158483SAnthony Liguori 
2101c6158483SAnthony Liguori     separator = gtk_separator_menu_item_new();
2102bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2103c6158483SAnthony Liguori 
21043d914488SCole Robinson     s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In"));
2105c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
2106c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom In");
2107b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
2108b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
210966f6b82bSZiyue Yang     gtk_accel_group_connect(s->accel_group, GDK_KEY_equal, HOTKEY_MODIFIERS, 0,
211066f6b82bSZiyue Yang             g_cclosure_new_swap(G_CALLBACK(gd_accel_zoom_in), s, NULL));
2111bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
2112c6158483SAnthony Liguori 
21133d914488SCole Robinson     s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
2114c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
2115c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom Out");
2116b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus,
2117b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
2118bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
2119c6158483SAnthony Liguori 
21203d914488SCole Robinson     s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit"));
2121c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
2122c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom Fixed");
2123b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0,
2124b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
2125bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
2126c6158483SAnthony Liguori 
2127834574eaSAnthony Liguori     s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
2128bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
2129c6158483SAnthony Liguori 
2130c6158483SAnthony Liguori     separator = gtk_separator_menu_item_new();
2131bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2132c6158483SAnthony Liguori 
2133834574eaSAnthony Liguori     s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
2134bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
21355104a1f6SAnthony Liguori 
2136834574eaSAnthony Liguori     s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
21375104a1f6SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
21385104a1f6SAnthony Liguori                                  "<QEMU>/View/Grab Input");
2139b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g,
2140b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
2141bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
21425104a1f6SAnthony Liguori 
2143a4ccabcfSAnthony Liguori     separator = gtk_separator_menu_item_new();
2144bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2145a4ccabcfSAnthony Liguori 
2146e3500d1fSGerd Hoffmann     /* gfx */
2147ed1132e4SGerd Hoffmann     for (vc = 0;; vc++) {
2148ed1132e4SGerd Hoffmann         con = qemu_console_lookup_by_index(vc);
2149f8c223f6SGerd Hoffmann         if (!con) {
2150ed1132e4SGerd Hoffmann             break;
2151ed1132e4SGerd Hoffmann         }
2152ed1132e4SGerd Hoffmann         group = gd_vc_gfx_init(s, &s->vc[vc], con,
2153ed1132e4SGerd Hoffmann                                vc, group, view_menu);
2154ed1132e4SGerd Hoffmann         s->nb_vcs++;
2155ed1132e4SGerd Hoffmann     }
2156a4ccabcfSAnthony Liguori 
2157ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
2158e3500d1fSGerd Hoffmann     /* vte */
2159ed1132e4SGerd Hoffmann     gd_vcs_init(s, group, view_menu);
2160ee5f31e4SGerd Hoffmann #endif
2161d861def3SAnthony Liguori 
2162a4ccabcfSAnthony Liguori     separator = gtk_separator_menu_item_new();
2163bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2164a4ccabcfSAnthony Liguori 
2165834574eaSAnthony Liguori     s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
2166bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
2167a4ccabcfSAnthony Liguori 
2168cdeb7090SGerd Hoffmann     s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
2169cdeb7090SGerd Hoffmann     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
2170cdeb7090SGerd Hoffmann 
21711d187745SPeter Wu     s->show_menubar_item = gtk_check_menu_item_new_with_mnemonic(
21721d187745SPeter Wu             _("Show Menubar"));
21731d187745SPeter Wu     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->show_menubar_item),
21741d187745SPeter Wu                                    TRUE);
21751d187745SPeter Wu     gtk_accel_group_connect(s->accel_group, GDK_KEY_m, HOTKEY_MODIFIERS, 0,
21761d187745SPeter Wu             g_cclosure_new_swap(G_CALLBACK(gd_accel_show_menubar), s, NULL));
21771d187745SPeter Wu     gtk_accel_label_set_accel(
21781d187745SPeter Wu             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->show_menubar_item))),
21791d187745SPeter Wu             GDK_KEY_m, HOTKEY_MODIFIERS);
21801d187745SPeter Wu     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_menubar_item);
21811d187745SPeter Wu 
2182bf9b255fSAnthony Liguori     return view_menu;
2183bf9b255fSAnthony Liguori }
2184a4ccabcfSAnthony Liguori 
2185bf9b255fSAnthony Liguori static void gd_create_menus(GtkDisplayState *s)
2186bf9b255fSAnthony Liguori {
2187677b4905SPeter Wu     GtkSettings *settings;
2188677b4905SPeter Wu 
2189400519d2SCole Robinson     s->accel_group = gtk_accel_group_new();
2190400519d2SCole Robinson     s->machine_menu = gd_create_menu_machine(s);
2191400519d2SCole Robinson     s->view_menu = gd_create_menu_view(s);
2192bf9b255fSAnthony Liguori 
2193bf9b255fSAnthony Liguori     s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
219430e8f22bSJan Kiszka     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
219530e8f22bSJan Kiszka                               s->machine_menu);
219630e8f22bSJan Kiszka     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
2197a4ccabcfSAnthony Liguori 
2198bf9b255fSAnthony Liguori     s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
2199a4ccabcfSAnthony Liguori     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
2200a4ccabcfSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
2201bf9b255fSAnthony Liguori 
2202400519d2SCole Robinson     g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group);
2203400519d2SCole Robinson     gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group);
2204677b4905SPeter Wu 
2205677b4905SPeter Wu     /* Disable the default "F10" menu shortcut. */
2206677b4905SPeter Wu     settings = gtk_widget_get_settings(s->window);
2207677b4905SPeter Wu     g_object_set(G_OBJECT(settings), "gtk-menu-bar-accel", "", NULL);
2208a4ccabcfSAnthony Liguori }
2209a4ccabcfSAnthony Liguori 
22103158a348SBruce Rogers 
2211060ab763SGerd Hoffmann static gboolean gtkinit;
2212060ab763SGerd Hoffmann 
2213db71589fSGerd Hoffmann static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
2214a4ccabcfSAnthony Liguori {
22153d4da9d6SHervé Poussineau     VirtualConsole *vc;
22163d4da9d6SHervé Poussineau 
2217a4ccabcfSAnthony Liguori     GtkDisplayState *s = g_malloc0(sizeof(*s));
221863c67b6dSMax Reitz     GdkDisplay *window_display;
2219a8260d38SDaniel P. Berrangé     GtkIconTheme *theme;
2220a4ccabcfSAnthony Liguori 
2221060ab763SGerd Hoffmann     if (!gtkinit) {
2222060ab763SGerd Hoffmann         fprintf(stderr, "gtk initialization failed\n");
2223060ab763SGerd Hoffmann         exit(1);
2224060ab763SGerd Hoffmann     }
22250c8d7065SGerd Hoffmann     assert(opts->type == DISPLAY_TYPE_GTK);
22260c8d7065SGerd Hoffmann     s->opts = opts;
2227060ab763SGerd Hoffmann 
2228a8260d38SDaniel P. Berrangé     theme = gtk_icon_theme_get_default();
2229a8260d38SDaniel P. Berrangé     gtk_icon_theme_prepend_search_path(theme, CONFIG_QEMU_ICONDIR);
223067ea9546SDaniel P. Berrangé     g_set_prgname("qemu");
2231a8260d38SDaniel P. Berrangé 
2232a4ccabcfSAnthony Liguori     s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
223351572ab0SDaniel P. Berrange     s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2234a4ccabcfSAnthony Liguori     s->notebook = gtk_notebook_new();
2235a4ccabcfSAnthony Liguori     s->menu_bar = gtk_menu_bar_new();
2236a4ccabcfSAnthony Liguori 
2237c6158483SAnthony Liguori     s->free_scale = FALSE;
2238a4ccabcfSAnthony Liguori 
223927b224a6SKevin Wolf     /* Mostly LC_MESSAGES only. See early_gtk_display_init() for details. For
224027b224a6SKevin Wolf      * LC_CTYPE, we need to make sure that non-ASCII characters are considered
224127b224a6SKevin Wolf      * printable, but without changing any of the character classes to make
224227b224a6SKevin Wolf      * sure that we don't accidentally break implicit assumptions.  */
22432cb5d2a4SAlberto Garcia     setlocale(LC_MESSAGES, "");
224427b224a6SKevin Wolf     setlocale(LC_CTYPE, "C.UTF-8");
2245834574eaSAnthony Liguori     bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
2246c55c9744Syanminhui     bind_textdomain_codeset("qemu", "UTF-8");
2247834574eaSAnthony Liguori     textdomain("qemu");
2248834574eaSAnthony Liguori 
224963c67b6dSMax Reitz     window_display = gtk_widget_get_display(s->window);
22509cfca0b9SGerd Hoffmann     if (s->opts->has_show_cursor && s->opts->show_cursor) {
22519cfca0b9SGerd Hoffmann         s->null_cursor = NULL; /* default pointer */
22529cfca0b9SGerd Hoffmann     } else {
225363c67b6dSMax Reitz         s->null_cursor = gdk_cursor_new_for_display(window_display,
225463c67b6dSMax Reitz                                                     GDK_BLANK_CURSOR);
22559cfca0b9SGerd Hoffmann     }
2256a4ccabcfSAnthony Liguori 
2257a4ccabcfSAnthony Liguori     s->mouse_mode_notifier.notify = gd_mouse_mode_change;
2258a4ccabcfSAnthony Liguori     qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
2259a4ccabcfSAnthony Liguori     qemu_add_vm_change_state_handler(gd_change_runstate, s);
2260a4ccabcfSAnthony Liguori 
2261a8260d38SDaniel P. Berrangé     gtk_window_set_icon_name(GTK_WINDOW(s->window), "qemu");
2262d819cdccSStefan Weil 
2263a4ccabcfSAnthony Liguori     gd_create_menus(s);
2264a4ccabcfSAnthony Liguori 
2265a4ccabcfSAnthony Liguori     gd_connect_signals(s);
2266a4ccabcfSAnthony Liguori 
2267a4ccabcfSAnthony Liguori     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
2268a4ccabcfSAnthony Liguori     gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
2269a4ccabcfSAnthony Liguori 
2270a4ccabcfSAnthony Liguori     gd_update_caption(s);
2271a4ccabcfSAnthony Liguori 
2272a4ccabcfSAnthony Liguori     gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
2273a4ccabcfSAnthony Liguori     gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
2274a4ccabcfSAnthony Liguori 
2275a4ccabcfSAnthony Liguori     gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
2276a4ccabcfSAnthony Liguori 
2277a4ccabcfSAnthony Liguori     gtk_widget_show_all(s->window);
2278a4ccabcfSAnthony Liguori 
22793d4da9d6SHervé Poussineau     vc = gd_vc_find_current(s);
22803d4da9d6SHervé Poussineau     gtk_widget_set_sensitive(s->view_menu, vc != NULL);
2281a0815632SStefan Hajnoczi #ifdef CONFIG_VTE
2282a0815632SStefan Hajnoczi     gtk_widget_set_sensitive(s->copy_item,
22833d4da9d6SHervé Poussineau                              vc && vc->type == GD_VC_VTE);
2284a0815632SStefan Hajnoczi #endif
2285a0815632SStefan Hajnoczi 
22860c8d7065SGerd Hoffmann     if (opts->has_full_screen &&
22870c8d7065SGerd Hoffmann         opts->full_screen) {
2288787ba4f0SPeter Wu         gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
2289787ba4f0SPeter Wu     }
22900c8d7065SGerd Hoffmann     if (opts->u.gtk.has_grab_on_hover &&
22910c8d7065SGerd Hoffmann         opts->u.gtk.grab_on_hover) {
2292881249c7SJan Kiszka         gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
2293881249c7SJan Kiszka     }
2294a4ccabcfSAnthony Liguori }
2295ee5f31e4SGerd Hoffmann 
2296db71589fSGerd Hoffmann static void early_gtk_display_init(DisplayOptions *opts)
2297ee5f31e4SGerd Hoffmann {
22982cb5d2a4SAlberto Garcia     /* The QEMU code relies on the assumption that it's always run in
22992cb5d2a4SAlberto Garcia      * the C locale. Therefore it is not prepared to deal with
23002cb5d2a4SAlberto Garcia      * operations that produce different results depending on the
23012cb5d2a4SAlberto Garcia      * locale, such as printf's formatting of decimal numbers, and
23022cb5d2a4SAlberto Garcia      * possibly others.
23032cb5d2a4SAlberto Garcia      *
23042cb5d2a4SAlberto Garcia      * Since GTK+ calls setlocale() by default -importing the locale
23052cb5d2a4SAlberto Garcia      * settings from the environment- we must prevent it from doing so
23062cb5d2a4SAlberto Garcia      * using gtk_disable_setlocale().
23072cb5d2a4SAlberto Garcia      *
23082cb5d2a4SAlberto Garcia      * QEMU's GTK+ UI, however, _does_ have translations for some of
23092cb5d2a4SAlberto Garcia      * the menu items. As a trade-off between a functionally correct
23102cb5d2a4SAlberto Garcia      * QEMU and a fully internationalized UI we support importing
23112cb5d2a4SAlberto Garcia      * LC_MESSAGES from the environment (see the setlocale() call
23122cb5d2a4SAlberto Garcia      * earlier in this file). This allows us to display translated
23132cb5d2a4SAlberto Garcia      * messages leaving everything else untouched.
23142cb5d2a4SAlberto Garcia      */
23152cb5d2a4SAlberto Garcia     gtk_disable_setlocale();
2316060ab763SGerd Hoffmann     gtkinit = gtk_init_check(NULL, NULL);
2317060ab763SGerd Hoffmann     if (!gtkinit) {
2318060ab763SGerd Hoffmann         /* don't exit yet, that'll break -help */
2319060ab763SGerd Hoffmann         return;
2320060ab763SGerd Hoffmann     }
232197edf3bdSGerd Hoffmann 
23220c8d7065SGerd Hoffmann     assert(opts->type == DISPLAY_TYPE_GTK);
2323002b2902SGerd Hoffmann     if (opts->has_gl && opts->gl != DISPLAYGL_MODE_OFF) {
232497edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
23254c702805SGerd Hoffmann #if defined(CONFIG_GTK_GL) && defined(GDK_WINDOWING_WAYLAND)
23264c702805SGerd Hoffmann         if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
232711c82b58SGerd Hoffmann             gtk_use_gl_area = true;
2328925a0400SGerd Hoffmann             gtk_gl_area_init();
23294f4cb828STomeu Vizoso         } else
233097edf3bdSGerd Hoffmann #endif
23314c702805SGerd Hoffmann         {
233254d208ffSGerd Hoffmann             DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON;
233354d208ffSGerd Hoffmann             gtk_egl_init(mode);
23344c702805SGerd Hoffmann         }
2335925a0400SGerd Hoffmann #endif
233697edf3bdSGerd Hoffmann     }
233797edf3bdSGerd Hoffmann 
23382ec78706SDaniel P. Berrange     keycode_map = gd_get_keymap(&keycode_maplen);
23392ec78706SDaniel P. Berrange 
2340ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
2341777357d7SMarc-André Lureau     type_register(&char_gd_vc_type_info);
2342ee5f31e4SGerd Hoffmann #endif
2343ee5f31e4SGerd Hoffmann }
2344db71589fSGerd Hoffmann 
2345db71589fSGerd Hoffmann static QemuDisplay qemu_display_gtk = {
2346db71589fSGerd Hoffmann     .type       = DISPLAY_TYPE_GTK,
2347db71589fSGerd Hoffmann     .early_init = early_gtk_display_init,
2348db71589fSGerd Hoffmann     .init       = gtk_display_init,
2349db71589fSGerd Hoffmann };
2350db71589fSGerd Hoffmann 
2351db71589fSGerd Hoffmann static void register_gtk(void)
2352db71589fSGerd Hoffmann {
2353db71589fSGerd Hoffmann     qemu_display_register(&qemu_display_gtk);
2354db71589fSGerd Hoffmann }
2355db71589fSGerd Hoffmann 
2356db71589fSGerd Hoffmann type_init(register_gtk);
2357