xref: /qemu/ui/gtk.c (revision a69fc693e9a7e51b4be08cd37bb3fb02c9386152)
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  *
9a4ccabcfSAnthony Liguori  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10a4ccabcfSAnthony Liguori  * See the COPYING file in the top-level directory.
11a4ccabcfSAnthony Liguori  *
12a4ccabcfSAnthony Liguori  * Portions from gtk-vnc:
13a4ccabcfSAnthony Liguori  *
14a4ccabcfSAnthony Liguori  * GTK VNC Widget
15a4ccabcfSAnthony Liguori  *
16a4ccabcfSAnthony Liguori  * Copyright (C) 2006  Anthony Liguori <anthony@codemonkey.ws>
17a4ccabcfSAnthony Liguori  * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
18a4ccabcfSAnthony Liguori  *
19a4ccabcfSAnthony Liguori  * This library is free software; you can redistribute it and/or
20a4ccabcfSAnthony Liguori  * modify it under the terms of the GNU Lesser General Public
21a4ccabcfSAnthony Liguori  * License as published by the Free Software Foundation; either
22a4ccabcfSAnthony Liguori  * version 2.0 of the License, or (at your option) any later version.
23a4ccabcfSAnthony Liguori  *
24a4ccabcfSAnthony Liguori  * This library is distributed in the hope that it will be useful,
25a4ccabcfSAnthony Liguori  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26a4ccabcfSAnthony Liguori  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
27a4ccabcfSAnthony Liguori  * Lesser General Public License for more details.
28a4ccabcfSAnthony Liguori  *
29a4ccabcfSAnthony Liguori  * You should have received a copy of the GNU Lesser General Public
30a4ccabcfSAnthony Liguori  * License along with this library; if not, write to the Free Software
31a4ccabcfSAnthony Liguori  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
32a4ccabcfSAnthony Liguori  */
33a4ccabcfSAnthony Liguori 
34834574eaSAnthony Liguori #define GETTEXT_PACKAGE "qemu"
35834574eaSAnthony Liguori #define LOCALEDIR "po"
36834574eaSAnthony Liguori 
37e16f4c87SPeter Maydell #include "qemu/osdep.h"
38c95e3080SKevin Wolf #include "qemu-common.h"
39f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
40c95e3080SKevin Wolf 
41dc7ff344SGerd Hoffmann #include "ui/console.h"
42dc7ff344SGerd Hoffmann #include "ui/gtk.h"
43c95e3080SKevin Wolf 
44834574eaSAnthony Liguori #include <glib/gi18n.h>
453f58eadeSStefan Weil #include <locale.h>
46bbbf9bfbSStefan Weil #if defined(CONFIG_VTE)
47a4ccabcfSAnthony Liguori #include <vte/vte.h>
48bbbf9bfbSStefan Weil #endif
49a4ccabcfSAnthony Liguori #include <math.h>
50a4ccabcfSAnthony Liguori 
51ef0dd982SStefan Weil #include "trace.h"
52af98ba92SGerd Hoffmann #include "ui/input.h"
53a4ccabcfSAnthony Liguori #include "sysemu/sysemu.h"
54a4ccabcfSAnthony Liguori #include "qmp-commands.h"
55a4ccabcfSAnthony Liguori #include "x_keymap.h"
56a4ccabcfSAnthony Liguori #include "keymaps.h"
57dccfcd0eSPaolo Bonzini #include "sysemu/char.h"
586a24ced5SGerd Hoffmann #include "qom/object.h"
59a4ccabcfSAnthony Liguori 
60d861def3SAnthony Liguori #define MAX_VCS 10
6182fc1809SGerd Hoffmann #define VC_WINDOW_X_MIN  320
6282fc1809SGerd Hoffmann #define VC_WINDOW_Y_MIN  240
6382fc1809SGerd Hoffmann #define VC_TERM_X_MIN     80
6482fc1809SGerd Hoffmann #define VC_TERM_Y_MIN     25
6582fc1809SGerd Hoffmann #define VC_SCALE_MIN    0.25
6682fc1809SGerd Hoffmann #define VC_SCALE_STEP   0.25
67d861def3SAnthony Liguori 
68bbbf9bfbSStefan Weil #if !defined(CONFIG_VTE)
69bbbf9bfbSStefan Weil # define VTE_CHECK_VERSION(a, b, c) 0
70bbbf9bfbSStefan Weil #endif
71cba68834SDaniel P. Berrange 
726fa27697SGerd Hoffmann #if defined(CONFIG_VTE) && !GTK_CHECK_VERSION(3, 0, 0)
736fa27697SGerd Hoffmann /*
746fa27697SGerd Hoffmann  * The gtk2 vte terminal widget seriously messes up the window resize
756fa27697SGerd Hoffmann  * for some reason.  You basically can't make the qemu window smaller
766fa27697SGerd Hoffmann  * any more because the toplevel window geoemtry hints are overridden.
776fa27697SGerd Hoffmann  *
786fa27697SGerd Hoffmann  * Workaround that by hiding all vte widgets, except the one in the
796fa27697SGerd Hoffmann  * current tab.
806fa27697SGerd Hoffmann  *
816fa27697SGerd Hoffmann  * Luckily everything works smooth in gtk3.
826fa27697SGerd Hoffmann  */
836fa27697SGerd Hoffmann # define VTE_RESIZE_HACK 1
846fa27697SGerd Hoffmann #endif
856fa27697SGerd Hoffmann 
86ef6413a2SDaniel P. Berrange #if !GTK_CHECK_VERSION(2, 20, 0)
87ef6413a2SDaniel P. Berrange #define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget)
88ef6413a2SDaniel P. Berrange #endif
89ef6413a2SDaniel P. Berrange 
900a337ed0SGerd Hoffmann #ifndef GDK_IS_X11_DISPLAY
910a337ed0SGerd Hoffmann #define GDK_IS_X11_DISPLAY(dpy) (dpy == dpy)
920a337ed0SGerd Hoffmann #endif
930a337ed0SGerd Hoffmann #ifndef GDK_IS_WIN32_DISPLAY
940a337ed0SGerd Hoffmann #define GDK_IS_WIN32_DISPLAY(dpy) (dpy == dpy)
950a337ed0SGerd Hoffmann #endif
960a337ed0SGerd Hoffmann 
97bc0477c7SDaniel P. Berrange #ifndef GDK_KEY_0
98bc0477c7SDaniel P. Berrange #define GDK_KEY_0 GDK_0
99bc0477c7SDaniel P. Berrange #define GDK_KEY_1 GDK_1
100bc0477c7SDaniel P. Berrange #define GDK_KEY_2 GDK_2
101bc0477c7SDaniel P. Berrange #define GDK_KEY_f GDK_f
102bc0477c7SDaniel P. Berrange #define GDK_KEY_g GDK_g
103db1da1f2SCole Robinson #define GDK_KEY_q GDK_q
104bc0477c7SDaniel P. Berrange #define GDK_KEY_plus GDK_plus
105bc0477c7SDaniel P. Berrange #define GDK_KEY_minus GDK_minus
106dc520171SGerd Hoffmann #define GDK_KEY_Pause GDK_Pause
107bc0477c7SDaniel P. Berrange #endif
108ef6413a2SDaniel P. Berrange 
109c8f3f17cSDaniel P. Berrange /* Some older mingw versions lack this constant or have
110c8f3f17cSDaniel P. Berrange  * it conditionally defined */
111c8f3f17cSDaniel P. Berrange #ifdef _WIN32
112c8f3f17cSDaniel P. Berrange # ifndef MAPVK_VK_TO_VSC
113c8f3f17cSDaniel P. Berrange #  define MAPVK_VK_TO_VSC 0
114c8f3f17cSDaniel P. Berrange # endif
115c8f3f17cSDaniel P. Berrange #endif
116c8f3f17cSDaniel P. Berrange 
117c8f3f17cSDaniel P. Berrange 
118b1e749c0SJan Kiszka #define HOTKEY_MODIFIERS        (GDK_CONTROL_MASK | GDK_MOD1_MASK)
119b1e749c0SJan Kiszka 
1206db253caSJan Kiszka static const int modifier_keycode[] = {
1216db253caSJan Kiszka     /* shift, control, alt keys, meta keys, both left & right */
1226db253caSJan Kiszka     0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd,
1236db253caSJan Kiszka };
1246db253caSJan Kiszka 
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;
155a4ccabcfSAnthony Liguori 
156a4ccabcfSAnthony Liguori     GtkWidget *vbox;
157a4ccabcfSAnthony Liguori     GtkWidget *notebook;
158a4ccabcfSAnthony Liguori     int button_mask;
159e61031cdSTakashi Iwai     gboolean last_set;
160a4ccabcfSAnthony Liguori     int last_x;
161a4ccabcfSAnthony Liguori     int last_y;
162ecce1929STakashi Iwai     int grab_x_root;
163ecce1929STakashi Iwai     int grab_y_root;
1644c638e2eSGerd Hoffmann     VirtualConsole *kbd_owner;
1654c638e2eSGerd Hoffmann     VirtualConsole *ptr_owner;
166a4ccabcfSAnthony Liguori 
167c6158483SAnthony Liguori     gboolean full_screen;
168a4ccabcfSAnthony Liguori 
169a4ccabcfSAnthony Liguori     GdkCursor *null_cursor;
170a4ccabcfSAnthony Liguori     Notifier mouse_mode_notifier;
171c6158483SAnthony Liguori     gboolean free_scale;
17230e8f22bSJan Kiszka 
17330e8f22bSJan Kiszka     bool external_pause_update;
1746db253caSJan Kiszka 
1756db253caSJan Kiszka     bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
1763158a348SBruce Rogers     bool has_evdev;
1771a01716aSJan Kiszka     bool ignore_keys;
178e3500d1fSGerd Hoffmann };
179a4ccabcfSAnthony Liguori 
180d531deefSGerd Hoffmann static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
1812884cf5bSGerd Hoffmann static void gd_ungrab_pointer(GtkDisplayState *s);
182d531deefSGerd Hoffmann static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
183aa4f4058SGerd Hoffmann static void gd_ungrab_keyboard(GtkDisplayState *s);
1842884cf5bSGerd Hoffmann 
185a4ccabcfSAnthony Liguori /** Utility Functions **/
186a4ccabcfSAnthony Liguori 
187271a25c0SGerd Hoffmann static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s)
188271a25c0SGerd Hoffmann {
189271a25c0SGerd Hoffmann     VirtualConsole *vc;
190271a25c0SGerd Hoffmann     gint i;
191271a25c0SGerd Hoffmann 
192271a25c0SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
193271a25c0SGerd Hoffmann         vc = &s->vc[i];
194271a25c0SGerd Hoffmann         if (gtk_check_menu_item_get_active
195271a25c0SGerd Hoffmann             (GTK_CHECK_MENU_ITEM(vc->menu_item))) {
196271a25c0SGerd Hoffmann             return vc;
197271a25c0SGerd Hoffmann         }
198271a25c0SGerd Hoffmann     }
199271a25c0SGerd Hoffmann     return NULL;
200271a25c0SGerd Hoffmann }
201271a25c0SGerd Hoffmann 
202271a25c0SGerd Hoffmann static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page)
203271a25c0SGerd Hoffmann {
204271a25c0SGerd Hoffmann     VirtualConsole *vc;
205271a25c0SGerd Hoffmann     gint i, p;
206271a25c0SGerd Hoffmann 
207271a25c0SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
208271a25c0SGerd Hoffmann         vc = &s->vc[i];
209271a25c0SGerd Hoffmann         p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item);
210271a25c0SGerd Hoffmann         if (p == page) {
211271a25c0SGerd Hoffmann             return vc;
212271a25c0SGerd Hoffmann         }
213271a25c0SGerd Hoffmann     }
214271a25c0SGerd Hoffmann     return NULL;
215271a25c0SGerd Hoffmann }
216271a25c0SGerd Hoffmann 
217e3500d1fSGerd Hoffmann static VirtualConsole *gd_vc_find_current(GtkDisplayState *s)
218e3500d1fSGerd Hoffmann {
219e3500d1fSGerd Hoffmann     gint page;
220e3500d1fSGerd Hoffmann 
221e3500d1fSGerd Hoffmann     page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook));
222e3500d1fSGerd Hoffmann     return gd_vc_find_by_page(s, page);
223e3500d1fSGerd Hoffmann }
224e3500d1fSGerd Hoffmann 
2255104a1f6SAnthony Liguori static bool gd_is_grab_active(GtkDisplayState *s)
2265104a1f6SAnthony Liguori {
2275104a1f6SAnthony Liguori     return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
2285104a1f6SAnthony Liguori }
2295104a1f6SAnthony Liguori 
2305104a1f6SAnthony Liguori static bool gd_grab_on_hover(GtkDisplayState *s)
2315104a1f6SAnthony Liguori {
2325104a1f6SAnthony Liguori     return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
2335104a1f6SAnthony Liguori }
2345104a1f6SAnthony Liguori 
235e3500d1fSGerd Hoffmann static void gd_update_cursor(VirtualConsole *vc)
2365104a1f6SAnthony Liguori {
237e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
238e3500d1fSGerd Hoffmann     GdkWindow *window;
239832189c9SGerd Hoffmann 
240f8c223f6SGerd Hoffmann     if (vc->type != GD_VC_GFX ||
241f8c223f6SGerd Hoffmann         !qemu_console_is_graphic(vc->gfx.dcl.con)) {
242e3500d1fSGerd Hoffmann         return;
2435104a1f6SAnthony Liguori     }
2445104a1f6SAnthony Liguori 
2454cdfc935SHervé Poussineau     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
2464cdfc935SHervé Poussineau         return;
2474cdfc935SHervé Poussineau     }
2484cdfc935SHervé Poussineau 
249e3500d1fSGerd Hoffmann     window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area));
2502884cf5bSGerd Hoffmann     if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) {
251a4ccabcfSAnthony Liguori         gdk_window_set_cursor(window, s->null_cursor);
252a4ccabcfSAnthony Liguori     } else {
253a4ccabcfSAnthony Liguori         gdk_window_set_cursor(window, NULL);
254a4ccabcfSAnthony Liguori     }
255a4ccabcfSAnthony Liguori }
256a4ccabcfSAnthony Liguori 
257a4ccabcfSAnthony Liguori static void gd_update_caption(GtkDisplayState *s)
258a4ccabcfSAnthony Liguori {
259a4ccabcfSAnthony Liguori     const char *status = "";
2604eeaa3a8SGerd Hoffmann     gchar *prefix;
261a4ccabcfSAnthony Liguori     gchar *title;
2625104a1f6SAnthony Liguori     const char *grab = "";
26330e8f22bSJan Kiszka     bool is_paused = !runstate_is_running();
2644eeaa3a8SGerd Hoffmann     int i;
2655104a1f6SAnthony Liguori 
2664eeaa3a8SGerd Hoffmann     if (qemu_name) {
2674eeaa3a8SGerd Hoffmann         prefix = g_strdup_printf("QEMU (%s)", qemu_name);
2684eeaa3a8SGerd Hoffmann     } else {
2694eeaa3a8SGerd Hoffmann         prefix = g_strdup_printf("QEMU");
2704eeaa3a8SGerd Hoffmann     }
2714eeaa3a8SGerd Hoffmann 
2724eeaa3a8SGerd Hoffmann     if (s->ptr_owner != NULL &&
2734eeaa3a8SGerd Hoffmann         s->ptr_owner->window == NULL) {
274d8da9ee8SAurelien Jarno         grab = _(" - Press Ctrl+Alt+G to release grab");
2755104a1f6SAnthony Liguori     }
276a4ccabcfSAnthony Liguori 
27730e8f22bSJan Kiszka     if (is_paused) {
278d8da9ee8SAurelien Jarno         status = _(" [Paused]");
279a4ccabcfSAnthony Liguori     }
28030e8f22bSJan Kiszka     s->external_pause_update = true;
28130e8f22bSJan Kiszka     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
28230e8f22bSJan Kiszka                                    is_paused);
28330e8f22bSJan Kiszka     s->external_pause_update = false;
284a4ccabcfSAnthony Liguori 
2854eeaa3a8SGerd Hoffmann     title = g_strdup_printf("%s%s%s", prefix, status, grab);
2864eeaa3a8SGerd Hoffmann     gtk_window_set_title(GTK_WINDOW(s->window), title);
2874eeaa3a8SGerd Hoffmann     g_free(title);
2884eeaa3a8SGerd Hoffmann 
2894eeaa3a8SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
2904eeaa3a8SGerd Hoffmann         VirtualConsole *vc = &s->vc[i];
2914eeaa3a8SGerd Hoffmann 
2924eeaa3a8SGerd Hoffmann         if (!vc->window) {
2934eeaa3a8SGerd Hoffmann             continue;
2944eeaa3a8SGerd Hoffmann         }
2954eeaa3a8SGerd Hoffmann         title = g_strdup_printf("%s: %s%s%s", prefix, vc->label,
2964eeaa3a8SGerd Hoffmann                                 vc == s->kbd_owner ? " +kbd" : "",
2974eeaa3a8SGerd Hoffmann                                 vc == s->ptr_owner ? " +ptr" : "");
2984eeaa3a8SGerd Hoffmann         gtk_window_set_title(GTK_WINDOW(vc->window), title);
2994eeaa3a8SGerd Hoffmann         g_free(title);
300a4ccabcfSAnthony Liguori     }
301a4ccabcfSAnthony Liguori 
3024eeaa3a8SGerd Hoffmann     g_free(prefix);
303a4ccabcfSAnthony Liguori }
304a4ccabcfSAnthony Liguori 
30582fc1809SGerd Hoffmann static void gd_update_geometry_hints(VirtualConsole *vc)
30682fc1809SGerd Hoffmann {
30782fc1809SGerd Hoffmann     GtkDisplayState *s = vc->s;
30882fc1809SGerd Hoffmann     GdkWindowHints mask = 0;
30982fc1809SGerd Hoffmann     GdkGeometry geo = {};
31082fc1809SGerd Hoffmann     GtkWidget *geo_widget = NULL;
31182fc1809SGerd Hoffmann     GtkWindow *geo_window;
31282fc1809SGerd Hoffmann 
31382fc1809SGerd Hoffmann     if (vc->type == GD_VC_GFX) {
314f98f43eaSGerd Hoffmann         if (!vc->gfx.ds) {
315f98f43eaSGerd Hoffmann             return;
316f98f43eaSGerd Hoffmann         }
31782fc1809SGerd Hoffmann         if (s->free_scale) {
31882fc1809SGerd Hoffmann             geo.min_width  = surface_width(vc->gfx.ds) * VC_SCALE_MIN;
31982fc1809SGerd Hoffmann             geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN;
32082fc1809SGerd Hoffmann             mask |= GDK_HINT_MIN_SIZE;
32182fc1809SGerd Hoffmann         } else {
32282fc1809SGerd Hoffmann             geo.min_width  = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
32382fc1809SGerd Hoffmann             geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
32482fc1809SGerd Hoffmann             mask |= GDK_HINT_MIN_SIZE;
32582fc1809SGerd Hoffmann         }
32682fc1809SGerd Hoffmann         geo_widget = vc->gfx.drawing_area;
32782fc1809SGerd Hoffmann         gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height);
32882fc1809SGerd Hoffmann 
32982fc1809SGerd Hoffmann #if defined(CONFIG_VTE)
33082fc1809SGerd Hoffmann     } else if (vc->type == GD_VC_VTE) {
33182fc1809SGerd Hoffmann         VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
33282fc1809SGerd Hoffmann         GtkBorder *ib;
33382fc1809SGerd Hoffmann 
33484e2dc4bSCole Robinson #if VTE_CHECK_VERSION(0, 37, 0)
33584e2dc4bSCole Robinson         GtkBorder padding;
33684e2dc4bSCole Robinson         gtk_style_context_get_padding(
33784e2dc4bSCole Robinson                 gtk_widget_get_style_context(vc->vte.terminal),
33884e2dc4bSCole Robinson                 gtk_widget_get_state_flags(vc->vte.terminal),
33984e2dc4bSCole Robinson                 &padding);
34084e2dc4bSCole Robinson         ib = &padding;
34184e2dc4bSCole Robinson #else
34284e2dc4bSCole Robinson         gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL);
34384e2dc4bSCole Robinson #endif
34484e2dc4bSCole Robinson 
34582fc1809SGerd Hoffmann         geo.width_inc  = vte_terminal_get_char_width(term);
34682fc1809SGerd Hoffmann         geo.height_inc = vte_terminal_get_char_height(term);
34782fc1809SGerd Hoffmann         mask |= GDK_HINT_RESIZE_INC;
34882fc1809SGerd Hoffmann         geo.base_width  = geo.width_inc;
34982fc1809SGerd Hoffmann         geo.base_height = geo.height_inc;
35082fc1809SGerd Hoffmann         mask |= GDK_HINT_BASE_SIZE;
35182fc1809SGerd Hoffmann         geo.min_width  = geo.width_inc * VC_TERM_X_MIN;
35282fc1809SGerd Hoffmann         geo.min_height = geo.height_inc * VC_TERM_Y_MIN;
35382fc1809SGerd Hoffmann         mask |= GDK_HINT_MIN_SIZE;
35484e2dc4bSCole Robinson 
3554fd811a6SCole Robinson         if (ib) {
35682fc1809SGerd Hoffmann             geo.base_width  += ib->left + ib->right;
35782fc1809SGerd Hoffmann             geo.base_height += ib->top + ib->bottom;
35882fc1809SGerd Hoffmann             geo.min_width   += ib->left + ib->right;
35982fc1809SGerd Hoffmann             geo.min_height  += ib->top + ib->bottom;
3604fd811a6SCole Robinson         }
36182fc1809SGerd Hoffmann         geo_widget = vc->vte.terminal;
36282fc1809SGerd Hoffmann #endif
36382fc1809SGerd Hoffmann     }
36482fc1809SGerd Hoffmann 
36582fc1809SGerd Hoffmann     geo_window = GTK_WINDOW(vc->window ? vc->window : s->window);
36682fc1809SGerd Hoffmann     gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
36782fc1809SGerd Hoffmann }
36882fc1809SGerd Hoffmann 
36997edf3bdSGerd Hoffmann void gd_update_windowsize(VirtualConsole *vc)
370a4ccabcfSAnthony Liguori {
371e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
372c6158483SAnthony Liguori 
37382fc1809SGerd Hoffmann     gd_update_geometry_hints(vc);
374c6158483SAnthony Liguori 
37582fc1809SGerd Hoffmann     if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) {
37682fc1809SGerd Hoffmann         gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window),
37782fc1809SGerd Hoffmann                           VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN);
378a4ccabcfSAnthony Liguori     }
379aa0a55d4SGerd Hoffmann }
380a4ccabcfSAnthony Liguori 
381e3500d1fSGerd Hoffmann static void gd_update_full_redraw(VirtualConsole *vc)
3829d9801cfSGerd Hoffmann {
383e3500d1fSGerd Hoffmann     GtkWidget *area = vc->gfx.drawing_area;
3849d9801cfSGerd Hoffmann     int ww, wh;
385e3500d1fSGerd Hoffmann     gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
386925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
387925a0400SGerd Hoffmann     if (vc->gfx.gls) {
388925a0400SGerd Hoffmann         gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
389925a0400SGerd Hoffmann         return;
390925a0400SGerd Hoffmann     }
391925a0400SGerd Hoffmann #endif
392e3500d1fSGerd Hoffmann     gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
3939d9801cfSGerd Hoffmann }
3949d9801cfSGerd Hoffmann 
3956db253caSJan Kiszka static void gtk_release_modifiers(GtkDisplayState *s)
3966db253caSJan Kiszka {
397e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
3986db253caSJan Kiszka     int i, keycode;
3996db253caSJan Kiszka 
400f8c223f6SGerd Hoffmann     if (vc->type != GD_VC_GFX ||
401f8c223f6SGerd Hoffmann         !qemu_console_is_graphic(vc->gfx.dcl.con)) {
4026db253caSJan Kiszka         return;
4036db253caSJan Kiszka     }
4046db253caSJan Kiszka     for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
4056db253caSJan Kiszka         keycode = modifier_keycode[i];
4066db253caSJan Kiszka         if (!s->modifier_pressed[i]) {
4076db253caSJan Kiszka             continue;
4086db253caSJan Kiszka         }
409e3500d1fSGerd Hoffmann         qemu_input_event_send_key_number(vc->gfx.dcl.con, keycode, false);
4106db253caSJan Kiszka         s->modifier_pressed[i] = false;
4116db253caSJan Kiszka     }
4126db253caSJan Kiszka }
4136db253caSJan Kiszka 
414316cb068SGerd Hoffmann static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
415316cb068SGerd Hoffmann                                GtkWidget *widget)
416316cb068SGerd Hoffmann {
417316cb068SGerd Hoffmann     g_object_ref(G_OBJECT(widget));
418316cb068SGerd Hoffmann     gtk_container_remove(GTK_CONTAINER(from), widget);
419316cb068SGerd Hoffmann     gtk_container_add(GTK_CONTAINER(to), widget);
420316cb068SGerd Hoffmann     g_object_unref(G_OBJECT(widget));
421316cb068SGerd Hoffmann }
422316cb068SGerd Hoffmann 
4239d9801cfSGerd Hoffmann /** DisplayState Callbacks **/
4249d9801cfSGerd Hoffmann 
4259d9801cfSGerd Hoffmann static void gd_update(DisplayChangeListener *dcl,
426bc2ed970SGerd Hoffmann                       int x, int y, int w, int h)
4279d9801cfSGerd Hoffmann {
428e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
429f8c223f6SGerd Hoffmann     GdkWindow *win;
4309d9801cfSGerd Hoffmann     int x1, x2, y1, y2;
4319d9801cfSGerd Hoffmann     int mx, my;
4329d9801cfSGerd Hoffmann     int fbw, fbh;
4339d9801cfSGerd Hoffmann     int ww, wh;
4349d9801cfSGerd Hoffmann 
43574444bc1SGerd Hoffmann     trace_gd_update(vc->label, x, y, w, h);
4369d9801cfSGerd Hoffmann 
4374cdfc935SHervé Poussineau     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
4384cdfc935SHervé Poussineau         return;
4394cdfc935SHervé Poussineau     }
4404cdfc935SHervé Poussineau 
441e3500d1fSGerd Hoffmann     if (vc->gfx.convert) {
442e3500d1fSGerd Hoffmann         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
443e3500d1fSGerd Hoffmann                                NULL, vc->gfx.convert,
444f0875536SGerd Hoffmann                                x, y, 0, 0, x, y, w, h);
445f0875536SGerd Hoffmann     }
446f0875536SGerd Hoffmann 
447e3500d1fSGerd Hoffmann     x1 = floor(x * vc->gfx.scale_x);
448e3500d1fSGerd Hoffmann     y1 = floor(y * vc->gfx.scale_y);
4499d9801cfSGerd Hoffmann 
450e3500d1fSGerd Hoffmann     x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
451e3500d1fSGerd Hoffmann     y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
4529d9801cfSGerd Hoffmann 
453e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
454e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
4559d9801cfSGerd Hoffmann 
456f8c223f6SGerd Hoffmann     win = gtk_widget_get_window(vc->gfx.drawing_area);
457f8c223f6SGerd Hoffmann     if (!win) {
458f8c223f6SGerd Hoffmann         return;
459f8c223f6SGerd Hoffmann     }
460f8c223f6SGerd Hoffmann     gdk_drawable_get_size(win, &ww, &wh);
4619d9801cfSGerd Hoffmann 
4629d9801cfSGerd Hoffmann     mx = my = 0;
4639d9801cfSGerd Hoffmann     if (ww > fbw) {
4649d9801cfSGerd Hoffmann         mx = (ww - fbw) / 2;
4659d9801cfSGerd Hoffmann     }
4669d9801cfSGerd Hoffmann     if (wh > fbh) {
4679d9801cfSGerd Hoffmann         my = (wh - fbh) / 2;
4689d9801cfSGerd Hoffmann     }
4699d9801cfSGerd Hoffmann 
470e3500d1fSGerd Hoffmann     gtk_widget_queue_draw_area(vc->gfx.drawing_area,
471e3500d1fSGerd Hoffmann                                mx + x1, my + y1, (x2 - x1), (y2 - y1));
4729d9801cfSGerd Hoffmann }
4739d9801cfSGerd Hoffmann 
474bc2ed970SGerd Hoffmann static void gd_refresh(DisplayChangeListener *dcl)
4759d9801cfSGerd Hoffmann {
476284d1c6bSGerd Hoffmann     graphic_hw_update(dcl->con);
4779d9801cfSGerd Hoffmann }
4789d9801cfSGerd Hoffmann 
479b087143bSIgor Mitsyanko #if GTK_CHECK_VERSION(3, 0, 0)
480bb732ee7SCole Robinson static GdkDevice *gd_get_pointer(GdkDisplay *dpy)
481bb732ee7SCole Robinson {
482bb732ee7SCole Robinson #if GTK_CHECK_VERSION(3, 20, 0)
483bb732ee7SCole Robinson     return gdk_seat_get_pointer(gdk_display_get_default_seat(dpy));
484bb732ee7SCole Robinson #else
485bb732ee7SCole Robinson     return gdk_device_manager_get_client_pointer(
486bb732ee7SCole Robinson         gdk_display_get_device_manager(dpy));
487bb732ee7SCole Robinson #endif
488bb732ee7SCole Robinson }
489bb732ee7SCole Robinson 
490b087143bSIgor Mitsyanko static void gd_mouse_set(DisplayChangeListener *dcl,
491b087143bSIgor Mitsyanko                          int x, int y, int visible)
492b087143bSIgor Mitsyanko {
493e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
494b087143bSIgor Mitsyanko     GdkDisplay *dpy;
495b087143bSIgor Mitsyanko     gint x_root, y_root;
496b087143bSIgor Mitsyanko 
4972bda6602SCole Robinson     if (qemu_input_is_absolute()) {
4982bda6602SCole Robinson         return;
4992bda6602SCole Robinson     }
5002bda6602SCole Robinson 
501e3500d1fSGerd Hoffmann     dpy = gtk_widget_get_display(vc->gfx.drawing_area);
502e3500d1fSGerd Hoffmann     gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
503b087143bSIgor Mitsyanko                                x, y, &x_root, &y_root);
504bb732ee7SCole Robinson     gdk_device_warp(gd_get_pointer(dpy),
505e3500d1fSGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
506298526feSCole Robinson                     x_root, y_root);
5071271f7f7SGerd Hoffmann     vc->s->last_x = x;
5081271f7f7SGerd Hoffmann     vc->s->last_y = y;
509b087143bSIgor Mitsyanko }
510b087143bSIgor Mitsyanko #else
5119697f5d2SGerd Hoffmann static void gd_mouse_set(DisplayChangeListener *dcl,
5129697f5d2SGerd Hoffmann                          int x, int y, int visible)
5139697f5d2SGerd Hoffmann {
514e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
5159697f5d2SGerd Hoffmann     gint x_root, y_root;
5169697f5d2SGerd Hoffmann 
5172bda6602SCole Robinson     if (qemu_input_is_absolute()) {
5182bda6602SCole Robinson         return;
5192bda6602SCole Robinson     }
5202bda6602SCole Robinson 
521e3500d1fSGerd Hoffmann     gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
5229697f5d2SGerd Hoffmann                                x, y, &x_root, &y_root);
523e3500d1fSGerd Hoffmann     gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area),
524e3500d1fSGerd Hoffmann                              gtk_widget_get_screen(vc->gfx.drawing_area),
5259697f5d2SGerd Hoffmann                              x_root, y_root);
5269697f5d2SGerd Hoffmann }
527b087143bSIgor Mitsyanko #endif
5289697f5d2SGerd Hoffmann 
5299697f5d2SGerd Hoffmann static void gd_cursor_define(DisplayChangeListener *dcl,
5309697f5d2SGerd Hoffmann                              QEMUCursor *c)
5319697f5d2SGerd Hoffmann {
532e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
5339697f5d2SGerd Hoffmann     GdkPixbuf *pixbuf;
5349697f5d2SGerd Hoffmann     GdkCursor *cursor;
5359697f5d2SGerd Hoffmann 
5364cdfc935SHervé Poussineau     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
5374cdfc935SHervé Poussineau         return;
5384cdfc935SHervé Poussineau     }
5394cdfc935SHervé Poussineau 
5409697f5d2SGerd Hoffmann     pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
5419697f5d2SGerd Hoffmann                                       GDK_COLORSPACE_RGB, true, 8,
5429697f5d2SGerd Hoffmann                                       c->width, c->height, c->width * 4,
5439697f5d2SGerd Hoffmann                                       NULL, NULL);
544e3500d1fSGerd Hoffmann     cursor = gdk_cursor_new_from_pixbuf
545e3500d1fSGerd Hoffmann         (gtk_widget_get_display(vc->gfx.drawing_area),
5469697f5d2SGerd Hoffmann          pixbuf, c->hot_x, c->hot_y);
547e3500d1fSGerd Hoffmann     gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor);
5489697f5d2SGerd Hoffmann     g_object_unref(pixbuf);
549030b4b7dSStefan Weil #if !GTK_CHECK_VERSION(3, 0, 0)
55017139240SAnthony Liguori     gdk_cursor_unref(cursor);
551030b4b7dSStefan Weil #else
552030b4b7dSStefan Weil     g_object_unref(cursor);
553030b4b7dSStefan Weil #endif
5549697f5d2SGerd Hoffmann }
5559697f5d2SGerd Hoffmann 
5569d9801cfSGerd Hoffmann static void gd_switch(DisplayChangeListener *dcl,
5579d9801cfSGerd Hoffmann                       DisplaySurface *surface)
5589d9801cfSGerd Hoffmann {
559e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
5609d9801cfSGerd Hoffmann     bool resized = true;
5619d9801cfSGerd Hoffmann 
562f98f43eaSGerd Hoffmann     trace_gd_switch(vc->label,
563f98f43eaSGerd Hoffmann                     surface ? surface_width(surface)  : 0,
564f98f43eaSGerd Hoffmann                     surface ? surface_height(surface) : 0);
5659d9801cfSGerd Hoffmann 
566e3500d1fSGerd Hoffmann     if (vc->gfx.surface) {
567e3500d1fSGerd Hoffmann         cairo_surface_destroy(vc->gfx.surface);
568f98f43eaSGerd Hoffmann         vc->gfx.surface = NULL;
569f98f43eaSGerd Hoffmann     }
570f98f43eaSGerd Hoffmann     if (vc->gfx.convert) {
571f98f43eaSGerd Hoffmann         pixman_image_unref(vc->gfx.convert);
572f98f43eaSGerd Hoffmann         vc->gfx.convert = NULL;
5739d9801cfSGerd Hoffmann     }
5749d9801cfSGerd Hoffmann 
575f98f43eaSGerd Hoffmann     if (vc->gfx.ds && surface &&
576e3500d1fSGerd Hoffmann         surface_width(vc->gfx.ds) == surface_width(surface) &&
577e3500d1fSGerd Hoffmann         surface_height(vc->gfx.ds) == surface_height(surface)) {
5789d9801cfSGerd Hoffmann         resized = false;
5799d9801cfSGerd Hoffmann     }
580e3500d1fSGerd Hoffmann     vc->gfx.ds = surface;
581f0875536SGerd Hoffmann 
582f98f43eaSGerd Hoffmann     if (!surface) {
583f98f43eaSGerd Hoffmann         return;
5849d9801cfSGerd Hoffmann     }
5859d9801cfSGerd Hoffmann 
586f0875536SGerd Hoffmann     if (surface->format == PIXMAN_x8r8g8b8) {
587f0875536SGerd Hoffmann         /*
588f0875536SGerd Hoffmann          * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24
589f0875536SGerd Hoffmann          *
590f0875536SGerd Hoffmann          * No need to convert, use surface directly.  Should be the
591f0875536SGerd Hoffmann          * common case as this is qemu_default_pixelformat(32) too.
592f0875536SGerd Hoffmann          */
593e3500d1fSGerd Hoffmann         vc->gfx.surface = cairo_image_surface_create_for_data
594f0875536SGerd Hoffmann             (surface_data(surface),
595f0875536SGerd Hoffmann              CAIRO_FORMAT_RGB24,
5969d9801cfSGerd Hoffmann              surface_width(surface),
5979d9801cfSGerd Hoffmann              surface_height(surface),
5989d9801cfSGerd Hoffmann              surface_stride(surface));
599f0875536SGerd Hoffmann     } else {
600f0875536SGerd Hoffmann         /* Must convert surface, use pixman to do it. */
601e3500d1fSGerd Hoffmann         vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8,
602f0875536SGerd Hoffmann                                                    surface_width(surface),
603f0875536SGerd Hoffmann                                                    surface_height(surface),
604f0875536SGerd Hoffmann                                                    NULL, 0);
605e3500d1fSGerd Hoffmann         vc->gfx.surface = cairo_image_surface_create_for_data
606e3500d1fSGerd Hoffmann             ((void *)pixman_image_get_data(vc->gfx.convert),
607f0875536SGerd Hoffmann              CAIRO_FORMAT_RGB24,
608e3500d1fSGerd Hoffmann              pixman_image_get_width(vc->gfx.convert),
609e3500d1fSGerd Hoffmann              pixman_image_get_height(vc->gfx.convert),
610e3500d1fSGerd Hoffmann              pixman_image_get_stride(vc->gfx.convert));
611e3500d1fSGerd Hoffmann         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
612e3500d1fSGerd Hoffmann                                NULL, vc->gfx.convert,
613f0875536SGerd Hoffmann                                0, 0, 0, 0, 0, 0,
614e3500d1fSGerd Hoffmann                                pixman_image_get_width(vc->gfx.convert),
615e3500d1fSGerd Hoffmann                                pixman_image_get_height(vc->gfx.convert));
616f0875536SGerd Hoffmann     }
6179d9801cfSGerd Hoffmann 
6189d9801cfSGerd Hoffmann     if (resized) {
619e3500d1fSGerd Hoffmann         gd_update_windowsize(vc);
6209d9801cfSGerd Hoffmann     } else {
621e3500d1fSGerd Hoffmann         gd_update_full_redraw(vc);
6229d9801cfSGerd Hoffmann     }
6239d9801cfSGerd Hoffmann }
6249d9801cfSGerd Hoffmann 
62597edf3bdSGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = {
62697edf3bdSGerd Hoffmann     .dpy_name             = "gtk",
62797edf3bdSGerd Hoffmann     .dpy_gfx_update       = gd_update,
62897edf3bdSGerd Hoffmann     .dpy_gfx_switch       = gd_switch,
62997edf3bdSGerd Hoffmann     .dpy_gfx_check_format = qemu_pixman_check_format,
63097edf3bdSGerd Hoffmann     .dpy_refresh          = gd_refresh,
63197edf3bdSGerd Hoffmann     .dpy_mouse_set        = gd_mouse_set,
63297edf3bdSGerd Hoffmann     .dpy_cursor_define    = gd_cursor_define,
63397edf3bdSGerd Hoffmann };
63497edf3bdSGerd Hoffmann 
63597edf3bdSGerd Hoffmann 
63697edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
63797edf3bdSGerd Hoffmann 
63897edf3bdSGerd Hoffmann /** DisplayState Callbacks (opengl version) **/
63997edf3bdSGerd Hoffmann 
640925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
641925a0400SGerd Hoffmann 
642925a0400SGerd Hoffmann static const DisplayChangeListenerOps dcl_gl_area_ops = {
643925a0400SGerd Hoffmann     .dpy_name             = "gtk-egl",
644925a0400SGerd Hoffmann     .dpy_gfx_update       = gd_gl_area_update,
645925a0400SGerd Hoffmann     .dpy_gfx_switch       = gd_gl_area_switch,
646925a0400SGerd Hoffmann     .dpy_gfx_check_format = console_gl_check_format,
647925a0400SGerd Hoffmann     .dpy_refresh          = gd_gl_area_refresh,
648925a0400SGerd Hoffmann     .dpy_mouse_set        = gd_mouse_set,
649925a0400SGerd Hoffmann     .dpy_cursor_define    = gd_cursor_define,
650925a0400SGerd Hoffmann 
651925a0400SGerd Hoffmann     .dpy_gl_ctx_create       = gd_gl_area_create_context,
652925a0400SGerd Hoffmann     .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
653925a0400SGerd Hoffmann     .dpy_gl_ctx_make_current = gd_gl_area_make_current,
654925a0400SGerd Hoffmann     .dpy_gl_ctx_get_current  = gd_gl_area_get_current_context,
655925a0400SGerd Hoffmann     .dpy_gl_scanout          = gd_gl_area_scanout,
656925a0400SGerd Hoffmann     .dpy_gl_update           = gd_gl_area_scanout_flush,
657925a0400SGerd Hoffmann };
658925a0400SGerd Hoffmann 
659925a0400SGerd Hoffmann #else
660925a0400SGerd Hoffmann 
66197edf3bdSGerd Hoffmann static const DisplayChangeListenerOps dcl_egl_ops = {
66297edf3bdSGerd Hoffmann     .dpy_name             = "gtk-egl",
66397edf3bdSGerd Hoffmann     .dpy_gfx_update       = gd_egl_update,
66497edf3bdSGerd Hoffmann     .dpy_gfx_switch       = gd_egl_switch,
66597edf3bdSGerd Hoffmann     .dpy_gfx_check_format = console_gl_check_format,
66697edf3bdSGerd Hoffmann     .dpy_refresh          = gd_egl_refresh,
66797edf3bdSGerd Hoffmann     .dpy_mouse_set        = gd_mouse_set,
66897edf3bdSGerd Hoffmann     .dpy_cursor_define    = gd_cursor_define,
6694782aeb7SGerd Hoffmann 
6704782aeb7SGerd Hoffmann     .dpy_gl_ctx_create       = gd_egl_create_context,
6714782aeb7SGerd Hoffmann     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
6724782aeb7SGerd Hoffmann     .dpy_gl_ctx_make_current = gd_egl_make_current,
6734782aeb7SGerd Hoffmann     .dpy_gl_ctx_get_current  = qemu_egl_get_current_context,
6744782aeb7SGerd Hoffmann     .dpy_gl_scanout          = gd_egl_scanout,
6754782aeb7SGerd Hoffmann     .dpy_gl_update           = gd_egl_scanout_flush,
67697edf3bdSGerd Hoffmann };
67797edf3bdSGerd Hoffmann 
678925a0400SGerd Hoffmann #endif /* CONFIG_GTK_GL */
679925a0400SGerd Hoffmann #endif /* CONFIG_OPENGL */
68097edf3bdSGerd Hoffmann 
681a4ccabcfSAnthony Liguori /** QEMU Events **/
682a4ccabcfSAnthony Liguori 
683a4ccabcfSAnthony Liguori static void gd_change_runstate(void *opaque, int running, RunState state)
684a4ccabcfSAnthony Liguori {
685a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
686a4ccabcfSAnthony Liguori 
687a4ccabcfSAnthony Liguori     gd_update_caption(s);
688a4ccabcfSAnthony Liguori }
689a4ccabcfSAnthony Liguori 
690a4ccabcfSAnthony Liguori static void gd_mouse_mode_change(Notifier *notify, void *data)
691a4ccabcfSAnthony Liguori {
692800b0e81STakashi Iwai     GtkDisplayState *s;
69399623c90SGerd Hoffmann     int i;
694800b0e81STakashi Iwai 
695800b0e81STakashi Iwai     s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
696800b0e81STakashi Iwai     /* release the grab at switching to absolute mode */
697800b0e81STakashi Iwai     if (qemu_input_is_absolute() && gd_is_grab_active(s)) {
698800b0e81STakashi Iwai         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
699a4ccabcfSAnthony Liguori                                        FALSE);
700a4ccabcfSAnthony Liguori     }
70199623c90SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
70299623c90SGerd Hoffmann         VirtualConsole *vc = &s->vc[i];
70399623c90SGerd Hoffmann         gd_update_cursor(vc);
70499623c90SGerd Hoffmann     }
705800b0e81STakashi Iwai }
706a4ccabcfSAnthony Liguori 
707a4ccabcfSAnthony Liguori /** GTK Events **/
708a4ccabcfSAnthony Liguori 
709a4ccabcfSAnthony Liguori static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
710a4ccabcfSAnthony Liguori                                 void *opaque)
711a4ccabcfSAnthony Liguori {
712a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
713e3500d1fSGerd Hoffmann     int i;
714a4ccabcfSAnthony Liguori 
715a4ccabcfSAnthony Liguori     if (!no_quit) {
716e3500d1fSGerd Hoffmann         for (i = 0; i < s->nb_vcs; i++) {
717e3500d1fSGerd Hoffmann             if (s->vc[i].type != GD_VC_GFX) {
718e3500d1fSGerd Hoffmann                 continue;
719e3500d1fSGerd Hoffmann             }
720e3500d1fSGerd Hoffmann             unregister_displaychangelistener(&s->vc[i].gfx.dcl);
721e3500d1fSGerd Hoffmann         }
722a4ccabcfSAnthony Liguori         qmp_quit(NULL);
723a4ccabcfSAnthony Liguori         return FALSE;
724a4ccabcfSAnthony Liguori     }
725a4ccabcfSAnthony Liguori 
726a4ccabcfSAnthony Liguori     return TRUE;
727a4ccabcfSAnthony Liguori }
728a4ccabcfSAnthony Liguori 
729925a0400SGerd Hoffmann static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height)
730925a0400SGerd Hoffmann {
731925a0400SGerd Hoffmann     QemuUIInfo info;
732925a0400SGerd Hoffmann 
733925a0400SGerd Hoffmann     memset(&info, 0, sizeof(info));
734925a0400SGerd Hoffmann     info.width = width;
735925a0400SGerd Hoffmann     info.height = height;
736925a0400SGerd Hoffmann     dpy_set_ui_info(vc->gfx.dcl.con, &info);
737925a0400SGerd Hoffmann }
738925a0400SGerd Hoffmann 
739925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
740925a0400SGerd Hoffmann 
741925a0400SGerd Hoffmann static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
742925a0400SGerd Hoffmann                                 void *opaque)
743925a0400SGerd Hoffmann {
744925a0400SGerd Hoffmann     VirtualConsole *vc = opaque;
745925a0400SGerd Hoffmann 
746925a0400SGerd Hoffmann     if (vc->gfx.gls) {
747925a0400SGerd Hoffmann         gd_gl_area_draw(vc);
748925a0400SGerd Hoffmann     }
749925a0400SGerd Hoffmann     return TRUE;
750925a0400SGerd Hoffmann }
751925a0400SGerd Hoffmann 
752925a0400SGerd Hoffmann static void gd_resize_event(GtkGLArea *area,
753925a0400SGerd Hoffmann                             gint width, gint height, gpointer *opaque)
754925a0400SGerd Hoffmann {
755925a0400SGerd Hoffmann     VirtualConsole *vc = (void *)opaque;
756925a0400SGerd Hoffmann 
757925a0400SGerd Hoffmann     gd_set_ui_info(vc, width, height);
758925a0400SGerd Hoffmann }
759925a0400SGerd Hoffmann 
760925a0400SGerd Hoffmann #endif
761925a0400SGerd Hoffmann 
762a4ccabcfSAnthony Liguori static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
763a4ccabcfSAnthony Liguori {
764e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
765e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
766c6158483SAnthony Liguori     int mx, my;
767a4ccabcfSAnthony Liguori     int ww, wh;
768a4ccabcfSAnthony Liguori     int fbw, fbh;
769a4ccabcfSAnthony Liguori 
77097edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
77197edf3bdSGerd Hoffmann     if (vc->gfx.gls) {
772925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
773925a0400SGerd Hoffmann         /* invoke render callback please */
774925a0400SGerd Hoffmann         return FALSE;
775925a0400SGerd Hoffmann #else
77697edf3bdSGerd Hoffmann         gd_egl_draw(vc);
77797edf3bdSGerd Hoffmann         return TRUE;
778925a0400SGerd Hoffmann #endif
77997edf3bdSGerd Hoffmann     }
78097edf3bdSGerd Hoffmann #endif
78197edf3bdSGerd Hoffmann 
782c6158483SAnthony Liguori     if (!gtk_widget_get_realized(widget)) {
783c6158483SAnthony Liguori         return FALSE;
784c6158483SAnthony Liguori     }
785f98f43eaSGerd Hoffmann     if (!vc->gfx.ds) {
786f98f43eaSGerd Hoffmann         return FALSE;
787f98f43eaSGerd Hoffmann     }
788c6158483SAnthony Liguori 
789e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds);
790e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds);
791a4ccabcfSAnthony Liguori 
792a4ccabcfSAnthony Liguori     gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
793a4ccabcfSAnthony Liguori 
794c6158483SAnthony Liguori     if (s->full_screen) {
795e3500d1fSGerd Hoffmann         vc->gfx.scale_x = (double)ww / fbw;
796e3500d1fSGerd Hoffmann         vc->gfx.scale_y = (double)wh / fbh;
797c6158483SAnthony Liguori     } else if (s->free_scale) {
798c6158483SAnthony Liguori         double sx, sy;
799c6158483SAnthony Liguori 
800c6158483SAnthony Liguori         sx = (double)ww / fbw;
801c6158483SAnthony Liguori         sy = (double)wh / fbh;
802c6158483SAnthony Liguori 
803e3500d1fSGerd Hoffmann         vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
804a4ccabcfSAnthony Liguori     }
805a4ccabcfSAnthony Liguori 
806e3500d1fSGerd Hoffmann     fbw *= vc->gfx.scale_x;
807e3500d1fSGerd Hoffmann     fbh *= vc->gfx.scale_y;
8085104a1f6SAnthony Liguori 
809c6158483SAnthony Liguori     mx = my = 0;
810c6158483SAnthony Liguori     if (ww > fbw) {
811c6158483SAnthony Liguori         mx = (ww - fbw) / 2;
812c6158483SAnthony Liguori     }
813c6158483SAnthony Liguori     if (wh > fbh) {
814c6158483SAnthony Liguori         my = (wh - fbh) / 2;
815c6158483SAnthony Liguori     }
816c6158483SAnthony Liguori 
817c6158483SAnthony Liguori     cairo_rectangle(cr, 0, 0, ww, wh);
818c6158483SAnthony Liguori 
819c6158483SAnthony Liguori     /* Optionally cut out the inner area where the pixmap
820c6158483SAnthony Liguori        will be drawn. This avoids 'flashing' since we're
821c6158483SAnthony Liguori        not double-buffering. Note we're using the undocumented
822c6158483SAnthony Liguori        behaviour of drawing the rectangle from right to left
823c6158483SAnthony Liguori        to cut out the whole */
824c6158483SAnthony Liguori     cairo_rectangle(cr, mx + fbw, my,
825c6158483SAnthony Liguori                     -1 * fbw, fbh);
826c6158483SAnthony Liguori     cairo_fill(cr);
827c6158483SAnthony Liguori 
828e3500d1fSGerd Hoffmann     cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
829e3500d1fSGerd Hoffmann     cairo_set_source_surface(cr, vc->gfx.surface,
830e3500d1fSGerd Hoffmann                              mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
831a4ccabcfSAnthony Liguori     cairo_paint(cr);
832a4ccabcfSAnthony Liguori 
833a4ccabcfSAnthony Liguori     return TRUE;
834a4ccabcfSAnthony Liguori }
835a4ccabcfSAnthony Liguori 
836fe43bca8SDaniel P. Berrange #if !GTK_CHECK_VERSION(3, 0, 0)
837a4ccabcfSAnthony Liguori static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
838a4ccabcfSAnthony Liguori                                 void *opaque)
839a4ccabcfSAnthony Liguori {
840a4ccabcfSAnthony Liguori     cairo_t *cr;
841a4ccabcfSAnthony Liguori     gboolean ret;
842a4ccabcfSAnthony Liguori 
843a4ccabcfSAnthony Liguori     cr = gdk_cairo_create(gtk_widget_get_window(widget));
844a4ccabcfSAnthony Liguori     cairo_rectangle(cr,
845a4ccabcfSAnthony Liguori                     expose->area.x,
846a4ccabcfSAnthony Liguori                     expose->area.y,
847a4ccabcfSAnthony Liguori                     expose->area.width,
848a4ccabcfSAnthony Liguori                     expose->area.height);
849a4ccabcfSAnthony Liguori     cairo_clip(cr);
850a4ccabcfSAnthony Liguori 
851a4ccabcfSAnthony Liguori     ret = gd_draw_event(widget, cr, opaque);
852a4ccabcfSAnthony Liguori 
853a4ccabcfSAnthony Liguori     cairo_destroy(cr);
854a4ccabcfSAnthony Liguori 
855a4ccabcfSAnthony Liguori     return ret;
856a4ccabcfSAnthony Liguori }
857fe43bca8SDaniel P. Berrange #endif
858a4ccabcfSAnthony Liguori 
859a4ccabcfSAnthony Liguori static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
860a4ccabcfSAnthony Liguori                                 void *opaque)
861a4ccabcfSAnthony Liguori {
862e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
863e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
864a4ccabcfSAnthony Liguori     int x, y;
865c6158483SAnthony Liguori     int mx, my;
866c6158483SAnthony Liguori     int fbh, fbw;
867c6158483SAnthony Liguori     int ww, wh;
868a4ccabcfSAnthony Liguori 
869f98f43eaSGerd Hoffmann     if (!vc->gfx.ds) {
870f98f43eaSGerd Hoffmann         return TRUE;
871f98f43eaSGerd Hoffmann     }
872f98f43eaSGerd Hoffmann 
873e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
874e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
875c6158483SAnthony Liguori 
876e3500d1fSGerd Hoffmann     gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area),
877e3500d1fSGerd Hoffmann                           &ww, &wh);
878c6158483SAnthony Liguori 
879c6158483SAnthony Liguori     mx = my = 0;
880c6158483SAnthony Liguori     if (ww > fbw) {
881c6158483SAnthony Liguori         mx = (ww - fbw) / 2;
882c6158483SAnthony Liguori     }
883c6158483SAnthony Liguori     if (wh > fbh) {
884c6158483SAnthony Liguori         my = (wh - fbh) / 2;
885c6158483SAnthony Liguori     }
886c6158483SAnthony Liguori 
887e3500d1fSGerd Hoffmann     x = (motion->x - mx) / vc->gfx.scale_x;
888e3500d1fSGerd Hoffmann     y = (motion->y - my) / vc->gfx.scale_y;
889c6158483SAnthony Liguori 
890e61031cdSTakashi Iwai     if (qemu_input_is_absolute()) {
891c6158483SAnthony Liguori         if (x < 0 || y < 0 ||
892e3500d1fSGerd Hoffmann             x >= surface_width(vc->gfx.ds) ||
893e3500d1fSGerd Hoffmann             y >= surface_height(vc->gfx.ds)) {
894c6158483SAnthony Liguori             return TRUE;
895c6158483SAnthony Liguori         }
896e3500d1fSGerd Hoffmann         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
897e3500d1fSGerd Hoffmann                              surface_width(vc->gfx.ds));
898e3500d1fSGerd Hoffmann         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
899e3500d1fSGerd Hoffmann                              surface_height(vc->gfx.ds));
900192f81bfSGerd Hoffmann         qemu_input_event_sync();
9012884cf5bSGerd Hoffmann     } else if (s->last_set && s->ptr_owner == vc) {
902e3500d1fSGerd Hoffmann         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
903e3500d1fSGerd Hoffmann         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
904192f81bfSGerd Hoffmann         qemu_input_event_sync();
905a4ccabcfSAnthony Liguori     }
906a4ccabcfSAnthony Liguori     s->last_x = x;
907a4ccabcfSAnthony Liguori     s->last_y = y;
908e61031cdSTakashi Iwai     s->last_set = TRUE;
909a4ccabcfSAnthony Liguori 
9102884cf5bSGerd Hoffmann     if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
911e3500d1fSGerd Hoffmann         GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
9125104a1f6SAnthony Liguori         int x = (int)motion->x_root;
9135104a1f6SAnthony Liguori         int y = (int)motion->y_root;
9145104a1f6SAnthony Liguori 
9155104a1f6SAnthony Liguori         /* In relative mode check to see if client pointer hit
9165104a1f6SAnthony Liguori          * one of the screen edges, and if so move it back by
9175104a1f6SAnthony Liguori          * 200 pixels. This is important because the pointer
9185104a1f6SAnthony Liguori          * in the server doesn't correspond 1-for-1, and so
9195104a1f6SAnthony Liguori          * may still be only half way across the screen. Without
9205104a1f6SAnthony Liguori          * this warp, the server pointer would thus appear to hit
9215104a1f6SAnthony Liguori          * an invisible wall */
9225104a1f6SAnthony Liguori         if (x == 0) {
9235104a1f6SAnthony Liguori             x += 200;
9245104a1f6SAnthony Liguori         }
9255104a1f6SAnthony Liguori         if (y == 0) {
9265104a1f6SAnthony Liguori             y += 200;
9275104a1f6SAnthony Liguori         }
9285104a1f6SAnthony Liguori         if (x == (gdk_screen_get_width(screen) - 1)) {
9295104a1f6SAnthony Liguori             x -= 200;
9305104a1f6SAnthony Liguori         }
9315104a1f6SAnthony Liguori         if (y == (gdk_screen_get_height(screen) - 1)) {
9325104a1f6SAnthony Liguori             y -= 200;
9335104a1f6SAnthony Liguori         }
9345104a1f6SAnthony Liguori 
9355104a1f6SAnthony Liguori         if (x != (int)motion->x_root || y != (int)motion->y_root) {
9368906de76SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
9378906de76SDaniel P. Berrange             GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
9388906de76SDaniel P. Berrange             gdk_device_warp(dev, screen, x, y);
9398906de76SDaniel P. Berrange #else
9408906de76SDaniel P. Berrange             GdkDisplay *display = gtk_widget_get_display(widget);
9415104a1f6SAnthony Liguori             gdk_display_warp_pointer(display, screen, x, y);
9428906de76SDaniel P. Berrange #endif
943e61031cdSTakashi Iwai             s->last_set = FALSE;
9445104a1f6SAnthony Liguori             return FALSE;
9455104a1f6SAnthony Liguori         }
9465104a1f6SAnthony Liguori     }
947a4ccabcfSAnthony Liguori     return TRUE;
948a4ccabcfSAnthony Liguori }
949a4ccabcfSAnthony Liguori 
950a4ccabcfSAnthony Liguori static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
951a4ccabcfSAnthony Liguori                                 void *opaque)
952a4ccabcfSAnthony Liguori {
953e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
954e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
955192f81bfSGerd Hoffmann     InputButton btn;
956a4ccabcfSAnthony Liguori 
957800b0e81STakashi Iwai     /* implicitly grab the input at the first click in the relative mode */
958800b0e81STakashi Iwai     if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
9592884cf5bSGerd Hoffmann         !qemu_input_is_absolute() && s->ptr_owner != vc) {
9602884cf5bSGerd Hoffmann         if (!vc->window) {
961800b0e81STakashi Iwai             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
962800b0e81STakashi Iwai                                            TRUE);
9632884cf5bSGerd Hoffmann         } else {
964d531deefSGerd Hoffmann             gd_grab_pointer(vc, "relative-mode-click");
9652884cf5bSGerd Hoffmann         }
966800b0e81STakashi Iwai         return TRUE;
967800b0e81STakashi Iwai     }
968800b0e81STakashi Iwai 
969a4ccabcfSAnthony Liguori     if (button->button == 1) {
970192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_LEFT;
971a4ccabcfSAnthony Liguori     } else if (button->button == 2) {
972192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_MIDDLE;
973a4ccabcfSAnthony Liguori     } else if (button->button == 3) {
974192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_RIGHT;
975a4ccabcfSAnthony Liguori     } else {
976192f81bfSGerd Hoffmann         return TRUE;
977a4ccabcfSAnthony Liguori     }
978a4ccabcfSAnthony Liguori 
979e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn,
980e3500d1fSGerd Hoffmann                          button->type == GDK_BUTTON_PRESS);
981192f81bfSGerd Hoffmann     qemu_input_event_sync();
982a4ccabcfSAnthony Liguori     return TRUE;
983a4ccabcfSAnthony Liguori }
984a4ccabcfSAnthony Liguori 
985d58b9122SJan Kiszka static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
986d58b9122SJan Kiszka                                 void *opaque)
987d58b9122SJan Kiszka {
988e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
989d58b9122SJan Kiszka     InputButton btn;
990d58b9122SJan Kiszka 
991d58b9122SJan Kiszka     if (scroll->direction == GDK_SCROLL_UP) {
992f22d0af0SGerd Hoffmann         btn = INPUT_BUTTON_WHEEL_UP;
993d58b9122SJan Kiszka     } else if (scroll->direction == GDK_SCROLL_DOWN) {
994f22d0af0SGerd Hoffmann         btn = INPUT_BUTTON_WHEEL_DOWN;
995d58b9122SJan Kiszka     } else {
996d58b9122SJan Kiszka         return TRUE;
997d58b9122SJan Kiszka     }
998d58b9122SJan Kiszka 
999e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
1000d58b9122SJan Kiszka     qemu_input_event_sync();
1001e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
1002d58b9122SJan Kiszka     qemu_input_event_sync();
1003d58b9122SJan Kiszka     return TRUE;
1004d58b9122SJan Kiszka }
1005d58b9122SJan Kiszka 
10060a337ed0SGerd Hoffmann static int gd_map_keycode(GtkDisplayState *s, GdkDisplay *dpy, int gdk_keycode)
1007a4ccabcfSAnthony Liguori {
1008932f2d7eSGerd Hoffmann     int qemu_keycode;
1009a4ccabcfSAnthony Liguori 
10100a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_WIN32
10110a337ed0SGerd Hoffmann     if (GDK_IS_WIN32_DISPLAY(dpy)) {
1012932f2d7eSGerd Hoffmann         qemu_keycode = MapVirtualKey(gdk_keycode, MAPVK_VK_TO_VSC);
10132777ccc5SStefan Weil         switch (qemu_keycode) {
10142777ccc5SStefan Weil         case 103:   /* alt gr */
10152777ccc5SStefan Weil             qemu_keycode = 56 | SCANCODE_GREY;
10162777ccc5SStefan Weil             break;
10172777ccc5SStefan Weil         }
10180a337ed0SGerd Hoffmann         return qemu_keycode;
10190a337ed0SGerd Hoffmann     }
10200a337ed0SGerd Hoffmann #endif
1021a4ccabcfSAnthony Liguori 
1022a4ccabcfSAnthony Liguori     if (gdk_keycode < 9) {
1023a4ccabcfSAnthony Liguori         qemu_keycode = 0;
1024a4ccabcfSAnthony Liguori     } else if (gdk_keycode < 97) {
1025a4ccabcfSAnthony Liguori         qemu_keycode = gdk_keycode - 8;
10260a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_X11
10270a337ed0SGerd Hoffmann     } else if (GDK_IS_X11_DISPLAY(dpy) && gdk_keycode < 158) {
10283158a348SBruce Rogers         if (s->has_evdev) {
1029a4ccabcfSAnthony Liguori             qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
10303158a348SBruce Rogers         } else {
10313158a348SBruce Rogers             qemu_keycode = translate_xfree86_keycode(gdk_keycode - 97);
10323158a348SBruce Rogers         }
10330a337ed0SGerd Hoffmann #endif
1034a4ccabcfSAnthony Liguori     } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
1035a4ccabcfSAnthony Liguori         qemu_keycode = 0x70;
1036a4ccabcfSAnthony Liguori     } else if (gdk_keycode == 211) { /* backslash */
1037a4ccabcfSAnthony Liguori         qemu_keycode = 0x73;
1038a4ccabcfSAnthony Liguori     } else {
1039a4ccabcfSAnthony Liguori         qemu_keycode = 0;
1040a4ccabcfSAnthony Liguori     }
1041a4ccabcfSAnthony Liguori 
1042932f2d7eSGerd Hoffmann     return qemu_keycode;
1043932f2d7eSGerd Hoffmann }
1044932f2d7eSGerd Hoffmann 
1045f8c223f6SGerd Hoffmann static gboolean gd_text_key_down(GtkWidget *widget,
1046f8c223f6SGerd Hoffmann                                  GdkEventKey *key, void *opaque)
1047f8c223f6SGerd Hoffmann {
1048f8c223f6SGerd Hoffmann     VirtualConsole *vc = opaque;
1049f8c223f6SGerd Hoffmann     QemuConsole *con = vc->gfx.dcl.con;
1050f8c223f6SGerd Hoffmann 
1051f8c223f6SGerd Hoffmann     if (key->length) {
1052f8c223f6SGerd Hoffmann         kbd_put_string_console(con, key->string, key->length);
1053f8c223f6SGerd Hoffmann     } else {
1054f8c223f6SGerd Hoffmann         int num = gd_map_keycode(vc->s, gtk_widget_get_display(widget),
1055f8c223f6SGerd Hoffmann                                  key->hardware_keycode);
1056f8c223f6SGerd Hoffmann         int qcode = qemu_input_key_number_to_qcode(num);
1057f8c223f6SGerd Hoffmann         kbd_put_qcode_console(con, qcode);
1058f8c223f6SGerd Hoffmann     }
1059f8c223f6SGerd Hoffmann     return TRUE;
1060f8c223f6SGerd Hoffmann }
1061f8c223f6SGerd Hoffmann 
1062932f2d7eSGerd Hoffmann static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
1063932f2d7eSGerd Hoffmann {
1064932f2d7eSGerd Hoffmann     VirtualConsole *vc = opaque;
1065932f2d7eSGerd Hoffmann     GtkDisplayState *s = vc->s;
1066932f2d7eSGerd Hoffmann     int gdk_keycode = key->hardware_keycode;
1067932f2d7eSGerd Hoffmann     int qemu_keycode;
1068932f2d7eSGerd Hoffmann     int i;
1069932f2d7eSGerd Hoffmann 
10701a01716aSJan Kiszka     if (s->ignore_keys) {
10711a01716aSJan Kiszka         s->ignore_keys = (key->type == GDK_KEY_PRESS);
10721a01716aSJan Kiszka         return TRUE;
10731a01716aSJan Kiszka     }
10741a01716aSJan Kiszka 
10755c960521SMartin Decky     if (key->keyval == GDK_KEY_Pause) {
10765c960521SMartin Decky         qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE,
10775c960521SMartin Decky                                         key->type == GDK_KEY_PRESS);
10785c960521SMartin Decky         return TRUE;
10795c960521SMartin Decky     }
10805c960521SMartin Decky 
10810a337ed0SGerd Hoffmann     qemu_keycode = gd_map_keycode(s, gtk_widget_get_display(widget),
10820a337ed0SGerd Hoffmann                                   gdk_keycode);
1083932f2d7eSGerd Hoffmann 
108474444bc1SGerd Hoffmann     trace_gd_key_event(vc->label, gdk_keycode, qemu_keycode,
1085a4ccabcfSAnthony Liguori                        (key->type == GDK_KEY_PRESS) ? "down" : "up");
1086a4ccabcfSAnthony Liguori 
10876db253caSJan Kiszka     for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
10886db253caSJan Kiszka         if (qemu_keycode == modifier_keycode[i]) {
10896db253caSJan Kiszka             s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
10906db253caSJan Kiszka         }
10916db253caSJan Kiszka     }
10926db253caSJan Kiszka 
1093e3500d1fSGerd Hoffmann     qemu_input_event_send_key_number(vc->gfx.dcl.con, qemu_keycode,
1094af98ba92SGerd Hoffmann                                      key->type == GDK_KEY_PRESS);
1095a4ccabcfSAnthony Liguori 
1096a4ccabcfSAnthony Liguori     return TRUE;
1097a4ccabcfSAnthony Liguori }
1098a4ccabcfSAnthony Liguori 
10990d0e044dSTakashi Iwai static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
11000d0e044dSTakashi Iwai {
11010d0e044dSTakashi Iwai     if (event->type == GDK_MOTION_NOTIFY) {
11020d0e044dSTakashi Iwai         return gd_motion_event(widget, &event->motion, opaque);
11030d0e044dSTakashi Iwai     }
11040d0e044dSTakashi Iwai     return FALSE;
11050d0e044dSTakashi Iwai }
11060d0e044dSTakashi Iwai 
1107a4ccabcfSAnthony Liguori /** Window Menu Actions **/
1108a4ccabcfSAnthony Liguori 
110930e8f22bSJan Kiszka static void gd_menu_pause(GtkMenuItem *item, void *opaque)
111030e8f22bSJan Kiszka {
111130e8f22bSJan Kiszka     GtkDisplayState *s = opaque;
111230e8f22bSJan Kiszka 
111330e8f22bSJan Kiszka     if (s->external_pause_update) {
111430e8f22bSJan Kiszka         return;
111530e8f22bSJan Kiszka     }
111630e8f22bSJan Kiszka     if (runstate_is_running()) {
111730e8f22bSJan Kiszka         qmp_stop(NULL);
111830e8f22bSJan Kiszka     } else {
111930e8f22bSJan Kiszka         qmp_cont(NULL);
112030e8f22bSJan Kiszka     }
112130e8f22bSJan Kiszka }
112230e8f22bSJan Kiszka 
112330e8f22bSJan Kiszka static void gd_menu_reset(GtkMenuItem *item, void *opaque)
112430e8f22bSJan Kiszka {
112530e8f22bSJan Kiszka     qmp_system_reset(NULL);
112630e8f22bSJan Kiszka }
112730e8f22bSJan Kiszka 
112830e8f22bSJan Kiszka static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
112930e8f22bSJan Kiszka {
113030e8f22bSJan Kiszka     qmp_system_powerdown(NULL);
113130e8f22bSJan Kiszka }
113230e8f22bSJan Kiszka 
1133a4ccabcfSAnthony Liguori static void gd_menu_quit(GtkMenuItem *item, void *opaque)
1134a4ccabcfSAnthony Liguori {
1135a4ccabcfSAnthony Liguori     qmp_quit(NULL);
1136a4ccabcfSAnthony Liguori }
1137a4ccabcfSAnthony Liguori 
1138a4ccabcfSAnthony Liguori static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
1139a4ccabcfSAnthony Liguori {
1140a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
1141e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_by_menu(s);
1142e72b59faSJohn Snow     GtkNotebook *nb = GTK_NOTEBOOK(s->notebook);
1143832189c9SGerd Hoffmann     gint page;
1144a4ccabcfSAnthony Liguori 
11456db253caSJan Kiszka     gtk_release_modifiers(s);
1146271a25c0SGerd Hoffmann     if (vc) {
1147e72b59faSJohn Snow         page = gtk_notebook_page_num(nb, vc->tab_item);
1148e72b59faSJohn Snow         gtk_notebook_set_current_page(nb, page);
11499d677e1cSJan Kiszka         gtk_widget_grab_focus(vc->focus);
1150d861def3SAnthony Liguori     }
11511a01716aSJan Kiszka     s->ignore_keys = false;
1152d861def3SAnthony Liguori }
1153a4ccabcfSAnthony Liguori 
1154277836c8SCole Robinson static void gd_accel_switch_vc(void *opaque)
1155277836c8SCole Robinson {
1156277836c8SCole Robinson     VirtualConsole *vc = opaque;
11571a01716aSJan Kiszka 
1158277836c8SCole Robinson     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
11591a01716aSJan Kiszka #if !GTK_CHECK_VERSION(3, 0, 0)
11601a01716aSJan Kiszka     /* GTK2 sends the accel key to the target console - ignore this until */
11611a01716aSJan Kiszka     vc->s->ignore_keys = true;
11621a01716aSJan Kiszka #endif
1163277836c8SCole Robinson }
1164277836c8SCole Robinson 
1165a4ccabcfSAnthony Liguori static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
1166a4ccabcfSAnthony Liguori {
1167a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
1168fa7a1e52SGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1169a4ccabcfSAnthony Liguori 
1170a4ccabcfSAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
1171a4ccabcfSAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
1172a4ccabcfSAnthony Liguori     } else {
1173a4ccabcfSAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1174a4ccabcfSAnthony Liguori     }
1175fa7a1e52SGerd Hoffmann     gd_update_windowsize(vc);
1176a4ccabcfSAnthony Liguori }
1177a4ccabcfSAnthony Liguori 
1178cdeb7090SGerd Hoffmann static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
1179cdeb7090SGerd Hoffmann                                     void *opaque)
1180cdeb7090SGerd Hoffmann {
1181cdeb7090SGerd Hoffmann     VirtualConsole *vc = opaque;
1182cdeb7090SGerd Hoffmann     GtkDisplayState *s = vc->s;
1183cdeb7090SGerd Hoffmann 
1184cdeb7090SGerd Hoffmann     gtk_widget_set_sensitive(vc->menu_item, true);
1185316cb068SGerd Hoffmann     gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
1186cdeb7090SGerd Hoffmann     gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
1187cdeb7090SGerd Hoffmann                                     vc->tab_item, vc->label);
1188cdeb7090SGerd Hoffmann     gtk_widget_destroy(vc->window);
1189cdeb7090SGerd Hoffmann     vc->window = NULL;
1190cdeb7090SGerd Hoffmann     return TRUE;
1191cdeb7090SGerd Hoffmann }
1192cdeb7090SGerd Hoffmann 
11930c77a37fSGerd Hoffmann static gboolean gd_win_grab(void *opaque)
11940c77a37fSGerd Hoffmann {
11950c77a37fSGerd Hoffmann     VirtualConsole *vc = opaque;
11960c77a37fSGerd Hoffmann 
11970c77a37fSGerd Hoffmann     fprintf(stderr, "%s: %s\n", __func__, vc->label);
11980c77a37fSGerd Hoffmann     if (vc->s->ptr_owner) {
11990c77a37fSGerd Hoffmann         gd_ungrab_pointer(vc->s);
12000c77a37fSGerd Hoffmann     } else {
1201d531deefSGerd Hoffmann         gd_grab_pointer(vc, "user-request-detached-tab");
12020c77a37fSGerd Hoffmann     }
12030c77a37fSGerd Hoffmann     return TRUE;
12040c77a37fSGerd Hoffmann }
12050c77a37fSGerd Hoffmann 
1206cdeb7090SGerd Hoffmann static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
1207cdeb7090SGerd Hoffmann {
1208cdeb7090SGerd Hoffmann     GtkDisplayState *s = opaque;
1209cdeb7090SGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1210cdeb7090SGerd Hoffmann 
1211f8c223f6SGerd Hoffmann     if (vc->type == GD_VC_GFX &&
1212f8c223f6SGerd Hoffmann         qemu_console_is_graphic(vc->gfx.dcl.con)) {
1213aa0a55d4SGerd Hoffmann         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1214aa0a55d4SGerd Hoffmann                                        FALSE);
1215cdeb7090SGerd Hoffmann     }
1216cdeb7090SGerd Hoffmann     if (!vc->window) {
1217cdeb7090SGerd Hoffmann         gtk_widget_set_sensitive(vc->menu_item, false);
1218cdeb7090SGerd Hoffmann         vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1219316cb068SGerd Hoffmann         gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
1220cdeb7090SGerd Hoffmann 
1221cdeb7090SGerd Hoffmann         g_signal_connect(vc->window, "delete-event",
1222cdeb7090SGerd Hoffmann                          G_CALLBACK(gd_tab_window_close), vc);
1223cdeb7090SGerd Hoffmann         gtk_widget_show_all(vc->window);
12244eeaa3a8SGerd Hoffmann 
1225f8c223f6SGerd Hoffmann         if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
12260c77a37fSGerd Hoffmann             GtkAccelGroup *ag = gtk_accel_group_new();
12270c77a37fSGerd Hoffmann             gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
12280c77a37fSGerd Hoffmann 
1229f8c223f6SGerd Hoffmann             GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
1230f8c223f6SGerd Hoffmann                                                vc, NULL);
12310c77a37fSGerd Hoffmann             gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
1232f8c223f6SGerd Hoffmann         }
12330c77a37fSGerd Hoffmann 
123482fc1809SGerd Hoffmann         gd_update_geometry_hints(vc);
12354eeaa3a8SGerd Hoffmann         gd_update_caption(s);
1236cdeb7090SGerd Hoffmann     }
1237cdeb7090SGerd Hoffmann }
1238cdeb7090SGerd Hoffmann 
1239c6158483SAnthony Liguori static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
1240c6158483SAnthony Liguori {
1241c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1242e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1243c6158483SAnthony Liguori 
124410409282SStefan Weil     if (!s->full_screen) {
1245c6158483SAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1246b0f31820SCole Robinson         gtk_widget_hide(s->menu_bar);
1247e3500d1fSGerd Hoffmann         if (vc->type == GD_VC_GFX) {
1248e3500d1fSGerd Hoffmann             gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
1249c6158483SAnthony Liguori         }
1250e3500d1fSGerd Hoffmann         gtk_window_fullscreen(GTK_WINDOW(s->window));
1251c6158483SAnthony Liguori         s->full_screen = TRUE;
1252c6158483SAnthony Liguori     } else {
1253c6158483SAnthony Liguori         gtk_window_unfullscreen(GTK_WINDOW(s->window));
1254c6158483SAnthony Liguori         gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
1255b0f31820SCole Robinson         gtk_widget_show(s->menu_bar);
1256c6158483SAnthony Liguori         s->full_screen = FALSE;
1257e3500d1fSGerd Hoffmann         if (vc->type == GD_VC_GFX) {
1258e3500d1fSGerd Hoffmann             vc->gfx.scale_x = 1.0;
1259e3500d1fSGerd Hoffmann             vc->gfx.scale_y = 1.0;
126082fc1809SGerd Hoffmann             gd_update_windowsize(vc);
1261e3500d1fSGerd Hoffmann         }
1262c6158483SAnthony Liguori     }
1263c6158483SAnthony Liguori 
1264e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
1265c6158483SAnthony Liguori }
1266c6158483SAnthony Liguori 
126795414914SCole Robinson static void gd_accel_full_screen(void *opaque)
126895414914SCole Robinson {
126995414914SCole Robinson     GtkDisplayState *s = opaque;
127095414914SCole Robinson     gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
127195414914SCole Robinson }
127295414914SCole Robinson 
1273c6158483SAnthony Liguori static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
1274c6158483SAnthony Liguori {
1275c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1276e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1277c6158483SAnthony Liguori 
1278c6158483SAnthony Liguori     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1279c6158483SAnthony Liguori                                    FALSE);
1280c6158483SAnthony Liguori 
128182fc1809SGerd Hoffmann     vc->gfx.scale_x += VC_SCALE_STEP;
128282fc1809SGerd Hoffmann     vc->gfx.scale_y += VC_SCALE_STEP;
1283c6158483SAnthony Liguori 
1284e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1285c6158483SAnthony Liguori }
1286c6158483SAnthony Liguori 
1287c6158483SAnthony Liguori static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
1288c6158483SAnthony Liguori {
1289c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1290e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1291c6158483SAnthony Liguori 
1292c6158483SAnthony Liguori     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1293c6158483SAnthony Liguori                                    FALSE);
1294c6158483SAnthony Liguori 
129582fc1809SGerd Hoffmann     vc->gfx.scale_x -= VC_SCALE_STEP;
129682fc1809SGerd Hoffmann     vc->gfx.scale_y -= VC_SCALE_STEP;
1297c6158483SAnthony Liguori 
129882fc1809SGerd Hoffmann     vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
129982fc1809SGerd Hoffmann     vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
1300c6158483SAnthony Liguori 
1301e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1302c6158483SAnthony Liguori }
1303c6158483SAnthony Liguori 
1304c6158483SAnthony Liguori static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
1305c6158483SAnthony Liguori {
1306c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1307e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1308c6158483SAnthony Liguori 
1309e3500d1fSGerd Hoffmann     vc->gfx.scale_x = 1.0;
1310e3500d1fSGerd Hoffmann     vc->gfx.scale_y = 1.0;
1311c6158483SAnthony Liguori 
1312e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1313c6158483SAnthony Liguori }
1314c6158483SAnthony Liguori 
1315c6158483SAnthony Liguori static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
1316c6158483SAnthony Liguori {
1317c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1318e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1319c6158483SAnthony Liguori 
1320c6158483SAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
1321c6158483SAnthony Liguori         s->free_scale = TRUE;
1322c6158483SAnthony Liguori     } else {
1323c6158483SAnthony Liguori         s->free_scale = FALSE;
1324e3500d1fSGerd Hoffmann         vc->gfx.scale_x = 1.0;
1325e3500d1fSGerd Hoffmann         vc->gfx.scale_y = 1.0;
1326c6158483SAnthony Liguori     }
1327c6158483SAnthony Liguori 
132882fc1809SGerd Hoffmann     gd_update_windowsize(vc);
1329e3500d1fSGerd Hoffmann     gd_update_full_redraw(vc);
1330c6158483SAnthony Liguori }
1331c6158483SAnthony Liguori 
1332*a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1333*a69fc693SGerd Hoffmann static void gd_grab_update(VirtualConsole *vc, bool kbd, bool ptr)
1334*a69fc693SGerd Hoffmann {
1335*a69fc693SGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1336*a69fc693SGerd Hoffmann     GdkSeat *seat = gdk_display_get_default_seat(display);
1337*a69fc693SGerd Hoffmann     GdkWindow *window = gtk_widget_get_window(vc->gfx.drawing_area);
1338*a69fc693SGerd Hoffmann     GdkSeatCapabilities caps = 0;
1339*a69fc693SGerd Hoffmann     GdkCursor *cursor = NULL;
1340*a69fc693SGerd Hoffmann 
1341*a69fc693SGerd Hoffmann     if (kbd) {
1342*a69fc693SGerd Hoffmann         caps |= GDK_SEAT_CAPABILITY_KEYBOARD;
1343*a69fc693SGerd Hoffmann     }
1344*a69fc693SGerd Hoffmann     if (ptr) {
1345*a69fc693SGerd Hoffmann         caps |= GDK_SEAT_CAPABILITY_ALL_POINTING;
1346*a69fc693SGerd Hoffmann         cursor = vc->s->null_cursor;
1347*a69fc693SGerd Hoffmann     }
1348*a69fc693SGerd Hoffmann 
1349*a69fc693SGerd Hoffmann     if (caps) {
1350*a69fc693SGerd Hoffmann         gdk_seat_grab(seat, window, caps, false, cursor,
1351*a69fc693SGerd Hoffmann                       NULL, NULL, NULL);
1352*a69fc693SGerd Hoffmann     } else {
1353*a69fc693SGerd Hoffmann         gdk_seat_ungrab(seat);
1354*a69fc693SGerd Hoffmann     }
1355*a69fc693SGerd Hoffmann }
1356*a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0)
1357f50def91SGerd Hoffmann static void gd_grab_devices(VirtualConsole *vc, bool grab,
1358f50def91SGerd Hoffmann                             GdkInputSource source, GdkEventMask mask,
1359f50def91SGerd Hoffmann                             GdkCursor *cursor)
1360f50def91SGerd Hoffmann {
1361f50def91SGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1362f50def91SGerd Hoffmann     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1363f50def91SGerd Hoffmann     GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
1364f50def91SGerd Hoffmann     GList *tmp = devs;
1365f50def91SGerd Hoffmann 
1366f50def91SGerd Hoffmann     for (tmp = devs; tmp; tmp = tmp->next) {
1367f50def91SGerd Hoffmann         GdkDevice *dev = tmp->data;
1368f50def91SGerd Hoffmann         if (gdk_device_get_source(dev) != source) {
1369f50def91SGerd Hoffmann             continue;
1370f50def91SGerd Hoffmann         }
1371f50def91SGerd Hoffmann         if (grab) {
1372f50def91SGerd Hoffmann             GdkWindow *win = gtk_widget_get_window(vc->gfx.drawing_area);
1373f50def91SGerd Hoffmann             gdk_device_grab(dev, win, GDK_OWNERSHIP_NONE, FALSE,
1374f50def91SGerd Hoffmann                             mask, cursor, GDK_CURRENT_TIME);
1375f50def91SGerd Hoffmann         } else {
1376f50def91SGerd Hoffmann             gdk_device_ungrab(dev, GDK_CURRENT_TIME);
1377f50def91SGerd Hoffmann         }
1378f50def91SGerd Hoffmann     }
1379f50def91SGerd Hoffmann     g_list_free(devs);
1380f50def91SGerd Hoffmann }
1381f50def91SGerd Hoffmann #endif
1382f50def91SGerd Hoffmann 
1383d531deefSGerd Hoffmann static void gd_grab_keyboard(VirtualConsole *vc, const char *reason)
13845104a1f6SAnthony Liguori {
1385aa4f4058SGerd Hoffmann     if (vc->s->kbd_owner) {
1386aa4f4058SGerd Hoffmann         if (vc->s->kbd_owner == vc) {
1387aa4f4058SGerd Hoffmann             return;
1388aa4f4058SGerd Hoffmann         } else {
1389aa4f4058SGerd Hoffmann             gd_ungrab_keyboard(vc->s);
1390aa4f4058SGerd Hoffmann         }
1391aa4f4058SGerd Hoffmann     }
1392aa4f4058SGerd Hoffmann 
1393*a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1394*a69fc693SGerd Hoffmann     gd_grab_update(vc, true, vc->s->ptr_owner == vc);
1395*a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0)
1396f50def91SGerd Hoffmann     gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD,
1397655199daSDaniel P. Berrange                    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
1398f50def91SGerd Hoffmann                    NULL);
1399655199daSDaniel P. Berrange #else
1400e3500d1fSGerd Hoffmann     gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area),
14015104a1f6SAnthony Liguori                       FALSE,
14025104a1f6SAnthony Liguori                       GDK_CURRENT_TIME);
1403655199daSDaniel P. Berrange #endif
14044c638e2eSGerd Hoffmann     vc->s->kbd_owner = vc;
1405695cc59dSGerd Hoffmann     gd_update_caption(vc->s);
1406d531deefSGerd Hoffmann     trace_gd_grab(vc->label, "kbd", reason);
14075104a1f6SAnthony Liguori }
14085104a1f6SAnthony Liguori 
14094c638e2eSGerd Hoffmann static void gd_ungrab_keyboard(GtkDisplayState *s)
14105104a1f6SAnthony Liguori {
14114c638e2eSGerd Hoffmann     VirtualConsole *vc = s->kbd_owner;
14124c638e2eSGerd Hoffmann 
14134c638e2eSGerd Hoffmann     if (vc == NULL) {
14144c638e2eSGerd Hoffmann         return;
14154c638e2eSGerd Hoffmann     }
14164c638e2eSGerd Hoffmann     s->kbd_owner = NULL;
14174c638e2eSGerd Hoffmann 
1418*a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1419*a69fc693SGerd Hoffmann     gd_grab_update(vc, false, vc->s->ptr_owner == vc);
1420*a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0)
1421f50def91SGerd Hoffmann     gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL);
1422655199daSDaniel P. Berrange #else
14235104a1f6SAnthony Liguori     gdk_keyboard_ungrab(GDK_CURRENT_TIME);
1424655199daSDaniel P. Berrange #endif
1425695cc59dSGerd Hoffmann     gd_update_caption(s);
1426d531deefSGerd Hoffmann     trace_gd_ungrab(vc->label, "kbd");
14275104a1f6SAnthony Liguori }
14285104a1f6SAnthony Liguori 
1429d531deefSGerd Hoffmann static void gd_grab_pointer(VirtualConsole *vc, const char *reason)
14305104a1f6SAnthony Liguori {
1431e3500d1fSGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1432aa4f4058SGerd Hoffmann 
1433aa4f4058SGerd Hoffmann     if (vc->s->ptr_owner) {
1434aa4f4058SGerd Hoffmann         if (vc->s->ptr_owner == vc) {
1435aa4f4058SGerd Hoffmann             return;
1436aa4f4058SGerd Hoffmann         } else {
1437aa4f4058SGerd Hoffmann             gd_ungrab_pointer(vc->s);
1438aa4f4058SGerd Hoffmann         }
1439aa4f4058SGerd Hoffmann     }
1440aa4f4058SGerd Hoffmann 
1441*a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1442*a69fc693SGerd Hoffmann     gd_grab_update(vc, vc->s->kbd_owner == vc, true);
1443*a69fc693SGerd Hoffmann     gdk_device_get_position(gd_get_pointer(display),
1444*a69fc693SGerd Hoffmann                             NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
1445*a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0)
1446f50def91SGerd Hoffmann     gd_grab_devices(vc, true, GDK_SOURCE_MOUSE,
14472a05485dSDaniel P. Berrange                     GDK_POINTER_MOTION_MASK |
14482a05485dSDaniel P. Berrange                     GDK_BUTTON_PRESS_MASK |
14492a05485dSDaniel P. Berrange                     GDK_BUTTON_RELEASE_MASK |
14502a05485dSDaniel P. Berrange                     GDK_BUTTON_MOTION_MASK |
14512a05485dSDaniel P. Berrange                     GDK_SCROLL_MASK,
1452f50def91SGerd Hoffmann                     vc->s->null_cursor);
1453bb732ee7SCole Robinson     gdk_device_get_position(gd_get_pointer(display),
1454e3500d1fSGerd Hoffmann                             NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
14552a05485dSDaniel P. Berrange #else
1456e3500d1fSGerd Hoffmann     gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area),
14575104a1f6SAnthony Liguori                      FALSE, /* All events to come to our window directly */
14585104a1f6SAnthony Liguori                      GDK_POINTER_MOTION_MASK |
14595104a1f6SAnthony Liguori                      GDK_BUTTON_PRESS_MASK |
14605104a1f6SAnthony Liguori                      GDK_BUTTON_RELEASE_MASK |
14615104a1f6SAnthony Liguori                      GDK_BUTTON_MOTION_MASK |
14625104a1f6SAnthony Liguori                      GDK_SCROLL_MASK,
14635104a1f6SAnthony Liguori                      NULL, /* Allow cursor to move over entire desktop */
1464e3500d1fSGerd Hoffmann                      vc->s->null_cursor,
14655104a1f6SAnthony Liguori                      GDK_CURRENT_TIME);
1466ecce1929STakashi Iwai     gdk_display_get_pointer(display, NULL,
1467e3500d1fSGerd Hoffmann                             &vc->s->grab_x_root, &vc->s->grab_y_root, NULL);
14682a05485dSDaniel P. Berrange #endif
14694c638e2eSGerd Hoffmann     vc->s->ptr_owner = vc;
1470695cc59dSGerd Hoffmann     gd_update_caption(vc->s);
1471d531deefSGerd Hoffmann     trace_gd_grab(vc->label, "ptr", reason);
14722a05485dSDaniel P. Berrange }
14732a05485dSDaniel P. Berrange 
14744c638e2eSGerd Hoffmann static void gd_ungrab_pointer(GtkDisplayState *s)
14752a05485dSDaniel P. Berrange {
14764c638e2eSGerd Hoffmann     VirtualConsole *vc = s->ptr_owner;
1477*a69fc693SGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
14784c638e2eSGerd Hoffmann 
14794c638e2eSGerd Hoffmann     if (vc == NULL) {
14804c638e2eSGerd Hoffmann         return;
14814c638e2eSGerd Hoffmann     }
14824c638e2eSGerd Hoffmann     s->ptr_owner = NULL;
14834c638e2eSGerd Hoffmann 
1484*a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1485*a69fc693SGerd Hoffmann     gd_grab_update(vc, vc->s->kbd_owner == vc, false);
1486*a69fc693SGerd Hoffmann     gdk_device_warp(gd_get_pointer(display),
1487*a69fc693SGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
1488*a69fc693SGerd Hoffmann                     vc->s->grab_x_root, vc->s->grab_y_root);
1489*a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0)
1490f50def91SGerd Hoffmann     gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL);
1491bb732ee7SCole Robinson     gdk_device_warp(gd_get_pointer(display),
1492e3500d1fSGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
1493e3500d1fSGerd Hoffmann                     vc->s->grab_x_root, vc->s->grab_y_root);
14942a05485dSDaniel P. Berrange #else
14952a05485dSDaniel P. Berrange     gdk_pointer_ungrab(GDK_CURRENT_TIME);
1496ecce1929STakashi Iwai     gdk_display_warp_pointer(display,
1497e3500d1fSGerd Hoffmann                              gtk_widget_get_screen(vc->gfx.drawing_area),
1498e3500d1fSGerd Hoffmann                              vc->s->grab_x_root, vc->s->grab_y_root);
14992a05485dSDaniel P. Berrange #endif
1500695cc59dSGerd Hoffmann     gd_update_caption(s);
1501d531deefSGerd Hoffmann     trace_gd_ungrab(vc->label, "ptr");
15022a05485dSDaniel P. Berrange }
15032a05485dSDaniel P. Berrange 
15042a05485dSDaniel P. Berrange static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
15052a05485dSDaniel P. Berrange {
15062a05485dSDaniel P. Berrange     GtkDisplayState *s = opaque;
1507e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
15082a05485dSDaniel P. Berrange 
15092a05485dSDaniel P. Berrange     if (gd_is_grab_active(s)) {
1510d531deefSGerd Hoffmann         gd_grab_keyboard(vc, "user-request-main-window");
1511d531deefSGerd Hoffmann         gd_grab_pointer(vc, "user-request-main-window");
15125104a1f6SAnthony Liguori     } else {
15134c638e2eSGerd Hoffmann         gd_ungrab_keyboard(s);
15144c638e2eSGerd Hoffmann         gd_ungrab_pointer(s);
15155104a1f6SAnthony Liguori     }
15165104a1f6SAnthony Liguori 
1517e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
15185104a1f6SAnthony Liguori }
15195104a1f6SAnthony Liguori 
1520a4ccabcfSAnthony Liguori static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
1521a4ccabcfSAnthony Liguori                            gpointer data)
1522a4ccabcfSAnthony Liguori {
1523a4ccabcfSAnthony Liguori     GtkDisplayState *s = data;
1524e3500d1fSGerd Hoffmann     VirtualConsole *vc;
15255104a1f6SAnthony Liguori     gboolean on_vga;
1526a4ccabcfSAnthony Liguori 
1527a4ccabcfSAnthony Liguori     if (!gtk_widget_get_realized(s->notebook)) {
1528a4ccabcfSAnthony Liguori         return;
1529a4ccabcfSAnthony Liguori     }
1530a4ccabcfSAnthony Liguori 
15316fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK
15326fa27697SGerd Hoffmann     vc = gd_vc_find_current(s);
15336fa27697SGerd Hoffmann     if (vc && vc->type == GD_VC_VTE) {
15346fa27697SGerd Hoffmann         gtk_widget_hide(vc->vte.terminal);
15356fa27697SGerd Hoffmann     }
15366fa27697SGerd Hoffmann #endif
1537e3500d1fSGerd Hoffmann     vc = gd_vc_find_by_page(s, arg2);
1538e3500d1fSGerd Hoffmann     if (!vc) {
1539e3500d1fSGerd Hoffmann         return;
1540e3500d1fSGerd Hoffmann     }
15416fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK
15426fa27697SGerd Hoffmann     if (vc->type == GD_VC_VTE) {
15436fa27697SGerd Hoffmann         gtk_widget_show(vc->vte.terminal);
15446fa27697SGerd Hoffmann     }
15456fa27697SGerd Hoffmann #endif
1546e3500d1fSGerd Hoffmann     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
1547e3500d1fSGerd Hoffmann                                    TRUE);
1548f8c223f6SGerd Hoffmann     on_vga = (vc->type == GD_VC_GFX &&
1549f8c223f6SGerd Hoffmann               qemu_console_is_graphic(vc->gfx.dcl.con));
15505104a1f6SAnthony Liguori     if (!on_vga) {
15515104a1f6SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
15525104a1f6SAnthony Liguori                                        FALSE);
1553c6158483SAnthony Liguori     } else if (s->full_screen) {
1554c6158483SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1555c6158483SAnthony Liguori                                        TRUE);
15565104a1f6SAnthony Liguori     }
15575104a1f6SAnthony Liguori     gtk_widget_set_sensitive(s->grab_item, on_vga);
15585104a1f6SAnthony Liguori 
155982fc1809SGerd Hoffmann     gd_update_windowsize(vc);
1560e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
1561a4ccabcfSAnthony Liguori }
1562a4ccabcfSAnthony Liguori 
1563e3500d1fSGerd Hoffmann static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
1564e3500d1fSGerd Hoffmann                                gpointer opaque)
15655104a1f6SAnthony Liguori {
1566e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1567e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
15685104a1f6SAnthony Liguori 
15692884cf5bSGerd Hoffmann     if (gd_grab_on_hover(s)) {
1570d531deefSGerd Hoffmann         gd_grab_keyboard(vc, "grab-on-hover");
15715104a1f6SAnthony Liguori     }
15725104a1f6SAnthony Liguori     return TRUE;
15735104a1f6SAnthony Liguori }
15745104a1f6SAnthony Liguori 
1575e3500d1fSGerd Hoffmann static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
1576e3500d1fSGerd Hoffmann                                gpointer opaque)
15775104a1f6SAnthony Liguori {
1578e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1579e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
15805104a1f6SAnthony Liguori 
15812884cf5bSGerd Hoffmann     if (gd_grab_on_hover(s)) {
15824c638e2eSGerd Hoffmann         gd_ungrab_keyboard(s);
15835104a1f6SAnthony Liguori     }
15845104a1f6SAnthony Liguori     return TRUE;
15855104a1f6SAnthony Liguori }
15865104a1f6SAnthony Liguori 
15876db253caSJan Kiszka static gboolean gd_focus_out_event(GtkWidget *widget,
1588e3500d1fSGerd Hoffmann                                    GdkEventCrossing *crossing, gpointer opaque)
15896db253caSJan Kiszka {
1590e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1591e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
15926db253caSJan Kiszka 
15936db253caSJan Kiszka     gtk_release_modifiers(s);
15946db253caSJan Kiszka     return TRUE;
15956db253caSJan Kiszka }
15966db253caSJan Kiszka 
15971301e515SGerd Hoffmann static gboolean gd_configure(GtkWidget *widget,
15981301e515SGerd Hoffmann                              GdkEventConfigure *cfg, gpointer opaque)
15991301e515SGerd Hoffmann {
16001301e515SGerd Hoffmann     VirtualConsole *vc = opaque;
16011301e515SGerd Hoffmann 
1602925a0400SGerd Hoffmann     gd_set_ui_info(vc, cfg->width, cfg->height);
16031301e515SGerd Hoffmann     return FALSE;
16041301e515SGerd Hoffmann }
16051301e515SGerd Hoffmann 
1606d861def3SAnthony Liguori /** Virtual Console Callbacks **/
1607d861def3SAnthony Liguori 
1608ed1132e4SGerd Hoffmann static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
1609cdeb7090SGerd Hoffmann                                int idx, GSList *group, GtkWidget *view_menu)
1610ed1132e4SGerd Hoffmann {
1611cdeb7090SGerd Hoffmann     vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
1612277836c8SCole Robinson     gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx,
1613277836c8SCole Robinson             HOTKEY_MODIFIERS, 0,
1614277836c8SCole Robinson             g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL));
1615277836c8SCole Robinson #if GTK_CHECK_VERSION(3, 8, 0)
1616277836c8SCole Robinson     gtk_accel_label_set_accel(
1617277836c8SCole Robinson             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))),
1618277836c8SCole Robinson             GDK_KEY_1 + idx, HOTKEY_MODIFIERS);
1619277836c8SCole Robinson #endif
1620ed1132e4SGerd Hoffmann 
1621ed1132e4SGerd Hoffmann     g_signal_connect(vc->menu_item, "activate",
1622ed1132e4SGerd Hoffmann                      G_CALLBACK(gd_menu_switch_vc), s);
1623ed1132e4SGerd Hoffmann     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
1624ed1132e4SGerd Hoffmann 
1625277836c8SCole Robinson     group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
1626ed1132e4SGerd Hoffmann     return group;
1627ed1132e4SGerd Hoffmann }
1628ed1132e4SGerd Hoffmann 
1629ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
163044b31e0bSMichael S. Tsirkin static void gd_menu_copy(GtkMenuItem *item, void *opaque)
163144b31e0bSMichael S. Tsirkin {
163244b31e0bSMichael S. Tsirkin     GtkDisplayState *s = opaque;
163344b31e0bSMichael S. Tsirkin     VirtualConsole *vc = gd_vc_find_current(s);
163444b31e0bSMichael S. Tsirkin 
163544b31e0bSMichael S. Tsirkin     vte_terminal_copy_clipboard(VTE_TERMINAL(vc->vte.terminal));
163644b31e0bSMichael S. Tsirkin }
163744b31e0bSMichael S. Tsirkin 
16380fb20d1cSCole Robinson static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
16390fb20d1cSCole Robinson {
16400fb20d1cSCole Robinson     VirtualConsole *vc = opaque;
16410fb20d1cSCole Robinson 
16420fb20d1cSCole Robinson     if (gtk_adjustment_get_upper(adjustment) >
16430fb20d1cSCole Robinson         gtk_adjustment_get_page_size(adjustment)) {
1644271a25c0SGerd Hoffmann         gtk_widget_show(vc->vte.scrollbar);
16450fb20d1cSCole Robinson     } else {
1646271a25c0SGerd Hoffmann         gtk_widget_hide(vc->vte.scrollbar);
16470fb20d1cSCole Robinson     }
16480fb20d1cSCole Robinson }
16490fb20d1cSCole Robinson 
1650d861def3SAnthony Liguori static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
1651d861def3SAnthony Liguori {
1652d861def3SAnthony Liguori     VirtualConsole *vc = chr->opaque;
1653d861def3SAnthony Liguori 
1654271a25c0SGerd Hoffmann     vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
1655d4370741SCole Robinson     return len;
1656d861def3SAnthony Liguori }
1657d861def3SAnthony Liguori 
1658fba958c6SPaolo Bonzini static void gd_vc_chr_set_echo(CharDriverState *chr, bool echo)
1659fba958c6SPaolo Bonzini {
1660fba958c6SPaolo Bonzini     VirtualConsole *vc = chr->opaque;
1661fba958c6SPaolo Bonzini 
1662fba958c6SPaolo Bonzini     vc->vte.echo = echo;
1663fba958c6SPaolo Bonzini }
1664fba958c6SPaolo Bonzini 
1665d861def3SAnthony Liguori static int nb_vcs;
1666d861def3SAnthony Liguori static CharDriverState *vcs[MAX_VCS];
1667d861def3SAnthony Liguori 
1668919e11f3SDaniel P. Berrange static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
1669d861def3SAnthony Liguori {
1670919e11f3SDaniel P. Berrange     ChardevCommon *common = qapi_ChardevVC_base(vc);
1671d861def3SAnthony Liguori     CharDriverState *chr;
1672d861def3SAnthony Liguori 
1673919e11f3SDaniel P. Berrange     chr = qemu_chr_alloc(common, errp);
1674919e11f3SDaniel P. Berrange     if (!chr) {
1675919e11f3SDaniel P. Berrange         return NULL;
1676919e11f3SDaniel P. Berrange     }
1677919e11f3SDaniel P. Berrange 
1678d861def3SAnthony Liguori     chr->chr_write = gd_vc_chr_write;
1679fba958c6SPaolo Bonzini     chr->chr_set_echo = gd_vc_chr_set_echo;
1680fba958c6SPaolo Bonzini 
1681fba958c6SPaolo Bonzini     /* Temporary, until gd_vc_vte_init runs.  */
1682327d83baSPaolo Bonzini     chr->opaque = g_new0(VirtualConsole, 1);
1683fba958c6SPaolo Bonzini 
1684bd5c51eeSMichael Roth     /* defer OPENED events until our vc is fully initialized */
1685bd5c51eeSMichael Roth     chr->explicit_be_open = true;
1686d861def3SAnthony Liguori 
1687d861def3SAnthony Liguori     vcs[nb_vcs++] = chr;
1688d861def3SAnthony Liguori 
1689d861def3SAnthony Liguori     return chr;
1690d861def3SAnthony Liguori }
1691d861def3SAnthony Liguori 
1692d4370741SCole Robinson static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
1693d4370741SCole Robinson                          gpointer user_data)
1694d861def3SAnthony Liguori {
1695d4370741SCole Robinson     VirtualConsole *vc = user_data;
1696d861def3SAnthony Liguori 
1697fba958c6SPaolo Bonzini     if (vc->vte.echo) {
1698fba958c6SPaolo Bonzini         VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
1699fba958c6SPaolo Bonzini         int i;
1700fba958c6SPaolo Bonzini         for (i = 0; i < size; i++) {
1701fba958c6SPaolo Bonzini             uint8_t c = text[i];
1702fba958c6SPaolo Bonzini             if (c >= 128 || isprint(c)) {
1703fba958c6SPaolo Bonzini                 /* 8-bit characters are considered printable.  */
1704fba958c6SPaolo Bonzini                 vte_terminal_feed(term, &text[i], 1);
1705fba958c6SPaolo Bonzini             } else if (c == '\r' || c == '\n') {
1706fba958c6SPaolo Bonzini                 vte_terminal_feed(term, "\r\n", 2);
1707fba958c6SPaolo Bonzini             } else {
1708fba958c6SPaolo Bonzini                 char ctrl[2] = { '^', 0};
1709fba958c6SPaolo Bonzini                 ctrl[1] = text[i] ^ 64;
1710fba958c6SPaolo Bonzini                 vte_terminal_feed(term, ctrl, 2);
1711fba958c6SPaolo Bonzini             }
1712fba958c6SPaolo Bonzini         }
1713fba958c6SPaolo Bonzini     }
1714fba958c6SPaolo Bonzini 
1715271a25c0SGerd Hoffmann     qemu_chr_be_write(vc->vte.chr, (uint8_t  *)text, (unsigned int)size);
1716d861def3SAnthony Liguori     return TRUE;
1717d861def3SAnthony Liguori }
1718d861def3SAnthony Liguori 
1719ed1132e4SGerd Hoffmann static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
1720ed1132e4SGerd Hoffmann                               CharDriverState *chr, int idx,
1721e3500d1fSGerd Hoffmann                               GSList *group, GtkWidget *view_menu)
1722d861def3SAnthony Liguori {
1723d861def3SAnthony Liguori     char buffer[32];
17240fb20d1cSCole Robinson     GtkWidget *box;
17250fb20d1cSCole Robinson     GtkWidget *scrollbar;
17260fb20d1cSCole Robinson     GtkAdjustment *vadjustment;
1727fba958c6SPaolo Bonzini     VirtualConsole *tmp_vc = chr->opaque;
1728d861def3SAnthony Liguori 
1729e3500d1fSGerd Hoffmann     vc->s = s;
1730fba958c6SPaolo Bonzini     vc->vte.echo = tmp_vc->vte.echo;
1731fba958c6SPaolo Bonzini 
1732ed1132e4SGerd Hoffmann     vc->vte.chr = chr;
1733fba958c6SPaolo Bonzini     chr->opaque = vc;
1734fba958c6SPaolo Bonzini     g_free(tmp_vc);
1735d861def3SAnthony Liguori 
1736ed1132e4SGerd Hoffmann     snprintf(buffer, sizeof(buffer), "vc%d", idx);
1737cdeb7090SGerd Hoffmann     vc->label = g_strdup_printf("%s", vc->vte.chr->label
1738cdeb7090SGerd Hoffmann                                 ? vc->vte.chr->label : buffer);
1739cdeb7090SGerd Hoffmann     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1740d861def3SAnthony Liguori 
1741271a25c0SGerd Hoffmann     vc->vte.terminal = vte_terminal_new();
1742271a25c0SGerd Hoffmann     g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
1743d861def3SAnthony Liguori 
1744fba958c6SPaolo Bonzini     /* The documentation says that the default is UTF-8, but actually it is
1745fba958c6SPaolo Bonzini      * 7-bit ASCII at least in VTE 0.38.
1746fba958c6SPaolo Bonzini      */
1747fba958c6SPaolo Bonzini #if VTE_CHECK_VERSION(0, 40, 0)
1748fba958c6SPaolo Bonzini     vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8", NULL);
1749fba958c6SPaolo Bonzini #else
1750fba958c6SPaolo Bonzini     vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8");
1751fba958c6SPaolo Bonzini #endif
1752fba958c6SPaolo Bonzini 
1753271a25c0SGerd Hoffmann     vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
175482fc1809SGerd Hoffmann     vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
175582fc1809SGerd Hoffmann                           VC_TERM_X_MIN, VC_TERM_Y_MIN);
1756d861def3SAnthony Liguori 
17570fb20d1cSCole Robinson #if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0)
1758271a25c0SGerd Hoffmann     vadjustment = gtk_scrollable_get_vadjustment
1759271a25c0SGerd Hoffmann         (GTK_SCROLLABLE(vc->vte.terminal));
17600fb20d1cSCole Robinson #else
1761271a25c0SGerd Hoffmann     vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
17620fb20d1cSCole Robinson #endif
1763d861def3SAnthony Liguori 
17640fb20d1cSCole Robinson #if GTK_CHECK_VERSION(3, 0, 0)
17650fb20d1cSCole Robinson     box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
17660fb20d1cSCole Robinson     scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
17670fb20d1cSCole Robinson #else
17680fb20d1cSCole Robinson     box = gtk_hbox_new(false, 2);
17690fb20d1cSCole Robinson     scrollbar = gtk_vscrollbar_new(vadjustment);
17700fb20d1cSCole Robinson #endif
17710fb20d1cSCole Robinson 
1772271a25c0SGerd Hoffmann     gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
17730fb20d1cSCole Robinson     gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);
17740fb20d1cSCole Robinson 
1775271a25c0SGerd Hoffmann     vc->vte.box = box;
1776271a25c0SGerd Hoffmann     vc->vte.scrollbar = scrollbar;
17770fb20d1cSCole Robinson 
17780fb20d1cSCole Robinson     g_signal_connect(vadjustment, "changed",
17790fb20d1cSCole Robinson                      G_CALLBACK(gd_vc_adjustment_changed), vc);
17800fb20d1cSCole Robinson 
1781e3500d1fSGerd Hoffmann     vc->type = GD_VC_VTE;
1782271a25c0SGerd Hoffmann     vc->tab_item = box;
17839d677e1cSJan Kiszka     vc->focus = vc->vte.terminal;
1784271a25c0SGerd Hoffmann     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
1785cdeb7090SGerd Hoffmann                              gtk_label_new(vc->label));
1786d861def3SAnthony Liguori 
1787271a25c0SGerd Hoffmann     qemu_chr_be_generic_open(vc->vte.chr);
1788271a25c0SGerd Hoffmann     if (vc->vte.chr->init) {
1789271a25c0SGerd Hoffmann         vc->vte.chr->init(vc->vte.chr);
1790d861def3SAnthony Liguori     }
1791d861def3SAnthony Liguori 
1792d861def3SAnthony Liguori     return group;
1793a4ccabcfSAnthony Liguori }
1794a4ccabcfSAnthony Liguori 
1795ed1132e4SGerd Hoffmann static void gd_vcs_init(GtkDisplayState *s, GSList *group,
1796ee5f31e4SGerd Hoffmann                         GtkWidget *view_menu)
1797ee5f31e4SGerd Hoffmann {
1798ee5f31e4SGerd Hoffmann     int i;
1799ee5f31e4SGerd Hoffmann 
1800ee5f31e4SGerd Hoffmann     for (i = 0; i < nb_vcs; i++) {
1801ed1132e4SGerd Hoffmann         VirtualConsole *vc = &s->vc[s->nb_vcs];
1802ed1132e4SGerd Hoffmann         group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu);
1803ee5f31e4SGerd Hoffmann         s->nb_vcs++;
1804ee5f31e4SGerd Hoffmann     }
1805ee5f31e4SGerd Hoffmann }
1806ee5f31e4SGerd Hoffmann #endif /* CONFIG_VTE */
1807ee5f31e4SGerd Hoffmann 
1808a4ccabcfSAnthony Liguori /** Window Creation **/
1809a4ccabcfSAnthony Liguori 
1810e3500d1fSGerd Hoffmann static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
1811e3500d1fSGerd Hoffmann {
1812e3500d1fSGerd Hoffmann #if GTK_CHECK_VERSION(3, 0, 0)
1813e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "draw",
1814e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_draw_event), vc);
1815925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
1816925a0400SGerd Hoffmann     if (display_opengl) {
1817925a0400SGerd Hoffmann         /* wire up GtkGlArea events */
1818925a0400SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "render",
1819925a0400SGerd Hoffmann                          G_CALLBACK(gd_render_event), vc);
1820925a0400SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "resize",
1821925a0400SGerd Hoffmann                          G_CALLBACK(gd_resize_event), vc);
1822925a0400SGerd Hoffmann     }
1823925a0400SGerd Hoffmann #endif
1824e3500d1fSGerd Hoffmann #else
1825e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "expose-event",
1826e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_expose_event), vc);
1827e3500d1fSGerd Hoffmann #endif
1828f8c223f6SGerd Hoffmann     if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
1829e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "event",
1830e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_event), vc);
1831e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "button-press-event",
1832e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_button_event), vc);
1833e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "button-release-event",
1834e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_button_event), vc);
1835e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "scroll-event",
1836e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_scroll_event), vc);
1837e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1838e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_key_event), vc);
1839e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "key-release-event",
1840e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_key_event), vc);
1841e3500d1fSGerd Hoffmann 
1842e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "enter-notify-event",
1843e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_enter_event), vc);
1844e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "leave-notify-event",
1845e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_leave_event), vc);
1846e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
1847e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_focus_out_event), vc);
18481301e515SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "configure-event",
18491301e515SGerd Hoffmann                          G_CALLBACK(gd_configure), vc);
1850f8c223f6SGerd Hoffmann     } else {
1851f8c223f6SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1852f8c223f6SGerd Hoffmann                          G_CALLBACK(gd_text_key_down), vc);
1853f8c223f6SGerd Hoffmann     }
1854e3500d1fSGerd Hoffmann }
1855e3500d1fSGerd Hoffmann 
1856a4ccabcfSAnthony Liguori static void gd_connect_signals(GtkDisplayState *s)
1857a4ccabcfSAnthony Liguori {
1858a4ccabcfSAnthony Liguori     g_signal_connect(s->show_tabs_item, "activate",
1859a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_show_tabs), s);
1860cdeb7090SGerd Hoffmann     g_signal_connect(s->untabify_item, "activate",
1861cdeb7090SGerd Hoffmann                      G_CALLBACK(gd_menu_untabify), s);
1862a4ccabcfSAnthony Liguori 
1863a4ccabcfSAnthony Liguori     g_signal_connect(s->window, "delete-event",
1864a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_window_close), s);
1865a4ccabcfSAnthony Liguori 
186630e8f22bSJan Kiszka     g_signal_connect(s->pause_item, "activate",
186730e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_pause), s);
186830e8f22bSJan Kiszka     g_signal_connect(s->reset_item, "activate",
186930e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_reset), s);
187030e8f22bSJan Kiszka     g_signal_connect(s->powerdown_item, "activate",
187130e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_powerdown), s);
1872a4ccabcfSAnthony Liguori     g_signal_connect(s->quit_item, "activate",
1873a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_quit), s);
187444b31e0bSMichael S. Tsirkin #if defined(CONFIG_VTE)
187544b31e0bSMichael S. Tsirkin     g_signal_connect(s->copy_item, "activate",
187644b31e0bSMichael S. Tsirkin                      G_CALLBACK(gd_menu_copy), s);
187744b31e0bSMichael S. Tsirkin #endif
1878c6158483SAnthony Liguori     g_signal_connect(s->full_screen_item, "activate",
1879c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_full_screen), s);
1880c6158483SAnthony Liguori     g_signal_connect(s->zoom_in_item, "activate",
1881c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_in), s);
1882c6158483SAnthony Liguori     g_signal_connect(s->zoom_out_item, "activate",
1883c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_out), s);
1884c6158483SAnthony Liguori     g_signal_connect(s->zoom_fixed_item, "activate",
1885c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_fixed), s);
1886c6158483SAnthony Liguori     g_signal_connect(s->zoom_fit_item, "activate",
1887c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_fit), s);
18885104a1f6SAnthony Liguori     g_signal_connect(s->grab_item, "activate",
18895104a1f6SAnthony Liguori                      G_CALLBACK(gd_menu_grab_input), s);
1890a4ccabcfSAnthony Liguori     g_signal_connect(s->notebook, "switch-page",
1891a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_change_page), s);
1892a4ccabcfSAnthony Liguori }
1893a4ccabcfSAnthony Liguori 
1894400519d2SCole Robinson static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
1895a4ccabcfSAnthony Liguori {
1896bf9b255fSAnthony Liguori     GtkWidget *machine_menu;
1897a4ccabcfSAnthony Liguori     GtkWidget *separator;
1898a4ccabcfSAnthony Liguori 
1899bf9b255fSAnthony Liguori     machine_menu = gtk_menu_new();
1900400519d2SCole Robinson     gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group);
190130e8f22bSJan Kiszka 
190230e8f22bSJan Kiszka     s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
1903bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
190430e8f22bSJan Kiszka 
190530e8f22bSJan Kiszka     separator = gtk_separator_menu_item_new();
1906bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
190730e8f22bSJan Kiszka 
19089068f20dSCole Robinson     s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset"));
1909bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
191030e8f22bSJan Kiszka 
19119068f20dSCole Robinson     s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down"));
1912bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
191330e8f22bSJan Kiszka 
191430e8f22bSJan Kiszka     separator = gtk_separator_menu_item_new();
1915bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1916a4ccabcfSAnthony Liguori 
19173d914488SCole Robinson     s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
1918a4ccabcfSAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
191930e8f22bSJan Kiszka                                  "<QEMU>/Machine/Quit");
19203d914488SCole Robinson     gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
1921db1da1f2SCole Robinson                             GDK_KEY_q, HOTKEY_MODIFIERS);
1922bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
1923a4ccabcfSAnthony Liguori 
1924bf9b255fSAnthony Liguori     return machine_menu;
1925bf9b255fSAnthony Liguori }
1926bf9b255fSAnthony Liguori 
1927e3500d1fSGerd Hoffmann static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
1928ed1132e4SGerd Hoffmann                               QemuConsole *con, int idx,
1929e3500d1fSGerd Hoffmann                               GSList *group, GtkWidget *view_menu)
1930e3500d1fSGerd Hoffmann {
1931779ce88fSGerd Hoffmann     vc->label = qemu_console_get_label(con);
1932e3500d1fSGerd Hoffmann     vc->s = s;
1933e3500d1fSGerd Hoffmann     vc->gfx.scale_x = 1.0;
1934e3500d1fSGerd Hoffmann     vc->gfx.scale_y = 1.0;
1935e3500d1fSGerd Hoffmann 
1936925a0400SGerd Hoffmann #if defined(CONFIG_OPENGL)
1937925a0400SGerd Hoffmann     if (display_opengl) {
1938925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
1939925a0400SGerd Hoffmann         vc->gfx.drawing_area = gtk_gl_area_new();
1940925a0400SGerd Hoffmann         vc->gfx.dcl.ops = &dcl_gl_area_ops;
1941925a0400SGerd Hoffmann #else
1942e3500d1fSGerd Hoffmann         vc->gfx.drawing_area = gtk_drawing_area_new();
1943925a0400SGerd Hoffmann         /*
1944925a0400SGerd Hoffmann          * gtk_widget_set_double_buffered() was deprecated in 3.14.
1945925a0400SGerd Hoffmann          * It is required for opengl rendering on X11 though.  A
1946925a0400SGerd Hoffmann          * proper replacement (native opengl support) is only
1947925a0400SGerd Hoffmann          * available in 3.16+.  Silence the warning if possible.
1948925a0400SGerd Hoffmann          */
1949925a0400SGerd Hoffmann #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
1950925a0400SGerd Hoffmann #pragma GCC diagnostic push
1951925a0400SGerd Hoffmann #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1952925a0400SGerd Hoffmann #endif
1953925a0400SGerd Hoffmann         gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
1954925a0400SGerd Hoffmann #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
1955925a0400SGerd Hoffmann #pragma GCC diagnostic pop
1956925a0400SGerd Hoffmann #endif
1957925a0400SGerd Hoffmann         vc->gfx.dcl.ops = &dcl_egl_ops;
1958925a0400SGerd Hoffmann #endif /* CONFIG_GTK_GL */
1959925a0400SGerd Hoffmann     } else
1960925a0400SGerd Hoffmann #endif
1961925a0400SGerd Hoffmann     {
1962925a0400SGerd Hoffmann         vc->gfx.drawing_area = gtk_drawing_area_new();
1963925a0400SGerd Hoffmann         vc->gfx.dcl.ops = &dcl_ops;
1964925a0400SGerd Hoffmann     }
1965925a0400SGerd Hoffmann 
1966925a0400SGerd Hoffmann 
1967e3500d1fSGerd Hoffmann     gtk_widget_add_events(vc->gfx.drawing_area,
1968e3500d1fSGerd Hoffmann                           GDK_POINTER_MOTION_MASK |
1969e3500d1fSGerd Hoffmann                           GDK_BUTTON_PRESS_MASK |
1970e3500d1fSGerd Hoffmann                           GDK_BUTTON_RELEASE_MASK |
1971e3500d1fSGerd Hoffmann                           GDK_BUTTON_MOTION_MASK |
1972e3500d1fSGerd Hoffmann                           GDK_ENTER_NOTIFY_MASK |
1973e3500d1fSGerd Hoffmann                           GDK_LEAVE_NOTIFY_MASK |
1974e3500d1fSGerd Hoffmann                           GDK_SCROLL_MASK |
1975e3500d1fSGerd Hoffmann                           GDK_KEY_PRESS_MASK);
1976e3500d1fSGerd Hoffmann     gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
1977e3500d1fSGerd Hoffmann 
1978e3500d1fSGerd Hoffmann     vc->type = GD_VC_GFX;
1979e3500d1fSGerd Hoffmann     vc->tab_item = vc->gfx.drawing_area;
19809d677e1cSJan Kiszka     vc->focus = vc->gfx.drawing_area;
1981e3500d1fSGerd Hoffmann     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
1982cdeb7090SGerd Hoffmann                              vc->tab_item, gtk_label_new(vc->label));
1983e3500d1fSGerd Hoffmann 
1984e3500d1fSGerd Hoffmann     vc->gfx.dcl.con = con;
1985e3500d1fSGerd Hoffmann     register_displaychangelistener(&vc->gfx.dcl);
1986e3500d1fSGerd Hoffmann 
1987f8c223f6SGerd Hoffmann     gd_connect_vc_gfx_signals(vc);
1988f8c223f6SGerd Hoffmann     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1989f8c223f6SGerd Hoffmann 
19901301e515SGerd Hoffmann     if (dpy_ui_info_supported(vc->gfx.dcl.con)) {
19911301e515SGerd Hoffmann         gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item));
19921d73cd78SGerd Hoffmann         s->free_scale = true;
19931301e515SGerd Hoffmann     }
19941301e515SGerd Hoffmann 
1995e3500d1fSGerd Hoffmann     return group;
1996e3500d1fSGerd Hoffmann }
1997e3500d1fSGerd Hoffmann 
1998400519d2SCole Robinson static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
1999bf9b255fSAnthony Liguori {
2000bf9b255fSAnthony Liguori     GSList *group = NULL;
2001bf9b255fSAnthony Liguori     GtkWidget *view_menu;
2002bf9b255fSAnthony Liguori     GtkWidget *separator;
2003ed1132e4SGerd Hoffmann     QemuConsole *con;
2004ed1132e4SGerd Hoffmann     int vc;
2005bf9b255fSAnthony Liguori 
2006bf9b255fSAnthony Liguori     view_menu = gtk_menu_new();
2007400519d2SCole Robinson     gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group);
2008a4ccabcfSAnthony Liguori 
20093d914488SCole Robinson     s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen"));
201095414914SCole Robinson 
201144b31e0bSMichael S. Tsirkin #if defined(CONFIG_VTE)
201244b31e0bSMichael S. Tsirkin     s->copy_item = gtk_menu_item_new_with_mnemonic(_("_Copy"));
201344b31e0bSMichael S. Tsirkin     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->copy_item);
201444b31e0bSMichael S. Tsirkin #endif
201544b31e0bSMichael S. Tsirkin 
201695414914SCole Robinson     gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0,
201795414914SCole Robinson             g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL));
201895414914SCole Robinson #if GTK_CHECK_VERSION(3, 8, 0)
201995414914SCole Robinson     gtk_accel_label_set_accel(
202095414914SCole Robinson             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))),
202195414914SCole Robinson             GDK_KEY_f, HOTKEY_MODIFIERS);
202295414914SCole Robinson #endif
2023bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
2024c6158483SAnthony Liguori 
2025c6158483SAnthony Liguori     separator = gtk_separator_menu_item_new();
2026bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2027c6158483SAnthony Liguori 
20283d914488SCole Robinson     s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In"));
2029c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
2030c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom In");
2031b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
2032b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
2033bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
2034c6158483SAnthony Liguori 
20353d914488SCole Robinson     s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
2036c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
2037c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom Out");
2038b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus,
2039b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
2040bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
2041c6158483SAnthony Liguori 
20423d914488SCole Robinson     s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit"));
2043c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
2044c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom Fixed");
2045b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0,
2046b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
2047bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
2048c6158483SAnthony Liguori 
2049834574eaSAnthony Liguori     s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
2050bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
2051c6158483SAnthony Liguori 
2052c6158483SAnthony Liguori     separator = gtk_separator_menu_item_new();
2053bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2054c6158483SAnthony Liguori 
2055834574eaSAnthony Liguori     s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
2056bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
20575104a1f6SAnthony Liguori 
2058834574eaSAnthony Liguori     s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
20595104a1f6SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
20605104a1f6SAnthony Liguori                                  "<QEMU>/View/Grab Input");
2061b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g,
2062b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
2063bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
20645104a1f6SAnthony Liguori 
2065a4ccabcfSAnthony Liguori     separator = gtk_separator_menu_item_new();
2066bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2067a4ccabcfSAnthony Liguori 
2068e3500d1fSGerd Hoffmann     /* gfx */
2069ed1132e4SGerd Hoffmann     for (vc = 0;; vc++) {
2070ed1132e4SGerd Hoffmann         con = qemu_console_lookup_by_index(vc);
2071f8c223f6SGerd Hoffmann         if (!con) {
2072ed1132e4SGerd Hoffmann             break;
2073ed1132e4SGerd Hoffmann         }
2074ed1132e4SGerd Hoffmann         group = gd_vc_gfx_init(s, &s->vc[vc], con,
2075ed1132e4SGerd Hoffmann                                vc, group, view_menu);
2076ed1132e4SGerd Hoffmann         s->nb_vcs++;
2077ed1132e4SGerd Hoffmann     }
2078a4ccabcfSAnthony Liguori 
2079ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
2080e3500d1fSGerd Hoffmann     /* vte */
2081ed1132e4SGerd Hoffmann     gd_vcs_init(s, group, view_menu);
2082ee5f31e4SGerd Hoffmann #endif
2083d861def3SAnthony Liguori 
2084a4ccabcfSAnthony Liguori     separator = gtk_separator_menu_item_new();
2085bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2086a4ccabcfSAnthony Liguori 
2087834574eaSAnthony Liguori     s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
2088bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
2089a4ccabcfSAnthony Liguori 
2090cdeb7090SGerd Hoffmann     s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
2091cdeb7090SGerd Hoffmann     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
2092cdeb7090SGerd Hoffmann 
2093bf9b255fSAnthony Liguori     return view_menu;
2094bf9b255fSAnthony Liguori }
2095a4ccabcfSAnthony Liguori 
2096bf9b255fSAnthony Liguori static void gd_create_menus(GtkDisplayState *s)
2097bf9b255fSAnthony Liguori {
2098400519d2SCole Robinson     s->accel_group = gtk_accel_group_new();
2099400519d2SCole Robinson     s->machine_menu = gd_create_menu_machine(s);
2100400519d2SCole Robinson     s->view_menu = gd_create_menu_view(s);
2101bf9b255fSAnthony Liguori 
2102bf9b255fSAnthony Liguori     s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
210330e8f22bSJan Kiszka     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
210430e8f22bSJan Kiszka                               s->machine_menu);
210530e8f22bSJan Kiszka     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
2106a4ccabcfSAnthony Liguori 
2107bf9b255fSAnthony Liguori     s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
2108a4ccabcfSAnthony Liguori     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
2109a4ccabcfSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
2110bf9b255fSAnthony Liguori 
2111400519d2SCole Robinson     g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group);
2112400519d2SCole Robinson     gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group);
2113a4ccabcfSAnthony Liguori }
2114a4ccabcfSAnthony Liguori 
21153158a348SBruce Rogers static void gd_set_keycode_type(GtkDisplayState *s)
21163158a348SBruce Rogers {
21170a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_X11
21183158a348SBruce Rogers     GdkDisplay *display = gtk_widget_get_display(s->window);
21190a337ed0SGerd Hoffmann     if (GDK_IS_X11_DISPLAY(display)) {
21203158a348SBruce Rogers         Display *x11_display = gdk_x11_display_get_xdisplay(display);
21213158a348SBruce Rogers         XkbDescPtr desc = XkbGetKeyboard(x11_display, XkbGBN_AllComponentsMask,
21223158a348SBruce Rogers                                          XkbUseCoreKbd);
21230a337ed0SGerd Hoffmann         char *keycodes = NULL;
21243158a348SBruce Rogers 
21253158a348SBruce Rogers         if (desc && desc->names) {
21263158a348SBruce Rogers             keycodes = XGetAtomName(x11_display, desc->names->keycodes);
21273158a348SBruce Rogers         }
21283158a348SBruce Rogers         if (keycodes == NULL) {
21293158a348SBruce Rogers             fprintf(stderr, "could not lookup keycode name\n");
21303158a348SBruce Rogers         } else if (strstart(keycodes, "evdev", NULL)) {
21313158a348SBruce Rogers             s->has_evdev = true;
21323158a348SBruce Rogers         } else if (!strstart(keycodes, "xfree86", NULL)) {
21333158a348SBruce Rogers             fprintf(stderr, "unknown keycodes `%s', please report to "
21343158a348SBruce Rogers                     "qemu-devel@nongnu.org\n", keycodes);
21353158a348SBruce Rogers         }
213684961407SChen Fan 
213784961407SChen Fan         if (desc) {
213884961407SChen Fan             XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
213984961407SChen Fan         }
214084961407SChen Fan         if (keycodes) {
214184961407SChen Fan             XFree(keycodes);
214284961407SChen Fan         }
21430a337ed0SGerd Hoffmann     }
21443158a348SBruce Rogers #endif
21453158a348SBruce Rogers }
21463158a348SBruce Rogers 
2147060ab763SGerd Hoffmann static gboolean gtkinit;
2148060ab763SGerd Hoffmann 
2149881249c7SJan Kiszka void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
2150a4ccabcfSAnthony Liguori {
2151a4ccabcfSAnthony Liguori     GtkDisplayState *s = g_malloc0(sizeof(*s));
2152d819cdccSStefan Weil     char *filename;
215363c67b6dSMax Reitz     GdkDisplay *window_display;
2154a4ccabcfSAnthony Liguori 
2155060ab763SGerd Hoffmann     if (!gtkinit) {
2156060ab763SGerd Hoffmann         fprintf(stderr, "gtk initialization failed\n");
2157060ab763SGerd Hoffmann         exit(1);
2158060ab763SGerd Hoffmann     }
2159060ab763SGerd Hoffmann 
2160a4ccabcfSAnthony Liguori     s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
216151572ab0SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 2, 0)
216251572ab0SDaniel P. Berrange     s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
216351572ab0SDaniel P. Berrange #else
2164a4ccabcfSAnthony Liguori     s->vbox = gtk_vbox_new(FALSE, 0);
216551572ab0SDaniel P. Berrange #endif
2166a4ccabcfSAnthony Liguori     s->notebook = gtk_notebook_new();
2167a4ccabcfSAnthony Liguori     s->menu_bar = gtk_menu_bar_new();
2168a4ccabcfSAnthony Liguori 
2169c6158483SAnthony Liguori     s->free_scale = FALSE;
2170a4ccabcfSAnthony Liguori 
21712cb5d2a4SAlberto Garcia     /* LC_MESSAGES only. See early_gtk_display_init() for details */
21722cb5d2a4SAlberto Garcia     setlocale(LC_MESSAGES, "");
2173834574eaSAnthony Liguori     bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
2174834574eaSAnthony Liguori     textdomain("qemu");
2175834574eaSAnthony Liguori 
217663c67b6dSMax Reitz     window_display = gtk_widget_get_display(s->window);
217763c67b6dSMax Reitz     s->null_cursor = gdk_cursor_new_for_display(window_display,
217863c67b6dSMax Reitz                                                 GDK_BLANK_CURSOR);
2179a4ccabcfSAnthony Liguori 
2180a4ccabcfSAnthony Liguori     s->mouse_mode_notifier.notify = gd_mouse_mode_change;
2181a4ccabcfSAnthony Liguori     qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
2182a4ccabcfSAnthony Liguori     qemu_add_vm_change_state_handler(gd_change_runstate, s);
2183a4ccabcfSAnthony Liguori 
2184f7da9c17SAnthony Liguori     filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg");
2185d819cdccSStefan Weil     if (filename) {
2186d819cdccSStefan Weil         GError *error = NULL;
2187d819cdccSStefan Weil         GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error);
2188d819cdccSStefan Weil         if (pixbuf) {
2189d819cdccSStefan Weil             gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf);
2190d819cdccSStefan Weil         } else {
2191d819cdccSStefan Weil             g_error_free(error);
2192d819cdccSStefan Weil         }
2193d819cdccSStefan Weil         g_free(filename);
2194d819cdccSStefan Weil     }
2195d819cdccSStefan Weil 
2196a4ccabcfSAnthony Liguori     gd_create_menus(s);
2197a4ccabcfSAnthony Liguori 
2198a4ccabcfSAnthony Liguori     gd_connect_signals(s);
2199a4ccabcfSAnthony Liguori 
2200a4ccabcfSAnthony Liguori     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
2201a4ccabcfSAnthony Liguori     gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
2202a4ccabcfSAnthony Liguori 
2203a4ccabcfSAnthony Liguori     gd_update_caption(s);
2204a4ccabcfSAnthony Liguori 
2205a4ccabcfSAnthony Liguori     gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
2206a4ccabcfSAnthony Liguori     gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
2207a4ccabcfSAnthony Liguori 
2208a4ccabcfSAnthony Liguori     gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
2209a4ccabcfSAnthony Liguori 
2210a4ccabcfSAnthony Liguori     gtk_widget_show_all(s->window);
2211a4ccabcfSAnthony Liguori 
22126fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK
22136fa27697SGerd Hoffmann     {
22146fa27697SGerd Hoffmann         VirtualConsole *cur = gd_vc_find_current(s);
2215b310a2a6SFam Zheng         if (cur) {
22166fa27697SGerd Hoffmann             int i;
22176fa27697SGerd Hoffmann 
22186fa27697SGerd Hoffmann             for (i = 0; i < s->nb_vcs; i++) {
22196fa27697SGerd Hoffmann                 VirtualConsole *vc = &s->vc[i];
22206fa27697SGerd Hoffmann                 if (vc && vc->type == GD_VC_VTE && vc != cur) {
22216fa27697SGerd Hoffmann                     gtk_widget_hide(vc->vte.terminal);
22226fa27697SGerd Hoffmann                 }
22236fa27697SGerd Hoffmann             }
22246fa27697SGerd Hoffmann             gd_update_windowsize(cur);
22256fa27697SGerd Hoffmann         }
2226b310a2a6SFam Zheng     }
22276fa27697SGerd Hoffmann #endif
22286fa27697SGerd Hoffmann 
2229787ba4f0SPeter Wu     if (full_screen) {
2230787ba4f0SPeter Wu         gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
2231787ba4f0SPeter Wu     }
2232881249c7SJan Kiszka     if (grab_on_hover) {
2233881249c7SJan Kiszka         gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
2234881249c7SJan Kiszka     }
2235787ba4f0SPeter Wu 
22363158a348SBruce Rogers     gd_set_keycode_type(s);
2237a4ccabcfSAnthony Liguori }
2238ee5f31e4SGerd Hoffmann 
223997edf3bdSGerd Hoffmann void early_gtk_display_init(int opengl)
2240ee5f31e4SGerd Hoffmann {
22412cb5d2a4SAlberto Garcia     /* The QEMU code relies on the assumption that it's always run in
22422cb5d2a4SAlberto Garcia      * the C locale. Therefore it is not prepared to deal with
22432cb5d2a4SAlberto Garcia      * operations that produce different results depending on the
22442cb5d2a4SAlberto Garcia      * locale, such as printf's formatting of decimal numbers, and
22452cb5d2a4SAlberto Garcia      * possibly others.
22462cb5d2a4SAlberto Garcia      *
22472cb5d2a4SAlberto Garcia      * Since GTK+ calls setlocale() by default -importing the locale
22482cb5d2a4SAlberto Garcia      * settings from the environment- we must prevent it from doing so
22492cb5d2a4SAlberto Garcia      * using gtk_disable_setlocale().
22502cb5d2a4SAlberto Garcia      *
22512cb5d2a4SAlberto Garcia      * QEMU's GTK+ UI, however, _does_ have translations for some of
22522cb5d2a4SAlberto Garcia      * the menu items. As a trade-off between a functionally correct
22532cb5d2a4SAlberto Garcia      * QEMU and a fully internationalized UI we support importing
22542cb5d2a4SAlberto Garcia      * LC_MESSAGES from the environment (see the setlocale() call
22552cb5d2a4SAlberto Garcia      * earlier in this file). This allows us to display translated
22562cb5d2a4SAlberto Garcia      * messages leaving everything else untouched.
22572cb5d2a4SAlberto Garcia      */
22582cb5d2a4SAlberto Garcia     gtk_disable_setlocale();
2259060ab763SGerd Hoffmann     gtkinit = gtk_init_check(NULL, NULL);
2260060ab763SGerd Hoffmann     if (!gtkinit) {
2261060ab763SGerd Hoffmann         /* don't exit yet, that'll break -help */
2262060ab763SGerd Hoffmann         return;
2263060ab763SGerd Hoffmann     }
226497edf3bdSGerd Hoffmann 
226597edf3bdSGerd Hoffmann     switch (opengl) {
226697edf3bdSGerd Hoffmann     case -1: /* default */
226797edf3bdSGerd Hoffmann     case 0:  /* off */
226897edf3bdSGerd Hoffmann         break;
226997edf3bdSGerd Hoffmann     case 1: /* on */
227097edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
2271925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
2272925a0400SGerd Hoffmann         gtk_gl_area_init();
2273925a0400SGerd Hoffmann #else
227497edf3bdSGerd Hoffmann         gtk_egl_init();
227597edf3bdSGerd Hoffmann #endif
2276925a0400SGerd Hoffmann #endif
227797edf3bdSGerd Hoffmann         break;
227897edf3bdSGerd Hoffmann     default:
227997edf3bdSGerd Hoffmann         g_assert_not_reached();
228097edf3bdSGerd Hoffmann         break;
228197edf3bdSGerd Hoffmann     }
228297edf3bdSGerd Hoffmann 
2283ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
2284ee5f31e4SGerd Hoffmann     register_vc_handler(gd_vc_handler);
2285ee5f31e4SGerd Hoffmann #endif
2286ee5f31e4SGerd Hoffmann }
2287