xref: /qemu/ui/gtk.c (revision 0a337ed0)
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 
372777ccc5SStefan Weil #ifdef _WIN32
382777ccc5SStefan Weil # define _WIN32_WINNT 0x0601 /* needed to get definition of MAPVK_VK_TO_VSC */
392777ccc5SStefan Weil #endif
402777ccc5SStefan Weil 
41c95e3080SKevin Wolf #include "qemu-common.h"
42c95e3080SKevin Wolf 
43c95e3080SKevin Wolf #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
44c95e3080SKevin Wolf /* Work around an -Wstrict-prototypes warning in GTK headers */
45e6f53fd5SMarkus Armbruster #pragma GCC diagnostic push
46c95e3080SKevin Wolf #pragma GCC diagnostic ignored "-Wstrict-prototypes"
47c95e3080SKevin Wolf #endif
48a4ccabcfSAnthony Liguori #include <gtk/gtk.h>
49c95e3080SKevin Wolf #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
50e6f53fd5SMarkus Armbruster #pragma GCC diagnostic pop
51c95e3080SKevin Wolf #endif
52c95e3080SKevin Wolf 
53c95e3080SKevin Wolf 
54a4ccabcfSAnthony Liguori #include <gdk/gdkkeysyms.h>
55834574eaSAnthony Liguori #include <glib/gi18n.h>
563f58eadeSStefan Weil #include <locale.h>
57bbbf9bfbSStefan Weil #if defined(CONFIG_VTE)
58a4ccabcfSAnthony Liguori #include <vte/vte.h>
59bbbf9bfbSStefan Weil #endif
60a4ccabcfSAnthony Liguori #include <math.h>
61a4ccabcfSAnthony Liguori 
62ef0dd982SStefan Weil #include "trace.h"
63a4ccabcfSAnthony Liguori #include "ui/console.h"
64af98ba92SGerd Hoffmann #include "ui/input.h"
65a4ccabcfSAnthony Liguori #include "sysemu/sysemu.h"
66a4ccabcfSAnthony Liguori #include "qmp-commands.h"
67a4ccabcfSAnthony Liguori #include "x_keymap.h"
68a4ccabcfSAnthony Liguori #include "keymaps.h"
69dccfcd0eSPaolo Bonzini #include "sysemu/char.h"
706a24ced5SGerd Hoffmann #include "qom/object.h"
71*0a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_X11
723158a348SBruce Rogers #include <gdk/gdkx.h>
733158a348SBruce Rogers #include <X11/XKBlib.h>
743158a348SBruce Rogers #endif
75a4ccabcfSAnthony Liguori 
76d861def3SAnthony Liguori #define MAX_VCS 10
7782fc1809SGerd Hoffmann #define VC_WINDOW_X_MIN  320
7882fc1809SGerd Hoffmann #define VC_WINDOW_Y_MIN  240
7982fc1809SGerd Hoffmann #define VC_TERM_X_MIN     80
8082fc1809SGerd Hoffmann #define VC_TERM_Y_MIN     25
8182fc1809SGerd Hoffmann #define VC_SCALE_MIN    0.25
8282fc1809SGerd Hoffmann #define VC_SCALE_STEP   0.25
83d861def3SAnthony Liguori 
84bbbf9bfbSStefan Weil #if !defined(CONFIG_VTE)
85bbbf9bfbSStefan Weil # define VTE_CHECK_VERSION(a, b, c) 0
86bbbf9bfbSStefan Weil #endif
87cba68834SDaniel P. Berrange 
886fa27697SGerd Hoffmann #if defined(CONFIG_VTE) && !GTK_CHECK_VERSION(3, 0, 0)
896fa27697SGerd Hoffmann /*
906fa27697SGerd Hoffmann  * The gtk2 vte terminal widget seriously messes up the window resize
916fa27697SGerd Hoffmann  * for some reason.  You basically can't make the qemu window smaller
926fa27697SGerd Hoffmann  * any more because the toplevel window geoemtry hints are overridden.
936fa27697SGerd Hoffmann  *
946fa27697SGerd Hoffmann  * Workaround that by hiding all vte widgets, except the one in the
956fa27697SGerd Hoffmann  * current tab.
966fa27697SGerd Hoffmann  *
976fa27697SGerd Hoffmann  * Luckily everything works smooth in gtk3.
986fa27697SGerd Hoffmann  */
996fa27697SGerd Hoffmann # define VTE_RESIZE_HACK 1
1006fa27697SGerd Hoffmann #endif
1016fa27697SGerd Hoffmann 
102cba68834SDaniel P. Berrange /* Compatibility define to let us build on both Gtk2 and Gtk3 */
103cba68834SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
104cba68834SDaniel P. Berrange static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh)
105cba68834SDaniel P. Berrange {
106cba68834SDaniel P. Berrange     *ww = gdk_window_get_width(w);
107cba68834SDaniel P. Berrange     *wh = gdk_window_get_height(w);
108cba68834SDaniel P. Berrange }
109cba68834SDaniel P. Berrange #endif
110cba68834SDaniel P. Berrange 
111ef6413a2SDaniel P. Berrange #if !GTK_CHECK_VERSION(2, 20, 0)
112ef6413a2SDaniel P. Berrange #define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget)
113ef6413a2SDaniel P. Berrange #endif
114ef6413a2SDaniel P. Berrange 
115*0a337ed0SGerd Hoffmann #ifndef GDK_IS_X11_DISPLAY
116*0a337ed0SGerd Hoffmann #define GDK_IS_X11_DISPLAY(dpy) (dpy == dpy)
117*0a337ed0SGerd Hoffmann #endif
118*0a337ed0SGerd Hoffmann #ifndef GDK_IS_WIN32_DISPLAY
119*0a337ed0SGerd Hoffmann #define GDK_IS_WIN32_DISPLAY(dpy) (dpy == dpy)
120*0a337ed0SGerd Hoffmann #endif
121*0a337ed0SGerd Hoffmann 
122bc0477c7SDaniel P. Berrange #ifndef GDK_KEY_0
123bc0477c7SDaniel P. Berrange #define GDK_KEY_0 GDK_0
124bc0477c7SDaniel P. Berrange #define GDK_KEY_1 GDK_1
125bc0477c7SDaniel P. Berrange #define GDK_KEY_2 GDK_2
126bc0477c7SDaniel P. Berrange #define GDK_KEY_f GDK_f
127bc0477c7SDaniel P. Berrange #define GDK_KEY_g GDK_g
128db1da1f2SCole Robinson #define GDK_KEY_q GDK_q
129bc0477c7SDaniel P. Berrange #define GDK_KEY_plus GDK_plus
130bc0477c7SDaniel P. Berrange #define GDK_KEY_minus GDK_minus
131bc0477c7SDaniel P. Berrange #endif
132ef6413a2SDaniel P. Berrange 
133b1e749c0SJan Kiszka #define HOTKEY_MODIFIERS        (GDK_CONTROL_MASK | GDK_MOD1_MASK)
134b1e749c0SJan Kiszka 
1356db253caSJan Kiszka static const int modifier_keycode[] = {
1366db253caSJan Kiszka     /* shift, control, alt keys, meta keys, both left & right */
1376db253caSJan Kiszka     0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd,
1386db253caSJan Kiszka };
1396db253caSJan Kiszka 
140e3500d1fSGerd Hoffmann typedef struct GtkDisplayState GtkDisplayState;
141e3500d1fSGerd Hoffmann 
142e3500d1fSGerd Hoffmann typedef struct VirtualGfxConsole {
143e3500d1fSGerd Hoffmann     GtkWidget *drawing_area;
144e3500d1fSGerd Hoffmann     DisplayChangeListener dcl;
145e3500d1fSGerd Hoffmann     DisplaySurface *ds;
146e3500d1fSGerd Hoffmann     pixman_image_t *convert;
147e3500d1fSGerd Hoffmann     cairo_surface_t *surface;
148e3500d1fSGerd Hoffmann     double scale_x;
149e3500d1fSGerd Hoffmann     double scale_y;
150e3500d1fSGerd Hoffmann } VirtualGfxConsole;
151e3500d1fSGerd Hoffmann 
152bbbf9bfbSStefan Weil #if defined(CONFIG_VTE)
153271a25c0SGerd Hoffmann typedef struct VirtualVteConsole {
1540fb20d1cSCole Robinson     GtkWidget *box;
1550fb20d1cSCole Robinson     GtkWidget *scrollbar;
156ee5f31e4SGerd Hoffmann     GtkWidget *terminal;
157a4ccabcfSAnthony Liguori     CharDriverState *chr;
158271a25c0SGerd Hoffmann } VirtualVteConsole;
159bbbf9bfbSStefan Weil #endif
160271a25c0SGerd Hoffmann 
161e3500d1fSGerd Hoffmann typedef enum VirtualConsoleType {
162e3500d1fSGerd Hoffmann     GD_VC_GFX,
163e3500d1fSGerd Hoffmann     GD_VC_VTE,
164e3500d1fSGerd Hoffmann } VirtualConsoleType;
165e3500d1fSGerd Hoffmann 
166271a25c0SGerd Hoffmann typedef struct VirtualConsole {
167e3500d1fSGerd Hoffmann     GtkDisplayState *s;
168cdeb7090SGerd Hoffmann     char *label;
169cdeb7090SGerd Hoffmann     GtkWidget *window;
170271a25c0SGerd Hoffmann     GtkWidget *menu_item;
171271a25c0SGerd Hoffmann     GtkWidget *tab_item;
172e3500d1fSGerd Hoffmann     VirtualConsoleType type;
173271a25c0SGerd Hoffmann     union {
174e3500d1fSGerd Hoffmann         VirtualGfxConsole gfx;
175271a25c0SGerd Hoffmann #if defined(CONFIG_VTE)
176271a25c0SGerd Hoffmann         VirtualVteConsole vte;
177271a25c0SGerd Hoffmann #endif
178271a25c0SGerd Hoffmann     };
179a4ccabcfSAnthony Liguori } VirtualConsole;
180a4ccabcfSAnthony Liguori 
181e3500d1fSGerd Hoffmann struct GtkDisplayState {
182a4ccabcfSAnthony Liguori     GtkWidget *window;
183a4ccabcfSAnthony Liguori 
184a4ccabcfSAnthony Liguori     GtkWidget *menu_bar;
185a4ccabcfSAnthony Liguori 
18673d4dc71SAnthony Liguori     GtkAccelGroup *accel_group;
18773d4dc71SAnthony Liguori 
18830e8f22bSJan Kiszka     GtkWidget *machine_menu_item;
18930e8f22bSJan Kiszka     GtkWidget *machine_menu;
19030e8f22bSJan Kiszka     GtkWidget *pause_item;
19130e8f22bSJan Kiszka     GtkWidget *reset_item;
19230e8f22bSJan Kiszka     GtkWidget *powerdown_item;
193a4ccabcfSAnthony Liguori     GtkWidget *quit_item;
194a4ccabcfSAnthony Liguori 
195a4ccabcfSAnthony Liguori     GtkWidget *view_menu_item;
196a4ccabcfSAnthony Liguori     GtkWidget *view_menu;
197c6158483SAnthony Liguori     GtkWidget *full_screen_item;
198c6158483SAnthony Liguori     GtkWidget *zoom_in_item;
199c6158483SAnthony Liguori     GtkWidget *zoom_out_item;
200c6158483SAnthony Liguori     GtkWidget *zoom_fixed_item;
201c6158483SAnthony Liguori     GtkWidget *zoom_fit_item;
2025104a1f6SAnthony Liguori     GtkWidget *grab_item;
2035104a1f6SAnthony Liguori     GtkWidget *grab_on_hover_item;
204a4ccabcfSAnthony Liguori 
205d861def3SAnthony Liguori     int nb_vcs;
206d861def3SAnthony Liguori     VirtualConsole vc[MAX_VCS];
207d861def3SAnthony Liguori 
208a4ccabcfSAnthony Liguori     GtkWidget *show_tabs_item;
209cdeb7090SGerd Hoffmann     GtkWidget *untabify_item;
210a4ccabcfSAnthony Liguori 
211a4ccabcfSAnthony Liguori     GtkWidget *vbox;
212a4ccabcfSAnthony Liguori     GtkWidget *notebook;
213a4ccabcfSAnthony Liguori     int button_mask;
214e61031cdSTakashi Iwai     gboolean last_set;
215a4ccabcfSAnthony Liguori     int last_x;
216a4ccabcfSAnthony Liguori     int last_y;
217ecce1929STakashi Iwai     int grab_x_root;
218ecce1929STakashi Iwai     int grab_y_root;
2194c638e2eSGerd Hoffmann     VirtualConsole *kbd_owner;
2204c638e2eSGerd Hoffmann     VirtualConsole *ptr_owner;
221a4ccabcfSAnthony Liguori 
222c6158483SAnthony Liguori     gboolean full_screen;
223a4ccabcfSAnthony Liguori 
224a4ccabcfSAnthony Liguori     GdkCursor *null_cursor;
225a4ccabcfSAnthony Liguori     Notifier mouse_mode_notifier;
226c6158483SAnthony Liguori     gboolean free_scale;
22730e8f22bSJan Kiszka 
22830e8f22bSJan Kiszka     bool external_pause_update;
2296db253caSJan Kiszka 
2306db253caSJan Kiszka     bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
2313158a348SBruce Rogers     bool has_evdev;
232e3500d1fSGerd Hoffmann };
233a4ccabcfSAnthony Liguori 
2342884cf5bSGerd Hoffmann static void gd_grab_pointer(VirtualConsole *vc);
2352884cf5bSGerd Hoffmann static void gd_ungrab_pointer(GtkDisplayState *s);
2362884cf5bSGerd Hoffmann 
237a4ccabcfSAnthony Liguori /** Utility Functions **/
238a4ccabcfSAnthony Liguori 
239271a25c0SGerd Hoffmann static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s)
240271a25c0SGerd Hoffmann {
241271a25c0SGerd Hoffmann     VirtualConsole *vc;
242271a25c0SGerd Hoffmann     gint i;
243271a25c0SGerd Hoffmann 
244271a25c0SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
245271a25c0SGerd Hoffmann         vc = &s->vc[i];
246271a25c0SGerd Hoffmann         if (gtk_check_menu_item_get_active
247271a25c0SGerd Hoffmann             (GTK_CHECK_MENU_ITEM(vc->menu_item))) {
248271a25c0SGerd Hoffmann             return vc;
249271a25c0SGerd Hoffmann         }
250271a25c0SGerd Hoffmann     }
251271a25c0SGerd Hoffmann     return NULL;
252271a25c0SGerd Hoffmann }
253271a25c0SGerd Hoffmann 
254271a25c0SGerd Hoffmann static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page)
255271a25c0SGerd Hoffmann {
256271a25c0SGerd Hoffmann     VirtualConsole *vc;
257271a25c0SGerd Hoffmann     gint i, p;
258271a25c0SGerd Hoffmann 
259271a25c0SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
260271a25c0SGerd Hoffmann         vc = &s->vc[i];
261271a25c0SGerd Hoffmann         p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item);
262271a25c0SGerd Hoffmann         if (p == page) {
263271a25c0SGerd Hoffmann             return vc;
264271a25c0SGerd Hoffmann         }
265271a25c0SGerd Hoffmann     }
266271a25c0SGerd Hoffmann     return NULL;
267271a25c0SGerd Hoffmann }
268271a25c0SGerd Hoffmann 
269e3500d1fSGerd Hoffmann static VirtualConsole *gd_vc_find_current(GtkDisplayState *s)
270e3500d1fSGerd Hoffmann {
271e3500d1fSGerd Hoffmann     gint page;
272e3500d1fSGerd Hoffmann 
273e3500d1fSGerd Hoffmann     page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook));
274e3500d1fSGerd Hoffmann     return gd_vc_find_by_page(s, page);
275e3500d1fSGerd Hoffmann }
276e3500d1fSGerd Hoffmann 
2775104a1f6SAnthony Liguori static bool gd_is_grab_active(GtkDisplayState *s)
2785104a1f6SAnthony Liguori {
2795104a1f6SAnthony Liguori     return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
2805104a1f6SAnthony Liguori }
2815104a1f6SAnthony Liguori 
2825104a1f6SAnthony Liguori static bool gd_grab_on_hover(GtkDisplayState *s)
2835104a1f6SAnthony Liguori {
2845104a1f6SAnthony Liguori     return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
2855104a1f6SAnthony Liguori }
2865104a1f6SAnthony Liguori 
287e3500d1fSGerd Hoffmann static void gd_update_cursor(VirtualConsole *vc)
2885104a1f6SAnthony Liguori {
289e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
290e3500d1fSGerd Hoffmann     GdkWindow *window;
291832189c9SGerd Hoffmann 
292e3500d1fSGerd Hoffmann     if (vc->type != GD_VC_GFX) {
293e3500d1fSGerd Hoffmann         return;
2945104a1f6SAnthony Liguori     }
2955104a1f6SAnthony Liguori 
296e3500d1fSGerd Hoffmann     window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area));
2972884cf5bSGerd Hoffmann     if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) {
298a4ccabcfSAnthony Liguori         gdk_window_set_cursor(window, s->null_cursor);
299a4ccabcfSAnthony Liguori     } else {
300a4ccabcfSAnthony Liguori         gdk_window_set_cursor(window, NULL);
301a4ccabcfSAnthony Liguori     }
302a4ccabcfSAnthony Liguori }
303a4ccabcfSAnthony Liguori 
304a4ccabcfSAnthony Liguori static void gd_update_caption(GtkDisplayState *s)
305a4ccabcfSAnthony Liguori {
306a4ccabcfSAnthony Liguori     const char *status = "";
3074eeaa3a8SGerd Hoffmann     gchar *prefix;
308a4ccabcfSAnthony Liguori     gchar *title;
3095104a1f6SAnthony Liguori     const char *grab = "";
31030e8f22bSJan Kiszka     bool is_paused = !runstate_is_running();
3114eeaa3a8SGerd Hoffmann     int i;
3125104a1f6SAnthony Liguori 
3134eeaa3a8SGerd Hoffmann     if (qemu_name) {
3144eeaa3a8SGerd Hoffmann         prefix = g_strdup_printf("QEMU (%s)", qemu_name);
3154eeaa3a8SGerd Hoffmann     } else {
3164eeaa3a8SGerd Hoffmann         prefix = g_strdup_printf("QEMU");
3174eeaa3a8SGerd Hoffmann     }
3184eeaa3a8SGerd Hoffmann 
3194eeaa3a8SGerd Hoffmann     if (s->ptr_owner != NULL &&
3204eeaa3a8SGerd Hoffmann         s->ptr_owner->window == NULL) {
321d8da9ee8SAurelien Jarno         grab = _(" - Press Ctrl+Alt+G to release grab");
3225104a1f6SAnthony Liguori     }
323a4ccabcfSAnthony Liguori 
32430e8f22bSJan Kiszka     if (is_paused) {
325d8da9ee8SAurelien Jarno         status = _(" [Paused]");
326a4ccabcfSAnthony Liguori     }
32730e8f22bSJan Kiszka     s->external_pause_update = true;
32830e8f22bSJan Kiszka     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
32930e8f22bSJan Kiszka                                    is_paused);
33030e8f22bSJan Kiszka     s->external_pause_update = false;
331a4ccabcfSAnthony Liguori 
3324eeaa3a8SGerd Hoffmann     title = g_strdup_printf("%s%s%s", prefix, status, grab);
3334eeaa3a8SGerd Hoffmann     gtk_window_set_title(GTK_WINDOW(s->window), title);
3344eeaa3a8SGerd Hoffmann     g_free(title);
3354eeaa3a8SGerd Hoffmann 
3364eeaa3a8SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
3374eeaa3a8SGerd Hoffmann         VirtualConsole *vc = &s->vc[i];
3384eeaa3a8SGerd Hoffmann 
3394eeaa3a8SGerd Hoffmann         if (!vc->window) {
3404eeaa3a8SGerd Hoffmann             continue;
3414eeaa3a8SGerd Hoffmann         }
3424eeaa3a8SGerd Hoffmann         title = g_strdup_printf("%s: %s%s%s", prefix, vc->label,
3434eeaa3a8SGerd Hoffmann                                 vc == s->kbd_owner ? " +kbd" : "",
3444eeaa3a8SGerd Hoffmann                                 vc == s->ptr_owner ? " +ptr" : "");
3454eeaa3a8SGerd Hoffmann         gtk_window_set_title(GTK_WINDOW(vc->window), title);
3464eeaa3a8SGerd Hoffmann         g_free(title);
347a4ccabcfSAnthony Liguori     }
348a4ccabcfSAnthony Liguori 
3494eeaa3a8SGerd Hoffmann     g_free(prefix);
350a4ccabcfSAnthony Liguori }
351a4ccabcfSAnthony Liguori 
35282fc1809SGerd Hoffmann static void gd_update_geometry_hints(VirtualConsole *vc)
35382fc1809SGerd Hoffmann {
35482fc1809SGerd Hoffmann     GtkDisplayState *s = vc->s;
35582fc1809SGerd Hoffmann     GdkWindowHints mask = 0;
35682fc1809SGerd Hoffmann     GdkGeometry geo = {};
35782fc1809SGerd Hoffmann     GtkWidget *geo_widget = NULL;
35882fc1809SGerd Hoffmann     GtkWindow *geo_window;
35982fc1809SGerd Hoffmann 
36082fc1809SGerd Hoffmann     if (vc->type == GD_VC_GFX) {
36182fc1809SGerd Hoffmann         if (s->free_scale) {
36282fc1809SGerd Hoffmann             geo.min_width  = surface_width(vc->gfx.ds) * VC_SCALE_MIN;
36382fc1809SGerd Hoffmann             geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN;
36482fc1809SGerd Hoffmann             mask |= GDK_HINT_MIN_SIZE;
36582fc1809SGerd Hoffmann         } else {
36682fc1809SGerd Hoffmann             geo.min_width  = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
36782fc1809SGerd Hoffmann             geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
36882fc1809SGerd Hoffmann             mask |= GDK_HINT_MIN_SIZE;
36982fc1809SGerd Hoffmann         }
37082fc1809SGerd Hoffmann         geo_widget = vc->gfx.drawing_area;
37182fc1809SGerd Hoffmann         gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height);
37282fc1809SGerd Hoffmann 
37382fc1809SGerd Hoffmann #if defined(CONFIG_VTE)
37482fc1809SGerd Hoffmann     } else if (vc->type == GD_VC_VTE) {
37582fc1809SGerd Hoffmann         VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
37682fc1809SGerd Hoffmann         GtkBorder *ib;
37782fc1809SGerd Hoffmann 
37882fc1809SGerd Hoffmann         geo.width_inc  = vte_terminal_get_char_width(term);
37982fc1809SGerd Hoffmann         geo.height_inc = vte_terminal_get_char_height(term);
38082fc1809SGerd Hoffmann         mask |= GDK_HINT_RESIZE_INC;
38182fc1809SGerd Hoffmann         geo.base_width  = geo.width_inc;
38282fc1809SGerd Hoffmann         geo.base_height = geo.height_inc;
38382fc1809SGerd Hoffmann         mask |= GDK_HINT_BASE_SIZE;
38482fc1809SGerd Hoffmann         geo.min_width  = geo.width_inc * VC_TERM_X_MIN;
38582fc1809SGerd Hoffmann         geo.min_height = geo.height_inc * VC_TERM_Y_MIN;
38682fc1809SGerd Hoffmann         mask |= GDK_HINT_MIN_SIZE;
38782fc1809SGerd Hoffmann         gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL);
38882fc1809SGerd Hoffmann         geo.base_width  += ib->left + ib->right;
38982fc1809SGerd Hoffmann         geo.base_height += ib->top + ib->bottom;
39082fc1809SGerd Hoffmann         geo.min_width   += ib->left + ib->right;
39182fc1809SGerd Hoffmann         geo.min_height  += ib->top + ib->bottom;
39282fc1809SGerd Hoffmann         geo_widget = vc->vte.terminal;
39382fc1809SGerd Hoffmann #endif
39482fc1809SGerd Hoffmann     }
39582fc1809SGerd Hoffmann 
39682fc1809SGerd Hoffmann     geo_window = GTK_WINDOW(vc->window ? vc->window : s->window);
39782fc1809SGerd Hoffmann     gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
39882fc1809SGerd Hoffmann }
39982fc1809SGerd Hoffmann 
400e3500d1fSGerd Hoffmann static void gd_update_windowsize(VirtualConsole *vc)
401a4ccabcfSAnthony Liguori {
402e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
403c6158483SAnthony Liguori 
40482fc1809SGerd Hoffmann     gd_update_geometry_hints(vc);
405c6158483SAnthony Liguori 
40682fc1809SGerd Hoffmann     if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) {
40782fc1809SGerd Hoffmann         gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window),
40882fc1809SGerd Hoffmann                           VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN);
409a4ccabcfSAnthony Liguori     }
410aa0a55d4SGerd Hoffmann }
411a4ccabcfSAnthony Liguori 
412e3500d1fSGerd Hoffmann static void gd_update_full_redraw(VirtualConsole *vc)
4139d9801cfSGerd Hoffmann {
414e3500d1fSGerd Hoffmann     GtkWidget *area = vc->gfx.drawing_area;
4159d9801cfSGerd Hoffmann     int ww, wh;
416e3500d1fSGerd Hoffmann     gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
417e3500d1fSGerd Hoffmann     gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
4189d9801cfSGerd Hoffmann }
4199d9801cfSGerd Hoffmann 
4206db253caSJan Kiszka static void gtk_release_modifiers(GtkDisplayState *s)
4216db253caSJan Kiszka {
422e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
4236db253caSJan Kiszka     int i, keycode;
4246db253caSJan Kiszka 
425e3500d1fSGerd Hoffmann     if (vc->type != GD_VC_GFX) {
4266db253caSJan Kiszka         return;
4276db253caSJan Kiszka     }
4286db253caSJan Kiszka     for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
4296db253caSJan Kiszka         keycode = modifier_keycode[i];
4306db253caSJan Kiszka         if (!s->modifier_pressed[i]) {
4316db253caSJan Kiszka             continue;
4326db253caSJan Kiszka         }
433e3500d1fSGerd Hoffmann         qemu_input_event_send_key_number(vc->gfx.dcl.con, keycode, false);
4346db253caSJan Kiszka         s->modifier_pressed[i] = false;
4356db253caSJan Kiszka     }
4366db253caSJan Kiszka }
4376db253caSJan Kiszka 
4389d9801cfSGerd Hoffmann /** DisplayState Callbacks **/
4399d9801cfSGerd Hoffmann 
4409d9801cfSGerd Hoffmann static void gd_update(DisplayChangeListener *dcl,
441bc2ed970SGerd Hoffmann                       int x, int y, int w, int h)
4429d9801cfSGerd Hoffmann {
443e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
4449d9801cfSGerd Hoffmann     int x1, x2, y1, y2;
4459d9801cfSGerd Hoffmann     int mx, my;
4469d9801cfSGerd Hoffmann     int fbw, fbh;
4479d9801cfSGerd Hoffmann     int ww, wh;
4489d9801cfSGerd Hoffmann 
44974444bc1SGerd Hoffmann     trace_gd_update(vc->label, x, y, w, h);
4509d9801cfSGerd Hoffmann 
451e3500d1fSGerd Hoffmann     if (vc->gfx.convert) {
452e3500d1fSGerd Hoffmann         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
453e3500d1fSGerd Hoffmann                                NULL, vc->gfx.convert,
454f0875536SGerd Hoffmann                                x, y, 0, 0, x, y, w, h);
455f0875536SGerd Hoffmann     }
456f0875536SGerd Hoffmann 
457e3500d1fSGerd Hoffmann     x1 = floor(x * vc->gfx.scale_x);
458e3500d1fSGerd Hoffmann     y1 = floor(y * vc->gfx.scale_y);
4599d9801cfSGerd Hoffmann 
460e3500d1fSGerd Hoffmann     x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
461e3500d1fSGerd Hoffmann     y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
4629d9801cfSGerd Hoffmann 
463e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
464e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
4659d9801cfSGerd Hoffmann 
466e3500d1fSGerd Hoffmann     gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area),
467e3500d1fSGerd Hoffmann                           &ww, &wh);
4689d9801cfSGerd Hoffmann 
4699d9801cfSGerd Hoffmann     mx = my = 0;
4709d9801cfSGerd Hoffmann     if (ww > fbw) {
4719d9801cfSGerd Hoffmann         mx = (ww - fbw) / 2;
4729d9801cfSGerd Hoffmann     }
4739d9801cfSGerd Hoffmann     if (wh > fbh) {
4749d9801cfSGerd Hoffmann         my = (wh - fbh) / 2;
4759d9801cfSGerd Hoffmann     }
4769d9801cfSGerd Hoffmann 
477e3500d1fSGerd Hoffmann     gtk_widget_queue_draw_area(vc->gfx.drawing_area,
478e3500d1fSGerd Hoffmann                                mx + x1, my + y1, (x2 - x1), (y2 - y1));
4799d9801cfSGerd Hoffmann }
4809d9801cfSGerd Hoffmann 
481bc2ed970SGerd Hoffmann static void gd_refresh(DisplayChangeListener *dcl)
4829d9801cfSGerd Hoffmann {
483284d1c6bSGerd Hoffmann     graphic_hw_update(dcl->con);
4849d9801cfSGerd Hoffmann }
4859d9801cfSGerd Hoffmann 
486b087143bSIgor Mitsyanko #if GTK_CHECK_VERSION(3, 0, 0)
487b087143bSIgor Mitsyanko static void gd_mouse_set(DisplayChangeListener *dcl,
488b087143bSIgor Mitsyanko                          int x, int y, int visible)
489b087143bSIgor Mitsyanko {
490e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
491b087143bSIgor Mitsyanko     GdkDisplay *dpy;
492b087143bSIgor Mitsyanko     GdkDeviceManager *mgr;
493b087143bSIgor Mitsyanko     gint x_root, y_root;
494b087143bSIgor Mitsyanko 
4952bda6602SCole Robinson     if (qemu_input_is_absolute()) {
4962bda6602SCole Robinson         return;
4972bda6602SCole Robinson     }
4982bda6602SCole Robinson 
499e3500d1fSGerd Hoffmann     dpy = gtk_widget_get_display(vc->gfx.drawing_area);
500b087143bSIgor Mitsyanko     mgr = gdk_display_get_device_manager(dpy);
501e3500d1fSGerd Hoffmann     gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
502b087143bSIgor Mitsyanko                                x, y, &x_root, &y_root);
503b087143bSIgor Mitsyanko     gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
504e3500d1fSGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
505298526feSCole Robinson                     x_root, y_root);
506b087143bSIgor Mitsyanko }
507b087143bSIgor Mitsyanko #else
5089697f5d2SGerd Hoffmann static void gd_mouse_set(DisplayChangeListener *dcl,
5099697f5d2SGerd Hoffmann                          int x, int y, int visible)
5109697f5d2SGerd Hoffmann {
511e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
5129697f5d2SGerd Hoffmann     gint x_root, y_root;
5139697f5d2SGerd Hoffmann 
5142bda6602SCole Robinson     if (qemu_input_is_absolute()) {
5152bda6602SCole Robinson         return;
5162bda6602SCole Robinson     }
5172bda6602SCole Robinson 
518e3500d1fSGerd Hoffmann     gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
5199697f5d2SGerd Hoffmann                                x, y, &x_root, &y_root);
520e3500d1fSGerd Hoffmann     gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area),
521e3500d1fSGerd Hoffmann                              gtk_widget_get_screen(vc->gfx.drawing_area),
5229697f5d2SGerd Hoffmann                              x_root, y_root);
5239697f5d2SGerd Hoffmann }
524b087143bSIgor Mitsyanko #endif
5259697f5d2SGerd Hoffmann 
5269697f5d2SGerd Hoffmann static void gd_cursor_define(DisplayChangeListener *dcl,
5279697f5d2SGerd Hoffmann                              QEMUCursor *c)
5289697f5d2SGerd Hoffmann {
529e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
5309697f5d2SGerd Hoffmann     GdkPixbuf *pixbuf;
5319697f5d2SGerd Hoffmann     GdkCursor *cursor;
5329697f5d2SGerd Hoffmann 
5339697f5d2SGerd Hoffmann     pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
5349697f5d2SGerd Hoffmann                                       GDK_COLORSPACE_RGB, true, 8,
5359697f5d2SGerd Hoffmann                                       c->width, c->height, c->width * 4,
5369697f5d2SGerd Hoffmann                                       NULL, NULL);
537e3500d1fSGerd Hoffmann     cursor = gdk_cursor_new_from_pixbuf
538e3500d1fSGerd Hoffmann         (gtk_widget_get_display(vc->gfx.drawing_area),
5399697f5d2SGerd Hoffmann          pixbuf, c->hot_x, c->hot_y);
540e3500d1fSGerd Hoffmann     gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor);
5419697f5d2SGerd Hoffmann     g_object_unref(pixbuf);
542030b4b7dSStefan Weil #if !GTK_CHECK_VERSION(3, 0, 0)
54317139240SAnthony Liguori     gdk_cursor_unref(cursor);
544030b4b7dSStefan Weil #else
545030b4b7dSStefan Weil     g_object_unref(cursor);
546030b4b7dSStefan Weil #endif
5479697f5d2SGerd Hoffmann }
5489697f5d2SGerd Hoffmann 
5499d9801cfSGerd Hoffmann static void gd_switch(DisplayChangeListener *dcl,
5509d9801cfSGerd Hoffmann                       DisplaySurface *surface)
5519d9801cfSGerd Hoffmann {
552e3500d1fSGerd Hoffmann     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
5539d9801cfSGerd Hoffmann     bool resized = true;
5549d9801cfSGerd Hoffmann 
55574444bc1SGerd Hoffmann     trace_gd_switch(vc->label, surface_width(surface), surface_height(surface));
5569d9801cfSGerd Hoffmann 
557e3500d1fSGerd Hoffmann     if (vc->gfx.surface) {
558e3500d1fSGerd Hoffmann         cairo_surface_destroy(vc->gfx.surface);
5599d9801cfSGerd Hoffmann     }
5609d9801cfSGerd Hoffmann 
561e3500d1fSGerd Hoffmann     if (vc->gfx.ds &&
562e3500d1fSGerd Hoffmann         surface_width(vc->gfx.ds) == surface_width(surface) &&
563e3500d1fSGerd Hoffmann         surface_height(vc->gfx.ds) == surface_height(surface)) {
5649d9801cfSGerd Hoffmann         resized = false;
5659d9801cfSGerd Hoffmann     }
566e3500d1fSGerd Hoffmann     vc->gfx.ds = surface;
567f0875536SGerd Hoffmann 
568e3500d1fSGerd Hoffmann     if (vc->gfx.convert) {
569e3500d1fSGerd Hoffmann         pixman_image_unref(vc->gfx.convert);
570e3500d1fSGerd Hoffmann         vc->gfx.convert = NULL;
5719d9801cfSGerd Hoffmann     }
5729d9801cfSGerd Hoffmann 
573f0875536SGerd Hoffmann     if (surface->format == PIXMAN_x8r8g8b8) {
574f0875536SGerd Hoffmann         /*
575f0875536SGerd Hoffmann          * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24
576f0875536SGerd Hoffmann          *
577f0875536SGerd Hoffmann          * No need to convert, use surface directly.  Should be the
578f0875536SGerd Hoffmann          * common case as this is qemu_default_pixelformat(32) too.
579f0875536SGerd Hoffmann          */
580e3500d1fSGerd Hoffmann         vc->gfx.surface = cairo_image_surface_create_for_data
581f0875536SGerd Hoffmann             (surface_data(surface),
582f0875536SGerd Hoffmann              CAIRO_FORMAT_RGB24,
5839d9801cfSGerd Hoffmann              surface_width(surface),
5849d9801cfSGerd Hoffmann              surface_height(surface),
5859d9801cfSGerd Hoffmann              surface_stride(surface));
586f0875536SGerd Hoffmann     } else {
587f0875536SGerd Hoffmann         /* Must convert surface, use pixman to do it. */
588e3500d1fSGerd Hoffmann         vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8,
589f0875536SGerd Hoffmann                                                    surface_width(surface),
590f0875536SGerd Hoffmann                                                    surface_height(surface),
591f0875536SGerd Hoffmann                                                    NULL, 0);
592e3500d1fSGerd Hoffmann         vc->gfx.surface = cairo_image_surface_create_for_data
593e3500d1fSGerd Hoffmann             ((void *)pixman_image_get_data(vc->gfx.convert),
594f0875536SGerd Hoffmann              CAIRO_FORMAT_RGB24,
595e3500d1fSGerd Hoffmann              pixman_image_get_width(vc->gfx.convert),
596e3500d1fSGerd Hoffmann              pixman_image_get_height(vc->gfx.convert),
597e3500d1fSGerd Hoffmann              pixman_image_get_stride(vc->gfx.convert));
598e3500d1fSGerd Hoffmann         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
599e3500d1fSGerd Hoffmann                                NULL, vc->gfx.convert,
600f0875536SGerd Hoffmann                                0, 0, 0, 0, 0, 0,
601e3500d1fSGerd Hoffmann                                pixman_image_get_width(vc->gfx.convert),
602e3500d1fSGerd Hoffmann                                pixman_image_get_height(vc->gfx.convert));
603f0875536SGerd Hoffmann     }
6049d9801cfSGerd Hoffmann 
6059d9801cfSGerd Hoffmann     if (resized) {
606e3500d1fSGerd Hoffmann         gd_update_windowsize(vc);
6079d9801cfSGerd Hoffmann     } else {
608e3500d1fSGerd Hoffmann         gd_update_full_redraw(vc);
6099d9801cfSGerd Hoffmann     }
6109d9801cfSGerd Hoffmann }
6119d9801cfSGerd Hoffmann 
612a4ccabcfSAnthony Liguori /** QEMU Events **/
613a4ccabcfSAnthony Liguori 
614a4ccabcfSAnthony Liguori static void gd_change_runstate(void *opaque, int running, RunState state)
615a4ccabcfSAnthony Liguori {
616a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
617a4ccabcfSAnthony Liguori 
618a4ccabcfSAnthony Liguori     gd_update_caption(s);
619a4ccabcfSAnthony Liguori }
620a4ccabcfSAnthony Liguori 
621a4ccabcfSAnthony Liguori static void gd_mouse_mode_change(Notifier *notify, void *data)
622a4ccabcfSAnthony Liguori {
623800b0e81STakashi Iwai     GtkDisplayState *s;
62499623c90SGerd Hoffmann     int i;
625800b0e81STakashi Iwai 
626800b0e81STakashi Iwai     s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
627800b0e81STakashi Iwai     /* release the grab at switching to absolute mode */
628800b0e81STakashi Iwai     if (qemu_input_is_absolute() && gd_is_grab_active(s)) {
629800b0e81STakashi Iwai         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
630a4ccabcfSAnthony Liguori                                        FALSE);
631a4ccabcfSAnthony Liguori     }
63299623c90SGerd Hoffmann     for (i = 0; i < s->nb_vcs; i++) {
63399623c90SGerd Hoffmann         VirtualConsole *vc = &s->vc[i];
63499623c90SGerd Hoffmann         gd_update_cursor(vc);
63599623c90SGerd Hoffmann     }
636800b0e81STakashi Iwai }
637a4ccabcfSAnthony Liguori 
638a4ccabcfSAnthony Liguori /** GTK Events **/
639a4ccabcfSAnthony Liguori 
640a4ccabcfSAnthony Liguori static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
641a4ccabcfSAnthony Liguori                                 void *opaque)
642a4ccabcfSAnthony Liguori {
643a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
644e3500d1fSGerd Hoffmann     int i;
645a4ccabcfSAnthony Liguori 
646a4ccabcfSAnthony Liguori     if (!no_quit) {
647e3500d1fSGerd Hoffmann         for (i = 0; i < s->nb_vcs; i++) {
648e3500d1fSGerd Hoffmann             if (s->vc[i].type != GD_VC_GFX) {
649e3500d1fSGerd Hoffmann                 continue;
650e3500d1fSGerd Hoffmann             }
651e3500d1fSGerd Hoffmann             unregister_displaychangelistener(&s->vc[i].gfx.dcl);
652e3500d1fSGerd Hoffmann         }
653a4ccabcfSAnthony Liguori         qmp_quit(NULL);
654a4ccabcfSAnthony Liguori         return FALSE;
655a4ccabcfSAnthony Liguori     }
656a4ccabcfSAnthony Liguori 
657a4ccabcfSAnthony Liguori     return TRUE;
658a4ccabcfSAnthony Liguori }
659a4ccabcfSAnthony Liguori 
660a4ccabcfSAnthony Liguori static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
661a4ccabcfSAnthony Liguori {
662e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
663e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
664c6158483SAnthony Liguori     int mx, my;
665a4ccabcfSAnthony Liguori     int ww, wh;
666a4ccabcfSAnthony Liguori     int fbw, fbh;
667a4ccabcfSAnthony Liguori 
668c6158483SAnthony Liguori     if (!gtk_widget_get_realized(widget)) {
669c6158483SAnthony Liguori         return FALSE;
670c6158483SAnthony Liguori     }
671c6158483SAnthony Liguori 
672e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds);
673e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds);
674a4ccabcfSAnthony Liguori 
675a4ccabcfSAnthony Liguori     gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
676a4ccabcfSAnthony Liguori 
677c6158483SAnthony Liguori     if (s->full_screen) {
678e3500d1fSGerd Hoffmann         vc->gfx.scale_x = (double)ww / fbw;
679e3500d1fSGerd Hoffmann         vc->gfx.scale_y = (double)wh / fbh;
680c6158483SAnthony Liguori     } else if (s->free_scale) {
681c6158483SAnthony Liguori         double sx, sy;
682c6158483SAnthony Liguori 
683c6158483SAnthony Liguori         sx = (double)ww / fbw;
684c6158483SAnthony Liguori         sy = (double)wh / fbh;
685c6158483SAnthony Liguori 
686e3500d1fSGerd Hoffmann         vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
687a4ccabcfSAnthony Liguori     }
688a4ccabcfSAnthony Liguori 
689e3500d1fSGerd Hoffmann     fbw *= vc->gfx.scale_x;
690e3500d1fSGerd Hoffmann     fbh *= vc->gfx.scale_y;
6915104a1f6SAnthony Liguori 
692c6158483SAnthony Liguori     mx = my = 0;
693c6158483SAnthony Liguori     if (ww > fbw) {
694c6158483SAnthony Liguori         mx = (ww - fbw) / 2;
695c6158483SAnthony Liguori     }
696c6158483SAnthony Liguori     if (wh > fbh) {
697c6158483SAnthony Liguori         my = (wh - fbh) / 2;
698c6158483SAnthony Liguori     }
699c6158483SAnthony Liguori 
700c6158483SAnthony Liguori     cairo_rectangle(cr, 0, 0, ww, wh);
701c6158483SAnthony Liguori 
702c6158483SAnthony Liguori     /* Optionally cut out the inner area where the pixmap
703c6158483SAnthony Liguori        will be drawn. This avoids 'flashing' since we're
704c6158483SAnthony Liguori        not double-buffering. Note we're using the undocumented
705c6158483SAnthony Liguori        behaviour of drawing the rectangle from right to left
706c6158483SAnthony Liguori        to cut out the whole */
707c6158483SAnthony Liguori     cairo_rectangle(cr, mx + fbw, my,
708c6158483SAnthony Liguori                     -1 * fbw, fbh);
709c6158483SAnthony Liguori     cairo_fill(cr);
710c6158483SAnthony Liguori 
711e3500d1fSGerd Hoffmann     cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
712e3500d1fSGerd Hoffmann     cairo_set_source_surface(cr, vc->gfx.surface,
713e3500d1fSGerd Hoffmann                              mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
714a4ccabcfSAnthony Liguori     cairo_paint(cr);
715a4ccabcfSAnthony Liguori 
716a4ccabcfSAnthony Liguori     return TRUE;
717a4ccabcfSAnthony Liguori }
718a4ccabcfSAnthony Liguori 
719fe43bca8SDaniel P. Berrange #if !GTK_CHECK_VERSION(3, 0, 0)
720a4ccabcfSAnthony Liguori static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
721a4ccabcfSAnthony Liguori                                 void *opaque)
722a4ccabcfSAnthony Liguori {
723a4ccabcfSAnthony Liguori     cairo_t *cr;
724a4ccabcfSAnthony Liguori     gboolean ret;
725a4ccabcfSAnthony Liguori 
726a4ccabcfSAnthony Liguori     cr = gdk_cairo_create(gtk_widget_get_window(widget));
727a4ccabcfSAnthony Liguori     cairo_rectangle(cr,
728a4ccabcfSAnthony Liguori                     expose->area.x,
729a4ccabcfSAnthony Liguori                     expose->area.y,
730a4ccabcfSAnthony Liguori                     expose->area.width,
731a4ccabcfSAnthony Liguori                     expose->area.height);
732a4ccabcfSAnthony Liguori     cairo_clip(cr);
733a4ccabcfSAnthony Liguori 
734a4ccabcfSAnthony Liguori     ret = gd_draw_event(widget, cr, opaque);
735a4ccabcfSAnthony Liguori 
736a4ccabcfSAnthony Liguori     cairo_destroy(cr);
737a4ccabcfSAnthony Liguori 
738a4ccabcfSAnthony Liguori     return ret;
739a4ccabcfSAnthony Liguori }
740fe43bca8SDaniel P. Berrange #endif
741a4ccabcfSAnthony Liguori 
742a4ccabcfSAnthony Liguori static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
743a4ccabcfSAnthony Liguori                                 void *opaque)
744a4ccabcfSAnthony Liguori {
745e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
746e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
747a4ccabcfSAnthony Liguori     int x, y;
748c6158483SAnthony Liguori     int mx, my;
749c6158483SAnthony Liguori     int fbh, fbw;
750c6158483SAnthony Liguori     int ww, wh;
751a4ccabcfSAnthony Liguori 
752e3500d1fSGerd Hoffmann     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
753e3500d1fSGerd Hoffmann     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
754c6158483SAnthony Liguori 
755e3500d1fSGerd Hoffmann     gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area),
756e3500d1fSGerd Hoffmann                           &ww, &wh);
757c6158483SAnthony Liguori 
758c6158483SAnthony Liguori     mx = my = 0;
759c6158483SAnthony Liguori     if (ww > fbw) {
760c6158483SAnthony Liguori         mx = (ww - fbw) / 2;
761c6158483SAnthony Liguori     }
762c6158483SAnthony Liguori     if (wh > fbh) {
763c6158483SAnthony Liguori         my = (wh - fbh) / 2;
764c6158483SAnthony Liguori     }
765c6158483SAnthony Liguori 
766e3500d1fSGerd Hoffmann     x = (motion->x - mx) / vc->gfx.scale_x;
767e3500d1fSGerd Hoffmann     y = (motion->y - my) / vc->gfx.scale_y;
768c6158483SAnthony Liguori 
769e61031cdSTakashi Iwai     if (qemu_input_is_absolute()) {
770c6158483SAnthony Liguori         if (x < 0 || y < 0 ||
771e3500d1fSGerd Hoffmann             x >= surface_width(vc->gfx.ds) ||
772e3500d1fSGerd Hoffmann             y >= surface_height(vc->gfx.ds)) {
773c6158483SAnthony Liguori             return TRUE;
774c6158483SAnthony Liguori         }
775e3500d1fSGerd Hoffmann         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
776e3500d1fSGerd Hoffmann                              surface_width(vc->gfx.ds));
777e3500d1fSGerd Hoffmann         qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
778e3500d1fSGerd Hoffmann                              surface_height(vc->gfx.ds));
779192f81bfSGerd Hoffmann         qemu_input_event_sync();
7802884cf5bSGerd Hoffmann     } else if (s->last_set && s->ptr_owner == vc) {
781e3500d1fSGerd Hoffmann         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
782e3500d1fSGerd Hoffmann         qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
783192f81bfSGerd Hoffmann         qemu_input_event_sync();
784a4ccabcfSAnthony Liguori     }
785a4ccabcfSAnthony Liguori     s->last_x = x;
786a4ccabcfSAnthony Liguori     s->last_y = y;
787e61031cdSTakashi Iwai     s->last_set = TRUE;
788a4ccabcfSAnthony Liguori 
7892884cf5bSGerd Hoffmann     if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
790e3500d1fSGerd Hoffmann         GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
7915104a1f6SAnthony Liguori         int x = (int)motion->x_root;
7925104a1f6SAnthony Liguori         int y = (int)motion->y_root;
7935104a1f6SAnthony Liguori 
7945104a1f6SAnthony Liguori         /* In relative mode check to see if client pointer hit
7955104a1f6SAnthony Liguori          * one of the screen edges, and if so move it back by
7965104a1f6SAnthony Liguori          * 200 pixels. This is important because the pointer
7975104a1f6SAnthony Liguori          * in the server doesn't correspond 1-for-1, and so
7985104a1f6SAnthony Liguori          * may still be only half way across the screen. Without
7995104a1f6SAnthony Liguori          * this warp, the server pointer would thus appear to hit
8005104a1f6SAnthony Liguori          * an invisible wall */
8015104a1f6SAnthony Liguori         if (x == 0) {
8025104a1f6SAnthony Liguori             x += 200;
8035104a1f6SAnthony Liguori         }
8045104a1f6SAnthony Liguori         if (y == 0) {
8055104a1f6SAnthony Liguori             y += 200;
8065104a1f6SAnthony Liguori         }
8075104a1f6SAnthony Liguori         if (x == (gdk_screen_get_width(screen) - 1)) {
8085104a1f6SAnthony Liguori             x -= 200;
8095104a1f6SAnthony Liguori         }
8105104a1f6SAnthony Liguori         if (y == (gdk_screen_get_height(screen) - 1)) {
8115104a1f6SAnthony Liguori             y -= 200;
8125104a1f6SAnthony Liguori         }
8135104a1f6SAnthony Liguori 
8145104a1f6SAnthony Liguori         if (x != (int)motion->x_root || y != (int)motion->y_root) {
8158906de76SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
8168906de76SDaniel P. Berrange             GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
8178906de76SDaniel P. Berrange             gdk_device_warp(dev, screen, x, y);
8188906de76SDaniel P. Berrange #else
8198906de76SDaniel P. Berrange             GdkDisplay *display = gtk_widget_get_display(widget);
8205104a1f6SAnthony Liguori             gdk_display_warp_pointer(display, screen, x, y);
8218906de76SDaniel P. Berrange #endif
822e61031cdSTakashi Iwai             s->last_set = FALSE;
8235104a1f6SAnthony Liguori             return FALSE;
8245104a1f6SAnthony Liguori         }
8255104a1f6SAnthony Liguori     }
826a4ccabcfSAnthony Liguori     return TRUE;
827a4ccabcfSAnthony Liguori }
828a4ccabcfSAnthony Liguori 
829a4ccabcfSAnthony Liguori static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
830a4ccabcfSAnthony Liguori                                 void *opaque)
831a4ccabcfSAnthony Liguori {
832e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
833e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
834192f81bfSGerd Hoffmann     InputButton btn;
835a4ccabcfSAnthony Liguori 
836800b0e81STakashi Iwai     /* implicitly grab the input at the first click in the relative mode */
837800b0e81STakashi Iwai     if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
8382884cf5bSGerd Hoffmann         !qemu_input_is_absolute() && s->ptr_owner != vc) {
8392884cf5bSGerd Hoffmann         gd_ungrab_pointer(s);
8402884cf5bSGerd Hoffmann         if (!vc->window) {
841800b0e81STakashi Iwai             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
842800b0e81STakashi Iwai                                            TRUE);
8432884cf5bSGerd Hoffmann         } else {
8442884cf5bSGerd Hoffmann             gd_grab_pointer(vc);
8452884cf5bSGerd Hoffmann             gd_update_caption(s);
8462884cf5bSGerd Hoffmann         }
847800b0e81STakashi Iwai         return TRUE;
848800b0e81STakashi Iwai     }
849800b0e81STakashi Iwai 
850a4ccabcfSAnthony Liguori     if (button->button == 1) {
851192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_LEFT;
852a4ccabcfSAnthony Liguori     } else if (button->button == 2) {
853192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_MIDDLE;
854a4ccabcfSAnthony Liguori     } else if (button->button == 3) {
855192f81bfSGerd Hoffmann         btn = INPUT_BUTTON_RIGHT;
856a4ccabcfSAnthony Liguori     } else {
857192f81bfSGerd Hoffmann         return TRUE;
858a4ccabcfSAnthony Liguori     }
859a4ccabcfSAnthony Liguori 
860e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn,
861e3500d1fSGerd Hoffmann                          button->type == GDK_BUTTON_PRESS);
862192f81bfSGerd Hoffmann     qemu_input_event_sync();
863a4ccabcfSAnthony Liguori     return TRUE;
864a4ccabcfSAnthony Liguori }
865a4ccabcfSAnthony Liguori 
866d58b9122SJan Kiszka static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
867d58b9122SJan Kiszka                                 void *opaque)
868d58b9122SJan Kiszka {
869e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
870d58b9122SJan Kiszka     InputButton btn;
871d58b9122SJan Kiszka 
872d58b9122SJan Kiszka     if (scroll->direction == GDK_SCROLL_UP) {
873d58b9122SJan Kiszka         btn = INPUT_BUTTON_WHEEL_UP;
874d58b9122SJan Kiszka     } else if (scroll->direction == GDK_SCROLL_DOWN) {
875d58b9122SJan Kiszka         btn = INPUT_BUTTON_WHEEL_DOWN;
876d58b9122SJan Kiszka     } else {
877d58b9122SJan Kiszka         return TRUE;
878d58b9122SJan Kiszka     }
879d58b9122SJan Kiszka 
880e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
881d58b9122SJan Kiszka     qemu_input_event_sync();
882e3500d1fSGerd Hoffmann     qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
883d58b9122SJan Kiszka     qemu_input_event_sync();
884d58b9122SJan Kiszka     return TRUE;
885d58b9122SJan Kiszka }
886d58b9122SJan Kiszka 
887*0a337ed0SGerd Hoffmann static int gd_map_keycode(GtkDisplayState *s, GdkDisplay *dpy, int gdk_keycode)
888a4ccabcfSAnthony Liguori {
889932f2d7eSGerd Hoffmann     int qemu_keycode;
890a4ccabcfSAnthony Liguori 
891*0a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_WIN32
892*0a337ed0SGerd Hoffmann     if (GDK_IS_WIN32_DISPLAY(dpy)) {
893932f2d7eSGerd Hoffmann         qemu_keycode = MapVirtualKey(gdk_keycode, MAPVK_VK_TO_VSC);
8942777ccc5SStefan Weil         switch (qemu_keycode) {
8952777ccc5SStefan Weil         case 103:   /* alt gr */
8962777ccc5SStefan Weil             qemu_keycode = 56 | SCANCODE_GREY;
8972777ccc5SStefan Weil             break;
8982777ccc5SStefan Weil         }
899*0a337ed0SGerd Hoffmann         return qemu_keycode;
900*0a337ed0SGerd Hoffmann     }
901*0a337ed0SGerd Hoffmann #endif
902a4ccabcfSAnthony Liguori 
903a4ccabcfSAnthony Liguori     if (gdk_keycode < 9) {
904a4ccabcfSAnthony Liguori         qemu_keycode = 0;
905a4ccabcfSAnthony Liguori     } else if (gdk_keycode < 97) {
906a4ccabcfSAnthony Liguori         qemu_keycode = gdk_keycode - 8;
907*0a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_X11
908*0a337ed0SGerd Hoffmann     } else if (GDK_IS_X11_DISPLAY(dpy) && gdk_keycode < 158) {
9093158a348SBruce Rogers         if (s->has_evdev) {
910a4ccabcfSAnthony Liguori             qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
9113158a348SBruce Rogers         } else {
9123158a348SBruce Rogers             qemu_keycode = translate_xfree86_keycode(gdk_keycode - 97);
9133158a348SBruce Rogers         }
914*0a337ed0SGerd Hoffmann #endif
915a4ccabcfSAnthony Liguori     } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
916a4ccabcfSAnthony Liguori         qemu_keycode = 0x70;
917a4ccabcfSAnthony Liguori     } else if (gdk_keycode == 211) { /* backslash */
918a4ccabcfSAnthony Liguori         qemu_keycode = 0x73;
919a4ccabcfSAnthony Liguori     } else {
920a4ccabcfSAnthony Liguori         qemu_keycode = 0;
921a4ccabcfSAnthony Liguori     }
922a4ccabcfSAnthony Liguori 
923932f2d7eSGerd Hoffmann     return qemu_keycode;
924932f2d7eSGerd Hoffmann }
925932f2d7eSGerd Hoffmann 
926932f2d7eSGerd Hoffmann static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
927932f2d7eSGerd Hoffmann {
928932f2d7eSGerd Hoffmann     VirtualConsole *vc = opaque;
929932f2d7eSGerd Hoffmann     GtkDisplayState *s = vc->s;
930932f2d7eSGerd Hoffmann     int gdk_keycode = key->hardware_keycode;
931932f2d7eSGerd Hoffmann     int qemu_keycode;
932932f2d7eSGerd Hoffmann     int i;
933932f2d7eSGerd Hoffmann 
934*0a337ed0SGerd Hoffmann     qemu_keycode = gd_map_keycode(s, gtk_widget_get_display(widget),
935*0a337ed0SGerd Hoffmann                                   gdk_keycode);
936932f2d7eSGerd Hoffmann 
93774444bc1SGerd Hoffmann     trace_gd_key_event(vc->label, gdk_keycode, qemu_keycode,
938a4ccabcfSAnthony Liguori                        (key->type == GDK_KEY_PRESS) ? "down" : "up");
939a4ccabcfSAnthony Liguori 
9406db253caSJan Kiszka     for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
9416db253caSJan Kiszka         if (qemu_keycode == modifier_keycode[i]) {
9426db253caSJan Kiszka             s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
9436db253caSJan Kiszka         }
9446db253caSJan Kiszka     }
9456db253caSJan Kiszka 
946e3500d1fSGerd Hoffmann     qemu_input_event_send_key_number(vc->gfx.dcl.con, qemu_keycode,
947af98ba92SGerd Hoffmann                                      key->type == GDK_KEY_PRESS);
948a4ccabcfSAnthony Liguori 
949a4ccabcfSAnthony Liguori     return TRUE;
950a4ccabcfSAnthony Liguori }
951a4ccabcfSAnthony Liguori 
9520d0e044dSTakashi Iwai static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
9530d0e044dSTakashi Iwai {
9540d0e044dSTakashi Iwai     if (event->type == GDK_MOTION_NOTIFY) {
9550d0e044dSTakashi Iwai         return gd_motion_event(widget, &event->motion, opaque);
9560d0e044dSTakashi Iwai     }
9570d0e044dSTakashi Iwai     return FALSE;
9580d0e044dSTakashi Iwai }
9590d0e044dSTakashi Iwai 
960a4ccabcfSAnthony Liguori /** Window Menu Actions **/
961a4ccabcfSAnthony Liguori 
96230e8f22bSJan Kiszka static void gd_menu_pause(GtkMenuItem *item, void *opaque)
96330e8f22bSJan Kiszka {
96430e8f22bSJan Kiszka     GtkDisplayState *s = opaque;
96530e8f22bSJan Kiszka 
96630e8f22bSJan Kiszka     if (s->external_pause_update) {
96730e8f22bSJan Kiszka         return;
96830e8f22bSJan Kiszka     }
96930e8f22bSJan Kiszka     if (runstate_is_running()) {
97030e8f22bSJan Kiszka         qmp_stop(NULL);
97130e8f22bSJan Kiszka     } else {
97230e8f22bSJan Kiszka         qmp_cont(NULL);
97330e8f22bSJan Kiszka     }
97430e8f22bSJan Kiszka }
97530e8f22bSJan Kiszka 
97630e8f22bSJan Kiszka static void gd_menu_reset(GtkMenuItem *item, void *opaque)
97730e8f22bSJan Kiszka {
97830e8f22bSJan Kiszka     qmp_system_reset(NULL);
97930e8f22bSJan Kiszka }
98030e8f22bSJan Kiszka 
98130e8f22bSJan Kiszka static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
98230e8f22bSJan Kiszka {
98330e8f22bSJan Kiszka     qmp_system_powerdown(NULL);
98430e8f22bSJan Kiszka }
98530e8f22bSJan Kiszka 
986a4ccabcfSAnthony Liguori static void gd_menu_quit(GtkMenuItem *item, void *opaque)
987a4ccabcfSAnthony Liguori {
988a4ccabcfSAnthony Liguori     qmp_quit(NULL);
989a4ccabcfSAnthony Liguori }
990a4ccabcfSAnthony Liguori 
991a4ccabcfSAnthony Liguori static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
992a4ccabcfSAnthony Liguori {
993a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
994e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_by_menu(s);
995832189c9SGerd Hoffmann     gint page;
996a4ccabcfSAnthony Liguori 
9976db253caSJan Kiszka     gtk_release_modifiers(s);
998271a25c0SGerd Hoffmann     if (vc) {
999832189c9SGerd Hoffmann         page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook),
1000271a25c0SGerd Hoffmann                                      vc->tab_item);
1001832189c9SGerd Hoffmann         gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), page);
1002d861def3SAnthony Liguori     }
1003d861def3SAnthony Liguori }
1004a4ccabcfSAnthony Liguori 
1005a4ccabcfSAnthony Liguori static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
1006a4ccabcfSAnthony Liguori {
1007a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
1008a4ccabcfSAnthony Liguori 
1009a4ccabcfSAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
1010a4ccabcfSAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
1011a4ccabcfSAnthony Liguori     } else {
1012a4ccabcfSAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1013a4ccabcfSAnthony Liguori     }
1014a4ccabcfSAnthony Liguori }
1015a4ccabcfSAnthony Liguori 
1016cdeb7090SGerd Hoffmann static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
1017cdeb7090SGerd Hoffmann                                     void *opaque)
1018cdeb7090SGerd Hoffmann {
1019cdeb7090SGerd Hoffmann     VirtualConsole *vc = opaque;
1020cdeb7090SGerd Hoffmann     GtkDisplayState *s = vc->s;
1021cdeb7090SGerd Hoffmann 
1022cdeb7090SGerd Hoffmann     gtk_widget_set_sensitive(vc->menu_item, true);
1023cdeb7090SGerd Hoffmann     gtk_widget_reparent(vc->tab_item, s->notebook);
1024cdeb7090SGerd Hoffmann     gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
1025cdeb7090SGerd Hoffmann                                     vc->tab_item, vc->label);
1026cdeb7090SGerd Hoffmann     gtk_widget_destroy(vc->window);
1027cdeb7090SGerd Hoffmann     vc->window = NULL;
1028cdeb7090SGerd Hoffmann     return TRUE;
1029cdeb7090SGerd Hoffmann }
1030cdeb7090SGerd Hoffmann 
10310c77a37fSGerd Hoffmann static gboolean gd_win_grab(void *opaque)
10320c77a37fSGerd Hoffmann {
10330c77a37fSGerd Hoffmann     VirtualConsole *vc = opaque;
10340c77a37fSGerd Hoffmann 
10350c77a37fSGerd Hoffmann     fprintf(stderr, "%s: %s\n", __func__, vc->label);
10360c77a37fSGerd Hoffmann     if (vc->s->ptr_owner) {
10370c77a37fSGerd Hoffmann         gd_ungrab_pointer(vc->s);
10380c77a37fSGerd Hoffmann     } else {
10390c77a37fSGerd Hoffmann         gd_grab_pointer(vc);
10400c77a37fSGerd Hoffmann     }
10410c77a37fSGerd Hoffmann     gd_update_caption(vc->s);
10420c77a37fSGerd Hoffmann     return TRUE;
10430c77a37fSGerd Hoffmann }
10440c77a37fSGerd Hoffmann 
1045cdeb7090SGerd Hoffmann static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
1046cdeb7090SGerd Hoffmann {
1047cdeb7090SGerd Hoffmann     GtkDisplayState *s = opaque;
1048cdeb7090SGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1049cdeb7090SGerd Hoffmann 
1050cdeb7090SGerd Hoffmann     if (vc->type == GD_VC_GFX) {
1051aa0a55d4SGerd Hoffmann         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1052aa0a55d4SGerd Hoffmann                                        FALSE);
1053cdeb7090SGerd Hoffmann     }
1054cdeb7090SGerd Hoffmann     if (!vc->window) {
1055cdeb7090SGerd Hoffmann         gtk_widget_set_sensitive(vc->menu_item, false);
1056cdeb7090SGerd Hoffmann         vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1057cdeb7090SGerd Hoffmann         gtk_widget_reparent(vc->tab_item, vc->window);
1058cdeb7090SGerd Hoffmann 
1059cdeb7090SGerd Hoffmann         g_signal_connect(vc->window, "delete-event",
1060cdeb7090SGerd Hoffmann                          G_CALLBACK(gd_tab_window_close), vc);
1061cdeb7090SGerd Hoffmann         gtk_widget_show_all(vc->window);
10624eeaa3a8SGerd Hoffmann 
10630c77a37fSGerd Hoffmann         GtkAccelGroup *ag = gtk_accel_group_new();
10640c77a37fSGerd Hoffmann         gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
10650c77a37fSGerd Hoffmann 
10660c77a37fSGerd Hoffmann         GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab), vc, NULL);
10670c77a37fSGerd Hoffmann         gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
10680c77a37fSGerd Hoffmann 
106982fc1809SGerd Hoffmann         gd_update_geometry_hints(vc);
10704eeaa3a8SGerd Hoffmann         gd_update_caption(s);
1071cdeb7090SGerd Hoffmann     }
1072cdeb7090SGerd Hoffmann }
1073cdeb7090SGerd Hoffmann 
1074c6158483SAnthony Liguori static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
1075c6158483SAnthony Liguori {
1076c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1077e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1078c6158483SAnthony Liguori 
107910409282SStefan Weil     if (!s->full_screen) {
1080c6158483SAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1081c6158483SAnthony Liguori         gtk_widget_set_size_request(s->menu_bar, 0, 0);
1082e3500d1fSGerd Hoffmann         if (vc->type == GD_VC_GFX) {
1083e3500d1fSGerd Hoffmann             gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
1084e3500d1fSGerd Hoffmann             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1085e3500d1fSGerd Hoffmann                                            TRUE);
1086c6158483SAnthony Liguori         }
1087e3500d1fSGerd Hoffmann         gtk_window_fullscreen(GTK_WINDOW(s->window));
1088c6158483SAnthony Liguori         s->full_screen = TRUE;
1089c6158483SAnthony Liguori     } else {
1090c6158483SAnthony Liguori         gtk_window_unfullscreen(GTK_WINDOW(s->window));
1091c6158483SAnthony Liguori         gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
1092c6158483SAnthony Liguori         gtk_widget_set_size_request(s->menu_bar, -1, -1);
1093c6158483SAnthony Liguori         s->full_screen = FALSE;
1094e3500d1fSGerd Hoffmann         if (vc->type == GD_VC_GFX) {
1095e3500d1fSGerd Hoffmann             vc->gfx.scale_x = 1.0;
1096e3500d1fSGerd Hoffmann             vc->gfx.scale_y = 1.0;
109782fc1809SGerd Hoffmann             gd_update_windowsize(vc);
1098e3500d1fSGerd Hoffmann             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1099e3500d1fSGerd Hoffmann                                            FALSE);
1100e3500d1fSGerd Hoffmann         }
1101c6158483SAnthony Liguori     }
1102c6158483SAnthony Liguori 
1103e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
1104c6158483SAnthony Liguori }
1105c6158483SAnthony Liguori 
1106c6158483SAnthony Liguori static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
1107c6158483SAnthony Liguori {
1108c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1109e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1110c6158483SAnthony Liguori 
1111c6158483SAnthony Liguori     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1112c6158483SAnthony Liguori                                    FALSE);
1113c6158483SAnthony Liguori 
111482fc1809SGerd Hoffmann     vc->gfx.scale_x += VC_SCALE_STEP;
111582fc1809SGerd Hoffmann     vc->gfx.scale_y += VC_SCALE_STEP;
1116c6158483SAnthony Liguori 
1117e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1118c6158483SAnthony Liguori }
1119c6158483SAnthony Liguori 
1120c6158483SAnthony Liguori static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
1121c6158483SAnthony Liguori {
1122c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1123e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1124c6158483SAnthony Liguori 
1125c6158483SAnthony Liguori     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
1126c6158483SAnthony Liguori                                    FALSE);
1127c6158483SAnthony Liguori 
112882fc1809SGerd Hoffmann     vc->gfx.scale_x -= VC_SCALE_STEP;
112982fc1809SGerd Hoffmann     vc->gfx.scale_y -= VC_SCALE_STEP;
1130c6158483SAnthony Liguori 
113182fc1809SGerd Hoffmann     vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
113282fc1809SGerd Hoffmann     vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
1133c6158483SAnthony Liguori 
1134e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1135c6158483SAnthony Liguori }
1136c6158483SAnthony Liguori 
1137c6158483SAnthony Liguori static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
1138c6158483SAnthony Liguori {
1139c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1140e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1141c6158483SAnthony Liguori 
1142e3500d1fSGerd Hoffmann     vc->gfx.scale_x = 1.0;
1143e3500d1fSGerd Hoffmann     vc->gfx.scale_y = 1.0;
1144c6158483SAnthony Liguori 
1145e3500d1fSGerd Hoffmann     gd_update_windowsize(vc);
1146c6158483SAnthony Liguori }
1147c6158483SAnthony Liguori 
1148c6158483SAnthony Liguori static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
1149c6158483SAnthony Liguori {
1150c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
1151e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
1152c6158483SAnthony Liguori 
1153c6158483SAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
1154c6158483SAnthony Liguori         s->free_scale = TRUE;
1155c6158483SAnthony Liguori     } else {
1156c6158483SAnthony Liguori         s->free_scale = FALSE;
1157e3500d1fSGerd Hoffmann         vc->gfx.scale_x = 1.0;
1158e3500d1fSGerd Hoffmann         vc->gfx.scale_y = 1.0;
1159c6158483SAnthony Liguori     }
1160c6158483SAnthony Liguori 
116182fc1809SGerd Hoffmann     gd_update_windowsize(vc);
1162e3500d1fSGerd Hoffmann     gd_update_full_redraw(vc);
1163c6158483SAnthony Liguori }
1164c6158483SAnthony Liguori 
1165e3500d1fSGerd Hoffmann static void gd_grab_keyboard(VirtualConsole *vc)
11665104a1f6SAnthony Liguori {
1167655199daSDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
1168e3500d1fSGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1169655199daSDaniel P. Berrange     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1170655199daSDaniel P. Berrange     GList *devices = gdk_device_manager_list_devices(mgr,
1171655199daSDaniel P. Berrange                                                      GDK_DEVICE_TYPE_MASTER);
1172655199daSDaniel P. Berrange     GList *tmp = devices;
1173655199daSDaniel P. Berrange     while (tmp) {
1174655199daSDaniel P. Berrange         GdkDevice *dev = tmp->data;
1175655199daSDaniel P. Berrange         if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) {
1176655199daSDaniel P. Berrange             gdk_device_grab(dev,
1177e3500d1fSGerd Hoffmann                             gtk_widget_get_window(vc->gfx.drawing_area),
1178655199daSDaniel P. Berrange                             GDK_OWNERSHIP_NONE,
1179655199daSDaniel P. Berrange                             FALSE,
1180655199daSDaniel P. Berrange                             GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
1181655199daSDaniel P. Berrange                             NULL,
1182655199daSDaniel P. Berrange                             GDK_CURRENT_TIME);
1183655199daSDaniel P. Berrange         }
1184655199daSDaniel P. Berrange         tmp = tmp->next;
1185655199daSDaniel P. Berrange     }
1186655199daSDaniel P. Berrange     g_list_free(devices);
1187655199daSDaniel P. Berrange #else
1188e3500d1fSGerd Hoffmann     gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area),
11895104a1f6SAnthony Liguori                       FALSE,
11905104a1f6SAnthony Liguori                       GDK_CURRENT_TIME);
1191655199daSDaniel P. Berrange #endif
11924c638e2eSGerd Hoffmann     vc->s->kbd_owner = vc;
11931c856da5SGerd Hoffmann     trace_gd_grab(vc->label, "kbd", true);
11945104a1f6SAnthony Liguori }
11955104a1f6SAnthony Liguori 
11964c638e2eSGerd Hoffmann static void gd_ungrab_keyboard(GtkDisplayState *s)
11975104a1f6SAnthony Liguori {
11984c638e2eSGerd Hoffmann     VirtualConsole *vc = s->kbd_owner;
11994c638e2eSGerd Hoffmann 
12004c638e2eSGerd Hoffmann     if (vc == NULL) {
12014c638e2eSGerd Hoffmann         return;
12024c638e2eSGerd Hoffmann     }
12034c638e2eSGerd Hoffmann     s->kbd_owner = NULL;
12044c638e2eSGerd Hoffmann 
1205655199daSDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
1206e3500d1fSGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1207655199daSDaniel P. Berrange     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
1208655199daSDaniel P. Berrange     GList *devices = gdk_device_manager_list_devices(mgr,
1209655199daSDaniel P. Berrange                                                      GDK_DEVICE_TYPE_MASTER);
1210655199daSDaniel P. Berrange     GList *tmp = devices;
1211655199daSDaniel P. Berrange     while (tmp) {
1212655199daSDaniel P. Berrange         GdkDevice *dev = tmp->data;
1213655199daSDaniel P. Berrange         if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) {
1214655199daSDaniel P. Berrange             gdk_device_ungrab(dev,
1215655199daSDaniel P. Berrange                               GDK_CURRENT_TIME);
1216655199daSDaniel P. Berrange         }
1217655199daSDaniel P. Berrange         tmp = tmp->next;
1218655199daSDaniel P. Berrange     }
1219655199daSDaniel P. Berrange     g_list_free(devices);
1220655199daSDaniel P. Berrange #else
12215104a1f6SAnthony Liguori     gdk_keyboard_ungrab(GDK_CURRENT_TIME);
1222655199daSDaniel P. Berrange #endif
12231c856da5SGerd Hoffmann     trace_gd_grab(vc->label, "kbd", false);
12245104a1f6SAnthony Liguori }
12255104a1f6SAnthony Liguori 
1226e3500d1fSGerd Hoffmann static void gd_grab_pointer(VirtualConsole *vc)
12275104a1f6SAnthony Liguori {
1228e3500d1fSGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1229ecce1929STakashi Iwai #if GTK_CHECK_VERSION(3, 0, 0)
12302a05485dSDaniel P. Berrange     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
12312a05485dSDaniel P. Berrange     GList *devices = gdk_device_manager_list_devices(mgr,
12322a05485dSDaniel P. Berrange                                                      GDK_DEVICE_TYPE_MASTER);
12332a05485dSDaniel P. Berrange     GList *tmp = devices;
12342a05485dSDaniel P. Berrange     while (tmp) {
12352a05485dSDaniel P. Berrange         GdkDevice *dev = tmp->data;
12362a05485dSDaniel P. Berrange         if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) {
12372a05485dSDaniel P. Berrange             gdk_device_grab(dev,
1238e3500d1fSGerd Hoffmann                             gtk_widget_get_window(vc->gfx.drawing_area),
12392a05485dSDaniel P. Berrange                             GDK_OWNERSHIP_NONE,
12402a05485dSDaniel P. Berrange                             FALSE, /* All events to come to our
12412a05485dSDaniel P. Berrange                                       window directly */
12422a05485dSDaniel P. Berrange                             GDK_POINTER_MOTION_MASK |
12432a05485dSDaniel P. Berrange                             GDK_BUTTON_PRESS_MASK |
12442a05485dSDaniel P. Berrange                             GDK_BUTTON_RELEASE_MASK |
12452a05485dSDaniel P. Berrange                             GDK_BUTTON_MOTION_MASK |
12462a05485dSDaniel P. Berrange                             GDK_SCROLL_MASK,
1247e3500d1fSGerd Hoffmann                             vc->s->null_cursor,
12482a05485dSDaniel P. Berrange                             GDK_CURRENT_TIME);
12492a05485dSDaniel P. Berrange         }
12502a05485dSDaniel P. Berrange         tmp = tmp->next;
12512a05485dSDaniel P. Berrange     }
12522a05485dSDaniel P. Berrange     g_list_free(devices);
1253ecce1929STakashi Iwai     gdk_device_get_position(gdk_device_manager_get_client_pointer(mgr),
1254e3500d1fSGerd Hoffmann                             NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
12552a05485dSDaniel P. Berrange #else
1256e3500d1fSGerd Hoffmann     gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area),
12575104a1f6SAnthony Liguori                      FALSE, /* All events to come to our window directly */
12585104a1f6SAnthony Liguori                      GDK_POINTER_MOTION_MASK |
12595104a1f6SAnthony Liguori                      GDK_BUTTON_PRESS_MASK |
12605104a1f6SAnthony Liguori                      GDK_BUTTON_RELEASE_MASK |
12615104a1f6SAnthony Liguori                      GDK_BUTTON_MOTION_MASK |
12625104a1f6SAnthony Liguori                      GDK_SCROLL_MASK,
12635104a1f6SAnthony Liguori                      NULL, /* Allow cursor to move over entire desktop */
1264e3500d1fSGerd Hoffmann                      vc->s->null_cursor,
12655104a1f6SAnthony Liguori                      GDK_CURRENT_TIME);
1266ecce1929STakashi Iwai     gdk_display_get_pointer(display, NULL,
1267e3500d1fSGerd Hoffmann                             &vc->s->grab_x_root, &vc->s->grab_y_root, NULL);
12682a05485dSDaniel P. Berrange #endif
12694c638e2eSGerd Hoffmann     vc->s->ptr_owner = vc;
12701c856da5SGerd Hoffmann     trace_gd_grab(vc->label, "ptr", true);
12712a05485dSDaniel P. Berrange }
12722a05485dSDaniel P. Berrange 
12734c638e2eSGerd Hoffmann static void gd_ungrab_pointer(GtkDisplayState *s)
12742a05485dSDaniel P. Berrange {
12754c638e2eSGerd Hoffmann     VirtualConsole *vc = s->ptr_owner;
12764c638e2eSGerd Hoffmann 
12774c638e2eSGerd Hoffmann     if (vc == NULL) {
12784c638e2eSGerd Hoffmann         return;
12794c638e2eSGerd Hoffmann     }
12804c638e2eSGerd Hoffmann     s->ptr_owner = NULL;
12814c638e2eSGerd Hoffmann 
1282e3500d1fSGerd Hoffmann     GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
1283ecce1929STakashi Iwai #if GTK_CHECK_VERSION(3, 0, 0)
12842a05485dSDaniel P. Berrange     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
12852a05485dSDaniel P. Berrange     GList *devices = gdk_device_manager_list_devices(mgr,
12862a05485dSDaniel P. Berrange                                                      GDK_DEVICE_TYPE_MASTER);
12872a05485dSDaniel P. Berrange     GList *tmp = devices;
12882a05485dSDaniel P. Berrange     while (tmp) {
12892a05485dSDaniel P. Berrange         GdkDevice *dev = tmp->data;
12902a05485dSDaniel P. Berrange         if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) {
12912a05485dSDaniel P. Berrange             gdk_device_ungrab(dev,
12922a05485dSDaniel P. Berrange                               GDK_CURRENT_TIME);
12932a05485dSDaniel P. Berrange         }
12942a05485dSDaniel P. Berrange         tmp = tmp->next;
12952a05485dSDaniel P. Berrange     }
12962a05485dSDaniel P. Berrange     g_list_free(devices);
1297ecce1929STakashi Iwai     gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
1298e3500d1fSGerd Hoffmann                     gtk_widget_get_screen(vc->gfx.drawing_area),
1299e3500d1fSGerd Hoffmann                     vc->s->grab_x_root, vc->s->grab_y_root);
13002a05485dSDaniel P. Berrange #else
13012a05485dSDaniel P. Berrange     gdk_pointer_ungrab(GDK_CURRENT_TIME);
1302ecce1929STakashi Iwai     gdk_display_warp_pointer(display,
1303e3500d1fSGerd Hoffmann                              gtk_widget_get_screen(vc->gfx.drawing_area),
1304e3500d1fSGerd Hoffmann                              vc->s->grab_x_root, vc->s->grab_y_root);
13052a05485dSDaniel P. Berrange #endif
13061c856da5SGerd Hoffmann     trace_gd_grab(vc->label, "ptr", false);
13072a05485dSDaniel P. Berrange }
13082a05485dSDaniel P. Berrange 
13092a05485dSDaniel P. Berrange static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
13102a05485dSDaniel P. Berrange {
13112a05485dSDaniel P. Berrange     GtkDisplayState *s = opaque;
1312e3500d1fSGerd Hoffmann     VirtualConsole *vc = gd_vc_find_current(s);
13132a05485dSDaniel P. Berrange 
13142a05485dSDaniel P. Berrange     if (gd_is_grab_active(s)) {
1315746b8670SGerd Hoffmann         if (!gd_grab_on_hover(s)) {
1316e3500d1fSGerd Hoffmann             gd_grab_keyboard(vc);
1317746b8670SGerd Hoffmann         }
1318e3500d1fSGerd Hoffmann         gd_grab_pointer(vc);
13195104a1f6SAnthony Liguori     } else {
13204c638e2eSGerd Hoffmann         gd_ungrab_keyboard(s);
13214c638e2eSGerd Hoffmann         gd_ungrab_pointer(s);
13225104a1f6SAnthony Liguori     }
13235104a1f6SAnthony Liguori 
13245104a1f6SAnthony Liguori     gd_update_caption(s);
1325e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
13265104a1f6SAnthony Liguori }
13275104a1f6SAnthony Liguori 
1328a4ccabcfSAnthony Liguori static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
1329a4ccabcfSAnthony Liguori                            gpointer data)
1330a4ccabcfSAnthony Liguori {
1331a4ccabcfSAnthony Liguori     GtkDisplayState *s = data;
1332e3500d1fSGerd Hoffmann     VirtualConsole *vc;
13335104a1f6SAnthony Liguori     gboolean on_vga;
1334a4ccabcfSAnthony Liguori 
1335a4ccabcfSAnthony Liguori     if (!gtk_widget_get_realized(s->notebook)) {
1336a4ccabcfSAnthony Liguori         return;
1337a4ccabcfSAnthony Liguori     }
1338a4ccabcfSAnthony Liguori 
13396fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK
13406fa27697SGerd Hoffmann     vc = gd_vc_find_current(s);
13416fa27697SGerd Hoffmann     if (vc && vc->type == GD_VC_VTE) {
13426fa27697SGerd Hoffmann         gtk_widget_hide(vc->vte.terminal);
13436fa27697SGerd Hoffmann     }
13446fa27697SGerd Hoffmann #endif
1345e3500d1fSGerd Hoffmann     vc = gd_vc_find_by_page(s, arg2);
1346e3500d1fSGerd Hoffmann     if (!vc) {
1347e3500d1fSGerd Hoffmann         return;
1348e3500d1fSGerd Hoffmann     }
13496fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK
13506fa27697SGerd Hoffmann     if (vc->type == GD_VC_VTE) {
13516fa27697SGerd Hoffmann         gtk_widget_show(vc->vte.terminal);
13526fa27697SGerd Hoffmann     }
13536fa27697SGerd Hoffmann #endif
1354e3500d1fSGerd Hoffmann     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
1355e3500d1fSGerd Hoffmann                                    TRUE);
1356e3500d1fSGerd Hoffmann     on_vga = (vc->type == GD_VC_GFX);
13575104a1f6SAnthony Liguori     if (!on_vga) {
13585104a1f6SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
13595104a1f6SAnthony Liguori                                        FALSE);
1360c6158483SAnthony Liguori     } else if (s->full_screen) {
1361c6158483SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
1362c6158483SAnthony Liguori                                        TRUE);
13635104a1f6SAnthony Liguori     }
13645104a1f6SAnthony Liguori     gtk_widget_set_sensitive(s->grab_item, on_vga);
13655104a1f6SAnthony Liguori 
136682fc1809SGerd Hoffmann     gd_update_windowsize(vc);
1367e3500d1fSGerd Hoffmann     gd_update_cursor(vc);
1368a4ccabcfSAnthony Liguori }
1369a4ccabcfSAnthony Liguori 
1370e3500d1fSGerd Hoffmann static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
1371e3500d1fSGerd Hoffmann                                gpointer opaque)
13725104a1f6SAnthony Liguori {
1373e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1374e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
13755104a1f6SAnthony Liguori 
13762884cf5bSGerd Hoffmann     if (gd_grab_on_hover(s)) {
13772884cf5bSGerd Hoffmann         gd_ungrab_keyboard(s);
1378e3500d1fSGerd Hoffmann         gd_grab_keyboard(vc);
13792884cf5bSGerd Hoffmann         gd_update_caption(s);
13805104a1f6SAnthony Liguori     }
13815104a1f6SAnthony Liguori     return TRUE;
13825104a1f6SAnthony Liguori }
13835104a1f6SAnthony Liguori 
1384e3500d1fSGerd Hoffmann static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
1385e3500d1fSGerd Hoffmann                                gpointer opaque)
13865104a1f6SAnthony Liguori {
1387e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1388e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
13895104a1f6SAnthony Liguori 
13902884cf5bSGerd Hoffmann     if (gd_grab_on_hover(s)) {
13914c638e2eSGerd Hoffmann         gd_ungrab_keyboard(s);
13922884cf5bSGerd Hoffmann         gd_update_caption(s);
13935104a1f6SAnthony Liguori     }
13945104a1f6SAnthony Liguori     return TRUE;
13955104a1f6SAnthony Liguori }
13965104a1f6SAnthony Liguori 
13976db253caSJan Kiszka static gboolean gd_focus_out_event(GtkWidget *widget,
1398e3500d1fSGerd Hoffmann                                    GdkEventCrossing *crossing, gpointer opaque)
13996db253caSJan Kiszka {
1400e3500d1fSGerd Hoffmann     VirtualConsole *vc = opaque;
1401e3500d1fSGerd Hoffmann     GtkDisplayState *s = vc->s;
14026db253caSJan Kiszka 
14036db253caSJan Kiszka     gtk_release_modifiers(s);
14046db253caSJan Kiszka     return TRUE;
14056db253caSJan Kiszka }
14066db253caSJan Kiszka 
1407d861def3SAnthony Liguori /** Virtual Console Callbacks **/
1408d861def3SAnthony Liguori 
1409ed1132e4SGerd Hoffmann static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
1410cdeb7090SGerd Hoffmann                                int idx, GSList *group, GtkWidget *view_menu)
1411ed1132e4SGerd Hoffmann {
1412ed1132e4SGerd Hoffmann     char path[32];
1413ed1132e4SGerd Hoffmann 
1414ed1132e4SGerd Hoffmann     snprintf(path, sizeof(path), "<QEMU>/View/VC%d", idx);
1415ed1132e4SGerd Hoffmann 
1416cdeb7090SGerd Hoffmann     vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
1417ed1132e4SGerd Hoffmann     group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
1418ed1132e4SGerd Hoffmann     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path);
1419ed1132e4SGerd Hoffmann     gtk_accel_map_add_entry(path, GDK_KEY_1 + idx, HOTKEY_MODIFIERS);
1420ed1132e4SGerd Hoffmann 
1421ed1132e4SGerd Hoffmann     g_signal_connect(vc->menu_item, "activate",
1422ed1132e4SGerd Hoffmann                      G_CALLBACK(gd_menu_switch_vc), s);
1423ed1132e4SGerd Hoffmann     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
1424ed1132e4SGerd Hoffmann 
1425ed1132e4SGerd Hoffmann     return group;
1426ed1132e4SGerd Hoffmann }
1427ed1132e4SGerd Hoffmann 
1428ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
14290fb20d1cSCole Robinson static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
14300fb20d1cSCole Robinson {
14310fb20d1cSCole Robinson     VirtualConsole *vc = opaque;
14320fb20d1cSCole Robinson 
14330fb20d1cSCole Robinson     if (gtk_adjustment_get_upper(adjustment) >
14340fb20d1cSCole Robinson         gtk_adjustment_get_page_size(adjustment)) {
1435271a25c0SGerd Hoffmann         gtk_widget_show(vc->vte.scrollbar);
14360fb20d1cSCole Robinson     } else {
1437271a25c0SGerd Hoffmann         gtk_widget_hide(vc->vte.scrollbar);
14380fb20d1cSCole Robinson     }
14390fb20d1cSCole Robinson }
14400fb20d1cSCole Robinson 
1441d861def3SAnthony Liguori static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
1442d861def3SAnthony Liguori {
1443d861def3SAnthony Liguori     VirtualConsole *vc = chr->opaque;
1444d861def3SAnthony Liguori 
1445271a25c0SGerd Hoffmann     vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
1446d4370741SCole Robinson     return len;
1447d861def3SAnthony Liguori }
1448d861def3SAnthony Liguori 
1449d861def3SAnthony Liguori static int nb_vcs;
1450d861def3SAnthony Liguori static CharDriverState *vcs[MAX_VCS];
1451d861def3SAnthony Liguori 
1452702ec69cSGerd Hoffmann static CharDriverState *gd_vc_handler(ChardevVC *unused)
1453d861def3SAnthony Liguori {
1454d861def3SAnthony Liguori     CharDriverState *chr;
1455d861def3SAnthony Liguori 
1456d861def3SAnthony Liguori     chr = g_malloc0(sizeof(*chr));
1457d861def3SAnthony Liguori     chr->chr_write = gd_vc_chr_write;
1458bd5c51eeSMichael Roth     /* defer OPENED events until our vc is fully initialized */
1459bd5c51eeSMichael Roth     chr->explicit_be_open = true;
1460d861def3SAnthony Liguori 
1461d861def3SAnthony Liguori     vcs[nb_vcs++] = chr;
1462d861def3SAnthony Liguori 
1463d861def3SAnthony Liguori     return chr;
1464d861def3SAnthony Liguori }
1465d861def3SAnthony Liguori 
1466d4370741SCole Robinson static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
1467d4370741SCole Robinson                          gpointer user_data)
1468d861def3SAnthony Liguori {
1469d4370741SCole Robinson     VirtualConsole *vc = user_data;
1470d861def3SAnthony Liguori 
1471271a25c0SGerd Hoffmann     qemu_chr_be_write(vc->vte.chr, (uint8_t  *)text, (unsigned int)size);
1472d861def3SAnthony Liguori     return TRUE;
1473d861def3SAnthony Liguori }
1474d861def3SAnthony Liguori 
1475ed1132e4SGerd Hoffmann static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
1476ed1132e4SGerd Hoffmann                               CharDriverState *chr, int idx,
1477e3500d1fSGerd Hoffmann                               GSList *group, GtkWidget *view_menu)
1478d861def3SAnthony Liguori {
1479d861def3SAnthony Liguori     char buffer[32];
14800fb20d1cSCole Robinson     GtkWidget *box;
14810fb20d1cSCole Robinson     GtkWidget *scrollbar;
14820fb20d1cSCole Robinson     GtkAdjustment *vadjustment;
1483d861def3SAnthony Liguori 
1484e3500d1fSGerd Hoffmann     vc->s = s;
1485ed1132e4SGerd Hoffmann     vc->vte.chr = chr;
1486d861def3SAnthony Liguori 
1487ed1132e4SGerd Hoffmann     snprintf(buffer, sizeof(buffer), "vc%d", idx);
1488cdeb7090SGerd Hoffmann     vc->label = g_strdup_printf("%s", vc->vte.chr->label
1489cdeb7090SGerd Hoffmann                                 ? vc->vte.chr->label : buffer);
1490cdeb7090SGerd Hoffmann     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1491d861def3SAnthony Liguori 
1492271a25c0SGerd Hoffmann     vc->vte.terminal = vte_terminal_new();
1493271a25c0SGerd Hoffmann     g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
1494d861def3SAnthony Liguori 
1495271a25c0SGerd Hoffmann     vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
149682fc1809SGerd Hoffmann     vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
149782fc1809SGerd Hoffmann                           VC_TERM_X_MIN, VC_TERM_Y_MIN);
1498d861def3SAnthony Liguori 
14990fb20d1cSCole Robinson #if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0)
1500271a25c0SGerd Hoffmann     vadjustment = gtk_scrollable_get_vadjustment
1501271a25c0SGerd Hoffmann         (GTK_SCROLLABLE(vc->vte.terminal));
15020fb20d1cSCole Robinson #else
1503271a25c0SGerd Hoffmann     vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
15040fb20d1cSCole Robinson #endif
1505d861def3SAnthony Liguori 
15060fb20d1cSCole Robinson #if GTK_CHECK_VERSION(3, 0, 0)
15070fb20d1cSCole Robinson     box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
15080fb20d1cSCole Robinson     scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
15090fb20d1cSCole Robinson #else
15100fb20d1cSCole Robinson     box = gtk_hbox_new(false, 2);
15110fb20d1cSCole Robinson     scrollbar = gtk_vscrollbar_new(vadjustment);
15120fb20d1cSCole Robinson #endif
15130fb20d1cSCole Robinson 
1514271a25c0SGerd Hoffmann     gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
15150fb20d1cSCole Robinson     gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);
15160fb20d1cSCole Robinson 
1517271a25c0SGerd Hoffmann     vc->vte.chr->opaque = vc;
1518271a25c0SGerd Hoffmann     vc->vte.box = box;
1519271a25c0SGerd Hoffmann     vc->vte.scrollbar = scrollbar;
15200fb20d1cSCole Robinson 
15210fb20d1cSCole Robinson     g_signal_connect(vadjustment, "changed",
15220fb20d1cSCole Robinson                      G_CALLBACK(gd_vc_adjustment_changed), vc);
15230fb20d1cSCole Robinson 
1524e3500d1fSGerd Hoffmann     vc->type = GD_VC_VTE;
1525271a25c0SGerd Hoffmann     vc->tab_item = box;
1526271a25c0SGerd Hoffmann     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
1527cdeb7090SGerd Hoffmann                              gtk_label_new(vc->label));
1528d861def3SAnthony Liguori 
1529271a25c0SGerd Hoffmann     qemu_chr_be_generic_open(vc->vte.chr);
1530271a25c0SGerd Hoffmann     if (vc->vte.chr->init) {
1531271a25c0SGerd Hoffmann         vc->vte.chr->init(vc->vte.chr);
1532d861def3SAnthony Liguori     }
1533d861def3SAnthony Liguori 
1534d861def3SAnthony Liguori     return group;
1535a4ccabcfSAnthony Liguori }
1536a4ccabcfSAnthony Liguori 
1537ed1132e4SGerd Hoffmann static void gd_vcs_init(GtkDisplayState *s, GSList *group,
1538ee5f31e4SGerd Hoffmann                         GtkWidget *view_menu)
1539ee5f31e4SGerd Hoffmann {
1540ee5f31e4SGerd Hoffmann     int i;
1541ee5f31e4SGerd Hoffmann 
1542ee5f31e4SGerd Hoffmann     for (i = 0; i < nb_vcs; i++) {
1543ed1132e4SGerd Hoffmann         VirtualConsole *vc = &s->vc[s->nb_vcs];
1544ed1132e4SGerd Hoffmann         group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu);
1545ee5f31e4SGerd Hoffmann         s->nb_vcs++;
1546ee5f31e4SGerd Hoffmann     }
1547ee5f31e4SGerd Hoffmann }
1548ee5f31e4SGerd Hoffmann #endif /* CONFIG_VTE */
1549ee5f31e4SGerd Hoffmann 
1550a4ccabcfSAnthony Liguori /** Window Creation **/
1551a4ccabcfSAnthony Liguori 
1552e3500d1fSGerd Hoffmann static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
1553e3500d1fSGerd Hoffmann {
1554e3500d1fSGerd Hoffmann #if GTK_CHECK_VERSION(3, 0, 0)
1555e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "draw",
1556e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_draw_event), vc);
1557e3500d1fSGerd Hoffmann #else
1558e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "expose-event",
1559e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_expose_event), vc);
1560e3500d1fSGerd Hoffmann #endif
1561e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "event",
1562e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_event), vc);
1563e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "button-press-event",
1564e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_button_event), vc);
1565e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "button-release-event",
1566e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_button_event), vc);
1567e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "scroll-event",
1568e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_scroll_event), vc);
1569e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "key-press-event",
1570e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_key_event), vc);
1571e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "key-release-event",
1572e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_key_event), vc);
1573e3500d1fSGerd Hoffmann 
1574e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "enter-notify-event",
1575e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_enter_event), vc);
1576e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "leave-notify-event",
1577e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_leave_event), vc);
1578e3500d1fSGerd Hoffmann     g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
1579e3500d1fSGerd Hoffmann                      G_CALLBACK(gd_focus_out_event), vc);
1580e3500d1fSGerd Hoffmann }
1581e3500d1fSGerd Hoffmann 
1582a4ccabcfSAnthony Liguori static void gd_connect_signals(GtkDisplayState *s)
1583a4ccabcfSAnthony Liguori {
1584a4ccabcfSAnthony Liguori     g_signal_connect(s->show_tabs_item, "activate",
1585a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_show_tabs), s);
1586cdeb7090SGerd Hoffmann     g_signal_connect(s->untabify_item, "activate",
1587cdeb7090SGerd Hoffmann                      G_CALLBACK(gd_menu_untabify), s);
1588a4ccabcfSAnthony Liguori 
1589a4ccabcfSAnthony Liguori     g_signal_connect(s->window, "delete-event",
1590a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_window_close), s);
1591a4ccabcfSAnthony Liguori 
159230e8f22bSJan Kiszka     g_signal_connect(s->pause_item, "activate",
159330e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_pause), s);
159430e8f22bSJan Kiszka     g_signal_connect(s->reset_item, "activate",
159530e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_reset), s);
159630e8f22bSJan Kiszka     g_signal_connect(s->powerdown_item, "activate",
159730e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_powerdown), s);
1598a4ccabcfSAnthony Liguori     g_signal_connect(s->quit_item, "activate",
1599a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_quit), s);
1600c6158483SAnthony Liguori     g_signal_connect(s->full_screen_item, "activate",
1601c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_full_screen), s);
1602c6158483SAnthony Liguori     g_signal_connect(s->zoom_in_item, "activate",
1603c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_in), s);
1604c6158483SAnthony Liguori     g_signal_connect(s->zoom_out_item, "activate",
1605c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_out), s);
1606c6158483SAnthony Liguori     g_signal_connect(s->zoom_fixed_item, "activate",
1607c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_fixed), s);
1608c6158483SAnthony Liguori     g_signal_connect(s->zoom_fit_item, "activate",
1609c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_fit), s);
16105104a1f6SAnthony Liguori     g_signal_connect(s->grab_item, "activate",
16115104a1f6SAnthony Liguori                      G_CALLBACK(gd_menu_grab_input), s);
1612a4ccabcfSAnthony Liguori     g_signal_connect(s->notebook, "switch-page",
1613a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_change_page), s);
1614a4ccabcfSAnthony Liguori }
1615a4ccabcfSAnthony Liguori 
1616bf9b255fSAnthony Liguori static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *accel_group)
1617a4ccabcfSAnthony Liguori {
1618bf9b255fSAnthony Liguori     GtkWidget *machine_menu;
1619a4ccabcfSAnthony Liguori     GtkWidget *separator;
1620a4ccabcfSAnthony Liguori 
1621bf9b255fSAnthony Liguori     machine_menu = gtk_menu_new();
1622bf9b255fSAnthony Liguori     gtk_menu_set_accel_group(GTK_MENU(machine_menu), accel_group);
162330e8f22bSJan Kiszka 
162430e8f22bSJan Kiszka     s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
1625bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
162630e8f22bSJan Kiszka 
162730e8f22bSJan Kiszka     separator = gtk_separator_menu_item_new();
1628bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
162930e8f22bSJan Kiszka 
16309068f20dSCole Robinson     s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset"));
1631bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
163230e8f22bSJan Kiszka 
16339068f20dSCole Robinson     s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down"));
1634bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
163530e8f22bSJan Kiszka 
163630e8f22bSJan Kiszka     separator = gtk_separator_menu_item_new();
1637bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
1638a4ccabcfSAnthony Liguori 
16393d914488SCole Robinson     s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
1640a4ccabcfSAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
164130e8f22bSJan Kiszka                                  "<QEMU>/Machine/Quit");
16423d914488SCole Robinson     gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
1643db1da1f2SCole Robinson                             GDK_KEY_q, HOTKEY_MODIFIERS);
1644bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
1645a4ccabcfSAnthony Liguori 
1646bf9b255fSAnthony Liguori     return machine_menu;
1647bf9b255fSAnthony Liguori }
1648bf9b255fSAnthony Liguori 
1649e3500d1fSGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = {
1650e3500d1fSGerd Hoffmann     .dpy_name          = "gtk",
1651e3500d1fSGerd Hoffmann     .dpy_gfx_update    = gd_update,
1652e3500d1fSGerd Hoffmann     .dpy_gfx_switch    = gd_switch,
1653e3500d1fSGerd Hoffmann     .dpy_refresh       = gd_refresh,
1654e3500d1fSGerd Hoffmann     .dpy_mouse_set     = gd_mouse_set,
1655e3500d1fSGerd Hoffmann     .dpy_cursor_define = gd_cursor_define,
1656e3500d1fSGerd Hoffmann };
1657e3500d1fSGerd Hoffmann 
1658e3500d1fSGerd Hoffmann static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
1659ed1132e4SGerd Hoffmann                               QemuConsole *con, int idx,
1660e3500d1fSGerd Hoffmann                               GSList *group, GtkWidget *view_menu)
1661e3500d1fSGerd Hoffmann {
16626a24ced5SGerd Hoffmann     Error *local_err = NULL;
16636a24ced5SGerd Hoffmann     Object *obj;
16646a24ced5SGerd Hoffmann 
16656a24ced5SGerd Hoffmann     obj = object_property_get_link(OBJECT(con), "device", &local_err);
16666a24ced5SGerd Hoffmann     if (obj) {
1667cdeb7090SGerd Hoffmann         vc->label = g_strdup_printf("%s", object_get_typename(obj));
1668cdeb7090SGerd Hoffmann     } else {
1669cdeb7090SGerd Hoffmann         vc->label = g_strdup_printf("VGA");
16706a24ced5SGerd Hoffmann     }
16716a24ced5SGerd Hoffmann 
1672e3500d1fSGerd Hoffmann     vc->s = s;
1673e3500d1fSGerd Hoffmann     vc->gfx.scale_x = 1.0;
1674e3500d1fSGerd Hoffmann     vc->gfx.scale_y = 1.0;
1675e3500d1fSGerd Hoffmann 
1676e3500d1fSGerd Hoffmann     vc->gfx.drawing_area = gtk_drawing_area_new();
1677e3500d1fSGerd Hoffmann     gtk_widget_add_events(vc->gfx.drawing_area,
1678e3500d1fSGerd Hoffmann                           GDK_POINTER_MOTION_MASK |
1679e3500d1fSGerd Hoffmann                           GDK_BUTTON_PRESS_MASK |
1680e3500d1fSGerd Hoffmann                           GDK_BUTTON_RELEASE_MASK |
1681e3500d1fSGerd Hoffmann                           GDK_BUTTON_MOTION_MASK |
1682e3500d1fSGerd Hoffmann                           GDK_ENTER_NOTIFY_MASK |
1683e3500d1fSGerd Hoffmann                           GDK_LEAVE_NOTIFY_MASK |
1684e3500d1fSGerd Hoffmann                           GDK_SCROLL_MASK |
1685e3500d1fSGerd Hoffmann                           GDK_KEY_PRESS_MASK);
1686e3500d1fSGerd Hoffmann     gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
1687e3500d1fSGerd Hoffmann     gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
1688e3500d1fSGerd Hoffmann 
1689e3500d1fSGerd Hoffmann     vc->type = GD_VC_GFX;
1690e3500d1fSGerd Hoffmann     vc->tab_item = vc->gfx.drawing_area;
1691e3500d1fSGerd Hoffmann     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
1692cdeb7090SGerd Hoffmann                              vc->tab_item, gtk_label_new(vc->label));
1693e3500d1fSGerd Hoffmann     gd_connect_vc_gfx_signals(vc);
1694e3500d1fSGerd Hoffmann 
1695cdeb7090SGerd Hoffmann     group = gd_vc_menu_init(s, vc, idx, group, view_menu);
1696e3500d1fSGerd Hoffmann 
1697e3500d1fSGerd Hoffmann     vc->gfx.dcl.ops = &dcl_ops;
1698e3500d1fSGerd Hoffmann     vc->gfx.dcl.con = con;
1699e3500d1fSGerd Hoffmann     register_displaychangelistener(&vc->gfx.dcl);
1700e3500d1fSGerd Hoffmann 
1701e3500d1fSGerd Hoffmann     return group;
1702e3500d1fSGerd Hoffmann }
1703e3500d1fSGerd Hoffmann 
1704bf9b255fSAnthony Liguori static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_group)
1705bf9b255fSAnthony Liguori {
1706bf9b255fSAnthony Liguori     GSList *group = NULL;
1707bf9b255fSAnthony Liguori     GtkWidget *view_menu;
1708bf9b255fSAnthony Liguori     GtkWidget *separator;
1709ed1132e4SGerd Hoffmann     QemuConsole *con;
1710ed1132e4SGerd Hoffmann     int vc;
1711bf9b255fSAnthony Liguori 
1712bf9b255fSAnthony Liguori     view_menu = gtk_menu_new();
1713bf9b255fSAnthony Liguori     gtk_menu_set_accel_group(GTK_MENU(view_menu), accel_group);
1714a4ccabcfSAnthony Liguori 
17153d914488SCole Robinson     s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen"));
1716c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->full_screen_item),
1717c6158483SAnthony Liguori                                  "<QEMU>/View/Full Screen");
1718b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Full Screen", GDK_KEY_f,
1719b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
1720bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
1721c6158483SAnthony Liguori 
1722c6158483SAnthony Liguori     separator = gtk_separator_menu_item_new();
1723bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1724c6158483SAnthony Liguori 
17253d914488SCole Robinson     s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In"));
1726c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
1727c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom In");
1728b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
1729b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
1730bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
1731c6158483SAnthony Liguori 
17323d914488SCole Robinson     s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
1733c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
1734c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom Out");
1735b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus,
1736b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
1737bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
1738c6158483SAnthony Liguori 
17393d914488SCole Robinson     s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit"));
1740c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
1741c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom Fixed");
1742b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0,
1743b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
1744bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
1745c6158483SAnthony Liguori 
1746834574eaSAnthony Liguori     s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
1747bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
1748c6158483SAnthony Liguori 
1749c6158483SAnthony Liguori     separator = gtk_separator_menu_item_new();
1750bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1751c6158483SAnthony Liguori 
1752834574eaSAnthony Liguori     s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
1753bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
17545104a1f6SAnthony Liguori 
1755834574eaSAnthony Liguori     s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
17565104a1f6SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
17575104a1f6SAnthony Liguori                                  "<QEMU>/View/Grab Input");
1758b1e749c0SJan Kiszka     gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g,
1759b1e749c0SJan Kiszka                             HOTKEY_MODIFIERS);
1760bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
17615104a1f6SAnthony Liguori 
1762a4ccabcfSAnthony Liguori     separator = gtk_separator_menu_item_new();
1763bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1764a4ccabcfSAnthony Liguori 
1765e3500d1fSGerd Hoffmann     /* gfx */
1766ed1132e4SGerd Hoffmann     for (vc = 0;; vc++) {
1767ed1132e4SGerd Hoffmann         con = qemu_console_lookup_by_index(vc);
1768ed1132e4SGerd Hoffmann         if (!con || !qemu_console_is_graphic(con)) {
1769ed1132e4SGerd Hoffmann             break;
1770ed1132e4SGerd Hoffmann         }
1771ed1132e4SGerd Hoffmann         group = gd_vc_gfx_init(s, &s->vc[vc], con,
1772ed1132e4SGerd Hoffmann                                vc, group, view_menu);
1773ed1132e4SGerd Hoffmann         s->nb_vcs++;
1774ed1132e4SGerd Hoffmann     }
1775a4ccabcfSAnthony Liguori 
1776ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
1777e3500d1fSGerd Hoffmann     /* vte */
1778ed1132e4SGerd Hoffmann     gd_vcs_init(s, group, view_menu);
1779ee5f31e4SGerd Hoffmann #endif
1780d861def3SAnthony Liguori 
1781a4ccabcfSAnthony Liguori     separator = gtk_separator_menu_item_new();
1782bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
1783a4ccabcfSAnthony Liguori 
1784834574eaSAnthony Liguori     s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
1785bf9b255fSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
1786a4ccabcfSAnthony Liguori 
1787cdeb7090SGerd Hoffmann     s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
1788cdeb7090SGerd Hoffmann     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
1789cdeb7090SGerd Hoffmann 
1790bf9b255fSAnthony Liguori     return view_menu;
1791bf9b255fSAnthony Liguori }
1792a4ccabcfSAnthony Liguori 
1793bf9b255fSAnthony Liguori static void gd_create_menus(GtkDisplayState *s)
1794bf9b255fSAnthony Liguori {
1795bf9b255fSAnthony Liguori     GtkAccelGroup *accel_group;
1796bf9b255fSAnthony Liguori 
1797bf9b255fSAnthony Liguori     accel_group = gtk_accel_group_new();
1798bf9b255fSAnthony Liguori     s->machine_menu = gd_create_menu_machine(s, accel_group);
1799bf9b255fSAnthony Liguori     s->view_menu = gd_create_menu_view(s, accel_group);
1800bf9b255fSAnthony Liguori 
1801bf9b255fSAnthony Liguori     s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
180230e8f22bSJan Kiszka     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
180330e8f22bSJan Kiszka                               s->machine_menu);
180430e8f22bSJan Kiszka     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
1805a4ccabcfSAnthony Liguori 
1806bf9b255fSAnthony Liguori     s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
1807a4ccabcfSAnthony Liguori     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
1808a4ccabcfSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
1809bf9b255fSAnthony Liguori 
1810bf9b255fSAnthony Liguori     g_object_set_data(G_OBJECT(s->window), "accel_group", accel_group);
1811bf9b255fSAnthony Liguori     gtk_window_add_accel_group(GTK_WINDOW(s->window), accel_group);
1812bf9b255fSAnthony Liguori     s->accel_group = accel_group;
1813a4ccabcfSAnthony Liguori }
1814a4ccabcfSAnthony Liguori 
18153158a348SBruce Rogers static void gd_set_keycode_type(GtkDisplayState *s)
18163158a348SBruce Rogers {
1817*0a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_X11
18183158a348SBruce Rogers     GdkDisplay *display = gtk_widget_get_display(s->window);
1819*0a337ed0SGerd Hoffmann     if (GDK_IS_X11_DISPLAY(display)) {
18203158a348SBruce Rogers         Display *x11_display = gdk_x11_display_get_xdisplay(display);
18213158a348SBruce Rogers         XkbDescPtr desc = XkbGetKeyboard(x11_display, XkbGBN_AllComponentsMask,
18223158a348SBruce Rogers                                          XkbUseCoreKbd);
1823*0a337ed0SGerd Hoffmann         char *keycodes = NULL;
18243158a348SBruce Rogers 
18253158a348SBruce Rogers         if (desc && desc->names) {
18263158a348SBruce Rogers             keycodes = XGetAtomName(x11_display, desc->names->keycodes);
18273158a348SBruce Rogers         }
18283158a348SBruce Rogers         if (keycodes == NULL) {
18293158a348SBruce Rogers             fprintf(stderr, "could not lookup keycode name\n");
18303158a348SBruce Rogers         } else if (strstart(keycodes, "evdev", NULL)) {
18313158a348SBruce Rogers             s->has_evdev = true;
18323158a348SBruce Rogers         } else if (!strstart(keycodes, "xfree86", NULL)) {
18333158a348SBruce Rogers             fprintf(stderr, "unknown keycodes `%s', please report to "
18343158a348SBruce Rogers                     "qemu-devel@nongnu.org\n", keycodes);
18353158a348SBruce Rogers         }
1836*0a337ed0SGerd Hoffmann     }
18373158a348SBruce Rogers #endif
18383158a348SBruce Rogers }
18393158a348SBruce Rogers 
1840881249c7SJan Kiszka void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
1841a4ccabcfSAnthony Liguori {
1842a4ccabcfSAnthony Liguori     GtkDisplayState *s = g_malloc0(sizeof(*s));
1843d819cdccSStefan Weil     char *filename;
1844a4ccabcfSAnthony Liguori 
1845a4ccabcfSAnthony Liguori     gtk_init(NULL, NULL);
1846a4ccabcfSAnthony Liguori 
1847a4ccabcfSAnthony Liguori     s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
184851572ab0SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 2, 0)
184951572ab0SDaniel P. Berrange     s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
185051572ab0SDaniel P. Berrange #else
1851a4ccabcfSAnthony Liguori     s->vbox = gtk_vbox_new(FALSE, 0);
185251572ab0SDaniel P. Berrange #endif
1853a4ccabcfSAnthony Liguori     s->notebook = gtk_notebook_new();
1854a4ccabcfSAnthony Liguori     s->menu_bar = gtk_menu_bar_new();
1855a4ccabcfSAnthony Liguori 
1856c6158483SAnthony Liguori     s->free_scale = FALSE;
1857a4ccabcfSAnthony Liguori 
1858834574eaSAnthony Liguori     setlocale(LC_ALL, "");
1859834574eaSAnthony Liguori     bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
1860834574eaSAnthony Liguori     textdomain("qemu");
1861834574eaSAnthony Liguori 
1862a4ccabcfSAnthony Liguori     s->null_cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
1863a4ccabcfSAnthony Liguori 
1864a4ccabcfSAnthony Liguori     s->mouse_mode_notifier.notify = gd_mouse_mode_change;
1865a4ccabcfSAnthony Liguori     qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
1866a4ccabcfSAnthony Liguori     qemu_add_vm_change_state_handler(gd_change_runstate, s);
1867a4ccabcfSAnthony Liguori 
1868f7da9c17SAnthony Liguori     filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg");
1869d819cdccSStefan Weil     if (filename) {
1870d819cdccSStefan Weil         GError *error = NULL;
1871d819cdccSStefan Weil         GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error);
1872d819cdccSStefan Weil         if (pixbuf) {
1873d819cdccSStefan Weil             gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf);
1874d819cdccSStefan Weil         } else {
1875d819cdccSStefan Weil             g_error_free(error);
1876d819cdccSStefan Weil         }
1877d819cdccSStefan Weil         g_free(filename);
1878d819cdccSStefan Weil     }
1879d819cdccSStefan Weil 
1880a4ccabcfSAnthony Liguori     gd_create_menus(s);
1881a4ccabcfSAnthony Liguori 
1882a4ccabcfSAnthony Liguori     gd_connect_signals(s);
1883a4ccabcfSAnthony Liguori 
1884a4ccabcfSAnthony Liguori     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1885a4ccabcfSAnthony Liguori     gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
1886a4ccabcfSAnthony Liguori 
1887a4ccabcfSAnthony Liguori     gd_update_caption(s);
1888a4ccabcfSAnthony Liguori 
1889a4ccabcfSAnthony Liguori     gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
1890a4ccabcfSAnthony Liguori     gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
1891a4ccabcfSAnthony Liguori 
1892a4ccabcfSAnthony Liguori     gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
1893a4ccabcfSAnthony Liguori 
1894a4ccabcfSAnthony Liguori     gtk_widget_show_all(s->window);
1895a4ccabcfSAnthony Liguori 
18966fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK
18976fa27697SGerd Hoffmann     {
18986fa27697SGerd Hoffmann         VirtualConsole *cur = gd_vc_find_current(s);
18996fa27697SGerd Hoffmann         int i;
19006fa27697SGerd Hoffmann 
19016fa27697SGerd Hoffmann         for (i = 0; i < s->nb_vcs; i++) {
19026fa27697SGerd Hoffmann             VirtualConsole *vc = &s->vc[i];
19036fa27697SGerd Hoffmann             if (vc && vc->type == GD_VC_VTE && vc != cur) {
19046fa27697SGerd Hoffmann                 gtk_widget_hide(vc->vte.terminal);
19056fa27697SGerd Hoffmann             }
19066fa27697SGerd Hoffmann         }
19076fa27697SGerd Hoffmann         gd_update_windowsize(cur);
19086fa27697SGerd Hoffmann     }
19096fa27697SGerd Hoffmann #endif
19106fa27697SGerd Hoffmann 
1911787ba4f0SPeter Wu     if (full_screen) {
1912787ba4f0SPeter Wu         gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
1913787ba4f0SPeter Wu     }
1914881249c7SJan Kiszka     if (grab_on_hover) {
1915881249c7SJan Kiszka         gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
1916881249c7SJan Kiszka     }
1917787ba4f0SPeter Wu 
19183158a348SBruce Rogers     gd_set_keycode_type(s);
1919a4ccabcfSAnthony Liguori }
1920ee5f31e4SGerd Hoffmann 
1921ee5f31e4SGerd Hoffmann void early_gtk_display_init(void)
1922ee5f31e4SGerd Hoffmann {
1923ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE)
1924ee5f31e4SGerd Hoffmann     register_vc_handler(gd_vc_handler);
1925ee5f31e4SGerd Hoffmann #endif
1926ee5f31e4SGerd Hoffmann }
1927