xref: /qemu/ui/gtk.c (revision 6978dc4adcdf27722aa6f9e13f88a903b30a3f8d)
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);
332*6978dc4aSAlberto Garcia         GtkBorder padding = { 0 };
33382fc1809SGerd Hoffmann 
33484e2dc4bSCole Robinson #if VTE_CHECK_VERSION(0, 37, 0)
33584e2dc4bSCole Robinson         gtk_style_context_get_padding(
33684e2dc4bSCole Robinson                 gtk_widget_get_style_context(vc->vte.terminal),
33784e2dc4bSCole Robinson                 gtk_widget_get_state_flags(vc->vte.terminal),
33884e2dc4bSCole Robinson                 &padding);
33984e2dc4bSCole Robinson #else
340*6978dc4aSAlberto Garcia         {
341*6978dc4aSAlberto Garcia             GtkBorder *ib = NULL;
34284e2dc4bSCole Robinson             gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL);
343*6978dc4aSAlberto Garcia             if (ib) {
344*6978dc4aSAlberto Garcia                 padding = *ib;
345*6978dc4aSAlberto Garcia                 gtk_border_free(ib);
346*6978dc4aSAlberto Garcia             }
347*6978dc4aSAlberto Garcia         }
34884e2dc4bSCole Robinson #endif
34984e2dc4bSCole Robinson 
35082fc1809SGerd Hoffmann         geo.width_inc  = vte_terminal_get_char_width(term);
35182fc1809SGerd Hoffmann         geo.height_inc = vte_terminal_get_char_height(term);
35282fc1809SGerd Hoffmann         mask |= GDK_HINT_RESIZE_INC;
35382fc1809SGerd Hoffmann         geo.base_width  = geo.width_inc;
35482fc1809SGerd Hoffmann         geo.base_height = geo.height_inc;
35582fc1809SGerd Hoffmann         mask |= GDK_HINT_BASE_SIZE;
35682fc1809SGerd Hoffmann         geo.min_width  = geo.width_inc * VC_TERM_X_MIN;
35782fc1809SGerd Hoffmann         geo.min_height = geo.height_inc * VC_TERM_Y_MIN;
35882fc1809SGerd Hoffmann         mask |= GDK_HINT_MIN_SIZE;
35984e2dc4bSCole Robinson 
360*6978dc4aSAlberto Garcia         geo.base_width  += padding.left + padding.right;
361*6978dc4aSAlberto Garcia         geo.base_height += padding.top + padding.bottom;
362*6978dc4aSAlberto Garcia         geo.min_width   += padding.left + padding.right;
363*6978dc4aSAlberto Garcia         geo.min_height  += padding.top + padding.bottom;
36482fc1809SGerd Hoffmann         geo_widget = vc->vte.terminal;
36582fc1809SGerd Hoffmann #endif
36682fc1809SGerd Hoffmann     }
36782fc1809SGerd Hoffmann 
36882fc1809SGerd Hoffmann     geo_window = GTK_WINDOW(vc->window ? vc->window : s->window);
36982fc1809SGerd Hoffmann     gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
37082fc1809SGerd Hoffmann }
37182fc1809SGerd Hoffmann 
37297edf3bdSGerd Hoffmann void gd_update_windowsize(VirtualConsole *vc)
373a4ccabcfSAnthony Liguori {
374e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
375c6158483SAnthony Liguori 
37682fc1809SGerd Hoffmann     gd_update_geometry_hints(vc);
377c6158483SAnthony Liguori 
37882fc1809SGerd Hoffmann     if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) {
37982fc1809SGerd Hoffmann         gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window),
38082fc1809SGerd Hoffmann                           VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN);
381a4ccabcfSAnthony Liguori     }
382aa0a55d4SGerd Hoffmann }
383a4ccabcfSAnthony Liguori 
384e3500d1fSGerd Hoffmann static void gd_update_full_redraw(VirtualConsole *vc)
3859d9801cfSGerd Hoffmann {
386e3500d1fSGerd Hoffmann     GtkWidget *area = vc->gfx.drawing_area;
3879d9801cfSGerd Hoffmann     int ww, wh;
388e3500d1fSGerd Hoffmann     gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
389925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
390925a0400SGerd Hoffmann     if (vc->gfx.gls) {
391925a0400SGerd Hoffmann         gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
392925a0400SGerd Hoffmann         return;
393925a0400SGerd Hoffmann     }
394925a0400SGerd Hoffmann #endif
395e3500d1fSGerd Hoffmann     gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
3969d9801cfSGerd Hoffmann }
3979d9801cfSGerd Hoffmann 
3986db253caSJan Kiszka static void gtk_release_modifiers(GtkDisplayState *s)
3996db253caSJan Kiszka {
400e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
4016db253caSJan Kiszka     int i, keycode;
4026db253caSJan Kiszka 
403f8c223f6SGerd Hoffmann     if (vc->type != GD_VC_GFX ||
404f8c223f6SGerd Hoffmann         !qemu_console_is_graphic(vc->gfx.dcl.con)) {
4056db253caSJan Kiszka         return;
4066db253caSJan Kiszka     }
4076db253caSJan Kiszka     for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
4086db253caSJan Kiszka         keycode = modifier_keycode[i];
4096db253caSJan Kiszka         if (!s->modifier_pressed[i]) {
4106db253caSJan Kiszka             continue;
4116db253caSJan Kiszka         }
412e3500d1fSGerd Hoffmann         qemu_input_event_send_key_number(vc->gfx.dcl.con, keycode, false);
4136db253caSJan Kiszka         s->modifier_pressed[i] = false;
4146db253caSJan Kiszka     }
4156db253caSJan Kiszka }
4166db253caSJan Kiszka 
417316cb068SGerd Hoffmann static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
418316cb068SGerd Hoffmann                                GtkWidget *widget)
419316cb068SGerd Hoffmann {
420316cb068SGerd Hoffmann     g_object_ref(G_OBJECT(widget));
421316cb068SGerd Hoffmann     gtk_container_remove(GTK_CONTAINER(from), widget);
422316cb068SGerd Hoffmann     gtk_container_add(GTK_CONTAINER(to), widget);
423316cb068SGerd Hoffmann     g_object_unref(G_OBJECT(widget));
424316cb068SGerd Hoffmann }
425316cb068SGerd Hoffmann 
4269d9801cfSGerd Hoffmann /** DisplayState Callbacks **/
4279d9801cfSGerd Hoffmann 
4289d9801cfSGerd Hoffmann static void gd_update(DisplayChangeListener *dcl,
429bc2ed970SGerd Hoffmann                       int x, int y, int w, int h)
4309d9801cfSGerd Hoffmann {
431e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
432f8c223f6SGerd Hoffmann     GdkWindow *win;
4339d9801cfSGerd Hoffmann     int x1, x2, y1, y2;
4349d9801cfSGerd Hoffmann     int mx, my;
4359d9801cfSGerd Hoffmann     int fbw, fbh;
4369d9801cfSGerd Hoffmann     int ww, wh;
4379d9801cfSGerd Hoffmann 
43874444bc1SGerd Hoffmann     trace_gd_update(vc->label, x, y, w, h);
4399d9801cfSGerd Hoffmann 
4404cdfc935SHervé Poussineau     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
4414cdfc935SHervé Poussineau         return;
4424cdfc935SHervé Poussineau     }
4434cdfc935SHervé Poussineau 
444e3500d1fSGerd Hoffmann     if (vc->gfx.convert) {
445e3500d1fSGerd Hoffmann         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
446e3500d1fSGerd Hoffmann                                NULL, vc->gfx.convert,
447f0875536SGerd Hoffmann                                x, y, 0, 0, x, y, w, h);
448f0875536SGerd Hoffmann     }
449f0875536SGerd Hoffmann 
450e3500d1fSGerd Hoffmann     x1 = floor(x * vc->gfx.scale_x);
451e3500d1fSGerd Hoffmann     y1 = floor(y * vc->gfx.scale_y);
4529d9801cfSGerd Hoffmann 
453e3500d1fSGerd Hoffmann     x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
454e3500d1fSGerd Hoffmann     y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
4559d9801cfSGerd Hoffmann 
456e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
457e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
4589d9801cfSGerd Hoffmann 
459f8c223f6SGerd Hoffmann     win = gtk_widget_get_window(vc->gfx.drawing_area);
460f8c223f6SGerd Hoffmann     if (!win) {
461f8c223f6SGerd Hoffmann         return;
462f8c223f6SGerd Hoffmann     }
463f8c223f6SGerd Hoffmann     gdk_drawable_get_size(win, &ww, &wh);
4649d9801cfSGerd Hoffmann 
4659d9801cfSGerd Hoffmann     mx = my = 0;
4669d9801cfSGerd Hoffmann     if (ww > fbw) {
4679d9801cfSGerd Hoffmann         mx = (ww - fbw) / 2;
4689d9801cfSGerd Hoffmann     }
4699d9801cfSGerd Hoffmann     if (wh > fbh) {
4709d9801cfSGerd Hoffmann         my = (wh - fbh) / 2;
4719d9801cfSGerd Hoffmann     }
4729d9801cfSGerd Hoffmann 
473e3500d1fSGerd Hoffmann     gtk_widget_queue_draw_area(vc->gfx.drawing_area,
474e3500d1fSGerd Hoffmann                                mx + x1, my + y1, (x2 - x1), (y2 - y1));
4759d9801cfSGerd Hoffmann }
4769d9801cfSGerd Hoffmann 
477bc2ed970SGerd Hoffmann static void gd_refresh(DisplayChangeListener *dcl)
4789d9801cfSGerd Hoffmann {
479284d1c6bSGerd Hoffmann     graphic_hw_update(dcl->con);
4809d9801cfSGerd Hoffmann }
4819d9801cfSGerd Hoffmann 
482b087143bSIgor Mitsyanko #if GTK_CHECK_VERSION(3, 0, 0)
483bb732ee7SCole Robinson static GdkDevice *gd_get_pointer(GdkDisplay *dpy)
484bb732ee7SCole Robinson {
485bb732ee7SCole Robinson #if GTK_CHECK_VERSION(3, 20, 0)
486bb732ee7SCole Robinson     return gdk_seat_get_pointer(gdk_display_get_default_seat(dpy));
487bb732ee7SCole Robinson #else
488bb732ee7SCole Robinson     return gdk_device_manager_get_client_pointer(
489bb732ee7SCole Robinson         gdk_display_get_device_manager(dpy));
490bb732ee7SCole Robinson #endif
491bb732ee7SCole Robinson }
492bb732ee7SCole Robinson 
493b087143bSIgor Mitsyanko static void gd_mouse_set(DisplayChangeListener *dcl,
494b087143bSIgor Mitsyanko                          int x, int y, int visible)
495b087143bSIgor Mitsyanko {
496e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
497b087143bSIgor Mitsyanko     GdkDisplay *dpy;
498b087143bSIgor Mitsyanko     gint x_root, y_root;
499b087143bSIgor Mitsyanko 
5002bda6602SCole Robinson     if (qemu_input_is_absolute()) {
5012bda6602SCole Robinson         return;
5022bda6602SCole Robinson     }
5032bda6602SCole Robinson 
504e3500d1fSGerd Hoffmann     dpy = gtk_widget_get_display(vc->gfx.drawing_area);
505e3500d1fSGerd Hoffmann     gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
506b087143bSIgor Mitsyanko                                x, y, &x_root, &y_root);
507bb732ee7SCole Robinson     gdk_device_warp(gd_get_pointer(dpy),
508e3500d1fSGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
509298526feSCole Robinson                     x_root, y_root);
5101271f7f7SGerd Hoffmann     vc->s->last_x = x;
5111271f7f7SGerd Hoffmann     vc->s->last_y = y;
512b087143bSIgor Mitsyanko }
513b087143bSIgor Mitsyanko #else
5149697f5d2SGerd Hoffmann static void gd_mouse_set(DisplayChangeListener *dcl,
5159697f5d2SGerd Hoffmann                          int x, int y, int visible)
5169697f5d2SGerd Hoffmann {
517e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
5189697f5d2SGerd Hoffmann     gint x_root, y_root;
5199697f5d2SGerd Hoffmann 
5202bda6602SCole Robinson     if (qemu_input_is_absolute()) {
5212bda6602SCole Robinson         return;
5222bda6602SCole Robinson     }
5232bda6602SCole Robinson 
524e3500d1fSGerd Hoffmann     gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
5259697f5d2SGerd Hoffmann                                x, y, &x_root, &y_root);
526e3500d1fSGerd Hoffmann     gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area),
527e3500d1fSGerd Hoffmann                              gtk_widget_get_screen(vc->gfx.drawing_area),
5289697f5d2SGerd Hoffmann                              x_root, y_root);
5299697f5d2SGerd Hoffmann }
530b087143bSIgor Mitsyanko #endif
5319697f5d2SGerd Hoffmann 
5329697f5d2SGerd Hoffmann static void gd_cursor_define(DisplayChangeListener *dcl,
5339697f5d2SGerd Hoffmann                              QEMUCursor *c)
5349697f5d2SGerd Hoffmann {
535e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
5369697f5d2SGerd Hoffmann     GdkPixbuf *pixbuf;
5379697f5d2SGerd Hoffmann     GdkCursor *cursor;
5389697f5d2SGerd Hoffmann 
5394cdfc935SHervé Poussineau     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
5404cdfc935SHervé Poussineau         return;
5414cdfc935SHervé Poussineau     }
5424cdfc935SHervé Poussineau 
5439697f5d2SGerd Hoffmann     pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
5449697f5d2SGerd Hoffmann                                       GDK_COLORSPACE_RGB, true, 8,
5459697f5d2SGerd Hoffmann                                       c->width, c->height, c->width * 4,
5469697f5d2SGerd Hoffmann                                       NULL, NULL);
547e3500d1fSGerd Hoffmann     cursor = gdk_cursor_new_from_pixbuf
548e3500d1fSGerd Hoffmann         (gtk_widget_get_display(vc->gfx.drawing_area),
5499697f5d2SGerd Hoffmann          pixbuf, c->hot_x, c->hot_y);
550e3500d1fSGerd Hoffmann     gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor);
5519697f5d2SGerd Hoffmann     g_object_unref(pixbuf);
552030b4b7dSStefan Weil #if !GTK_CHECK_VERSION(3, 0, 0)
55317139240SAnthony Liguori     gdk_cursor_unref(cursor);
554030b4b7dSStefan Weil #else
555030b4b7dSStefan Weil     g_object_unref(cursor);
556030b4b7dSStefan Weil #endif
5579697f5d2SGerd Hoffmann }
5589697f5d2SGerd Hoffmann 
5599d9801cfSGerd Hoffmann static void gd_switch(DisplayChangeListener *dcl,
5609d9801cfSGerd Hoffmann                       DisplaySurface *surface)
5619d9801cfSGerd Hoffmann {
562e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
5639d9801cfSGerd Hoffmann     bool resized = true;
5649d9801cfSGerd Hoffmann 
565f98f43eaSGerd Hoffmann     trace_gd_switch(vc->label,
566f98f43eaSGerd Hoffmann                     surface ? surface_width(surface)  : 0,
567f98f43eaSGerd Hoffmann                     surface ? surface_height(surface) : 0);
5689d9801cfSGerd Hoffmann 
569e3500d1fSGerd Hoffmann     if (vc->gfx.surface) {
570e3500d1fSGerd Hoffmann         cairo_surface_destroy(vc->gfx.surface);
571f98f43eaSGerd Hoffmann         vc->gfx.surface = NULL;
572f98f43eaSGerd Hoffmann     }
573f98f43eaSGerd Hoffmann     if (vc->gfx.convert) {
574f98f43eaSGerd Hoffmann         pixman_image_unref(vc->gfx.convert);
575f98f43eaSGerd Hoffmann         vc->gfx.convert = NULL;
5769d9801cfSGerd Hoffmann     }
5779d9801cfSGerd Hoffmann 
578f98f43eaSGerd Hoffmann     if (vc->gfx.ds && surface &&
579e3500d1fSGerd Hoffmann         surface_width(vc->gfx.ds) == surface_width(surface) &&
580e3500d1fSGerd Hoffmann         surface_height(vc->gfx.ds) == surface_height(surface)) {
5819d9801cfSGerd Hoffmann         resized = false;
5829d9801cfSGerd Hoffmann     }
583e3500d1fSGerd Hoffmann     vc->gfx.ds = surface;
584f0875536SGerd Hoffmann 
585f98f43eaSGerd Hoffmann     if (!surface) {
586f98f43eaSGerd Hoffmann         return;
5879d9801cfSGerd Hoffmann     }
5889d9801cfSGerd Hoffmann 
589f0875536SGerd Hoffmann     if (surface->format == PIXMAN_x8r8g8b8) {
590f0875536SGerd Hoffmann         /*
591f0875536SGerd Hoffmann          * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24
592f0875536SGerd Hoffmann          *
593f0875536SGerd Hoffmann          * No need to convert, use surface directly.  Should be the
594f0875536SGerd Hoffmann          * common case as this is qemu_default_pixelformat(32) too.
595f0875536SGerd Hoffmann          */
596e3500d1fSGerd Hoffmann         vc->gfx.surface = cairo_image_surface_create_for_data
597f0875536SGerd Hoffmann             (surface_data(surface),
598f0875536SGerd Hoffmann              CAIRO_FORMAT_RGB24,
5999d9801cfSGerd Hoffmann              surface_width(surface),
6009d9801cfSGerd Hoffmann              surface_height(surface),
6019d9801cfSGerd Hoffmann              surface_stride(surface));
602f0875536SGerd Hoffmann     } else {
603f0875536SGerd Hoffmann         /* Must convert surface, use pixman to do it. */
604e3500d1fSGerd Hoffmann         vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8,
605f0875536SGerd Hoffmann                                                    surface_width(surface),
606f0875536SGerd Hoffmann                                                    surface_height(surface),
607f0875536SGerd Hoffmann                                                    NULL, 0);
608e3500d1fSGerd Hoffmann         vc->gfx.surface = cairo_image_surface_create_for_data
609e3500d1fSGerd Hoffmann             ((void *)pixman_image_get_data(vc->gfx.convert),
610f0875536SGerd Hoffmann              CAIRO_FORMAT_RGB24,
611e3500d1fSGerd Hoffmann              pixman_image_get_width(vc->gfx.convert),
612e3500d1fSGerd Hoffmann              pixman_image_get_height(vc->gfx.convert),
613e3500d1fSGerd Hoffmann              pixman_image_get_stride(vc->gfx.convert));
614e3500d1fSGerd Hoffmann         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
615e3500d1fSGerd Hoffmann                                NULL, vc->gfx.convert,
616f0875536SGerd Hoffmann                                0, 0, 0, 0, 0, 0,
617e3500d1fSGerd Hoffmann                                pixman_image_get_width(vc->gfx.convert),
618e3500d1fSGerd Hoffmann                                pixman_image_get_height(vc->gfx.convert));
619f0875536SGerd Hoffmann     }
6209d9801cfSGerd Hoffmann 
6219d9801cfSGerd Hoffmann     if (resized) {
622e3500d1fSGerd Hoffmann         gd_update_windowsize(vc);
6239d9801cfSGerd Hoffmann     } else {
624e3500d1fSGerd Hoffmann         gd_update_full_redraw(vc);
6259d9801cfSGerd Hoffmann     }
6269d9801cfSGerd Hoffmann }
6279d9801cfSGerd Hoffmann 
62897edf3bdSGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = {
62997edf3bdSGerd Hoffmann     .dpy_name             = "gtk",
63097edf3bdSGerd Hoffmann     .dpy_gfx_update       = gd_update,
63197edf3bdSGerd Hoffmann     .dpy_gfx_switch       = gd_switch,
63297edf3bdSGerd Hoffmann     .dpy_gfx_check_format = qemu_pixman_check_format,
63397edf3bdSGerd Hoffmann     .dpy_refresh          = gd_refresh,
63497edf3bdSGerd Hoffmann     .dpy_mouse_set        = gd_mouse_set,
63597edf3bdSGerd Hoffmann     .dpy_cursor_define    = gd_cursor_define,
63697edf3bdSGerd Hoffmann };
63797edf3bdSGerd Hoffmann 
63897edf3bdSGerd Hoffmann 
63997edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
64097edf3bdSGerd Hoffmann 
64197edf3bdSGerd Hoffmann /** DisplayState Callbacks (opengl version) **/
64297edf3bdSGerd Hoffmann 
643925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
644925a0400SGerd Hoffmann 
645925a0400SGerd Hoffmann static const DisplayChangeListenerOps dcl_gl_area_ops = {
646925a0400SGerd Hoffmann     .dpy_name             = "gtk-egl",
647925a0400SGerd Hoffmann     .dpy_gfx_update       = gd_gl_area_update,
648925a0400SGerd Hoffmann     .dpy_gfx_switch       = gd_gl_area_switch,
649925a0400SGerd Hoffmann     .dpy_gfx_check_format = console_gl_check_format,
650925a0400SGerd Hoffmann     .dpy_refresh          = gd_gl_area_refresh,
651925a0400SGerd Hoffmann     .dpy_mouse_set        = gd_mouse_set,
652925a0400SGerd Hoffmann     .dpy_cursor_define    = gd_cursor_define,
653925a0400SGerd Hoffmann 
654925a0400SGerd Hoffmann     .dpy_gl_ctx_create       = gd_gl_area_create_context,
655925a0400SGerd Hoffmann     .dpy_gl_ctx_destroy      = gd_gl_area_destroy_context,
656925a0400SGerd Hoffmann     .dpy_gl_ctx_make_current = gd_gl_area_make_current,
657925a0400SGerd Hoffmann     .dpy_gl_ctx_get_current  = gd_gl_area_get_current_context,
658925a0400SGerd Hoffmann     .dpy_gl_scanout          = gd_gl_area_scanout,
659925a0400SGerd Hoffmann     .dpy_gl_update           = gd_gl_area_scanout_flush,
660925a0400SGerd Hoffmann };
661925a0400SGerd Hoffmann 
662925a0400SGerd Hoffmann #else
663925a0400SGerd Hoffmann 
66497edf3bdSGerd Hoffmann static const DisplayChangeListenerOps dcl_egl_ops = {
66597edf3bdSGerd Hoffmann     .dpy_name             = "gtk-egl",
66697edf3bdSGerd Hoffmann     .dpy_gfx_update       = gd_egl_update,
66797edf3bdSGerd Hoffmann     .dpy_gfx_switch       = gd_egl_switch,
66897edf3bdSGerd Hoffmann     .dpy_gfx_check_format = console_gl_check_format,
66997edf3bdSGerd Hoffmann     .dpy_refresh          = gd_egl_refresh,
67097edf3bdSGerd Hoffmann     .dpy_mouse_set        = gd_mouse_set,
67197edf3bdSGerd Hoffmann     .dpy_cursor_define    = gd_cursor_define,
6724782aeb7SGerd Hoffmann 
6734782aeb7SGerd Hoffmann     .dpy_gl_ctx_create       = gd_egl_create_context,
6744782aeb7SGerd Hoffmann     .dpy_gl_ctx_destroy      = qemu_egl_destroy_context,
6754782aeb7SGerd Hoffmann     .dpy_gl_ctx_make_current = gd_egl_make_current,
6764782aeb7SGerd Hoffmann     .dpy_gl_ctx_get_current  = qemu_egl_get_current_context,
6774782aeb7SGerd Hoffmann     .dpy_gl_scanout          = gd_egl_scanout,
6784782aeb7SGerd Hoffmann     .dpy_gl_update           = gd_egl_scanout_flush,
67997edf3bdSGerd Hoffmann };
68097edf3bdSGerd Hoffmann 
681925a0400SGerd Hoffmann #endif /* CONFIG_GTK_GL */
682925a0400SGerd Hoffmann #endif /* CONFIG_OPENGL */
68397edf3bdSGerd Hoffmann 
684a4ccabcfSAnthony Liguori /** QEMU Events **/
685a4ccabcfSAnthony Liguori 
686a4ccabcfSAnthony Liguori static void gd_change_runstate(void *opaque, int running, RunState state)
687a4ccabcfSAnthony Liguori {
688a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
689a4ccabcfSAnthony Liguori 
690a4ccabcfSAnthony Liguori     gd_update_caption(s);
691a4ccabcfSAnthony Liguori }
692a4ccabcfSAnthony Liguori 
693a4ccabcfSAnthony Liguori static void gd_mouse_mode_change(Notifier *notify, void *data)
694a4ccabcfSAnthony Liguori {
695800b0e81STakashi Iwai     GtkDisplayState *s;
69699623c90SGerd Hoffmann     int i;
697800b0e81STakashi Iwai 
698800b0e81STakashi Iwai     s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
699800b0e81STakashi Iwai     /* release the grab at switching to absolute mode */
700800b0e81STakashi Iwai     if (qemu_input_is_absolute() && gd_is_grab_active(s)) {
701800b0e81STakashi Iwai         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
702a4ccabcfSAnthony Liguori                                        FALSE);
703a4ccabcfSAnthony Liguori     }
70499623c90SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
70599623c90SGerd Hoffmann         VirtualConsole *vc = &s->vc[i];
70699623c90SGerd Hoffmann         gd_update_cursor(vc);
70799623c90SGerd Hoffmann     }
708800b0e81STakashi Iwai }
709a4ccabcfSAnthony Liguori 
710a4ccabcfSAnthony Liguori /** GTK Events **/
711a4ccabcfSAnthony Liguori 
712a4ccabcfSAnthony Liguori static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
713a4ccabcfSAnthony Liguori                                 void *opaque)
714a4ccabcfSAnthony Liguori {
715a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
716e3500d1fSGerd Hoffmann     int i;
717a4ccabcfSAnthony Liguori 
718a4ccabcfSAnthony Liguori     if (!no_quit) {
719e3500d1fSGerd Hoffmann         for (i = 0; i < s->nb_vcs; i++) {
720e3500d1fSGerd Hoffmann             if (s->vc[i].type != GD_VC_GFX) {
721e3500d1fSGerd Hoffmann                 continue;
722e3500d1fSGerd Hoffmann             }
723e3500d1fSGerd Hoffmann             unregister_displaychangelistener(&s->vc[i].gfx.dcl);
724e3500d1fSGerd Hoffmann         }
725a4ccabcfSAnthony Liguori         qmp_quit(NULL);
726a4ccabcfSAnthony Liguori         return FALSE;
727a4ccabcfSAnthony Liguori     }
728a4ccabcfSAnthony Liguori 
729a4ccabcfSAnthony Liguori     return TRUE;
730a4ccabcfSAnthony Liguori }
731a4ccabcfSAnthony Liguori 
732925a0400SGerd Hoffmann static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height)
733925a0400SGerd Hoffmann {
734925a0400SGerd Hoffmann     QemuUIInfo info;
735925a0400SGerd Hoffmann 
736925a0400SGerd Hoffmann     memset(&info, 0, sizeof(info));
737925a0400SGerd Hoffmann     info.width = width;
738925a0400SGerd Hoffmann     info.height = height;
739925a0400SGerd Hoffmann     dpy_set_ui_info(vc->gfx.dcl.con, &info);
740925a0400SGerd Hoffmann }
741925a0400SGerd Hoffmann 
742925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
743925a0400SGerd Hoffmann 
744925a0400SGerd Hoffmann static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
745925a0400SGerd Hoffmann                                 void *opaque)
746925a0400SGerd Hoffmann {
747925a0400SGerd Hoffmann     VirtualConsole *vc = opaque;
748925a0400SGerd Hoffmann 
749925a0400SGerd Hoffmann     if (vc->gfx.gls) {
750925a0400SGerd Hoffmann         gd_gl_area_draw(vc);
751925a0400SGerd Hoffmann     }
752925a0400SGerd Hoffmann     return TRUE;
753925a0400SGerd Hoffmann }
754925a0400SGerd Hoffmann 
755925a0400SGerd Hoffmann static void gd_resize_event(GtkGLArea *area,
756925a0400SGerd Hoffmann                             gint width, gint height, gpointer *opaque)
757925a0400SGerd Hoffmann {
758925a0400SGerd Hoffmann     VirtualConsole *vc = (void *)opaque;
759925a0400SGerd Hoffmann 
760925a0400SGerd Hoffmann     gd_set_ui_info(vc, width, height);
761925a0400SGerd Hoffmann }
762925a0400SGerd Hoffmann 
763925a0400SGerd Hoffmann #endif
764925a0400SGerd Hoffmann 
765a4ccabcfSAnthony Liguori static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
766a4ccabcfSAnthony Liguori {
767e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
768e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
769c6158483SAnthony Liguori     int mx, my;
770a4ccabcfSAnthony Liguori     int ww, wh;
771a4ccabcfSAnthony Liguori     int fbw, fbh;
772a4ccabcfSAnthony Liguori 
77397edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
77497edf3bdSGerd Hoffmann     if (vc->gfx.gls) {
775925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
776925a0400SGerd Hoffmann         /* invoke render callback please */
777925a0400SGerd Hoffmann         return FALSE;
778925a0400SGerd Hoffmann #else
77997edf3bdSGerd Hoffmann         gd_egl_draw(vc);
78097edf3bdSGerd Hoffmann         return TRUE;
781925a0400SGerd Hoffmann #endif
78297edf3bdSGerd Hoffmann     }
78397edf3bdSGerd Hoffmann #endif
78497edf3bdSGerd Hoffmann 
785c6158483SAnthony Liguori     if (!gtk_widget_get_realized(widget)) {
786c6158483SAnthony Liguori         return FALSE;
787c6158483SAnthony Liguori     }
788f98f43eaSGerd Hoffmann     if (!vc->gfx.ds) {
789f98f43eaSGerd Hoffmann         return FALSE;
790f98f43eaSGerd Hoffmann     }
791c6158483SAnthony Liguori 
792e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds);
793e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds);
794a4ccabcfSAnthony Liguori 
795a4ccabcfSAnthony Liguori     gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
796a4ccabcfSAnthony Liguori 
797c6158483SAnthony Liguori     if (s->full_screen) {
798e3500d1fSGerd Hoffmann         vc->gfx.scale_x = (double)ww / fbw;
799e3500d1fSGerd Hoffmann         vc->gfx.scale_y = (double)wh / fbh;
800c6158483SAnthony Liguori     } else if (s->free_scale) {
801c6158483SAnthony Liguori         double sx, sy;
802c6158483SAnthony Liguori 
803c6158483SAnthony Liguori         sx = (double)ww / fbw;
804c6158483SAnthony Liguori         sy = (double)wh / fbh;
805c6158483SAnthony Liguori 
806e3500d1fSGerd Hoffmann         vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
807a4ccabcfSAnthony Liguori     }
808a4ccabcfSAnthony Liguori 
809e3500d1fSGerd Hoffmann     fbw *= vc->gfx.scale_x;
810e3500d1fSGerd Hoffmann     fbh *= vc->gfx.scale_y;
8115104a1f6SAnthony Liguori 
812c6158483SAnthony Liguori     mx = my = 0;
813c6158483SAnthony Liguori     if (ww > fbw) {
814c6158483SAnthony Liguori         mx = (ww - fbw) / 2;
815c6158483SAnthony Liguori     }
816c6158483SAnthony Liguori     if (wh > fbh) {
817c6158483SAnthony Liguori         my = (wh - fbh) / 2;
818c6158483SAnthony Liguori     }
819c6158483SAnthony Liguori 
820c6158483SAnthony Liguori     cairo_rectangle(cr, 0, 0, ww, wh);
821c6158483SAnthony Liguori 
822c6158483SAnthony Liguori     /* Optionally cut out the inner area where the pixmap
823c6158483SAnthony Liguori        will be drawn. This avoids 'flashing' since we're
824c6158483SAnthony Liguori        not double-buffering. Note we're using the undocumented
825c6158483SAnthony Liguori        behaviour of drawing the rectangle from right to left
826c6158483SAnthony Liguori        to cut out the whole */
827c6158483SAnthony Liguori     cairo_rectangle(cr, mx + fbw, my,
828c6158483SAnthony Liguori                     -1 * fbw, fbh);
829c6158483SAnthony Liguori     cairo_fill(cr);
830c6158483SAnthony Liguori 
831e3500d1fSGerd Hoffmann     cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
832e3500d1fSGerd Hoffmann     cairo_set_source_surface(cr, vc->gfx.surface,
833e3500d1fSGerd Hoffmann                              mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
834a4ccabcfSAnthony Liguori     cairo_paint(cr);
835a4ccabcfSAnthony Liguori 
836a4ccabcfSAnthony Liguori     return TRUE;
837a4ccabcfSAnthony Liguori }
838a4ccabcfSAnthony Liguori 
839fe43bca8SDaniel P. Berrange #if !GTK_CHECK_VERSION(3, 0, 0)
840a4ccabcfSAnthony Liguori static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
841a4ccabcfSAnthony Liguori                                 void *opaque)
842a4ccabcfSAnthony Liguori {
843a4ccabcfSAnthony Liguori     cairo_t *cr;
844a4ccabcfSAnthony Liguori     gboolean ret;
845a4ccabcfSAnthony Liguori 
846a4ccabcfSAnthony Liguori     cr = gdk_cairo_create(gtk_widget_get_window(widget));
847a4ccabcfSAnthony Liguori     cairo_rectangle(cr,
848a4ccabcfSAnthony Liguori                     expose->area.x,
849a4ccabcfSAnthony Liguori                     expose->area.y,
850a4ccabcfSAnthony Liguori                     expose->area.width,
851a4ccabcfSAnthony Liguori                     expose->area.height);
852a4ccabcfSAnthony Liguori     cairo_clip(cr);
853a4ccabcfSAnthony Liguori 
854a4ccabcfSAnthony Liguori     ret = gd_draw_event(widget, cr, opaque);
855a4ccabcfSAnthony Liguori 
856a4ccabcfSAnthony Liguori     cairo_destroy(cr);
857a4ccabcfSAnthony Liguori 
858a4ccabcfSAnthony Liguori     return ret;
859a4ccabcfSAnthony Liguori }
860fe43bca8SDaniel P. Berrange #endif
861a4ccabcfSAnthony Liguori 
862a4ccabcfSAnthony Liguori static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
863a4ccabcfSAnthony Liguori                                 void *opaque)
864a4ccabcfSAnthony Liguori {
865e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
866e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
867a4ccabcfSAnthony Liguori     int x, y;
868c6158483SAnthony Liguori     int mx, my;
869c6158483SAnthony Liguori     int fbh, fbw;
870c6158483SAnthony Liguori     int ww, wh;
871a4ccabcfSAnthony Liguori 
872f98f43eaSGerd Hoffmann     if (!vc->gfx.ds) {
873f98f43eaSGerd Hoffmann         return TRUE;
874f98f43eaSGerd Hoffmann     }
875f98f43eaSGerd Hoffmann 
876e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
877e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
878c6158483SAnthony Liguori 
879e3500d1fSGerd Hoffmann     gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area),
880e3500d1fSGerd Hoffmann                           &ww, &wh);
881c6158483SAnthony Liguori 
882c6158483SAnthony Liguori     mx = my = 0;
883c6158483SAnthony Liguori     if (ww > fbw) {
884c6158483SAnthony Liguori         mx = (ww - fbw) / 2;
885c6158483SAnthony Liguori     }
886c6158483SAnthony Liguori     if (wh > fbh) {
887c6158483SAnthony Liguori         my = (wh - fbh) / 2;
888c6158483SAnthony Liguori     }
889c6158483SAnthony Liguori 
890e3500d1fSGerd Hoffmann     x = (motion->x - mx) / vc->gfx.scale_x;
891e3500d1fSGerd Hoffmann     y = (motion->y - my) / vc->gfx.scale_y;
892c6158483SAnthony Liguori 
893e61031cdSTakashi Iwai     if (qemu_input_is_absolute()) {
894c6158483SAnthony Liguori         if (x < 0 || y < 0 ||
895e3500d1fSGerd Hoffmann             x >= surface_width(vc->gfx.ds) ||
896e3500d1fSGerd Hoffmann             y >= surface_height(vc->gfx.ds)) {
897c6158483SAnthony Liguori             return TRUE;
898c6158483SAnthony Liguori         }
899e3500d1fSGerd Hoffmann         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
900e3500d1fSGerd Hoffmann                              surface_width(vc->gfx.ds));
901e3500d1fSGerd Hoffmann         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
902e3500d1fSGerd Hoffmann                              surface_height(vc->gfx.ds));
903192f81bfSGerd Hoffmann         qemu_input_event_sync();
9042884cf5bSGerd Hoffmann     } else if (s->last_set && s->ptr_owner == vc) {
905e3500d1fSGerd Hoffmann         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
906e3500d1fSGerd Hoffmann         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
907192f81bfSGerd Hoffmann         qemu_input_event_sync();
908a4ccabcfSAnthony Liguori     }
909a4ccabcfSAnthony Liguori     s->last_x = x;
910a4ccabcfSAnthony Liguori     s->last_y = y;
911e61031cdSTakashi Iwai     s->last_set = TRUE;
912a4ccabcfSAnthony Liguori 
9132884cf5bSGerd Hoffmann     if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
914e3500d1fSGerd Hoffmann         GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
9155104a1f6SAnthony Liguori         int x = (int)motion->x_root;
9165104a1f6SAnthony Liguori         int y = (int)motion->y_root;
9175104a1f6SAnthony Liguori 
9185104a1f6SAnthony Liguori         /* In relative mode check to see if client pointer hit
9195104a1f6SAnthony Liguori          * one of the screen edges, and if so move it back by
9205104a1f6SAnthony Liguori          * 200 pixels. This is important because the pointer
9215104a1f6SAnthony Liguori          * in the server doesn't correspond 1-for-1, and so
9225104a1f6SAnthony Liguori          * may still be only half way across the screen. Without
9235104a1f6SAnthony Liguori          * this warp, the server pointer would thus appear to hit
9245104a1f6SAnthony Liguori          * an invisible wall */
9255104a1f6SAnthony Liguori         if (x == 0) {
9265104a1f6SAnthony Liguori             x += 200;
9275104a1f6SAnthony Liguori         }
9285104a1f6SAnthony Liguori         if (y == 0) {
9295104a1f6SAnthony Liguori             y += 200;
9305104a1f6SAnthony Liguori         }
9315104a1f6SAnthony Liguori         if (x == (gdk_screen_get_width(screen) - 1)) {
9325104a1f6SAnthony Liguori             x -= 200;
9335104a1f6SAnthony Liguori         }
9345104a1f6SAnthony Liguori         if (y == (gdk_screen_get_height(screen) - 1)) {
9355104a1f6SAnthony Liguori             y -= 200;
9365104a1f6SAnthony Liguori         }
9375104a1f6SAnthony Liguori 
9385104a1f6SAnthony Liguori         if (x != (int)motion->x_root || y != (int)motion->y_root) {
9398906de76SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
9408906de76SDaniel P. Berrange             GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
9418906de76SDaniel P. Berrange             gdk_device_warp(dev, screen, x, y);
9428906de76SDaniel P. Berrange #else
9438906de76SDaniel P. Berrange             GdkDisplay *display = gtk_widget_get_display(widget);
9445104a1f6SAnthony Liguori             gdk_display_warp_pointer(display, screen, x, y);
9458906de76SDaniel P. Berrange #endif
946e61031cdSTakashi Iwai             s->last_set = FALSE;
9475104a1f6SAnthony Liguori             return FALSE;
9485104a1f6SAnthony Liguori         }
9495104a1f6SAnthony Liguori     }
950a4ccabcfSAnthony Liguori     return TRUE;
951a4ccabcfSAnthony Liguori }
952a4ccabcfSAnthony Liguori 
953a4ccabcfSAnthony Liguori static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
954a4ccabcfSAnthony Liguori                                 void *opaque)
955a4ccabcfSAnthony Liguori {
956e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
957e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
958192f81bfSGerd Hoffmann     InputButton btn;
959a4ccabcfSAnthony Liguori 
960800b0e81STakashi Iwai     /* implicitly grab the input at the first click in the relative mode */
961800b0e81STakashi Iwai     if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
9622884cf5bSGerd Hoffmann         !qemu_input_is_absolute() && s->ptr_owner != vc) {
9632884cf5bSGerd Hoffmann         if (!vc->window) {
964800b0e81STakashi Iwai             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
965800b0e81STakashi Iwai                                            TRUE);
9662884cf5bSGerd Hoffmann         } else {
967d531deefSGerd Hoffmann             gd_grab_pointer(vc, "relative-mode-click");
9682884cf5bSGerd Hoffmann         }
969800b0e81STakashi Iwai         return TRUE;
970800b0e81STakashi Iwai     }
971800b0e81STakashi Iwai 
972a4ccabcfSAnthony Liguori     if (button->button == 1) {
973192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_LEFT;
974a4ccabcfSAnthony Liguori     } else if (button->button == 2) {
975192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_MIDDLE;
976a4ccabcfSAnthony Liguori     } else if (button->button == 3) {
977192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_RIGHT;
978a4ccabcfSAnthony Liguori     } else {
979192f81bfSGerd Hoffmann         return TRUE;
980a4ccabcfSAnthony Liguori     }
981a4ccabcfSAnthony Liguori 
982e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn,
983e3500d1fSGerd Hoffmann                          button->type == GDK_BUTTON_PRESS);
984192f81bfSGerd Hoffmann     qemu_input_event_sync();
985a4ccabcfSAnthony Liguori     return TRUE;
986a4ccabcfSAnthony Liguori }
987a4ccabcfSAnthony Liguori 
988d58b9122SJan Kiszka static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
989d58b9122SJan Kiszka                                 void *opaque)
990d58b9122SJan Kiszka {
991e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
992d58b9122SJan Kiszka     InputButton btn;
993d58b9122SJan Kiszka 
994d58b9122SJan Kiszka     if (scroll->direction == GDK_SCROLL_UP) {
995f22d0af0SGerd Hoffmann         btn = INPUT_BUTTON_WHEEL_UP;
996d58b9122SJan Kiszka     } else if (scroll->direction == GDK_SCROLL_DOWN) {
997f22d0af0SGerd Hoffmann         btn = INPUT_BUTTON_WHEEL_DOWN;
998d58b9122SJan Kiszka     } else {
999d58b9122SJan Kiszka         return TRUE;
1000d58b9122SJan Kiszka     }
1001d58b9122SJan Kiszka 
1002e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
1003d58b9122SJan Kiszka     qemu_input_event_sync();
1004e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
1005d58b9122SJan Kiszka     qemu_input_event_sync();
1006d58b9122SJan Kiszka     return TRUE;
1007d58b9122SJan Kiszka }
1008d58b9122SJan Kiszka 
10090a337ed0SGerd Hoffmann static int gd_map_keycode(GtkDisplayState *s, GdkDisplay *dpy, int gdk_keycode)
1010a4ccabcfSAnthony Liguori {
1011932f2d7eSGerd Hoffmann     int qemu_keycode;
1012a4ccabcfSAnthony Liguori 
10130a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_WIN32
10140a337ed0SGerd Hoffmann     if (GDK_IS_WIN32_DISPLAY(dpy)) {
1015932f2d7eSGerd Hoffmann         qemu_keycode = MapVirtualKey(gdk_keycode, MAPVK_VK_TO_VSC);
10162777ccc5SStefan Weil         switch (qemu_keycode) {
10172777ccc5SStefan Weil         case 103:   /* alt gr */
10182777ccc5SStefan Weil             qemu_keycode = 56 | SCANCODE_GREY;
10192777ccc5SStefan Weil             break;
10202777ccc5SStefan Weil         }
10210a337ed0SGerd Hoffmann         return qemu_keycode;
10220a337ed0SGerd Hoffmann     }
10230a337ed0SGerd Hoffmann #endif
1024a4ccabcfSAnthony Liguori 
1025a4ccabcfSAnthony Liguori     if (gdk_keycode < 9) {
1026a4ccabcfSAnthony Liguori         qemu_keycode = 0;
1027a4ccabcfSAnthony Liguori     } else if (gdk_keycode < 97) {
1028a4ccabcfSAnthony Liguori         qemu_keycode = gdk_keycode - 8;
10290a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_X11
10300a337ed0SGerd Hoffmann     } else if (GDK_IS_X11_DISPLAY(dpy) && gdk_keycode < 158) {
10313158a348SBruce Rogers         if (s->has_evdev) {
1032a4ccabcfSAnthony Liguori             qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
10333158a348SBruce Rogers         } else {
10343158a348SBruce Rogers             qemu_keycode = translate_xfree86_keycode(gdk_keycode - 97);
10353158a348SBruce Rogers         }
10360a337ed0SGerd Hoffmann #endif
1037a4ccabcfSAnthony Liguori     } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
1038a4ccabcfSAnthony Liguori         qemu_keycode = 0x70;
1039a4ccabcfSAnthony Liguori     } else if (gdk_keycode == 211) { /* backslash */
1040a4ccabcfSAnthony Liguori         qemu_keycode = 0x73;
1041a4ccabcfSAnthony Liguori     } else {
1042a4ccabcfSAnthony Liguori         qemu_keycode = 0;
1043a4ccabcfSAnthony Liguori     }
1044a4ccabcfSAnthony Liguori 
1045932f2d7eSGerd Hoffmann     return qemu_keycode;
1046932f2d7eSGerd Hoffmann }
1047932f2d7eSGerd Hoffmann 
1048f8c223f6SGerd Hoffmann static gboolean gd_text_key_down(GtkWidget *widget,
1049f8c223f6SGerd Hoffmann                                  GdkEventKey *key, void *opaque)
1050f8c223f6SGerd Hoffmann {
1051f8c223f6SGerd Hoffmann     VirtualConsole *vc = opaque;
1052f8c223f6SGerd Hoffmann     QemuConsole *con = vc->gfx.dcl.con;
1053f8c223f6SGerd Hoffmann 
1054f8c223f6SGerd Hoffmann     if (key->length) {
1055f8c223f6SGerd Hoffmann         kbd_put_string_console(con, key->string, key->length);
1056f8c223f6SGerd Hoffmann     } else {
1057f8c223f6SGerd Hoffmann         int num = gd_map_keycode(vc->s, gtk_widget_get_display(widget),
1058f8c223f6SGerd Hoffmann                                  key->hardware_keycode);
1059f8c223f6SGerd Hoffmann         int qcode = qemu_input_key_number_to_qcode(num);
1060f8c223f6SGerd Hoffmann         kbd_put_qcode_console(con, qcode);
1061f8c223f6SGerd Hoffmann     }
1062f8c223f6SGerd Hoffmann     return TRUE;
1063f8c223f6SGerd Hoffmann }
1064f8c223f6SGerd Hoffmann 
1065932f2d7eSGerd Hoffmann static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
1066932f2d7eSGerd Hoffmann {
1067932f2d7eSGerd Hoffmann     VirtualConsole *vc = opaque;
1068932f2d7eSGerd Hoffmann     GtkDisplayState *s = vc->s;
1069932f2d7eSGerd Hoffmann     int gdk_keycode = key->hardware_keycode;
1070932f2d7eSGerd Hoffmann     int qemu_keycode;
1071932f2d7eSGerd Hoffmann     int i;
1072932f2d7eSGerd Hoffmann 
10731a01716aSJan Kiszka     if (s->ignore_keys) {
10741a01716aSJan Kiszka         s->ignore_keys = (key->type == GDK_KEY_PRESS);
10751a01716aSJan Kiszka         return TRUE;
10761a01716aSJan Kiszka     }
10771a01716aSJan Kiszka 
10785c960521SMartin Decky     if (key->keyval == GDK_KEY_Pause) {
10795c960521SMartin Decky         qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE,
10805c960521SMartin Decky                                         key->type == GDK_KEY_PRESS);
10815c960521SMartin Decky         return TRUE;
10825c960521SMartin Decky     }
10835c960521SMartin Decky 
10840a337ed0SGerd Hoffmann     qemu_keycode = gd_map_keycode(s, gtk_widget_get_display(widget),
10850a337ed0SGerd Hoffmann                                   gdk_keycode);
1086932f2d7eSGerd Hoffmann 
108774444bc1SGerd Hoffmann     trace_gd_key_event(vc->label, gdk_keycode, qemu_keycode,
1088a4ccabcfSAnthony Liguori                        (key->type == GDK_KEY_PRESS) ? "down" : "up");
1089a4ccabcfSAnthony Liguori 
10906db253caSJan Kiszka     for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
10916db253caSJan Kiszka         if (qemu_keycode == modifier_keycode[i]) {
10926db253caSJan Kiszka             s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
10936db253caSJan Kiszka         }
10946db253caSJan Kiszka     }
10956db253caSJan Kiszka 
1096e3500d1fSGerd Hoffmann     qemu_input_event_send_key_number(vc->gfx.dcl.con, qemu_keycode,
1097af98ba92SGerd Hoffmann                                      key->type == GDK_KEY_PRESS);
1098a4ccabcfSAnthony Liguori 
1099a4ccabcfSAnthony Liguori     return TRUE;
1100a4ccabcfSAnthony Liguori }
1101a4ccabcfSAnthony Liguori 
11020d0e044dSTakashi Iwai static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
11030d0e044dSTakashi Iwai {
11040d0e044dSTakashi Iwai     if (event->type == GDK_MOTION_NOTIFY) {
11050d0e044dSTakashi Iwai         return gd_motion_event(widget, &event->motion, opaque);
11060d0e044dSTakashi Iwai     }
11070d0e044dSTakashi Iwai     return FALSE;
11080d0e044dSTakashi Iwai }
11090d0e044dSTakashi Iwai 
1110a4ccabcfSAnthony Liguori /** Window Menu Actions **/
1111a4ccabcfSAnthony Liguori 
111230e8f22bSJan Kiszka static void gd_menu_pause(GtkMenuItem *item, void *opaque)
111330e8f22bSJan Kiszka {
111430e8f22bSJan Kiszka     GtkDisplayState *s = opaque;
111530e8f22bSJan Kiszka 
111630e8f22bSJan Kiszka     if (s->external_pause_update) {
111730e8f22bSJan Kiszka         return;
111830e8f22bSJan Kiszka     }
111930e8f22bSJan Kiszka     if (runstate_is_running()) {
112030e8f22bSJan Kiszka         qmp_stop(NULL);
112130e8f22bSJan Kiszka     } else {
112230e8f22bSJan Kiszka         qmp_cont(NULL);
112330e8f22bSJan Kiszka     }
112430e8f22bSJan Kiszka }
112530e8f22bSJan Kiszka 
112630e8f22bSJan Kiszka static void gd_menu_reset(GtkMenuItem *item, void *opaque)
112730e8f22bSJan Kiszka {
112830e8f22bSJan Kiszka     qmp_system_reset(NULL);
112930e8f22bSJan Kiszka }
113030e8f22bSJan Kiszka 
113130e8f22bSJan Kiszka static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
113230e8f22bSJan Kiszka {
113330e8f22bSJan Kiszka     qmp_system_powerdown(NULL);
113430e8f22bSJan Kiszka }
113530e8f22bSJan Kiszka 
1136a4ccabcfSAnthony Liguori static void gd_menu_quit(GtkMenuItem *item, void *opaque)
1137a4ccabcfSAnthony Liguori {
1138a4ccabcfSAnthony Liguori     qmp_quit(NULL);
1139a4ccabcfSAnthony Liguori }
1140a4ccabcfSAnthony Liguori 
1141a4ccabcfSAnthony Liguori static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
1142a4ccabcfSAnthony Liguori {
1143a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
1144e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_by_menu(s);
1145e72b59faSJohn Snow     GtkNotebook *nb = GTK_NOTEBOOK(s->notebook);
1146832189c9SGerd Hoffmann     gint page;
1147a4ccabcfSAnthony Liguori 
11486db253caSJan Kiszka     gtk_release_modifiers(s);
1149271a25c0SGerd Hoffmann     if (vc) {
1150e72b59faSJohn Snow         page = gtk_notebook_page_num(nb, vc->tab_item);
1151e72b59faSJohn Snow         gtk_notebook_set_current_page(nb, page);
11529d677e1cSJan Kiszka         gtk_widget_grab_focus(vc->focus);
1153d861def3SAnthony Liguori     }
11541a01716aSJan Kiszka     s->ignore_keys = false;
1155d861def3SAnthony Liguori }
1156a4ccabcfSAnthony Liguori 
1157277836c8SCole Robinson static void gd_accel_switch_vc(void *opaque)
1158277836c8SCole Robinson {
1159277836c8SCole Robinson     VirtualConsole *vc = opaque;
11601a01716aSJan Kiszka 
1161277836c8SCole Robinson     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
11621a01716aSJan Kiszka #if !GTK_CHECK_VERSION(3, 0, 0)
11631a01716aSJan Kiszka     /* GTK2 sends the accel key to the target console - ignore this until */
11641a01716aSJan Kiszka     vc->s->ignore_keys = true;
11651a01716aSJan Kiszka #endif
1166277836c8SCole Robinson }
1167277836c8SCole Robinson 
1168a4ccabcfSAnthony Liguori static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
1169a4ccabcfSAnthony Liguori {
1170a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
1171fa7a1e52SGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1172a4ccabcfSAnthony Liguori 
1173a4ccabcfSAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
1174a4ccabcfSAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
1175a4ccabcfSAnthony Liguori     } else {
1176a4ccabcfSAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1177a4ccabcfSAnthony Liguori     }
1178fa7a1e52SGerd Hoffmann     gd_update_windowsize(vc);
1179a4ccabcfSAnthony Liguori }
1180a4ccabcfSAnthony Liguori 
1181cdeb7090SGerd Hoffmann static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
1182cdeb7090SGerd Hoffmann                                     void *opaque)
1183cdeb7090SGerd Hoffmann {
1184cdeb7090SGerd Hoffmann     VirtualConsole *vc = opaque;
1185cdeb7090SGerd Hoffmann     GtkDisplayState *s = vc->s;
1186cdeb7090SGerd Hoffmann 
1187cdeb7090SGerd Hoffmann     gtk_widget_set_sensitive(vc->menu_item, true);
1188316cb068SGerd Hoffmann     gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
1189cdeb7090SGerd Hoffmann     gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
1190cdeb7090SGerd Hoffmann                                     vc->tab_item, vc->label);
1191cdeb7090SGerd Hoffmann     gtk_widget_destroy(vc->window);
1192cdeb7090SGerd Hoffmann     vc->window = NULL;
1193cdeb7090SGerd Hoffmann     return TRUE;
1194cdeb7090SGerd Hoffmann }
1195cdeb7090SGerd Hoffmann 
11960c77a37fSGerd Hoffmann static gboolean gd_win_grab(void *opaque)
11970c77a37fSGerd Hoffmann {
11980c77a37fSGerd Hoffmann     VirtualConsole *vc = opaque;
11990c77a37fSGerd Hoffmann 
12000c77a37fSGerd Hoffmann     fprintf(stderr, "%s: %s\n", __func__, vc->label);
12010c77a37fSGerd Hoffmann     if (vc->s->ptr_owner) {
12020c77a37fSGerd Hoffmann         gd_ungrab_pointer(vc->s);
12030c77a37fSGerd Hoffmann     } else {
1204d531deefSGerd Hoffmann         gd_grab_pointer(vc, "user-request-detached-tab");
12050c77a37fSGerd Hoffmann     }
12060c77a37fSGerd Hoffmann     return TRUE;
12070c77a37fSGerd Hoffmann }
12080c77a37fSGerd Hoffmann 
1209cdeb7090SGerd Hoffmann static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
1210cdeb7090SGerd Hoffmann {
1211cdeb7090SGerd Hoffmann     GtkDisplayState *s = opaque;
1212cdeb7090SGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1213cdeb7090SGerd Hoffmann 
1214f8c223f6SGerd Hoffmann     if (vc->type == GD_VC_GFX &&
1215f8c223f6SGerd Hoffmann         qemu_console_is_graphic(vc->gfx.dcl.con)) {
1216aa0a55d4SGerd Hoffmann         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1217aa0a55d4SGerd Hoffmann                                        FALSE);
1218cdeb7090SGerd Hoffmann     }
1219cdeb7090SGerd Hoffmann     if (!vc->window) {
1220cdeb7090SGerd Hoffmann         gtk_widget_set_sensitive(vc->menu_item, false);
1221cdeb7090SGerd Hoffmann         vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1222316cb068SGerd Hoffmann         gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
1223cdeb7090SGerd Hoffmann 
1224cdeb7090SGerd Hoffmann         g_signal_connect(vc->window, "delete-event",
1225cdeb7090SGerd Hoffmann                          G_CALLBACK(gd_tab_window_close), vc);
1226cdeb7090SGerd Hoffmann         gtk_widget_show_all(vc->window);
12274eeaa3a8SGerd Hoffmann 
1228f8c223f6SGerd Hoffmann         if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
12290c77a37fSGerd Hoffmann             GtkAccelGroup *ag = gtk_accel_group_new();
12300c77a37fSGerd Hoffmann             gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
12310c77a37fSGerd Hoffmann 
1232f8c223f6SGerd Hoffmann             GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
1233f8c223f6SGerd Hoffmann                                                vc, NULL);
12340c77a37fSGerd Hoffmann             gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
1235f8c223f6SGerd Hoffmann         }
12360c77a37fSGerd Hoffmann 
123782fc1809SGerd Hoffmann         gd_update_geometry_hints(vc);
12384eeaa3a8SGerd Hoffmann         gd_update_caption(s);
1239cdeb7090SGerd Hoffmann     }
1240cdeb7090SGerd Hoffmann }
1241cdeb7090SGerd Hoffmann 
1242c6158483SAnthony Liguori static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
1243c6158483SAnthony Liguori {
1244c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1245e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1246c6158483SAnthony Liguori 
124710409282SStefan Weil     if (!s->full_screen) {
1248c6158483SAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1249b0f31820SCole Robinson         gtk_widget_hide(s->menu_bar);
1250e3500d1fSGerd Hoffmann         if (vc->type == GD_VC_GFX) {
1251e3500d1fSGerd Hoffmann             gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
1252c6158483SAnthony Liguori         }
1253e3500d1fSGerd Hoffmann         gtk_window_fullscreen(GTK_WINDOW(s->window));
1254c6158483SAnthony Liguori         s->full_screen = TRUE;
1255c6158483SAnthony Liguori     } else {
1256c6158483SAnthony Liguori         gtk_window_unfullscreen(GTK_WINDOW(s->window));
1257c6158483SAnthony Liguori         gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
1258b0f31820SCole Robinson         gtk_widget_show(s->menu_bar);
1259c6158483SAnthony Liguori         s->full_screen = FALSE;
1260e3500d1fSGerd Hoffmann         if (vc->type == GD_VC_GFX) {
1261e3500d1fSGerd Hoffmann             vc->gfx.scale_x = 1.0;
1262e3500d1fSGerd Hoffmann             vc->gfx.scale_y = 1.0;
126382fc1809SGerd Hoffmann             gd_update_windowsize(vc);
1264e3500d1fSGerd Hoffmann         }
1265c6158483SAnthony Liguori     }
1266c6158483SAnthony Liguori 
1267e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
1268c6158483SAnthony Liguori }
1269c6158483SAnthony Liguori 
127095414914SCole Robinson static void gd_accel_full_screen(void *opaque)
127195414914SCole Robinson {
127295414914SCole Robinson     GtkDisplayState *s = opaque;
127395414914SCole Robinson     gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
127495414914SCole Robinson }
127595414914SCole Robinson 
1276c6158483SAnthony Liguori static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
1277c6158483SAnthony Liguori {
1278c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1279e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1280c6158483SAnthony Liguori 
1281c6158483SAnthony Liguori     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1282c6158483SAnthony Liguori                                    FALSE);
1283c6158483SAnthony Liguori 
128482fc1809SGerd Hoffmann     vc->gfx.scale_x += VC_SCALE_STEP;
128582fc1809SGerd Hoffmann     vc->gfx.scale_y += VC_SCALE_STEP;
1286c6158483SAnthony Liguori 
1287e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1288c6158483SAnthony Liguori }
1289c6158483SAnthony Liguori 
1290c6158483SAnthony Liguori static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
1291c6158483SAnthony Liguori {
1292c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1293e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1294c6158483SAnthony Liguori 
1295c6158483SAnthony Liguori     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1296c6158483SAnthony Liguori                                    FALSE);
1297c6158483SAnthony Liguori 
129882fc1809SGerd Hoffmann     vc->gfx.scale_x -= VC_SCALE_STEP;
129982fc1809SGerd Hoffmann     vc->gfx.scale_y -= VC_SCALE_STEP;
1300c6158483SAnthony Liguori 
130182fc1809SGerd Hoffmann     vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
130282fc1809SGerd Hoffmann     vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
1303c6158483SAnthony Liguori 
1304e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1305c6158483SAnthony Liguori }
1306c6158483SAnthony Liguori 
1307c6158483SAnthony Liguori static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
1308c6158483SAnthony Liguori {
1309c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1310e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1311c6158483SAnthony Liguori 
1312e3500d1fSGerd Hoffmann     vc->gfx.scale_x = 1.0;
1313e3500d1fSGerd Hoffmann     vc->gfx.scale_y = 1.0;
1314c6158483SAnthony Liguori 
1315e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1316c6158483SAnthony Liguori }
1317c6158483SAnthony Liguori 
1318c6158483SAnthony Liguori static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
1319c6158483SAnthony Liguori {
1320c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1321e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1322c6158483SAnthony Liguori 
1323c6158483SAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
1324c6158483SAnthony Liguori         s->free_scale = TRUE;
1325c6158483SAnthony Liguori     } else {
1326c6158483SAnthony Liguori         s->free_scale = FALSE;
1327e3500d1fSGerd Hoffmann         vc->gfx.scale_x = 1.0;
1328e3500d1fSGerd Hoffmann         vc->gfx.scale_y = 1.0;
1329c6158483SAnthony Liguori     }
1330c6158483SAnthony Liguori 
133182fc1809SGerd Hoffmann     gd_update_windowsize(vc);
1332e3500d1fSGerd Hoffmann     gd_update_full_redraw(vc);
1333c6158483SAnthony Liguori }
1334c6158483SAnthony Liguori 
1335a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1336a69fc693SGerd Hoffmann static void gd_grab_update(VirtualConsole *vc, bool kbd, bool ptr)
1337a69fc693SGerd Hoffmann {
1338a69fc693SGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1339a69fc693SGerd Hoffmann     GdkSeat *seat = gdk_display_get_default_seat(display);
1340a69fc693SGerd Hoffmann     GdkWindow *window = gtk_widget_get_window(vc->gfx.drawing_area);
1341a69fc693SGerd Hoffmann     GdkSeatCapabilities caps = 0;
1342a69fc693SGerd Hoffmann     GdkCursor *cursor = NULL;
1343a69fc693SGerd Hoffmann 
1344a69fc693SGerd Hoffmann     if (kbd) {
1345a69fc693SGerd Hoffmann         caps |= GDK_SEAT_CAPABILITY_KEYBOARD;
1346a69fc693SGerd Hoffmann     }
1347a69fc693SGerd Hoffmann     if (ptr) {
1348a69fc693SGerd Hoffmann         caps |= GDK_SEAT_CAPABILITY_ALL_POINTING;
1349a69fc693SGerd Hoffmann         cursor = vc->s->null_cursor;
1350a69fc693SGerd Hoffmann     }
1351a69fc693SGerd Hoffmann 
1352a69fc693SGerd Hoffmann     if (caps) {
1353a69fc693SGerd Hoffmann         gdk_seat_grab(seat, window, caps, false, cursor,
1354a69fc693SGerd Hoffmann                       NULL, NULL, NULL);
1355a69fc693SGerd Hoffmann     } else {
1356a69fc693SGerd Hoffmann         gdk_seat_ungrab(seat);
1357a69fc693SGerd Hoffmann     }
1358a69fc693SGerd Hoffmann }
1359a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0)
1360f50def91SGerd Hoffmann static void gd_grab_devices(VirtualConsole *vc, bool grab,
1361f50def91SGerd Hoffmann                             GdkInputSource source, GdkEventMask mask,
1362f50def91SGerd Hoffmann                             GdkCursor *cursor)
1363f50def91SGerd Hoffmann {
1364f50def91SGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1365f50def91SGerd Hoffmann     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1366f50def91SGerd Hoffmann     GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
1367f50def91SGerd Hoffmann     GList *tmp = devs;
1368f50def91SGerd Hoffmann 
1369f50def91SGerd Hoffmann     for (tmp = devs; tmp; tmp = tmp->next) {
1370f50def91SGerd Hoffmann         GdkDevice *dev = tmp->data;
1371f50def91SGerd Hoffmann         if (gdk_device_get_source(dev) != source) {
1372f50def91SGerd Hoffmann             continue;
1373f50def91SGerd Hoffmann         }
1374f50def91SGerd Hoffmann         if (grab) {
1375f50def91SGerd Hoffmann             GdkWindow *win = gtk_widget_get_window(vc->gfx.drawing_area);
1376f50def91SGerd Hoffmann             gdk_device_grab(dev, win, GDK_OWNERSHIP_NONE, FALSE,
1377f50def91SGerd Hoffmann                             mask, cursor, GDK_CURRENT_TIME);
1378f50def91SGerd Hoffmann         } else {
1379f50def91SGerd Hoffmann             gdk_device_ungrab(dev, GDK_CURRENT_TIME);
1380f50def91SGerd Hoffmann         }
1381f50def91SGerd Hoffmann     }
1382f50def91SGerd Hoffmann     g_list_free(devs);
1383f50def91SGerd Hoffmann }
1384f50def91SGerd Hoffmann #endif
1385f50def91SGerd Hoffmann 
1386d531deefSGerd Hoffmann static void gd_grab_keyboard(VirtualConsole *vc, const char *reason)
13875104a1f6SAnthony Liguori {
1388aa4f4058SGerd Hoffmann     if (vc->s->kbd_owner) {
1389aa4f4058SGerd Hoffmann         if (vc->s->kbd_owner == vc) {
1390aa4f4058SGerd Hoffmann             return;
1391aa4f4058SGerd Hoffmann         } else {
1392aa4f4058SGerd Hoffmann             gd_ungrab_keyboard(vc->s);
1393aa4f4058SGerd Hoffmann         }
1394aa4f4058SGerd Hoffmann     }
1395aa4f4058SGerd Hoffmann 
1396a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1397a69fc693SGerd Hoffmann     gd_grab_update(vc, true, vc->s->ptr_owner == vc);
1398a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0)
1399f50def91SGerd Hoffmann     gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD,
1400655199daSDaniel P. Berrange                    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
1401f50def91SGerd Hoffmann                    NULL);
1402655199daSDaniel P. Berrange #else
1403e3500d1fSGerd Hoffmann     gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area),
14045104a1f6SAnthony Liguori                       FALSE,
14055104a1f6SAnthony Liguori                       GDK_CURRENT_TIME);
1406655199daSDaniel P. Berrange #endif
14074c638e2eSGerd Hoffmann     vc->s->kbd_owner = vc;
1408695cc59dSGerd Hoffmann     gd_update_caption(vc->s);
1409d531deefSGerd Hoffmann     trace_gd_grab(vc->label, "kbd", reason);
14105104a1f6SAnthony Liguori }
14115104a1f6SAnthony Liguori 
14124c638e2eSGerd Hoffmann static void gd_ungrab_keyboard(GtkDisplayState *s)
14135104a1f6SAnthony Liguori {
14144c638e2eSGerd Hoffmann     VirtualConsole *vc = s->kbd_owner;
14154c638e2eSGerd Hoffmann 
14164c638e2eSGerd Hoffmann     if (vc == NULL) {
14174c638e2eSGerd Hoffmann         return;
14184c638e2eSGerd Hoffmann     }
14194c638e2eSGerd Hoffmann     s->kbd_owner = NULL;
14204c638e2eSGerd Hoffmann 
1421a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1422a69fc693SGerd Hoffmann     gd_grab_update(vc, false, vc->s->ptr_owner == vc);
1423a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0)
1424f50def91SGerd Hoffmann     gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL);
1425655199daSDaniel P. Berrange #else
14265104a1f6SAnthony Liguori     gdk_keyboard_ungrab(GDK_CURRENT_TIME);
1427655199daSDaniel P. Berrange #endif
1428695cc59dSGerd Hoffmann     gd_update_caption(s);
1429d531deefSGerd Hoffmann     trace_gd_ungrab(vc->label, "kbd");
14305104a1f6SAnthony Liguori }
14315104a1f6SAnthony Liguori 
1432d531deefSGerd Hoffmann static void gd_grab_pointer(VirtualConsole *vc, const char *reason)
14335104a1f6SAnthony Liguori {
1434e3500d1fSGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1435aa4f4058SGerd Hoffmann 
1436aa4f4058SGerd Hoffmann     if (vc->s->ptr_owner) {
1437aa4f4058SGerd Hoffmann         if (vc->s->ptr_owner == vc) {
1438aa4f4058SGerd Hoffmann             return;
1439aa4f4058SGerd Hoffmann         } else {
1440aa4f4058SGerd Hoffmann             gd_ungrab_pointer(vc->s);
1441aa4f4058SGerd Hoffmann         }
1442aa4f4058SGerd Hoffmann     }
1443aa4f4058SGerd Hoffmann 
1444a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1445a69fc693SGerd Hoffmann     gd_grab_update(vc, vc->s->kbd_owner == vc, true);
1446a69fc693SGerd Hoffmann     gdk_device_get_position(gd_get_pointer(display),
1447a69fc693SGerd Hoffmann                             NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
1448a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0)
1449f50def91SGerd Hoffmann     gd_grab_devices(vc, true, GDK_SOURCE_MOUSE,
14502a05485dSDaniel P. Berrange                     GDK_POINTER_MOTION_MASK |
14512a05485dSDaniel P. Berrange                     GDK_BUTTON_PRESS_MASK |
14522a05485dSDaniel P. Berrange                     GDK_BUTTON_RELEASE_MASK |
14532a05485dSDaniel P. Berrange                     GDK_BUTTON_MOTION_MASK |
14542a05485dSDaniel P. Berrange                     GDK_SCROLL_MASK,
1455f50def91SGerd Hoffmann                     vc->s->null_cursor);
1456bb732ee7SCole Robinson     gdk_device_get_position(gd_get_pointer(display),
1457e3500d1fSGerd Hoffmann                             NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
14582a05485dSDaniel P. Berrange #else
1459e3500d1fSGerd Hoffmann     gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area),
14605104a1f6SAnthony Liguori                      FALSE, /* All events to come to our window directly */
14615104a1f6SAnthony Liguori                      GDK_POINTER_MOTION_MASK |
14625104a1f6SAnthony Liguori                      GDK_BUTTON_PRESS_MASK |
14635104a1f6SAnthony Liguori                      GDK_BUTTON_RELEASE_MASK |
14645104a1f6SAnthony Liguori                      GDK_BUTTON_MOTION_MASK |
14655104a1f6SAnthony Liguori                      GDK_SCROLL_MASK,
14665104a1f6SAnthony Liguori                      NULL, /* Allow cursor to move over entire desktop */
1467e3500d1fSGerd Hoffmann                      vc->s->null_cursor,
14685104a1f6SAnthony Liguori                      GDK_CURRENT_TIME);
1469ecce1929STakashi Iwai     gdk_display_get_pointer(display, NULL,
1470e3500d1fSGerd Hoffmann                             &vc->s->grab_x_root, &vc->s->grab_y_root, NULL);
14712a05485dSDaniel P. Berrange #endif
14724c638e2eSGerd Hoffmann     vc->s->ptr_owner = vc;
1473695cc59dSGerd Hoffmann     gd_update_caption(vc->s);
1474d531deefSGerd Hoffmann     trace_gd_grab(vc->label, "ptr", reason);
14752a05485dSDaniel P. Berrange }
14762a05485dSDaniel P. Berrange 
14774c638e2eSGerd Hoffmann static void gd_ungrab_pointer(GtkDisplayState *s)
14782a05485dSDaniel P. Berrange {
14794c638e2eSGerd Hoffmann     VirtualConsole *vc = s->ptr_owner;
1480a69fc693SGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
14814c638e2eSGerd Hoffmann 
14824c638e2eSGerd Hoffmann     if (vc == NULL) {
14834c638e2eSGerd Hoffmann         return;
14844c638e2eSGerd Hoffmann     }
14854c638e2eSGerd Hoffmann     s->ptr_owner = NULL;
14864c638e2eSGerd Hoffmann 
1487a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0)
1488a69fc693SGerd Hoffmann     gd_grab_update(vc, vc->s->kbd_owner == vc, false);
1489a69fc693SGerd Hoffmann     gdk_device_warp(gd_get_pointer(display),
1490a69fc693SGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
1491a69fc693SGerd Hoffmann                     vc->s->grab_x_root, vc->s->grab_y_root);
1492a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0)
1493f50def91SGerd Hoffmann     gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL);
1494bb732ee7SCole Robinson     gdk_device_warp(gd_get_pointer(display),
1495e3500d1fSGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
1496e3500d1fSGerd Hoffmann                     vc->s->grab_x_root, vc->s->grab_y_root);
14972a05485dSDaniel P. Berrange #else
14982a05485dSDaniel P. Berrange     gdk_pointer_ungrab(GDK_CURRENT_TIME);
1499ecce1929STakashi Iwai     gdk_display_warp_pointer(display,
1500e3500d1fSGerd Hoffmann                              gtk_widget_get_screen(vc->gfx.drawing_area),
1501e3500d1fSGerd Hoffmann                              vc->s->grab_x_root, vc->s->grab_y_root);
15022a05485dSDaniel P. Berrange #endif
1503695cc59dSGerd Hoffmann     gd_update_caption(s);
1504d531deefSGerd Hoffmann     trace_gd_ungrab(vc->label, "ptr");
15052a05485dSDaniel P. Berrange }
15062a05485dSDaniel P. Berrange 
15072a05485dSDaniel P. Berrange static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
15082a05485dSDaniel P. Berrange {
15092a05485dSDaniel P. Berrange     GtkDisplayState *s = opaque;
1510e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
15112a05485dSDaniel P. Berrange 
15122a05485dSDaniel P. Berrange     if (gd_is_grab_active(s)) {
1513d531deefSGerd Hoffmann         gd_grab_keyboard(vc, "user-request-main-window");
1514d531deefSGerd Hoffmann         gd_grab_pointer(vc, "user-request-main-window");
15155104a1f6SAnthony Liguori     } else {
15164c638e2eSGerd Hoffmann         gd_ungrab_keyboard(s);
15174c638e2eSGerd Hoffmann         gd_ungrab_pointer(s);
15185104a1f6SAnthony Liguori     }
15195104a1f6SAnthony Liguori 
1520e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
15215104a1f6SAnthony Liguori }
15225104a1f6SAnthony Liguori 
1523a4ccabcfSAnthony Liguori static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
1524a4ccabcfSAnthony Liguori                            gpointer data)
1525a4ccabcfSAnthony Liguori {
1526a4ccabcfSAnthony Liguori     GtkDisplayState *s = data;
1527e3500d1fSGerd Hoffmann     VirtualConsole *vc;
15285104a1f6SAnthony Liguori     gboolean on_vga;
1529a4ccabcfSAnthony Liguori 
1530a4ccabcfSAnthony Liguori     if (!gtk_widget_get_realized(s->notebook)) {
1531a4ccabcfSAnthony Liguori         return;
1532a4ccabcfSAnthony Liguori     }
1533a4ccabcfSAnthony Liguori 
15346fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK
15356fa27697SGerd Hoffmann     vc = gd_vc_find_current(s);
15366fa27697SGerd Hoffmann     if (vc && vc->type == GD_VC_VTE) {
15376fa27697SGerd Hoffmann         gtk_widget_hide(vc->vte.terminal);
15386fa27697SGerd Hoffmann     }
15396fa27697SGerd Hoffmann #endif
1540e3500d1fSGerd Hoffmann     vc = gd_vc_find_by_page(s, arg2);
1541e3500d1fSGerd Hoffmann     if (!vc) {
1542e3500d1fSGerd Hoffmann         return;
1543e3500d1fSGerd Hoffmann     }
15446fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK
15456fa27697SGerd Hoffmann     if (vc->type == GD_VC_VTE) {
15466fa27697SGerd Hoffmann         gtk_widget_show(vc->vte.terminal);
15476fa27697SGerd Hoffmann     }
15486fa27697SGerd Hoffmann #endif
1549e3500d1fSGerd Hoffmann     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
1550e3500d1fSGerd Hoffmann                                    TRUE);
1551f8c223f6SGerd Hoffmann     on_vga = (vc->type == GD_VC_GFX &&
1552f8c223f6SGerd Hoffmann               qemu_console_is_graphic(vc->gfx.dcl.con));
15535104a1f6SAnthony Liguori     if (!on_vga) {
15545104a1f6SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
15555104a1f6SAnthony Liguori                                        FALSE);
1556c6158483SAnthony Liguori     } else if (s->full_screen) {
1557c6158483SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1558c6158483SAnthony Liguori                                        TRUE);
15595104a1f6SAnthony Liguori     }
15605104a1f6SAnthony Liguori     gtk_widget_set_sensitive(s->grab_item, on_vga);
15615104a1f6SAnthony Liguori 
156282fc1809SGerd Hoffmann     gd_update_windowsize(vc);
1563e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
1564a4ccabcfSAnthony Liguori }
1565a4ccabcfSAnthony Liguori 
1566e3500d1fSGerd Hoffmann static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
1567e3500d1fSGerd Hoffmann                                gpointer opaque)
15685104a1f6SAnthony Liguori {
1569e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1570e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
15715104a1f6SAnthony Liguori 
15722884cf5bSGerd Hoffmann     if (gd_grab_on_hover(s)) {
1573d531deefSGerd Hoffmann         gd_grab_keyboard(vc, "grab-on-hover");
15745104a1f6SAnthony Liguori     }
15755104a1f6SAnthony Liguori     return TRUE;
15765104a1f6SAnthony Liguori }
15775104a1f6SAnthony Liguori 
1578e3500d1fSGerd Hoffmann static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
1579e3500d1fSGerd Hoffmann                                gpointer opaque)
15805104a1f6SAnthony Liguori {
1581e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1582e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
15835104a1f6SAnthony Liguori 
15842884cf5bSGerd Hoffmann     if (gd_grab_on_hover(s)) {
15854c638e2eSGerd Hoffmann         gd_ungrab_keyboard(s);
15865104a1f6SAnthony Liguori     }
15875104a1f6SAnthony Liguori     return TRUE;
15885104a1f6SAnthony Liguori }
15895104a1f6SAnthony Liguori 
15906db253caSJan Kiszka static gboolean gd_focus_out_event(GtkWidget *widget,
1591e3500d1fSGerd Hoffmann                                    GdkEventCrossing *crossing, gpointer opaque)
15926db253caSJan Kiszka {
1593e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1594e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
15956db253caSJan Kiszka 
15966db253caSJan Kiszka     gtk_release_modifiers(s);
15976db253caSJan Kiszka     return TRUE;
15986db253caSJan Kiszka }
15996db253caSJan Kiszka 
16001301e515SGerd Hoffmann static gboolean gd_configure(GtkWidget *widget,
16011301e515SGerd Hoffmann                              GdkEventConfigure *cfg, gpointer opaque)
16021301e515SGerd Hoffmann {
16031301e515SGerd Hoffmann     VirtualConsole *vc = opaque;
16041301e515SGerd Hoffmann 
1605925a0400SGerd Hoffmann     gd_set_ui_info(vc, cfg->width, cfg->height);
16061301e515SGerd Hoffmann     return FALSE;
16071301e515SGerd Hoffmann }
16081301e515SGerd Hoffmann 
1609d861def3SAnthony Liguori /** Virtual Console Callbacks **/
1610d861def3SAnthony Liguori 
1611ed1132e4SGerd Hoffmann static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
1612cdeb7090SGerd Hoffmann                                int idx, GSList *group, GtkWidget *view_menu)
1613ed1132e4SGerd Hoffmann {
1614cdeb7090SGerd Hoffmann     vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
1615277836c8SCole Robinson     gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx,
1616277836c8SCole Robinson             HOTKEY_MODIFIERS, 0,
1617277836c8SCole Robinson             g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL));
1618277836c8SCole Robinson #if GTK_CHECK_VERSION(3, 8, 0)
1619277836c8SCole Robinson     gtk_accel_label_set_accel(
1620277836c8SCole Robinson             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))),
1621277836c8SCole Robinson             GDK_KEY_1 + idx, HOTKEY_MODIFIERS);
1622277836c8SCole Robinson #endif
1623ed1132e4SGerd Hoffmann 
1624ed1132e4SGerd Hoffmann     g_signal_connect(vc->menu_item, "activate",
1625ed1132e4SGerd Hoffmann                      G_CALLBACK(gd_menu_switch_vc), s);
1626ed1132e4SGerd Hoffmann     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
1627ed1132e4SGerd Hoffmann 
1628277836c8SCole Robinson     group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
1629ed1132e4SGerd Hoffmann     return group;
1630ed1132e4SGerd Hoffmann }
1631ed1132e4SGerd Hoffmann 
1632ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
163344b31e0bSMichael S. Tsirkin static void gd_menu_copy(GtkMenuItem *item, void *opaque)
163444b31e0bSMichael S. Tsirkin {
163544b31e0bSMichael S. Tsirkin     GtkDisplayState *s = opaque;
163644b31e0bSMichael S. Tsirkin     VirtualConsole *vc = gd_vc_find_current(s);
163744b31e0bSMichael S. Tsirkin 
163844b31e0bSMichael S. Tsirkin     vte_terminal_copy_clipboard(VTE_TERMINAL(vc->vte.terminal));
163944b31e0bSMichael S. Tsirkin }
164044b31e0bSMichael S. Tsirkin 
16410fb20d1cSCole Robinson static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
16420fb20d1cSCole Robinson {
16430fb20d1cSCole Robinson     VirtualConsole *vc = opaque;
16440fb20d1cSCole Robinson 
16450fb20d1cSCole Robinson     if (gtk_adjustment_get_upper(adjustment) >
16460fb20d1cSCole Robinson         gtk_adjustment_get_page_size(adjustment)) {
1647271a25c0SGerd Hoffmann         gtk_widget_show(vc->vte.scrollbar);
16480fb20d1cSCole Robinson     } else {
1649271a25c0SGerd Hoffmann         gtk_widget_hide(vc->vte.scrollbar);
16500fb20d1cSCole Robinson     }
16510fb20d1cSCole Robinson }
16520fb20d1cSCole Robinson 
1653d861def3SAnthony Liguori static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
1654d861def3SAnthony Liguori {
1655d861def3SAnthony Liguori     VirtualConsole *vc = chr->opaque;
1656d861def3SAnthony Liguori 
1657271a25c0SGerd Hoffmann     vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
1658d4370741SCole Robinson     return len;
1659d861def3SAnthony Liguori }
1660d861def3SAnthony Liguori 
1661fba958c6SPaolo Bonzini static void gd_vc_chr_set_echo(CharDriverState *chr, bool echo)
1662fba958c6SPaolo Bonzini {
1663fba958c6SPaolo Bonzini     VirtualConsole *vc = chr->opaque;
1664fba958c6SPaolo Bonzini 
1665fba958c6SPaolo Bonzini     vc->vte.echo = echo;
1666fba958c6SPaolo Bonzini }
1667fba958c6SPaolo Bonzini 
1668d861def3SAnthony Liguori static int nb_vcs;
1669d861def3SAnthony Liguori static CharDriverState *vcs[MAX_VCS];
1670d861def3SAnthony Liguori 
1671919e11f3SDaniel P. Berrange static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
1672d861def3SAnthony Liguori {
1673919e11f3SDaniel P. Berrange     ChardevCommon *common = qapi_ChardevVC_base(vc);
1674d861def3SAnthony Liguori     CharDriverState *chr;
1675d861def3SAnthony Liguori 
1676919e11f3SDaniel P. Berrange     chr = qemu_chr_alloc(common, errp);
1677919e11f3SDaniel P. Berrange     if (!chr) {
1678919e11f3SDaniel P. Berrange         return NULL;
1679919e11f3SDaniel P. Berrange     }
1680919e11f3SDaniel P. Berrange 
1681d861def3SAnthony Liguori     chr->chr_write = gd_vc_chr_write;
1682fba958c6SPaolo Bonzini     chr->chr_set_echo = gd_vc_chr_set_echo;
1683fba958c6SPaolo Bonzini 
1684fba958c6SPaolo Bonzini     /* Temporary, until gd_vc_vte_init runs.  */
1685327d83baSPaolo Bonzini     chr->opaque = g_new0(VirtualConsole, 1);
1686fba958c6SPaolo Bonzini 
1687bd5c51eeSMichael Roth     /* defer OPENED events until our vc is fully initialized */
1688bd5c51eeSMichael Roth     chr->explicit_be_open = true;
1689d861def3SAnthony Liguori 
1690d861def3SAnthony Liguori     vcs[nb_vcs++] = chr;
1691d861def3SAnthony Liguori 
1692d861def3SAnthony Liguori     return chr;
1693d861def3SAnthony Liguori }
1694d861def3SAnthony Liguori 
1695d4370741SCole Robinson static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
1696d4370741SCole Robinson                          gpointer user_data)
1697d861def3SAnthony Liguori {
1698d4370741SCole Robinson     VirtualConsole *vc = user_data;
1699d861def3SAnthony Liguori 
1700fba958c6SPaolo Bonzini     if (vc->vte.echo) {
1701fba958c6SPaolo Bonzini         VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
1702fba958c6SPaolo Bonzini         int i;
1703fba958c6SPaolo Bonzini         for (i = 0; i < size; i++) {
1704fba958c6SPaolo Bonzini             uint8_t c = text[i];
1705fba958c6SPaolo Bonzini             if (c >= 128 || isprint(c)) {
1706fba958c6SPaolo Bonzini                 /* 8-bit characters are considered printable.  */
1707fba958c6SPaolo Bonzini                 vte_terminal_feed(term, &text[i], 1);
1708fba958c6SPaolo Bonzini             } else if (c == '\r' || c == '\n') {
1709fba958c6SPaolo Bonzini                 vte_terminal_feed(term, "\r\n", 2);
1710fba958c6SPaolo Bonzini             } else {
1711fba958c6SPaolo Bonzini                 char ctrl[2] = { '^', 0};
1712fba958c6SPaolo Bonzini                 ctrl[1] = text[i] ^ 64;
1713fba958c6SPaolo Bonzini                 vte_terminal_feed(term, ctrl, 2);
1714fba958c6SPaolo Bonzini             }
1715fba958c6SPaolo Bonzini         }
1716fba958c6SPaolo Bonzini     }
1717fba958c6SPaolo Bonzini 
1718271a25c0SGerd Hoffmann     qemu_chr_be_write(vc->vte.chr, (uint8_t  *)text, (unsigned int)size);
1719d861def3SAnthony Liguori     return TRUE;
1720d861def3SAnthony Liguori }
1721d861def3SAnthony Liguori 
1722ed1132e4SGerd Hoffmann static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
1723ed1132e4SGerd Hoffmann                               CharDriverState *chr, int idx,
1724e3500d1fSGerd Hoffmann                               GSList *group, GtkWidget *view_menu)
1725d861def3SAnthony Liguori {
1726d861def3SAnthony Liguori     char buffer[32];
17270fb20d1cSCole Robinson     GtkWidget *box;
17280fb20d1cSCole Robinson     GtkWidget *scrollbar;
17290fb20d1cSCole Robinson     GtkAdjustment *vadjustment;
1730fba958c6SPaolo Bonzini     VirtualConsole *tmp_vc = chr->opaque;
1731d861def3SAnthony Liguori 
1732e3500d1fSGerd Hoffmann     vc->s = s;
1733fba958c6SPaolo Bonzini     vc->vte.echo = tmp_vc->vte.echo;
1734fba958c6SPaolo Bonzini 
1735ed1132e4SGerd Hoffmann     vc->vte.chr = chr;
1736fba958c6SPaolo Bonzini     chr->opaque = vc;
1737fba958c6SPaolo Bonzini     g_free(tmp_vc);
1738d861def3SAnthony Liguori 
1739ed1132e4SGerd Hoffmann     snprintf(buffer, sizeof(buffer), "vc%d", idx);
1740cdeb7090SGerd Hoffmann     vc->label = g_strdup_printf("%s", vc->vte.chr->label
1741cdeb7090SGerd Hoffmann                                 ? vc->vte.chr->label : buffer);
1742cdeb7090SGerd Hoffmann     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1743d861def3SAnthony Liguori 
1744271a25c0SGerd Hoffmann     vc->vte.terminal = vte_terminal_new();
1745271a25c0SGerd Hoffmann     g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
1746d861def3SAnthony Liguori 
1747fba958c6SPaolo Bonzini     /* The documentation says that the default is UTF-8, but actually it is
1748fba958c6SPaolo Bonzini      * 7-bit ASCII at least in VTE 0.38.
1749fba958c6SPaolo Bonzini      */
1750fba958c6SPaolo Bonzini #if VTE_CHECK_VERSION(0, 40, 0)
1751fba958c6SPaolo Bonzini     vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8", NULL);
1752fba958c6SPaolo Bonzini #else
1753fba958c6SPaolo Bonzini     vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8");
1754fba958c6SPaolo Bonzini #endif
1755fba958c6SPaolo Bonzini 
1756271a25c0SGerd Hoffmann     vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
175782fc1809SGerd Hoffmann     vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
175882fc1809SGerd Hoffmann                           VC_TERM_X_MIN, VC_TERM_Y_MIN);
1759d861def3SAnthony Liguori 
17600fb20d1cSCole Robinson #if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0)
1761271a25c0SGerd Hoffmann     vadjustment = gtk_scrollable_get_vadjustment
1762271a25c0SGerd Hoffmann         (GTK_SCROLLABLE(vc->vte.terminal));
17630fb20d1cSCole Robinson #else
1764271a25c0SGerd Hoffmann     vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
17650fb20d1cSCole Robinson #endif
1766d861def3SAnthony Liguori 
17670fb20d1cSCole Robinson #if GTK_CHECK_VERSION(3, 0, 0)
17680fb20d1cSCole Robinson     box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
17690fb20d1cSCole Robinson     scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
17700fb20d1cSCole Robinson #else
17710fb20d1cSCole Robinson     box = gtk_hbox_new(false, 2);
17720fb20d1cSCole Robinson     scrollbar = gtk_vscrollbar_new(vadjustment);
17730fb20d1cSCole Robinson #endif
17740fb20d1cSCole Robinson 
1775271a25c0SGerd Hoffmann     gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
17760fb20d1cSCole Robinson     gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);
17770fb20d1cSCole Robinson 
1778271a25c0SGerd Hoffmann     vc->vte.box = box;
1779271a25c0SGerd Hoffmann     vc->vte.scrollbar = scrollbar;
17800fb20d1cSCole Robinson 
17810fb20d1cSCole Robinson     g_signal_connect(vadjustment, "changed",
17820fb20d1cSCole Robinson                      G_CALLBACK(gd_vc_adjustment_changed), vc);
17830fb20d1cSCole Robinson 
1784e3500d1fSGerd Hoffmann     vc->type = GD_VC_VTE;
1785271a25c0SGerd Hoffmann     vc->tab_item = box;
17869d677e1cSJan Kiszka     vc->focus = vc->vte.terminal;
1787271a25c0SGerd Hoffmann     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
1788cdeb7090SGerd Hoffmann                              gtk_label_new(vc->label));
1789d861def3SAnthony Liguori 
1790271a25c0SGerd Hoffmann     qemu_chr_be_generic_open(vc->vte.chr);
1791271a25c0SGerd Hoffmann     if (vc->vte.chr->init) {
1792271a25c0SGerd Hoffmann         vc->vte.chr->init(vc->vte.chr);
1793d861def3SAnthony Liguori     }
1794d861def3SAnthony Liguori 
1795d861def3SAnthony Liguori     return group;
1796a4ccabcfSAnthony Liguori }
1797a4ccabcfSAnthony Liguori 
1798ed1132e4SGerd Hoffmann static void gd_vcs_init(GtkDisplayState *s, GSList *group,
1799ee5f31e4SGerd Hoffmann                         GtkWidget *view_menu)
1800ee5f31e4SGerd Hoffmann {
1801ee5f31e4SGerd Hoffmann     int i;
1802ee5f31e4SGerd Hoffmann 
1803ee5f31e4SGerd Hoffmann     for (i = 0; i < nb_vcs; i++) {
1804ed1132e4SGerd Hoffmann         VirtualConsole *vc = &s->vc[s->nb_vcs];
1805ed1132e4SGerd Hoffmann         group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu);
1806ee5f31e4SGerd Hoffmann         s->nb_vcs++;
1807ee5f31e4SGerd Hoffmann     }
1808ee5f31e4SGerd Hoffmann }
1809ee5f31e4SGerd Hoffmann #endif /* CONFIG_VTE */
1810ee5f31e4SGerd Hoffmann 
1811a4ccabcfSAnthony Liguori /** Window Creation **/
1812a4ccabcfSAnthony Liguori 
1813e3500d1fSGerd Hoffmann static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
1814e3500d1fSGerd Hoffmann {
1815e3500d1fSGerd Hoffmann #if GTK_CHECK_VERSION(3, 0, 0)
1816e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "draw",
1817e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_draw_event), vc);
1818925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
1819925a0400SGerd Hoffmann     if (display_opengl) {
1820925a0400SGerd Hoffmann         /* wire up GtkGlArea events */
1821925a0400SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "render",
1822925a0400SGerd Hoffmann                          G_CALLBACK(gd_render_event), vc);
1823925a0400SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "resize",
1824925a0400SGerd Hoffmann                          G_CALLBACK(gd_resize_event), vc);
1825925a0400SGerd Hoffmann     }
1826925a0400SGerd Hoffmann #endif
1827e3500d1fSGerd Hoffmann #else
1828e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "expose-event",
1829e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_expose_event), vc);
1830e3500d1fSGerd Hoffmann #endif
1831f8c223f6SGerd Hoffmann     if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
1832e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "event",
1833e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_event), vc);
1834e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "button-press-event",
1835e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_button_event), vc);
1836e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "button-release-event",
1837e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_button_event), vc);
1838e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "scroll-event",
1839e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_scroll_event), vc);
1840e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1841e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_key_event), vc);
1842e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "key-release-event",
1843e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_key_event), vc);
1844e3500d1fSGerd Hoffmann 
1845e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "enter-notify-event",
1846e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_enter_event), vc);
1847e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "leave-notify-event",
1848e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_leave_event), vc);
1849e3500d1fSGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
1850e3500d1fSGerd Hoffmann                          G_CALLBACK(gd_focus_out_event), vc);
18511301e515SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "configure-event",
18521301e515SGerd Hoffmann                          G_CALLBACK(gd_configure), vc);
1853f8c223f6SGerd Hoffmann     } else {
1854f8c223f6SGerd Hoffmann         g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1855f8c223f6SGerd Hoffmann                          G_CALLBACK(gd_text_key_down), vc);
1856f8c223f6SGerd Hoffmann     }
1857e3500d1fSGerd Hoffmann }
1858e3500d1fSGerd Hoffmann 
1859a4ccabcfSAnthony Liguori static void gd_connect_signals(GtkDisplayState *s)
1860a4ccabcfSAnthony Liguori {
1861a4ccabcfSAnthony Liguori     g_signal_connect(s->show_tabs_item, "activate",
1862a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_show_tabs), s);
1863cdeb7090SGerd Hoffmann     g_signal_connect(s->untabify_item, "activate",
1864cdeb7090SGerd Hoffmann                      G_CALLBACK(gd_menu_untabify), s);
1865a4ccabcfSAnthony Liguori 
1866a4ccabcfSAnthony Liguori     g_signal_connect(s->window, "delete-event",
1867a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_window_close), s);
1868a4ccabcfSAnthony Liguori 
186930e8f22bSJan Kiszka     g_signal_connect(s->pause_item, "activate",
187030e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_pause), s);
187130e8f22bSJan Kiszka     g_signal_connect(s->reset_item, "activate",
187230e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_reset), s);
187330e8f22bSJan Kiszka     g_signal_connect(s->powerdown_item, "activate",
187430e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_powerdown), s);
1875a4ccabcfSAnthony Liguori     g_signal_connect(s->quit_item, "activate",
1876a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_quit), s);
187744b31e0bSMichael S. Tsirkin #if defined(CONFIG_VTE)
187844b31e0bSMichael S. Tsirkin     g_signal_connect(s->copy_item, "activate",
187944b31e0bSMichael S. Tsirkin                      G_CALLBACK(gd_menu_copy), s);
188044b31e0bSMichael S. Tsirkin #endif
1881c6158483SAnthony Liguori     g_signal_connect(s->full_screen_item, "activate",
1882c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_full_screen), s);
1883c6158483SAnthony Liguori     g_signal_connect(s->zoom_in_item, "activate",
1884c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_in), s);
1885c6158483SAnthony Liguori     g_signal_connect(s->zoom_out_item, "activate",
1886c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_out), s);
1887c6158483SAnthony Liguori     g_signal_connect(s->zoom_fixed_item, "activate",
1888c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_fixed), s);
1889c6158483SAnthony Liguori     g_signal_connect(s->zoom_fit_item, "activate",
1890c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_fit), s);
18915104a1f6SAnthony Liguori     g_signal_connect(s->grab_item, "activate",
18925104a1f6SAnthony Liguori                      G_CALLBACK(gd_menu_grab_input), s);
1893a4ccabcfSAnthony Liguori     g_signal_connect(s->notebook, "switch-page",
1894a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_change_page), s);
1895a4ccabcfSAnthony Liguori }
1896a4ccabcfSAnthony Liguori 
1897400519d2SCole Robinson static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
1898a4ccabcfSAnthony Liguori {
1899bf9b255fSAnthony Liguori     GtkWidget *machine_menu;
1900a4ccabcfSAnthony Liguori     GtkWidget *separator;
1901a4ccabcfSAnthony Liguori 
1902bf9b255fSAnthony Liguori     machine_menu = gtk_menu_new();
1903400519d2SCole Robinson     gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group);
190430e8f22bSJan Kiszka 
190530e8f22bSJan Kiszka     s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
1906bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
190730e8f22bSJan Kiszka 
190830e8f22bSJan Kiszka     separator = gtk_separator_menu_item_new();
1909bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
191030e8f22bSJan Kiszka 
19119068f20dSCole Robinson     s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset"));
1912bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
191330e8f22bSJan Kiszka 
19149068f20dSCole Robinson     s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down"));
1915bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
191630e8f22bSJan Kiszka 
191730e8f22bSJan Kiszka     separator = gtk_separator_menu_item_new();
1918bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1919a4ccabcfSAnthony Liguori 
19203d914488SCole Robinson     s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
1921a4ccabcfSAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
192230e8f22bSJan Kiszka                                  "<QEMU>/Machine/Quit");
19233d914488SCole Robinson     gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
1924db1da1f2SCole Robinson                             GDK_KEY_q, HOTKEY_MODIFIERS);
1925bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
1926a4ccabcfSAnthony Liguori 
1927bf9b255fSAnthony Liguori     return machine_menu;
1928bf9b255fSAnthony Liguori }
1929bf9b255fSAnthony Liguori 
1930e3500d1fSGerd Hoffmann static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
1931ed1132e4SGerd Hoffmann                               QemuConsole *con, int idx,
1932e3500d1fSGerd Hoffmann                               GSList *group, GtkWidget *view_menu)
1933e3500d1fSGerd Hoffmann {
1934779ce88fSGerd Hoffmann     vc->label = qemu_console_get_label(con);
1935e3500d1fSGerd Hoffmann     vc->s = s;
1936e3500d1fSGerd Hoffmann     vc->gfx.scale_x = 1.0;
1937e3500d1fSGerd Hoffmann     vc->gfx.scale_y = 1.0;
1938e3500d1fSGerd Hoffmann 
1939925a0400SGerd Hoffmann #if defined(CONFIG_OPENGL)
1940925a0400SGerd Hoffmann     if (display_opengl) {
1941925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
1942925a0400SGerd Hoffmann         vc->gfx.drawing_area = gtk_gl_area_new();
1943925a0400SGerd Hoffmann         vc->gfx.dcl.ops = &dcl_gl_area_ops;
1944925a0400SGerd Hoffmann #else
1945e3500d1fSGerd Hoffmann         vc->gfx.drawing_area = gtk_drawing_area_new();
1946925a0400SGerd Hoffmann         /*
1947925a0400SGerd Hoffmann          * gtk_widget_set_double_buffered() was deprecated in 3.14.
1948925a0400SGerd Hoffmann          * It is required for opengl rendering on X11 though.  A
1949925a0400SGerd Hoffmann          * proper replacement (native opengl support) is only
1950925a0400SGerd Hoffmann          * available in 3.16+.  Silence the warning if possible.
1951925a0400SGerd Hoffmann          */
1952925a0400SGerd Hoffmann #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
1953925a0400SGerd Hoffmann #pragma GCC diagnostic push
1954925a0400SGerd Hoffmann #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1955925a0400SGerd Hoffmann #endif
1956925a0400SGerd Hoffmann         gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
1957925a0400SGerd Hoffmann #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
1958925a0400SGerd Hoffmann #pragma GCC diagnostic pop
1959925a0400SGerd Hoffmann #endif
1960925a0400SGerd Hoffmann         vc->gfx.dcl.ops = &dcl_egl_ops;
1961925a0400SGerd Hoffmann #endif /* CONFIG_GTK_GL */
1962925a0400SGerd Hoffmann     } else
1963925a0400SGerd Hoffmann #endif
1964925a0400SGerd Hoffmann     {
1965925a0400SGerd Hoffmann         vc->gfx.drawing_area = gtk_drawing_area_new();
1966925a0400SGerd Hoffmann         vc->gfx.dcl.ops = &dcl_ops;
1967925a0400SGerd Hoffmann     }
1968925a0400SGerd Hoffmann 
1969925a0400SGerd Hoffmann 
1970e3500d1fSGerd Hoffmann     gtk_widget_add_events(vc->gfx.drawing_area,
1971e3500d1fSGerd Hoffmann                           GDK_POINTER_MOTION_MASK |
1972e3500d1fSGerd Hoffmann                           GDK_BUTTON_PRESS_MASK |
1973e3500d1fSGerd Hoffmann                           GDK_BUTTON_RELEASE_MASK |
1974e3500d1fSGerd Hoffmann                           GDK_BUTTON_MOTION_MASK |
1975e3500d1fSGerd Hoffmann                           GDK_ENTER_NOTIFY_MASK |
1976e3500d1fSGerd Hoffmann                           GDK_LEAVE_NOTIFY_MASK |
1977e3500d1fSGerd Hoffmann                           GDK_SCROLL_MASK |
1978e3500d1fSGerd Hoffmann                           GDK_KEY_PRESS_MASK);
1979e3500d1fSGerd Hoffmann     gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
1980e3500d1fSGerd Hoffmann 
1981e3500d1fSGerd Hoffmann     vc->type = GD_VC_GFX;
1982e3500d1fSGerd Hoffmann     vc->tab_item = vc->gfx.drawing_area;
19839d677e1cSJan Kiszka     vc->focus = vc->gfx.drawing_area;
1984e3500d1fSGerd Hoffmann     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
1985cdeb7090SGerd Hoffmann                              vc->tab_item, gtk_label_new(vc->label));
1986e3500d1fSGerd Hoffmann 
1987e3500d1fSGerd Hoffmann     vc->gfx.dcl.con = con;
1988e3500d1fSGerd Hoffmann     register_displaychangelistener(&vc->gfx.dcl);
1989e3500d1fSGerd Hoffmann 
1990f8c223f6SGerd Hoffmann     gd_connect_vc_gfx_signals(vc);
1991f8c223f6SGerd Hoffmann     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1992f8c223f6SGerd Hoffmann 
19931301e515SGerd Hoffmann     if (dpy_ui_info_supported(vc->gfx.dcl.con)) {
19941301e515SGerd Hoffmann         gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item));
19951d73cd78SGerd Hoffmann         s->free_scale = true;
19961301e515SGerd Hoffmann     }
19971301e515SGerd Hoffmann 
1998e3500d1fSGerd Hoffmann     return group;
1999e3500d1fSGerd Hoffmann }
2000e3500d1fSGerd Hoffmann 
2001400519d2SCole Robinson static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
2002bf9b255fSAnthony Liguori {
2003bf9b255fSAnthony Liguori     GSList *group = NULL;
2004bf9b255fSAnthony Liguori     GtkWidget *view_menu;
2005bf9b255fSAnthony Liguori     GtkWidget *separator;
2006ed1132e4SGerd Hoffmann     QemuConsole *con;
2007ed1132e4SGerd Hoffmann     int vc;
2008bf9b255fSAnthony Liguori 
2009bf9b255fSAnthony Liguori     view_menu = gtk_menu_new();
2010400519d2SCole Robinson     gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group);
2011a4ccabcfSAnthony Liguori 
20123d914488SCole Robinson     s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen"));
201395414914SCole Robinson 
201444b31e0bSMichael S. Tsirkin #if defined(CONFIG_VTE)
201544b31e0bSMichael S. Tsirkin     s->copy_item = gtk_menu_item_new_with_mnemonic(_("_Copy"));
201644b31e0bSMichael S. Tsirkin     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->copy_item);
201744b31e0bSMichael S. Tsirkin #endif
201844b31e0bSMichael S. Tsirkin 
201995414914SCole Robinson     gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0,
202095414914SCole Robinson             g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL));
202195414914SCole Robinson #if GTK_CHECK_VERSION(3, 8, 0)
202295414914SCole Robinson     gtk_accel_label_set_accel(
202395414914SCole Robinson             GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))),
202495414914SCole Robinson             GDK_KEY_f, HOTKEY_MODIFIERS);
202595414914SCole Robinson #endif
2026bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
2027c6158483SAnthony Liguori 
2028c6158483SAnthony Liguori     separator = gtk_separator_menu_item_new();
2029bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2030c6158483SAnthony Liguori 
20313d914488SCole Robinson     s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In"));
2032c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
2033c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom In");
2034b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
2035b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
2036bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
2037c6158483SAnthony Liguori 
20383d914488SCole Robinson     s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
2039c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
2040c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom Out");
2041b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus,
2042b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
2043bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
2044c6158483SAnthony Liguori 
20453d914488SCole Robinson     s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit"));
2046c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
2047c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom Fixed");
2048b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0,
2049b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
2050bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
2051c6158483SAnthony Liguori 
2052834574eaSAnthony Liguori     s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
2053bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
2054c6158483SAnthony Liguori 
2055c6158483SAnthony Liguori     separator = gtk_separator_menu_item_new();
2056bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2057c6158483SAnthony Liguori 
2058834574eaSAnthony Liguori     s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
2059bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
20605104a1f6SAnthony Liguori 
2061834574eaSAnthony Liguori     s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
20625104a1f6SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
20635104a1f6SAnthony Liguori                                  "<QEMU>/View/Grab Input");
2064b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g,
2065b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
2066bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
20675104a1f6SAnthony Liguori 
2068a4ccabcfSAnthony Liguori     separator = gtk_separator_menu_item_new();
2069bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2070a4ccabcfSAnthony Liguori 
2071e3500d1fSGerd Hoffmann     /* gfx */
2072ed1132e4SGerd Hoffmann     for (vc = 0;; vc++) {
2073ed1132e4SGerd Hoffmann         con = qemu_console_lookup_by_index(vc);
2074f8c223f6SGerd Hoffmann         if (!con) {
2075ed1132e4SGerd Hoffmann             break;
2076ed1132e4SGerd Hoffmann         }
2077ed1132e4SGerd Hoffmann         group = gd_vc_gfx_init(s, &s->vc[vc], con,
2078ed1132e4SGerd Hoffmann                                vc, group, view_menu);
2079ed1132e4SGerd Hoffmann         s->nb_vcs++;
2080ed1132e4SGerd Hoffmann     }
2081a4ccabcfSAnthony Liguori 
2082ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
2083e3500d1fSGerd Hoffmann     /* vte */
2084ed1132e4SGerd Hoffmann     gd_vcs_init(s, group, view_menu);
2085ee5f31e4SGerd Hoffmann #endif
2086d861def3SAnthony Liguori 
2087a4ccabcfSAnthony Liguori     separator = gtk_separator_menu_item_new();
2088bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
2089a4ccabcfSAnthony Liguori 
2090834574eaSAnthony Liguori     s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
2091bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
2092a4ccabcfSAnthony Liguori 
2093cdeb7090SGerd Hoffmann     s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
2094cdeb7090SGerd Hoffmann     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
2095cdeb7090SGerd Hoffmann 
2096bf9b255fSAnthony Liguori     return view_menu;
2097bf9b255fSAnthony Liguori }
2098a4ccabcfSAnthony Liguori 
2099bf9b255fSAnthony Liguori static void gd_create_menus(GtkDisplayState *s)
2100bf9b255fSAnthony Liguori {
2101400519d2SCole Robinson     s->accel_group = gtk_accel_group_new();
2102400519d2SCole Robinson     s->machine_menu = gd_create_menu_machine(s);
2103400519d2SCole Robinson     s->view_menu = gd_create_menu_view(s);
2104bf9b255fSAnthony Liguori 
2105bf9b255fSAnthony Liguori     s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
210630e8f22bSJan Kiszka     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
210730e8f22bSJan Kiszka                               s->machine_menu);
210830e8f22bSJan Kiszka     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
2109a4ccabcfSAnthony Liguori 
2110bf9b255fSAnthony Liguori     s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
2111a4ccabcfSAnthony Liguori     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
2112a4ccabcfSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
2113bf9b255fSAnthony Liguori 
2114400519d2SCole Robinson     g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group);
2115400519d2SCole Robinson     gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group);
2116a4ccabcfSAnthony Liguori }
2117a4ccabcfSAnthony Liguori 
21183158a348SBruce Rogers static void gd_set_keycode_type(GtkDisplayState *s)
21193158a348SBruce Rogers {
21200a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_X11
21213158a348SBruce Rogers     GdkDisplay *display = gtk_widget_get_display(s->window);
21220a337ed0SGerd Hoffmann     if (GDK_IS_X11_DISPLAY(display)) {
21233158a348SBruce Rogers         Display *x11_display = gdk_x11_display_get_xdisplay(display);
21243158a348SBruce Rogers         XkbDescPtr desc = XkbGetKeyboard(x11_display, XkbGBN_AllComponentsMask,
21253158a348SBruce Rogers                                          XkbUseCoreKbd);
21260a337ed0SGerd Hoffmann         char *keycodes = NULL;
21273158a348SBruce Rogers 
21283158a348SBruce Rogers         if (desc && desc->names) {
21293158a348SBruce Rogers             keycodes = XGetAtomName(x11_display, desc->names->keycodes);
21303158a348SBruce Rogers         }
21313158a348SBruce Rogers         if (keycodes == NULL) {
21323158a348SBruce Rogers             fprintf(stderr, "could not lookup keycode name\n");
21333158a348SBruce Rogers         } else if (strstart(keycodes, "evdev", NULL)) {
21343158a348SBruce Rogers             s->has_evdev = true;
21353158a348SBruce Rogers         } else if (!strstart(keycodes, "xfree86", NULL)) {
21363158a348SBruce Rogers             fprintf(stderr, "unknown keycodes `%s', please report to "
21373158a348SBruce Rogers                     "qemu-devel@nongnu.org\n", keycodes);
21383158a348SBruce Rogers         }
213984961407SChen Fan 
214084961407SChen Fan         if (desc) {
214184961407SChen Fan             XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
214284961407SChen Fan         }
214384961407SChen Fan         if (keycodes) {
214484961407SChen Fan             XFree(keycodes);
214584961407SChen Fan         }
21460a337ed0SGerd Hoffmann     }
21473158a348SBruce Rogers #endif
21483158a348SBruce Rogers }
21493158a348SBruce Rogers 
2150060ab763SGerd Hoffmann static gboolean gtkinit;
2151060ab763SGerd Hoffmann 
2152881249c7SJan Kiszka void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
2153a4ccabcfSAnthony Liguori {
2154a4ccabcfSAnthony Liguori     GtkDisplayState *s = g_malloc0(sizeof(*s));
2155d819cdccSStefan Weil     char *filename;
215663c67b6dSMax Reitz     GdkDisplay *window_display;
2157a4ccabcfSAnthony Liguori 
2158060ab763SGerd Hoffmann     if (!gtkinit) {
2159060ab763SGerd Hoffmann         fprintf(stderr, "gtk initialization failed\n");
2160060ab763SGerd Hoffmann         exit(1);
2161060ab763SGerd Hoffmann     }
2162060ab763SGerd Hoffmann 
2163a4ccabcfSAnthony Liguori     s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
216451572ab0SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 2, 0)
216551572ab0SDaniel P. Berrange     s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
216651572ab0SDaniel P. Berrange #else
2167a4ccabcfSAnthony Liguori     s->vbox = gtk_vbox_new(FALSE, 0);
216851572ab0SDaniel P. Berrange #endif
2169a4ccabcfSAnthony Liguori     s->notebook = gtk_notebook_new();
2170a4ccabcfSAnthony Liguori     s->menu_bar = gtk_menu_bar_new();
2171a4ccabcfSAnthony Liguori 
2172c6158483SAnthony Liguori     s->free_scale = FALSE;
2173a4ccabcfSAnthony Liguori 
21742cb5d2a4SAlberto Garcia     /* LC_MESSAGES only. See early_gtk_display_init() for details */
21752cb5d2a4SAlberto Garcia     setlocale(LC_MESSAGES, "");
2176834574eaSAnthony Liguori     bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
2177834574eaSAnthony Liguori     textdomain("qemu");
2178834574eaSAnthony Liguori 
217963c67b6dSMax Reitz     window_display = gtk_widget_get_display(s->window);
218063c67b6dSMax Reitz     s->null_cursor = gdk_cursor_new_for_display(window_display,
218163c67b6dSMax Reitz                                                 GDK_BLANK_CURSOR);
2182a4ccabcfSAnthony Liguori 
2183a4ccabcfSAnthony Liguori     s->mouse_mode_notifier.notify = gd_mouse_mode_change;
2184a4ccabcfSAnthony Liguori     qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
2185a4ccabcfSAnthony Liguori     qemu_add_vm_change_state_handler(gd_change_runstate, s);
2186a4ccabcfSAnthony Liguori 
2187f7da9c17SAnthony Liguori     filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg");
2188d819cdccSStefan Weil     if (filename) {
2189d819cdccSStefan Weil         GError *error = NULL;
2190d819cdccSStefan Weil         GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error);
2191d819cdccSStefan Weil         if (pixbuf) {
2192d819cdccSStefan Weil             gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf);
2193d819cdccSStefan Weil         } else {
2194d819cdccSStefan Weil             g_error_free(error);
2195d819cdccSStefan Weil         }
2196d819cdccSStefan Weil         g_free(filename);
2197d819cdccSStefan Weil     }
2198d819cdccSStefan Weil 
2199a4ccabcfSAnthony Liguori     gd_create_menus(s);
2200a4ccabcfSAnthony Liguori 
2201a4ccabcfSAnthony Liguori     gd_connect_signals(s);
2202a4ccabcfSAnthony Liguori 
2203a4ccabcfSAnthony Liguori     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
2204a4ccabcfSAnthony Liguori     gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
2205a4ccabcfSAnthony Liguori 
2206a4ccabcfSAnthony Liguori     gd_update_caption(s);
2207a4ccabcfSAnthony Liguori 
2208a4ccabcfSAnthony Liguori     gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
2209a4ccabcfSAnthony Liguori     gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
2210a4ccabcfSAnthony Liguori 
2211a4ccabcfSAnthony Liguori     gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
2212a4ccabcfSAnthony Liguori 
2213a4ccabcfSAnthony Liguori     gtk_widget_show_all(s->window);
2214a4ccabcfSAnthony Liguori 
22156fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK
22166fa27697SGerd Hoffmann     {
22176fa27697SGerd Hoffmann         VirtualConsole *cur = gd_vc_find_current(s);
2218b310a2a6SFam Zheng         if (cur) {
22196fa27697SGerd Hoffmann             int i;
22206fa27697SGerd Hoffmann 
22216fa27697SGerd Hoffmann             for (i = 0; i < s->nb_vcs; i++) {
22226fa27697SGerd Hoffmann                 VirtualConsole *vc = &s->vc[i];
22236fa27697SGerd Hoffmann                 if (vc && vc->type == GD_VC_VTE && vc != cur) {
22246fa27697SGerd Hoffmann                     gtk_widget_hide(vc->vte.terminal);
22256fa27697SGerd Hoffmann                 }
22266fa27697SGerd Hoffmann             }
22276fa27697SGerd Hoffmann             gd_update_windowsize(cur);
22286fa27697SGerd Hoffmann         }
2229b310a2a6SFam Zheng     }
22306fa27697SGerd Hoffmann #endif
22316fa27697SGerd Hoffmann 
2232787ba4f0SPeter Wu     if (full_screen) {
2233787ba4f0SPeter Wu         gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
2234787ba4f0SPeter Wu     }
2235881249c7SJan Kiszka     if (grab_on_hover) {
2236881249c7SJan Kiszka         gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
2237881249c7SJan Kiszka     }
2238787ba4f0SPeter Wu 
22393158a348SBruce Rogers     gd_set_keycode_type(s);
2240a4ccabcfSAnthony Liguori }
2241ee5f31e4SGerd Hoffmann 
224297edf3bdSGerd Hoffmann void early_gtk_display_init(int opengl)
2243ee5f31e4SGerd Hoffmann {
22442cb5d2a4SAlberto Garcia     /* The QEMU code relies on the assumption that it's always run in
22452cb5d2a4SAlberto Garcia      * the C locale. Therefore it is not prepared to deal with
22462cb5d2a4SAlberto Garcia      * operations that produce different results depending on the
22472cb5d2a4SAlberto Garcia      * locale, such as printf's formatting of decimal numbers, and
22482cb5d2a4SAlberto Garcia      * possibly others.
22492cb5d2a4SAlberto Garcia      *
22502cb5d2a4SAlberto Garcia      * Since GTK+ calls setlocale() by default -importing the locale
22512cb5d2a4SAlberto Garcia      * settings from the environment- we must prevent it from doing so
22522cb5d2a4SAlberto Garcia      * using gtk_disable_setlocale().
22532cb5d2a4SAlberto Garcia      *
22542cb5d2a4SAlberto Garcia      * QEMU's GTK+ UI, however, _does_ have translations for some of
22552cb5d2a4SAlberto Garcia      * the menu items. As a trade-off between a functionally correct
22562cb5d2a4SAlberto Garcia      * QEMU and a fully internationalized UI we support importing
22572cb5d2a4SAlberto Garcia      * LC_MESSAGES from the environment (see the setlocale() call
22582cb5d2a4SAlberto Garcia      * earlier in this file). This allows us to display translated
22592cb5d2a4SAlberto Garcia      * messages leaving everything else untouched.
22602cb5d2a4SAlberto Garcia      */
22612cb5d2a4SAlberto Garcia     gtk_disable_setlocale();
2262060ab763SGerd Hoffmann     gtkinit = gtk_init_check(NULL, NULL);
2263060ab763SGerd Hoffmann     if (!gtkinit) {
2264060ab763SGerd Hoffmann         /* don't exit yet, that'll break -help */
2265060ab763SGerd Hoffmann         return;
2266060ab763SGerd Hoffmann     }
226797edf3bdSGerd Hoffmann 
226897edf3bdSGerd Hoffmann     switch (opengl) {
226997edf3bdSGerd Hoffmann     case -1: /* default */
227097edf3bdSGerd Hoffmann     case 0:  /* off */
227197edf3bdSGerd Hoffmann         break;
227297edf3bdSGerd Hoffmann     case 1: /* on */
227397edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL)
2274925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL)
2275925a0400SGerd Hoffmann         gtk_gl_area_init();
2276925a0400SGerd Hoffmann #else
227797edf3bdSGerd Hoffmann         gtk_egl_init();
227897edf3bdSGerd Hoffmann #endif
2279925a0400SGerd Hoffmann #endif
228097edf3bdSGerd Hoffmann         break;
228197edf3bdSGerd Hoffmann     default:
228297edf3bdSGerd Hoffmann         g_assert_not_reached();
228397edf3bdSGerd Hoffmann         break;
228497edf3bdSGerd Hoffmann     }
228597edf3bdSGerd Hoffmann 
2286ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
2287ee5f31e4SGerd Hoffmann     register_vc_handler(gd_vc_handler);
2288ee5f31e4SGerd Hoffmann #endif
2289ee5f31e4SGerd Hoffmann }
2290