xref: /qemu/ui/gtk.c (revision d819cdccffe08f6e12b4c2dec8f4aa381f0cd687)
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 
37c95e3080SKevin Wolf #include "qemu-common.h"
38c95e3080SKevin Wolf 
39c95e3080SKevin Wolf #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
40c95e3080SKevin Wolf /* Work around an -Wstrict-prototypes warning in GTK headers */
41c95e3080SKevin Wolf #pragma GCC diagnostic ignored "-Wstrict-prototypes"
42c95e3080SKevin Wolf #endif
43a4ccabcfSAnthony Liguori #include <gtk/gtk.h>
44c95e3080SKevin Wolf #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
45c95e3080SKevin Wolf #pragma GCC diagnostic error "-Wstrict-prototypes"
46c95e3080SKevin Wolf #endif
47c95e3080SKevin Wolf 
48c95e3080SKevin Wolf 
49a4ccabcfSAnthony Liguori #include <gdk/gdkkeysyms.h>
50834574eaSAnthony Liguori #include <glib/gi18n.h>
513f58eadeSStefan Weil #include <locale.h>
52a4ccabcfSAnthony Liguori #include <vte/vte.h>
53a4ccabcfSAnthony Liguori #include <sys/types.h>
54a4ccabcfSAnthony Liguori #include <sys/socket.h>
55a4ccabcfSAnthony Liguori #include <sys/un.h>
56a4ccabcfSAnthony Liguori #include <sys/wait.h>
57a4ccabcfSAnthony Liguori #include <pty.h>
58a4ccabcfSAnthony Liguori #include <math.h>
59a4ccabcfSAnthony Liguori 
60a4ccabcfSAnthony Liguori #include "ui/console.h"
61a4ccabcfSAnthony Liguori #include "sysemu/sysemu.h"
62a4ccabcfSAnthony Liguori #include "qmp-commands.h"
63a4ccabcfSAnthony Liguori #include "x_keymap.h"
64a4ccabcfSAnthony Liguori #include "keymaps.h"
65d861def3SAnthony Liguori #include "char/char.h"
66a4ccabcfSAnthony Liguori 
67a4ccabcfSAnthony Liguori //#define DEBUG_GTK
68a4ccabcfSAnthony Liguori 
69a4ccabcfSAnthony Liguori #ifdef DEBUG_GTK
70a4ccabcfSAnthony Liguori #define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
71a4ccabcfSAnthony Liguori #else
72a4ccabcfSAnthony Liguori #define DPRINTF(fmt, ...) do { } while (0)
73a4ccabcfSAnthony Liguori #endif
74a4ccabcfSAnthony Liguori 
75d861def3SAnthony Liguori #define MAX_VCS 10
76d861def3SAnthony Liguori 
77cba68834SDaniel P. Berrange 
78cba68834SDaniel P. Berrange /* Compatibility define to let us build on both Gtk2 and Gtk3 */
79cba68834SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
80cba68834SDaniel P. Berrange static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh)
81cba68834SDaniel P. Berrange {
82cba68834SDaniel P. Berrange     *ww = gdk_window_get_width(w);
83cba68834SDaniel P. Berrange     *wh = gdk_window_get_height(w);
84cba68834SDaniel P. Berrange }
85cba68834SDaniel P. Berrange #endif
86cba68834SDaniel P. Berrange 
87ef6413a2SDaniel P. Berrange #if !GTK_CHECK_VERSION(2, 20, 0)
88ef6413a2SDaniel P. Berrange #define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget)
89ef6413a2SDaniel P. Berrange #endif
90ef6413a2SDaniel P. Berrange 
91bc0477c7SDaniel P. Berrange #ifndef GDK_KEY_0
92bc0477c7SDaniel P. Berrange #define GDK_KEY_0 GDK_0
93bc0477c7SDaniel P. Berrange #define GDK_KEY_1 GDK_1
94bc0477c7SDaniel P. Berrange #define GDK_KEY_2 GDK_2
95bc0477c7SDaniel P. Berrange #define GDK_KEY_f GDK_f
96bc0477c7SDaniel P. Berrange #define GDK_KEY_g GDK_g
97bc0477c7SDaniel P. Berrange #define GDK_KEY_plus GDK_plus
98bc0477c7SDaniel P. Berrange #define GDK_KEY_minus GDK_minus
99bc0477c7SDaniel P. Berrange #endif
100ef6413a2SDaniel P. Berrange 
1016db253caSJan Kiszka static const int modifier_keycode[] = {
1026db253caSJan Kiszka     /* shift, control, alt keys, meta keys, both left & right */
1036db253caSJan Kiszka     0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd,
1046db253caSJan Kiszka };
1056db253caSJan Kiszka 
106a4ccabcfSAnthony Liguori typedef struct VirtualConsole
107a4ccabcfSAnthony Liguori {
108a4ccabcfSAnthony Liguori     GtkWidget *menu_item;
109a4ccabcfSAnthony Liguori     GtkWidget *terminal;
110a4ccabcfSAnthony Liguori     GtkWidget *scrolled_window;
111a4ccabcfSAnthony Liguori     CharDriverState *chr;
112a4ccabcfSAnthony Liguori     int fd;
113a4ccabcfSAnthony Liguori } VirtualConsole;
114a4ccabcfSAnthony Liguori 
115a4ccabcfSAnthony Liguori typedef struct GtkDisplayState
116a4ccabcfSAnthony Liguori {
117a4ccabcfSAnthony Liguori     GtkWidget *window;
118a4ccabcfSAnthony Liguori 
119a4ccabcfSAnthony Liguori     GtkWidget *menu_bar;
120a4ccabcfSAnthony Liguori 
12173d4dc71SAnthony Liguori     GtkAccelGroup *accel_group;
12273d4dc71SAnthony Liguori 
12330e8f22bSJan Kiszka     GtkWidget *machine_menu_item;
12430e8f22bSJan Kiszka     GtkWidget *machine_menu;
12530e8f22bSJan Kiszka     GtkWidget *pause_item;
12630e8f22bSJan Kiszka     GtkWidget *reset_item;
12730e8f22bSJan Kiszka     GtkWidget *powerdown_item;
128a4ccabcfSAnthony Liguori     GtkWidget *quit_item;
129a4ccabcfSAnthony Liguori 
130a4ccabcfSAnthony Liguori     GtkWidget *view_menu_item;
131a4ccabcfSAnthony Liguori     GtkWidget *view_menu;
132c6158483SAnthony Liguori     GtkWidget *full_screen_item;
133c6158483SAnthony Liguori     GtkWidget *zoom_in_item;
134c6158483SAnthony Liguori     GtkWidget *zoom_out_item;
135c6158483SAnthony Liguori     GtkWidget *zoom_fixed_item;
136c6158483SAnthony Liguori     GtkWidget *zoom_fit_item;
1375104a1f6SAnthony Liguori     GtkWidget *grab_item;
1385104a1f6SAnthony Liguori     GtkWidget *grab_on_hover_item;
139a4ccabcfSAnthony Liguori     GtkWidget *vga_item;
140a4ccabcfSAnthony Liguori 
141d861def3SAnthony Liguori     int nb_vcs;
142d861def3SAnthony Liguori     VirtualConsole vc[MAX_VCS];
143d861def3SAnthony Liguori 
144a4ccabcfSAnthony Liguori     GtkWidget *show_tabs_item;
145a4ccabcfSAnthony Liguori 
146a4ccabcfSAnthony Liguori     GtkWidget *vbox;
147a4ccabcfSAnthony Liguori     GtkWidget *notebook;
148a4ccabcfSAnthony Liguori     GtkWidget *drawing_area;
149a4ccabcfSAnthony Liguori     cairo_surface_t *surface;
150a4ccabcfSAnthony Liguori     DisplayChangeListener dcl;
1519d9801cfSGerd Hoffmann     DisplaySurface *ds;
152a4ccabcfSAnthony Liguori     int button_mask;
153a4ccabcfSAnthony Liguori     int last_x;
154a4ccabcfSAnthony Liguori     int last_y;
155a4ccabcfSAnthony Liguori 
156a4ccabcfSAnthony Liguori     double scale_x;
157a4ccabcfSAnthony Liguori     double scale_y;
158c6158483SAnthony Liguori     gboolean full_screen;
159a4ccabcfSAnthony Liguori 
160a4ccabcfSAnthony Liguori     GdkCursor *null_cursor;
161a4ccabcfSAnthony Liguori     Notifier mouse_mode_notifier;
162c6158483SAnthony Liguori     gboolean free_scale;
16330e8f22bSJan Kiszka 
16430e8f22bSJan Kiszka     bool external_pause_update;
1656db253caSJan Kiszka 
1666db253caSJan Kiszka     bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
167a4ccabcfSAnthony Liguori } GtkDisplayState;
168a4ccabcfSAnthony Liguori 
169a4ccabcfSAnthony Liguori static GtkDisplayState *global_state;
170a4ccabcfSAnthony Liguori 
171a4ccabcfSAnthony Liguori /** Utility Functions **/
172a4ccabcfSAnthony Liguori 
1735104a1f6SAnthony Liguori static bool gd_is_grab_active(GtkDisplayState *s)
1745104a1f6SAnthony Liguori {
1755104a1f6SAnthony Liguori     return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
1765104a1f6SAnthony Liguori }
1775104a1f6SAnthony Liguori 
1785104a1f6SAnthony Liguori static bool gd_grab_on_hover(GtkDisplayState *s)
1795104a1f6SAnthony Liguori {
1805104a1f6SAnthony Liguori     return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
1815104a1f6SAnthony Liguori }
1825104a1f6SAnthony Liguori 
1835104a1f6SAnthony Liguori static bool gd_on_vga(GtkDisplayState *s)
1845104a1f6SAnthony Liguori {
1855104a1f6SAnthony Liguori     return gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)) == 0;
1865104a1f6SAnthony Liguori }
1875104a1f6SAnthony Liguori 
188a4ccabcfSAnthony Liguori static void gd_update_cursor(GtkDisplayState *s, gboolean override)
189a4ccabcfSAnthony Liguori {
190a4ccabcfSAnthony Liguori     GdkWindow *window;
191a4ccabcfSAnthony Liguori     bool on_vga;
192a4ccabcfSAnthony Liguori 
193a4ccabcfSAnthony Liguori     window = gtk_widget_get_window(GTK_WIDGET(s->drawing_area));
194a4ccabcfSAnthony Liguori 
1955104a1f6SAnthony Liguori     on_vga = gd_on_vga(s);
196a4ccabcfSAnthony Liguori 
1975104a1f6SAnthony Liguori     if ((override || on_vga) &&
198c6158483SAnthony Liguori         (s->full_screen || kbd_mouse_is_absolute() || gd_is_grab_active(s))) {
199a4ccabcfSAnthony Liguori         gdk_window_set_cursor(window, s->null_cursor);
200a4ccabcfSAnthony Liguori     } else {
201a4ccabcfSAnthony Liguori         gdk_window_set_cursor(window, NULL);
202a4ccabcfSAnthony Liguori     }
203a4ccabcfSAnthony Liguori }
204a4ccabcfSAnthony Liguori 
205a4ccabcfSAnthony Liguori static void gd_update_caption(GtkDisplayState *s)
206a4ccabcfSAnthony Liguori {
207a4ccabcfSAnthony Liguori     const char *status = "";
208a4ccabcfSAnthony Liguori     gchar *title;
2095104a1f6SAnthony Liguori     const char *grab = "";
21030e8f22bSJan Kiszka     bool is_paused = !runstate_is_running();
2115104a1f6SAnthony Liguori 
2125104a1f6SAnthony Liguori     if (gd_is_grab_active(s)) {
213d8da9ee8SAurelien Jarno         grab = _(" - Press Ctrl+Alt+G to release grab");
2145104a1f6SAnthony Liguori     }
215a4ccabcfSAnthony Liguori 
21630e8f22bSJan Kiszka     if (is_paused) {
217d8da9ee8SAurelien Jarno         status = _(" [Paused]");
218a4ccabcfSAnthony Liguori     }
21930e8f22bSJan Kiszka     s->external_pause_update = true;
22030e8f22bSJan Kiszka     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
22130e8f22bSJan Kiszka                                    is_paused);
22230e8f22bSJan Kiszka     s->external_pause_update = false;
223a4ccabcfSAnthony Liguori 
224a4ccabcfSAnthony Liguori     if (qemu_name) {
2255104a1f6SAnthony Liguori         title = g_strdup_printf("QEMU (%s)%s%s", qemu_name, status, grab);
226a4ccabcfSAnthony Liguori     } else {
2275104a1f6SAnthony Liguori         title = g_strdup_printf("QEMU%s%s", status, grab);
228a4ccabcfSAnthony Liguori     }
229a4ccabcfSAnthony Liguori 
230a4ccabcfSAnthony Liguori     gtk_window_set_title(GTK_WINDOW(s->window), title);
231a4ccabcfSAnthony Liguori 
232a4ccabcfSAnthony Liguori     g_free(title);
233a4ccabcfSAnthony Liguori }
234a4ccabcfSAnthony Liguori 
2359d9801cfSGerd Hoffmann static void gd_update_windowsize(GtkDisplayState *s)
236a4ccabcfSAnthony Liguori {
237c6158483SAnthony Liguori     if (!s->full_screen) {
238c6158483SAnthony Liguori         GtkRequisition req;
239c6158483SAnthony Liguori         double sx, sy;
240c6158483SAnthony Liguori 
241c6158483SAnthony Liguori         if (s->free_scale) {
242c6158483SAnthony Liguori             sx = s->scale_x;
243c6158483SAnthony Liguori             sy = s->scale_y;
244c6158483SAnthony Liguori 
245c6158483SAnthony Liguori             s->scale_y = 1.0;
246c6158483SAnthony Liguori             s->scale_x = 1.0;
247c6158483SAnthony Liguori         } else {
248c6158483SAnthony Liguori             sx = 1.0;
249c6158483SAnthony Liguori             sy = 1.0;
250c6158483SAnthony Liguori         }
251c6158483SAnthony Liguori 
252a4ccabcfSAnthony Liguori         gtk_widget_set_size_request(s->drawing_area,
2539d9801cfSGerd Hoffmann                                     surface_width(s->ds) * s->scale_x,
2549d9801cfSGerd Hoffmann                                     surface_height(s->ds) * s->scale_y);
2551ed76b59SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
2561ed76b59SDaniel P. Berrange         gtk_widget_get_preferred_size(s->vbox, NULL, &req);
2571ed76b59SDaniel P. Berrange #else
258c6158483SAnthony Liguori         gtk_widget_size_request(s->vbox, &req);
2591ed76b59SDaniel P. Berrange #endif
260c6158483SAnthony Liguori 
261c6158483SAnthony Liguori         gtk_window_resize(GTK_WINDOW(s->window),
262c6158483SAnthony Liguori                           req.width * sx, req.height * sy);
263c6158483SAnthony Liguori     }
264a4ccabcfSAnthony Liguori }
265a4ccabcfSAnthony Liguori 
2669d9801cfSGerd Hoffmann static void gd_update_full_redraw(GtkDisplayState *s)
2679d9801cfSGerd Hoffmann {
2689d9801cfSGerd Hoffmann     int ww, wh;
2699d9801cfSGerd Hoffmann     gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
2709d9801cfSGerd Hoffmann     gtk_widget_queue_draw_area(s->drawing_area, 0, 0, ww, wh);
2719d9801cfSGerd Hoffmann }
2729d9801cfSGerd Hoffmann 
2736db253caSJan Kiszka static void gtk_release_modifiers(GtkDisplayState *s)
2746db253caSJan Kiszka {
2756db253caSJan Kiszka     int i, keycode;
2766db253caSJan Kiszka 
2776db253caSJan Kiszka     if (!gd_on_vga(s)) {
2786db253caSJan Kiszka         return;
2796db253caSJan Kiszka     }
2806db253caSJan Kiszka     for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
2816db253caSJan Kiszka         keycode = modifier_keycode[i];
2826db253caSJan Kiszka         if (!s->modifier_pressed[i]) {
2836db253caSJan Kiszka             continue;
2846db253caSJan Kiszka         }
2856db253caSJan Kiszka         if (keycode & SCANCODE_GREY) {
2866db253caSJan Kiszka             kbd_put_keycode(SCANCODE_EMUL0);
2876db253caSJan Kiszka         }
2886db253caSJan Kiszka         kbd_put_keycode(keycode | SCANCODE_UP);
2896db253caSJan Kiszka         s->modifier_pressed[i] = false;
2906db253caSJan Kiszka     }
2916db253caSJan Kiszka }
2926db253caSJan Kiszka 
2939d9801cfSGerd Hoffmann /** DisplayState Callbacks **/
2949d9801cfSGerd Hoffmann 
2959d9801cfSGerd Hoffmann static void gd_update(DisplayChangeListener *dcl,
296bc2ed970SGerd Hoffmann                       int x, int y, int w, int h)
2979d9801cfSGerd Hoffmann {
2989d9801cfSGerd Hoffmann     GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
2999d9801cfSGerd Hoffmann     int x1, x2, y1, y2;
3009d9801cfSGerd Hoffmann     int mx, my;
3019d9801cfSGerd Hoffmann     int fbw, fbh;
3029d9801cfSGerd Hoffmann     int ww, wh;
3039d9801cfSGerd Hoffmann 
3049d9801cfSGerd Hoffmann     DPRINTF("update(x=%d, y=%d, w=%d, h=%d)\n", x, y, w, h);
3059d9801cfSGerd Hoffmann 
3069d9801cfSGerd Hoffmann     x1 = floor(x * s->scale_x);
3079d9801cfSGerd Hoffmann     y1 = floor(y * s->scale_y);
3089d9801cfSGerd Hoffmann 
3099d9801cfSGerd Hoffmann     x2 = ceil(x * s->scale_x + w * s->scale_x);
3109d9801cfSGerd Hoffmann     y2 = ceil(y * s->scale_y + h * s->scale_y);
3119d9801cfSGerd Hoffmann 
3129d9801cfSGerd Hoffmann     fbw = surface_width(s->ds) * s->scale_x;
3139d9801cfSGerd Hoffmann     fbh = surface_height(s->ds) * s->scale_y;
3149d9801cfSGerd Hoffmann 
3159d9801cfSGerd Hoffmann     gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
3169d9801cfSGerd Hoffmann 
3179d9801cfSGerd Hoffmann     mx = my = 0;
3189d9801cfSGerd Hoffmann     if (ww > fbw) {
3199d9801cfSGerd Hoffmann         mx = (ww - fbw) / 2;
3209d9801cfSGerd Hoffmann     }
3219d9801cfSGerd Hoffmann     if (wh > fbh) {
3229d9801cfSGerd Hoffmann         my = (wh - fbh) / 2;
3239d9801cfSGerd Hoffmann     }
3249d9801cfSGerd Hoffmann 
3259d9801cfSGerd Hoffmann     gtk_widget_queue_draw_area(s->drawing_area, mx + x1, my + y1, (x2 - x1), (y2 - y1));
3269d9801cfSGerd Hoffmann }
3279d9801cfSGerd Hoffmann 
328bc2ed970SGerd Hoffmann static void gd_refresh(DisplayChangeListener *dcl)
3299d9801cfSGerd Hoffmann {
3309d9801cfSGerd Hoffmann     vga_hw_update();
3319d9801cfSGerd Hoffmann }
3329d9801cfSGerd Hoffmann 
3339d9801cfSGerd Hoffmann static void gd_switch(DisplayChangeListener *dcl,
3349d9801cfSGerd Hoffmann                       DisplaySurface *surface)
3359d9801cfSGerd Hoffmann {
3369d9801cfSGerd Hoffmann     GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
3379d9801cfSGerd Hoffmann     cairo_format_t kind;
3389d9801cfSGerd Hoffmann     bool resized = true;
3399d9801cfSGerd Hoffmann     int stride;
3409d9801cfSGerd Hoffmann 
3419d9801cfSGerd Hoffmann     DPRINTF("resize(width=%d, height=%d)\n",
3429d9801cfSGerd Hoffmann             surface_width(surface), surface_height(surface));
3439d9801cfSGerd Hoffmann 
3449d9801cfSGerd Hoffmann     if (s->surface) {
3459d9801cfSGerd Hoffmann         cairo_surface_destroy(s->surface);
3469d9801cfSGerd Hoffmann     }
3479d9801cfSGerd Hoffmann 
3489d9801cfSGerd Hoffmann     if (s->ds &&
3499d9801cfSGerd Hoffmann         surface_width(s->ds) == surface_width(surface) &&
3509d9801cfSGerd Hoffmann         surface_height(s->ds) == surface_height(surface)) {
3519d9801cfSGerd Hoffmann         resized = false;
3529d9801cfSGerd Hoffmann     }
3539d9801cfSGerd Hoffmann     s->ds = surface;
3549d9801cfSGerd Hoffmann     switch (surface_bits_per_pixel(surface)) {
3559d9801cfSGerd Hoffmann     case 8:
3569d9801cfSGerd Hoffmann         kind = CAIRO_FORMAT_A8;
3579d9801cfSGerd Hoffmann         break;
3589d9801cfSGerd Hoffmann     case 16:
3599d9801cfSGerd Hoffmann         kind = CAIRO_FORMAT_RGB16_565;
3609d9801cfSGerd Hoffmann         break;
3619d9801cfSGerd Hoffmann     case 32:
3629d9801cfSGerd Hoffmann         kind = CAIRO_FORMAT_RGB24;
3639d9801cfSGerd Hoffmann         break;
3649d9801cfSGerd Hoffmann     default:
3659d9801cfSGerd Hoffmann         g_assert_not_reached();
3669d9801cfSGerd Hoffmann         break;
3679d9801cfSGerd Hoffmann     }
3689d9801cfSGerd Hoffmann 
3699d9801cfSGerd Hoffmann     stride = cairo_format_stride_for_width(kind, surface_width(surface));
3709d9801cfSGerd Hoffmann     g_assert(surface_stride(surface) == stride);
3719d9801cfSGerd Hoffmann 
3729d9801cfSGerd Hoffmann     s->surface = cairo_image_surface_create_for_data(surface_data(surface),
3739d9801cfSGerd Hoffmann                                                      kind,
3749d9801cfSGerd Hoffmann                                                      surface_width(surface),
3759d9801cfSGerd Hoffmann                                                      surface_height(surface),
3769d9801cfSGerd Hoffmann                                                      surface_stride(surface));
3779d9801cfSGerd Hoffmann 
3789d9801cfSGerd Hoffmann     if (resized) {
3799d9801cfSGerd Hoffmann         gd_update_windowsize(s);
3809d9801cfSGerd Hoffmann     } else {
3819d9801cfSGerd Hoffmann         gd_update_full_redraw(s);
3829d9801cfSGerd Hoffmann     }
3839d9801cfSGerd Hoffmann }
3849d9801cfSGerd Hoffmann 
385a4ccabcfSAnthony Liguori /** QEMU Events **/
386a4ccabcfSAnthony Liguori 
387a4ccabcfSAnthony Liguori static void gd_change_runstate(void *opaque, int running, RunState state)
388a4ccabcfSAnthony Liguori {
389a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
390a4ccabcfSAnthony Liguori 
391a4ccabcfSAnthony Liguori     gd_update_caption(s);
392a4ccabcfSAnthony Liguori }
393a4ccabcfSAnthony Liguori 
394a4ccabcfSAnthony Liguori static void gd_mouse_mode_change(Notifier *notify, void *data)
395a4ccabcfSAnthony Liguori {
396a4ccabcfSAnthony Liguori     gd_update_cursor(container_of(notify, GtkDisplayState, mouse_mode_notifier),
397a4ccabcfSAnthony Liguori                      FALSE);
398a4ccabcfSAnthony Liguori }
399a4ccabcfSAnthony Liguori 
400a4ccabcfSAnthony Liguori /** GTK Events **/
401a4ccabcfSAnthony Liguori 
40273d4dc71SAnthony Liguori static gboolean gd_window_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
40373d4dc71SAnthony Liguori {
40473d4dc71SAnthony Liguori     GtkDisplayState *s = opaque;
40573d4dc71SAnthony Liguori     GtkAccelGroupEntry *entries;
40673d4dc71SAnthony Liguori     guint n_entries = 0;
40773d4dc71SAnthony Liguori     gboolean propagate_accel = TRUE;
40873d4dc71SAnthony Liguori     gboolean handled = FALSE;
40973d4dc71SAnthony Liguori 
41073d4dc71SAnthony Liguori     entries = gtk_accel_group_query(s->accel_group, key->keyval,
41173d4dc71SAnthony Liguori                                     key->state, &n_entries);
41273d4dc71SAnthony Liguori     if (n_entries) {
41373d4dc71SAnthony Liguori         const char *quark = g_quark_to_string(entries[0].accel_path_quark);
41473d4dc71SAnthony Liguori 
41573d4dc71SAnthony Liguori         if (gd_is_grab_active(s) && strstart(quark, "<QEMU>/File/", NULL)) {
41673d4dc71SAnthony Liguori             propagate_accel = FALSE;
41773d4dc71SAnthony Liguori         }
41873d4dc71SAnthony Liguori     }
41973d4dc71SAnthony Liguori 
42073d4dc71SAnthony Liguori     if (!handled && propagate_accel) {
42173d4dc71SAnthony Liguori         handled = gtk_window_activate_key(GTK_WINDOW(widget), key);
42273d4dc71SAnthony Liguori     }
4236db253caSJan Kiszka     if (handled) {
4246db253caSJan Kiszka         gtk_release_modifiers(s);
4256db253caSJan Kiszka     } else {
42673d4dc71SAnthony Liguori         handled = gtk_window_propagate_key_event(GTK_WINDOW(widget), key);
42773d4dc71SAnthony Liguori     }
42873d4dc71SAnthony Liguori 
42973d4dc71SAnthony Liguori     return handled;
43073d4dc71SAnthony Liguori }
43173d4dc71SAnthony Liguori 
432a4ccabcfSAnthony Liguori static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
433a4ccabcfSAnthony Liguori                                 void *opaque)
434a4ccabcfSAnthony Liguori {
435a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
436a4ccabcfSAnthony Liguori 
437a4ccabcfSAnthony Liguori     if (!no_quit) {
4387c20b4a3SGerd Hoffmann         unregister_displaychangelistener(&s->dcl);
439a4ccabcfSAnthony Liguori         qmp_quit(NULL);
440a4ccabcfSAnthony Liguori         return FALSE;
441a4ccabcfSAnthony Liguori     }
442a4ccabcfSAnthony Liguori 
443a4ccabcfSAnthony Liguori     return TRUE;
444a4ccabcfSAnthony Liguori }
445a4ccabcfSAnthony Liguori 
446a4ccabcfSAnthony Liguori static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
447a4ccabcfSAnthony Liguori {
448a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
449c6158483SAnthony Liguori     int mx, my;
450a4ccabcfSAnthony Liguori     int ww, wh;
451a4ccabcfSAnthony Liguori     int fbw, fbh;
452a4ccabcfSAnthony Liguori 
453c6158483SAnthony Liguori     if (!gtk_widget_get_realized(widget)) {
454c6158483SAnthony Liguori         return FALSE;
455c6158483SAnthony Liguori     }
456c6158483SAnthony Liguori 
4579d9801cfSGerd Hoffmann     fbw = surface_width(s->ds);
4589d9801cfSGerd Hoffmann     fbh = surface_height(s->ds);
459a4ccabcfSAnthony Liguori 
460a4ccabcfSAnthony Liguori     gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
461a4ccabcfSAnthony Liguori 
462c6158483SAnthony Liguori     if (s->full_screen) {
463a4ccabcfSAnthony Liguori         s->scale_x = (double)ww / fbw;
464a4ccabcfSAnthony Liguori         s->scale_y = (double)wh / fbh;
465c6158483SAnthony Liguori     } else if (s->free_scale) {
466c6158483SAnthony Liguori         double sx, sy;
467c6158483SAnthony Liguori 
468c6158483SAnthony Liguori         sx = (double)ww / fbw;
469c6158483SAnthony Liguori         sy = (double)wh / fbh;
470c6158483SAnthony Liguori 
471c6158483SAnthony Liguori         s->scale_x = s->scale_y = MIN(sx, sy);
472a4ccabcfSAnthony Liguori     }
473a4ccabcfSAnthony Liguori 
4745104a1f6SAnthony Liguori     fbw *= s->scale_x;
4755104a1f6SAnthony Liguori     fbh *= s->scale_y;
4765104a1f6SAnthony Liguori 
477c6158483SAnthony Liguori     mx = my = 0;
478c6158483SAnthony Liguori     if (ww > fbw) {
479c6158483SAnthony Liguori         mx = (ww - fbw) / 2;
480c6158483SAnthony Liguori     }
481c6158483SAnthony Liguori     if (wh > fbh) {
482c6158483SAnthony Liguori         my = (wh - fbh) / 2;
483c6158483SAnthony Liguori     }
484c6158483SAnthony Liguori 
485c6158483SAnthony Liguori     cairo_rectangle(cr, 0, 0, ww, wh);
486c6158483SAnthony Liguori 
487c6158483SAnthony Liguori     /* Optionally cut out the inner area where the pixmap
488c6158483SAnthony Liguori        will be drawn. This avoids 'flashing' since we're
489c6158483SAnthony Liguori        not double-buffering. Note we're using the undocumented
490c6158483SAnthony Liguori        behaviour of drawing the rectangle from right to left
491c6158483SAnthony Liguori        to cut out the whole */
492c6158483SAnthony Liguori     cairo_rectangle(cr, mx + fbw, my,
493c6158483SAnthony Liguori                     -1 * fbw, fbh);
494c6158483SAnthony Liguori     cairo_fill(cr);
495c6158483SAnthony Liguori 
496c6158483SAnthony Liguori     cairo_scale(cr, s->scale_x, s->scale_y);
497c6158483SAnthony Liguori     cairo_set_source_surface(cr, s->surface, mx / s->scale_x, my / s->scale_y);
498a4ccabcfSAnthony Liguori     cairo_paint(cr);
499a4ccabcfSAnthony Liguori 
500a4ccabcfSAnthony Liguori     return TRUE;
501a4ccabcfSAnthony Liguori }
502a4ccabcfSAnthony Liguori 
503fe43bca8SDaniel P. Berrange #if !GTK_CHECK_VERSION(3, 0, 0)
504a4ccabcfSAnthony Liguori static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
505a4ccabcfSAnthony Liguori                                 void *opaque)
506a4ccabcfSAnthony Liguori {
507a4ccabcfSAnthony Liguori     cairo_t *cr;
508a4ccabcfSAnthony Liguori     gboolean ret;
509a4ccabcfSAnthony Liguori 
510a4ccabcfSAnthony Liguori     cr = gdk_cairo_create(gtk_widget_get_window(widget));
511a4ccabcfSAnthony Liguori     cairo_rectangle(cr,
512a4ccabcfSAnthony Liguori                     expose->area.x,
513a4ccabcfSAnthony Liguori                     expose->area.y,
514a4ccabcfSAnthony Liguori                     expose->area.width,
515a4ccabcfSAnthony Liguori                     expose->area.height);
516a4ccabcfSAnthony Liguori     cairo_clip(cr);
517a4ccabcfSAnthony Liguori 
518a4ccabcfSAnthony Liguori     ret = gd_draw_event(widget, cr, opaque);
519a4ccabcfSAnthony Liguori 
520a4ccabcfSAnthony Liguori     cairo_destroy(cr);
521a4ccabcfSAnthony Liguori 
522a4ccabcfSAnthony Liguori     return ret;
523a4ccabcfSAnthony Liguori }
524fe43bca8SDaniel P. Berrange #endif
525a4ccabcfSAnthony Liguori 
526a4ccabcfSAnthony Liguori static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
527a4ccabcfSAnthony Liguori                                 void *opaque)
528a4ccabcfSAnthony Liguori {
529a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
530a4ccabcfSAnthony Liguori     int dx, dy;
531a4ccabcfSAnthony Liguori     int x, y;
532c6158483SAnthony Liguori     int mx, my;
533c6158483SAnthony Liguori     int fbh, fbw;
534c6158483SAnthony Liguori     int ww, wh;
535a4ccabcfSAnthony Liguori 
5369d9801cfSGerd Hoffmann     fbw = surface_width(s->ds) * s->scale_x;
5379d9801cfSGerd Hoffmann     fbh = surface_height(s->ds) * s->scale_y;
538c6158483SAnthony Liguori 
539c6158483SAnthony Liguori     gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
540c6158483SAnthony Liguori 
541c6158483SAnthony Liguori     mx = my = 0;
542c6158483SAnthony Liguori     if (ww > fbw) {
543c6158483SAnthony Liguori         mx = (ww - fbw) / 2;
544c6158483SAnthony Liguori     }
545c6158483SAnthony Liguori     if (wh > fbh) {
546c6158483SAnthony Liguori         my = (wh - fbh) / 2;
547c6158483SAnthony Liguori     }
548c6158483SAnthony Liguori 
549c6158483SAnthony Liguori     x = (motion->x - mx) / s->scale_x;
550c6158483SAnthony Liguori     y = (motion->y - my) / s->scale_y;
551c6158483SAnthony Liguori 
552c6158483SAnthony Liguori     if (x < 0 || y < 0 ||
5539d9801cfSGerd Hoffmann         x >= surface_width(s->ds) ||
5549d9801cfSGerd Hoffmann         y >= surface_height(s->ds)) {
555c6158483SAnthony Liguori         return TRUE;
556c6158483SAnthony Liguori     }
557a4ccabcfSAnthony Liguori 
558a4ccabcfSAnthony Liguori     if (kbd_mouse_is_absolute()) {
5599d9801cfSGerd Hoffmann         dx = x * 0x7FFF / (surface_width(s->ds) - 1);
5609d9801cfSGerd Hoffmann         dy = y * 0x7FFF / (surface_height(s->ds) - 1);
561a4ccabcfSAnthony Liguori     } else if (s->last_x == -1 || s->last_y == -1) {
562a4ccabcfSAnthony Liguori         dx = 0;
563a4ccabcfSAnthony Liguori         dy = 0;
564a4ccabcfSAnthony Liguori     } else {
565a4ccabcfSAnthony Liguori         dx = x - s->last_x;
566a4ccabcfSAnthony Liguori         dy = y - s->last_y;
567a4ccabcfSAnthony Liguori     }
568a4ccabcfSAnthony Liguori 
569a4ccabcfSAnthony Liguori     s->last_x = x;
570a4ccabcfSAnthony Liguori     s->last_y = y;
571a4ccabcfSAnthony Liguori 
5725104a1f6SAnthony Liguori     if (kbd_mouse_is_absolute() || gd_is_grab_active(s)) {
573a4ccabcfSAnthony Liguori         kbd_mouse_event(dx, dy, 0, s->button_mask);
574a4ccabcfSAnthony Liguori     }
575a4ccabcfSAnthony Liguori 
5765104a1f6SAnthony Liguori     if (!kbd_mouse_is_absolute() && gd_is_grab_active(s)) {
57766962f14SDaniel P. Berrange         GdkScreen *screen = gtk_widget_get_screen(s->drawing_area);
5785104a1f6SAnthony Liguori         int x = (int)motion->x_root;
5795104a1f6SAnthony Liguori         int y = (int)motion->y_root;
5805104a1f6SAnthony Liguori 
5815104a1f6SAnthony Liguori         /* In relative mode check to see if client pointer hit
5825104a1f6SAnthony Liguori          * one of the screen edges, and if so move it back by
5835104a1f6SAnthony Liguori          * 200 pixels. This is important because the pointer
5845104a1f6SAnthony Liguori          * in the server doesn't correspond 1-for-1, and so
5855104a1f6SAnthony Liguori          * may still be only half way across the screen. Without
5865104a1f6SAnthony Liguori          * this warp, the server pointer would thus appear to hit
5875104a1f6SAnthony Liguori          * an invisible wall */
5885104a1f6SAnthony Liguori         if (x == 0) {
5895104a1f6SAnthony Liguori             x += 200;
5905104a1f6SAnthony Liguori         }
5915104a1f6SAnthony Liguori         if (y == 0) {
5925104a1f6SAnthony Liguori             y += 200;
5935104a1f6SAnthony Liguori         }
5945104a1f6SAnthony Liguori         if (x == (gdk_screen_get_width(screen) - 1)) {
5955104a1f6SAnthony Liguori             x -= 200;
5965104a1f6SAnthony Liguori         }
5975104a1f6SAnthony Liguori         if (y == (gdk_screen_get_height(screen) - 1)) {
5985104a1f6SAnthony Liguori             y -= 200;
5995104a1f6SAnthony Liguori         }
6005104a1f6SAnthony Liguori 
6015104a1f6SAnthony Liguori         if (x != (int)motion->x_root || y != (int)motion->y_root) {
6028906de76SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
6038906de76SDaniel P. Berrange             GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
6048906de76SDaniel P. Berrange             gdk_device_warp(dev, screen, x, y);
6058906de76SDaniel P. Berrange #else
6068906de76SDaniel P. Berrange             GdkDisplay *display = gtk_widget_get_display(widget);
6075104a1f6SAnthony Liguori             gdk_display_warp_pointer(display, screen, x, y);
6088906de76SDaniel P. Berrange #endif
6095104a1f6SAnthony Liguori             s->last_x = -1;
6105104a1f6SAnthony Liguori             s->last_y = -1;
6115104a1f6SAnthony Liguori             return FALSE;
6125104a1f6SAnthony Liguori         }
6135104a1f6SAnthony Liguori     }
614a4ccabcfSAnthony Liguori     return TRUE;
615a4ccabcfSAnthony Liguori }
616a4ccabcfSAnthony Liguori 
617a4ccabcfSAnthony Liguori static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
618a4ccabcfSAnthony Liguori                                 void *opaque)
619a4ccabcfSAnthony Liguori {
620a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
621a4ccabcfSAnthony Liguori     int dx, dy;
622a4ccabcfSAnthony Liguori     int n;
623a4ccabcfSAnthony Liguori 
624a4ccabcfSAnthony Liguori     if (button->button == 1) {
625a4ccabcfSAnthony Liguori         n = 0x01;
626a4ccabcfSAnthony Liguori     } else if (button->button == 2) {
627a4ccabcfSAnthony Liguori         n = 0x04;
628a4ccabcfSAnthony Liguori     } else if (button->button == 3) {
629a4ccabcfSAnthony Liguori         n = 0x02;
630a4ccabcfSAnthony Liguori     } else {
631a4ccabcfSAnthony Liguori         n = 0x00;
632a4ccabcfSAnthony Liguori     }
633a4ccabcfSAnthony Liguori 
634a4ccabcfSAnthony Liguori     if (button->type == GDK_BUTTON_PRESS) {
635a4ccabcfSAnthony Liguori         s->button_mask |= n;
636a4ccabcfSAnthony Liguori     } else if (button->type == GDK_BUTTON_RELEASE) {
637a4ccabcfSAnthony Liguori         s->button_mask &= ~n;
638a4ccabcfSAnthony Liguori     }
639a4ccabcfSAnthony Liguori 
640a4ccabcfSAnthony Liguori     if (kbd_mouse_is_absolute()) {
6419d9801cfSGerd Hoffmann         dx = s->last_x * 0x7FFF / (surface_width(s->ds) - 1);
6429d9801cfSGerd Hoffmann         dy = s->last_y * 0x7FFF / (surface_height(s->ds) - 1);
643a4ccabcfSAnthony Liguori     } else {
644a4ccabcfSAnthony Liguori         dx = 0;
645a4ccabcfSAnthony Liguori         dy = 0;
646a4ccabcfSAnthony Liguori     }
647a4ccabcfSAnthony Liguori 
648a4ccabcfSAnthony Liguori     kbd_mouse_event(dx, dy, 0, s->button_mask);
649a4ccabcfSAnthony Liguori 
650a4ccabcfSAnthony Liguori     return TRUE;
651a4ccabcfSAnthony Liguori }
652a4ccabcfSAnthony Liguori 
653a4ccabcfSAnthony Liguori static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
654a4ccabcfSAnthony Liguori {
6556db253caSJan Kiszka     GtkDisplayState *s = opaque;
656a4ccabcfSAnthony Liguori     int gdk_keycode;
657a4ccabcfSAnthony Liguori     int qemu_keycode;
6586db253caSJan Kiszka     int i;
659a4ccabcfSAnthony Liguori 
660a4ccabcfSAnthony Liguori     gdk_keycode = key->hardware_keycode;
661a4ccabcfSAnthony Liguori 
662a4ccabcfSAnthony Liguori     if (gdk_keycode < 9) {
663a4ccabcfSAnthony Liguori         qemu_keycode = 0;
664a4ccabcfSAnthony Liguori     } else if (gdk_keycode < 97) {
665a4ccabcfSAnthony Liguori         qemu_keycode = gdk_keycode - 8;
666a4ccabcfSAnthony Liguori     } else if (gdk_keycode < 158) {
667a4ccabcfSAnthony Liguori         qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
668a4ccabcfSAnthony Liguori     } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
669a4ccabcfSAnthony Liguori         qemu_keycode = 0x70;
670a4ccabcfSAnthony Liguori     } else if (gdk_keycode == 211) { /* backslash */
671a4ccabcfSAnthony Liguori         qemu_keycode = 0x73;
672a4ccabcfSAnthony Liguori     } else {
673a4ccabcfSAnthony Liguori         qemu_keycode = 0;
674a4ccabcfSAnthony Liguori     }
675a4ccabcfSAnthony Liguori 
676a4ccabcfSAnthony Liguori     DPRINTF("translated GDK keycode %d to QEMU keycode %d (%s)\n",
677a4ccabcfSAnthony Liguori             gdk_keycode, qemu_keycode,
678a4ccabcfSAnthony Liguori             (key->type == GDK_KEY_PRESS) ? "down" : "up");
679a4ccabcfSAnthony Liguori 
6806db253caSJan Kiszka     for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
6816db253caSJan Kiszka         if (qemu_keycode == modifier_keycode[i]) {
6826db253caSJan Kiszka             s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
6836db253caSJan Kiszka         }
6846db253caSJan Kiszka     }
6856db253caSJan Kiszka 
686a4ccabcfSAnthony Liguori     if (qemu_keycode & SCANCODE_GREY) {
687a4ccabcfSAnthony Liguori         kbd_put_keycode(SCANCODE_EMUL0);
688a4ccabcfSAnthony Liguori     }
689a4ccabcfSAnthony Liguori 
690a4ccabcfSAnthony Liguori     if (key->type == GDK_KEY_PRESS) {
691a4ccabcfSAnthony Liguori         kbd_put_keycode(qemu_keycode & SCANCODE_KEYCODEMASK);
692a4ccabcfSAnthony Liguori     } else if (key->type == GDK_KEY_RELEASE) {
693a4ccabcfSAnthony Liguori         kbd_put_keycode(qemu_keycode | SCANCODE_UP);
694a4ccabcfSAnthony Liguori     } else {
695a4ccabcfSAnthony Liguori         g_assert_not_reached();
696a4ccabcfSAnthony Liguori     }
697a4ccabcfSAnthony Liguori 
698a4ccabcfSAnthony Liguori     return TRUE;
699a4ccabcfSAnthony Liguori }
700a4ccabcfSAnthony Liguori 
701a4ccabcfSAnthony Liguori /** Window Menu Actions **/
702a4ccabcfSAnthony Liguori 
70330e8f22bSJan Kiszka static void gd_menu_pause(GtkMenuItem *item, void *opaque)
70430e8f22bSJan Kiszka {
70530e8f22bSJan Kiszka     GtkDisplayState *s = opaque;
70630e8f22bSJan Kiszka 
70730e8f22bSJan Kiszka     if (s->external_pause_update) {
70830e8f22bSJan Kiszka         return;
70930e8f22bSJan Kiszka     }
71030e8f22bSJan Kiszka     if (runstate_is_running()) {
71130e8f22bSJan Kiszka         qmp_stop(NULL);
71230e8f22bSJan Kiszka     } else {
71330e8f22bSJan Kiszka         qmp_cont(NULL);
71430e8f22bSJan Kiszka     }
71530e8f22bSJan Kiszka }
71630e8f22bSJan Kiszka 
71730e8f22bSJan Kiszka static void gd_menu_reset(GtkMenuItem *item, void *opaque)
71830e8f22bSJan Kiszka {
71930e8f22bSJan Kiszka     qmp_system_reset(NULL);
72030e8f22bSJan Kiszka }
72130e8f22bSJan Kiszka 
72230e8f22bSJan Kiszka static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
72330e8f22bSJan Kiszka {
72430e8f22bSJan Kiszka     qmp_system_powerdown(NULL);
72530e8f22bSJan Kiszka }
72630e8f22bSJan Kiszka 
727a4ccabcfSAnthony Liguori static void gd_menu_quit(GtkMenuItem *item, void *opaque)
728a4ccabcfSAnthony Liguori {
729a4ccabcfSAnthony Liguori     qmp_quit(NULL);
730a4ccabcfSAnthony Liguori }
731a4ccabcfSAnthony Liguori 
732a4ccabcfSAnthony Liguori static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
733a4ccabcfSAnthony Liguori {
734a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
735a4ccabcfSAnthony Liguori 
736a4ccabcfSAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) {
737a4ccabcfSAnthony Liguori         gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), 0);
738d861def3SAnthony Liguori     } else {
739d861def3SAnthony Liguori         int i;
740d861def3SAnthony Liguori 
7416db253caSJan Kiszka         gtk_release_modifiers(s);
742d861def3SAnthony Liguori         for (i = 0; i < s->nb_vcs; i++) {
743d861def3SAnthony Liguori             if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) {
744d861def3SAnthony Liguori                 gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), i + 1);
745d861def3SAnthony Liguori                 break;
746d861def3SAnthony Liguori             }
747d861def3SAnthony Liguori         }
748a4ccabcfSAnthony Liguori     }
749a4ccabcfSAnthony Liguori }
750a4ccabcfSAnthony Liguori 
751a4ccabcfSAnthony Liguori static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
752a4ccabcfSAnthony Liguori {
753a4ccabcfSAnthony Liguori     GtkDisplayState *s = opaque;
754a4ccabcfSAnthony Liguori 
755a4ccabcfSAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
756a4ccabcfSAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
757a4ccabcfSAnthony Liguori     } else {
758a4ccabcfSAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
759a4ccabcfSAnthony Liguori     }
760a4ccabcfSAnthony Liguori }
761a4ccabcfSAnthony Liguori 
762c6158483SAnthony Liguori static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
763c6158483SAnthony Liguori {
764c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
765c6158483SAnthony Liguori 
76610409282SStefan Weil     if (!s->full_screen) {
767c6158483SAnthony Liguori         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
768c6158483SAnthony Liguori         gtk_widget_set_size_request(s->menu_bar, 0, 0);
769c6158483SAnthony Liguori         gtk_widget_set_size_request(s->drawing_area, -1, -1);
770c6158483SAnthony Liguori         gtk_window_fullscreen(GTK_WINDOW(s->window));
771c6158483SAnthony Liguori         if (gd_on_vga(s)) {
772c6158483SAnthony Liguori             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE);
773c6158483SAnthony Liguori         }
774c6158483SAnthony Liguori         s->full_screen = TRUE;
775c6158483SAnthony Liguori     } else {
776c6158483SAnthony Liguori         gtk_window_unfullscreen(GTK_WINDOW(s->window));
777c6158483SAnthony Liguori         gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
778c6158483SAnthony Liguori         gtk_widget_set_size_request(s->menu_bar, -1, -1);
779c6158483SAnthony Liguori         gtk_widget_set_size_request(s->drawing_area,
7809d9801cfSGerd Hoffmann                                     surface_width(s->ds),
7819d9801cfSGerd Hoffmann                                     surface_height(s->ds));
782c6158483SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE);
783c6158483SAnthony Liguori         s->full_screen = FALSE;
784c6158483SAnthony Liguori         s->scale_x = 1.0;
785c6158483SAnthony Liguori         s->scale_y = 1.0;
786c6158483SAnthony Liguori     }
787c6158483SAnthony Liguori 
788c6158483SAnthony Liguori     gd_update_cursor(s, FALSE);
789c6158483SAnthony Liguori }
790c6158483SAnthony Liguori 
791c6158483SAnthony Liguori static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
792c6158483SAnthony Liguori {
793c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
794c6158483SAnthony Liguori 
795c6158483SAnthony Liguori     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
796c6158483SAnthony Liguori                                    FALSE);
797c6158483SAnthony Liguori 
798c6158483SAnthony Liguori     s->scale_x += .25;
799c6158483SAnthony Liguori     s->scale_y += .25;
800c6158483SAnthony Liguori 
8019d9801cfSGerd Hoffmann     gd_update_windowsize(s);
802c6158483SAnthony Liguori }
803c6158483SAnthony Liguori 
804c6158483SAnthony Liguori static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
805c6158483SAnthony Liguori {
806c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
807c6158483SAnthony Liguori 
808c6158483SAnthony Liguori     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
809c6158483SAnthony Liguori                                    FALSE);
810c6158483SAnthony Liguori 
811c6158483SAnthony Liguori     s->scale_x -= .25;
812c6158483SAnthony Liguori     s->scale_y -= .25;
813c6158483SAnthony Liguori 
814c6158483SAnthony Liguori     s->scale_x = MAX(s->scale_x, .25);
815c6158483SAnthony Liguori     s->scale_y = MAX(s->scale_y, .25);
816c6158483SAnthony Liguori 
8179d9801cfSGerd Hoffmann     gd_update_windowsize(s);
818c6158483SAnthony Liguori }
819c6158483SAnthony Liguori 
820c6158483SAnthony Liguori static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
821c6158483SAnthony Liguori {
822c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
823c6158483SAnthony Liguori 
824c6158483SAnthony Liguori     s->scale_x = 1.0;
825c6158483SAnthony Liguori     s->scale_y = 1.0;
826c6158483SAnthony Liguori 
8279d9801cfSGerd Hoffmann     gd_update_windowsize(s);
828c6158483SAnthony Liguori }
829c6158483SAnthony Liguori 
830c6158483SAnthony Liguori static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
831c6158483SAnthony Liguori {
832c6158483SAnthony Liguori     GtkDisplayState *s = opaque;
833c6158483SAnthony Liguori 
834c6158483SAnthony Liguori     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
835c6158483SAnthony Liguori         s->free_scale = TRUE;
836c6158483SAnthony Liguori     } else {
837c6158483SAnthony Liguori         s->free_scale = FALSE;
838c6158483SAnthony Liguori     }
839c6158483SAnthony Liguori 
8409d9801cfSGerd Hoffmann     gd_update_windowsize(s);
8419d9801cfSGerd Hoffmann     gd_update_full_redraw(s);
842c6158483SAnthony Liguori }
843c6158483SAnthony Liguori 
8445104a1f6SAnthony Liguori static void gd_grab_keyboard(GtkDisplayState *s)
8455104a1f6SAnthony Liguori {
846655199daSDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
847655199daSDaniel P. Berrange     GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
848655199daSDaniel P. Berrange     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
849655199daSDaniel P. Berrange     GList *devices = gdk_device_manager_list_devices(mgr,
850655199daSDaniel P. Berrange                                                      GDK_DEVICE_TYPE_MASTER);
851655199daSDaniel P. Berrange     GList *tmp = devices;
852655199daSDaniel P. Berrange     while (tmp) {
853655199daSDaniel P. Berrange         GdkDevice *dev = tmp->data;
854655199daSDaniel P. Berrange         if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) {
855655199daSDaniel P. Berrange             gdk_device_grab(dev,
856655199daSDaniel P. Berrange                             gtk_widget_get_window(s->drawing_area),
857655199daSDaniel P. Berrange                             GDK_OWNERSHIP_NONE,
858655199daSDaniel P. Berrange                             FALSE,
859655199daSDaniel P. Berrange                             GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
860655199daSDaniel P. Berrange                             NULL,
861655199daSDaniel P. Berrange                             GDK_CURRENT_TIME);
862655199daSDaniel P. Berrange         }
863655199daSDaniel P. Berrange         tmp = tmp->next;
864655199daSDaniel P. Berrange     }
865655199daSDaniel P. Berrange     g_list_free(devices);
866655199daSDaniel P. Berrange #else
867655199daSDaniel P. Berrange     gdk_keyboard_grab(gtk_widget_get_window(s->drawing_area),
8685104a1f6SAnthony Liguori                       FALSE,
8695104a1f6SAnthony Liguori                       GDK_CURRENT_TIME);
870655199daSDaniel P. Berrange #endif
8715104a1f6SAnthony Liguori }
8725104a1f6SAnthony Liguori 
8735104a1f6SAnthony Liguori static void gd_ungrab_keyboard(GtkDisplayState *s)
8745104a1f6SAnthony Liguori {
875655199daSDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
876655199daSDaniel P. Berrange     GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
877655199daSDaniel P. Berrange     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
878655199daSDaniel P. Berrange     GList *devices = gdk_device_manager_list_devices(mgr,
879655199daSDaniel P. Berrange                                                      GDK_DEVICE_TYPE_MASTER);
880655199daSDaniel P. Berrange     GList *tmp = devices;
881655199daSDaniel P. Berrange     while (tmp) {
882655199daSDaniel P. Berrange         GdkDevice *dev = tmp->data;
883655199daSDaniel P. Berrange         if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) {
884655199daSDaniel P. Berrange             gdk_device_ungrab(dev,
885655199daSDaniel P. Berrange                               GDK_CURRENT_TIME);
886655199daSDaniel P. Berrange         }
887655199daSDaniel P. Berrange         tmp = tmp->next;
888655199daSDaniel P. Berrange     }
889655199daSDaniel P. Berrange     g_list_free(devices);
890655199daSDaniel P. Berrange #else
8915104a1f6SAnthony Liguori     gdk_keyboard_ungrab(GDK_CURRENT_TIME);
892655199daSDaniel P. Berrange #endif
8935104a1f6SAnthony Liguori }
8945104a1f6SAnthony Liguori 
8952a05485dSDaniel P. Berrange static void gd_grab_pointer(GtkDisplayState *s)
8965104a1f6SAnthony Liguori {
8972a05485dSDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
8982a05485dSDaniel P. Berrange     GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
8992a05485dSDaniel P. Berrange     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
9002a05485dSDaniel P. Berrange     GList *devices = gdk_device_manager_list_devices(mgr,
9012a05485dSDaniel P. Berrange                                                      GDK_DEVICE_TYPE_MASTER);
9022a05485dSDaniel P. Berrange     GList *tmp = devices;
9032a05485dSDaniel P. Berrange     while (tmp) {
9042a05485dSDaniel P. Berrange         GdkDevice *dev = tmp->data;
9052a05485dSDaniel P. Berrange         if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) {
9062a05485dSDaniel P. Berrange             gdk_device_grab(dev,
9072a05485dSDaniel P. Berrange                             gtk_widget_get_window(s->drawing_area),
9082a05485dSDaniel P. Berrange                             GDK_OWNERSHIP_NONE,
9092a05485dSDaniel P. Berrange                             FALSE, /* All events to come to our
9102a05485dSDaniel P. Berrange                                       window directly */
9112a05485dSDaniel P. Berrange                             GDK_POINTER_MOTION_MASK |
9122a05485dSDaniel P. Berrange                             GDK_BUTTON_PRESS_MASK |
9132a05485dSDaniel P. Berrange                             GDK_BUTTON_RELEASE_MASK |
9142a05485dSDaniel P. Berrange                             GDK_BUTTON_MOTION_MASK |
9152a05485dSDaniel P. Berrange                             GDK_SCROLL_MASK,
9162a05485dSDaniel P. Berrange                             s->null_cursor,
9172a05485dSDaniel P. Berrange                             GDK_CURRENT_TIME);
9182a05485dSDaniel P. Berrange         }
9192a05485dSDaniel P. Berrange         tmp = tmp->next;
9202a05485dSDaniel P. Berrange     }
9212a05485dSDaniel P. Berrange     g_list_free(devices);
9222a05485dSDaniel P. Berrange #else
9232a05485dSDaniel P. Berrange     gdk_pointer_grab(gtk_widget_get_window(s->drawing_area),
9245104a1f6SAnthony Liguori                      FALSE, /* All events to come to our window directly */
9255104a1f6SAnthony Liguori                      GDK_POINTER_MOTION_MASK |
9265104a1f6SAnthony Liguori                      GDK_BUTTON_PRESS_MASK |
9275104a1f6SAnthony Liguori                      GDK_BUTTON_RELEASE_MASK |
9285104a1f6SAnthony Liguori                      GDK_BUTTON_MOTION_MASK |
9295104a1f6SAnthony Liguori                      GDK_SCROLL_MASK,
9305104a1f6SAnthony Liguori                      NULL, /* Allow cursor to move over entire desktop */
9315104a1f6SAnthony Liguori                      s->null_cursor,
9325104a1f6SAnthony Liguori                      GDK_CURRENT_TIME);
9332a05485dSDaniel P. Berrange #endif
9342a05485dSDaniel P. Berrange }
9352a05485dSDaniel P. Berrange 
9362a05485dSDaniel P. Berrange static void gd_ungrab_pointer(GtkDisplayState *s)
9372a05485dSDaniel P. Berrange {
9382a05485dSDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
9392a05485dSDaniel P. Berrange     GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
9402a05485dSDaniel P. Berrange     GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
9412a05485dSDaniel P. Berrange     GList *devices = gdk_device_manager_list_devices(mgr,
9422a05485dSDaniel P. Berrange                                                      GDK_DEVICE_TYPE_MASTER);
9432a05485dSDaniel P. Berrange     GList *tmp = devices;
9442a05485dSDaniel P. Berrange     while (tmp) {
9452a05485dSDaniel P. Berrange         GdkDevice *dev = tmp->data;
9462a05485dSDaniel P. Berrange         if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) {
9472a05485dSDaniel P. Berrange             gdk_device_ungrab(dev,
9482a05485dSDaniel P. Berrange                               GDK_CURRENT_TIME);
9492a05485dSDaniel P. Berrange         }
9502a05485dSDaniel P. Berrange         tmp = tmp->next;
9512a05485dSDaniel P. Berrange     }
9522a05485dSDaniel P. Berrange     g_list_free(devices);
9532a05485dSDaniel P. Berrange #else
9542a05485dSDaniel P. Berrange     gdk_pointer_ungrab(GDK_CURRENT_TIME);
9552a05485dSDaniel P. Berrange #endif
9562a05485dSDaniel P. Berrange }
9572a05485dSDaniel P. Berrange 
9582a05485dSDaniel P. Berrange static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
9592a05485dSDaniel P. Berrange {
9602a05485dSDaniel P. Berrange     GtkDisplayState *s = opaque;
9612a05485dSDaniel P. Berrange 
9622a05485dSDaniel P. Berrange     if (gd_is_grab_active(s)) {
9632a05485dSDaniel P. Berrange         gd_grab_keyboard(s);
9642a05485dSDaniel P. Berrange         gd_grab_pointer(s);
9655104a1f6SAnthony Liguori     } else {
9665104a1f6SAnthony Liguori         gd_ungrab_keyboard(s);
9672a05485dSDaniel P. Berrange         gd_ungrab_pointer(s);
9685104a1f6SAnthony Liguori     }
9695104a1f6SAnthony Liguori 
9705104a1f6SAnthony Liguori     gd_update_caption(s);
9715104a1f6SAnthony Liguori     gd_update_cursor(s, FALSE);
9725104a1f6SAnthony Liguori }
9735104a1f6SAnthony Liguori 
974a4ccabcfSAnthony Liguori static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
975a4ccabcfSAnthony Liguori                            gpointer data)
976a4ccabcfSAnthony Liguori {
977a4ccabcfSAnthony Liguori     GtkDisplayState *s = data;
978d861def3SAnthony Liguori     guint last_page;
9795104a1f6SAnthony Liguori     gboolean on_vga;
980a4ccabcfSAnthony Liguori 
981a4ccabcfSAnthony Liguori     if (!gtk_widget_get_realized(s->notebook)) {
982a4ccabcfSAnthony Liguori         return;
983a4ccabcfSAnthony Liguori     }
984a4ccabcfSAnthony Liguori 
985d861def3SAnthony Liguori     last_page = gtk_notebook_get_current_page(nb);
986d861def3SAnthony Liguori 
987d861def3SAnthony Liguori     if (last_page) {
988d861def3SAnthony Liguori         gtk_widget_set_size_request(s->vc[last_page - 1].terminal, -1, -1);
989d861def3SAnthony Liguori     }
990d861def3SAnthony Liguori 
9915104a1f6SAnthony Liguori     on_vga = arg2 == 0;
9925104a1f6SAnthony Liguori 
9935104a1f6SAnthony Liguori     if (!on_vga) {
9945104a1f6SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
9955104a1f6SAnthony Liguori                                        FALSE);
996c6158483SAnthony Liguori     } else if (s->full_screen) {
997c6158483SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
998c6158483SAnthony Liguori                                        TRUE);
9995104a1f6SAnthony Liguori     }
10005104a1f6SAnthony Liguori 
1001d861def3SAnthony Liguori     if (arg2 == 0) {
1002d861def3SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE);
1003d861def3SAnthony Liguori     } else {
1004d861def3SAnthony Liguori         VirtualConsole *vc = &s->vc[arg2 - 1];
1005d861def3SAnthony Liguori         VteTerminal *term = VTE_TERMINAL(vc->terminal);
1006d861def3SAnthony Liguori         int width, height;
1007d861def3SAnthony Liguori 
1008d861def3SAnthony Liguori         width = 80 * vte_terminal_get_char_width(term);
1009d861def3SAnthony Liguori         height = 25 * vte_terminal_get_char_height(term);
1010d861def3SAnthony Liguori 
1011d861def3SAnthony Liguori         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
1012d861def3SAnthony Liguori         gtk_widget_set_size_request(vc->terminal, width, height);
1013d861def3SAnthony Liguori     }
1014d861def3SAnthony Liguori 
10155104a1f6SAnthony Liguori     gtk_widget_set_sensitive(s->grab_item, on_vga);
10165104a1f6SAnthony Liguori 
1017a4ccabcfSAnthony Liguori     gd_update_cursor(s, TRUE);
1018a4ccabcfSAnthony Liguori }
1019a4ccabcfSAnthony Liguori 
10205104a1f6SAnthony Liguori static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data)
10215104a1f6SAnthony Liguori {
10225104a1f6SAnthony Liguori     GtkDisplayState *s = data;
10235104a1f6SAnthony Liguori 
10245104a1f6SAnthony Liguori     if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) {
10255104a1f6SAnthony Liguori         gd_grab_keyboard(s);
10265104a1f6SAnthony Liguori     }
10275104a1f6SAnthony Liguori 
10285104a1f6SAnthony Liguori     return TRUE;
10295104a1f6SAnthony Liguori }
10305104a1f6SAnthony Liguori 
10315104a1f6SAnthony Liguori static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data)
10325104a1f6SAnthony Liguori {
10335104a1f6SAnthony Liguori     GtkDisplayState *s = data;
10345104a1f6SAnthony Liguori 
10355104a1f6SAnthony Liguori     if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) {
10365104a1f6SAnthony Liguori         gd_ungrab_keyboard(s);
10375104a1f6SAnthony Liguori     }
10385104a1f6SAnthony Liguori 
10395104a1f6SAnthony Liguori     return TRUE;
10405104a1f6SAnthony Liguori }
10415104a1f6SAnthony Liguori 
10426db253caSJan Kiszka static gboolean gd_focus_out_event(GtkWidget *widget,
10436db253caSJan Kiszka                                    GdkEventCrossing *crossing, gpointer data)
10446db253caSJan Kiszka {
10456db253caSJan Kiszka     GtkDisplayState *s = data;
10466db253caSJan Kiszka 
10476db253caSJan Kiszka     gtk_release_modifiers(s);
10486db253caSJan Kiszka 
10496db253caSJan Kiszka     return TRUE;
10506db253caSJan Kiszka }
10516db253caSJan Kiszka 
1052d861def3SAnthony Liguori /** Virtual Console Callbacks **/
1053d861def3SAnthony Liguori 
1054d861def3SAnthony Liguori static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
1055d861def3SAnthony Liguori {
1056d861def3SAnthony Liguori     VirtualConsole *vc = chr->opaque;
1057d861def3SAnthony Liguori 
1058d861def3SAnthony Liguori     return write(vc->fd, buf, len);
1059d861def3SAnthony Liguori }
1060d861def3SAnthony Liguori 
1061d861def3SAnthony Liguori static int nb_vcs;
1062d861def3SAnthony Liguori static CharDriverState *vcs[MAX_VCS];
1063d861def3SAnthony Liguori 
1064702ec69cSGerd Hoffmann static CharDriverState *gd_vc_handler(ChardevVC *unused)
1065d861def3SAnthony Liguori {
1066d861def3SAnthony Liguori     CharDriverState *chr;
1067d861def3SAnthony Liguori 
1068d861def3SAnthony Liguori     chr = g_malloc0(sizeof(*chr));
1069d861def3SAnthony Liguori     chr->chr_write = gd_vc_chr_write;
1070d861def3SAnthony Liguori 
1071d861def3SAnthony Liguori     vcs[nb_vcs++] = chr;
1072d861def3SAnthony Liguori 
1073d861def3SAnthony Liguori     return chr;
1074d861def3SAnthony Liguori }
1075d861def3SAnthony Liguori 
1076a4ccabcfSAnthony Liguori void early_gtk_display_init(void)
1077a4ccabcfSAnthony Liguori {
1078d861def3SAnthony Liguori     register_vc_handler(gd_vc_handler);
1079d861def3SAnthony Liguori }
1080d861def3SAnthony Liguori 
1081d861def3SAnthony Liguori static gboolean gd_vc_in(GIOChannel *chan, GIOCondition cond, void *opaque)
1082d861def3SAnthony Liguori {
1083d861def3SAnthony Liguori     VirtualConsole *vc = opaque;
1084d861def3SAnthony Liguori     uint8_t buffer[1024];
1085d861def3SAnthony Liguori     ssize_t len;
1086d861def3SAnthony Liguori 
1087d861def3SAnthony Liguori     len = read(vc->fd, buffer, sizeof(buffer));
1088d861def3SAnthony Liguori     if (len <= 0) {
1089d861def3SAnthony Liguori         return FALSE;
1090d861def3SAnthony Liguori     }
1091d861def3SAnthony Liguori 
1092d861def3SAnthony Liguori     qemu_chr_be_write(vc->chr, buffer, len);
1093d861def3SAnthony Liguori 
1094d861def3SAnthony Liguori     return TRUE;
1095d861def3SAnthony Liguori }
1096d861def3SAnthony Liguori 
1097d861def3SAnthony Liguori static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group)
1098d861def3SAnthony Liguori {
1099d861def3SAnthony Liguori     const char *label;
1100d861def3SAnthony Liguori     char buffer[32];
1101d861def3SAnthony Liguori     char path[32];
110228d2e5b2SStefan Weil #if VTE_CHECK_VERSION(0, 26, 0)
1103d861def3SAnthony Liguori     VtePty *pty;
110428d2e5b2SStefan Weil #endif
1105d861def3SAnthony Liguori     GIOChannel *chan;
1106d861def3SAnthony Liguori     GtkWidget *scrolled_window;
1107d861def3SAnthony Liguori     GtkAdjustment *vadjustment;
1108d861def3SAnthony Liguori     int master_fd, slave_fd, ret;
1109d861def3SAnthony Liguori     struct termios tty;
1110d861def3SAnthony Liguori 
1111d861def3SAnthony Liguori     snprintf(buffer, sizeof(buffer), "vc%d", index);
1112d861def3SAnthony Liguori     snprintf(path, sizeof(path), "<QEMU>/View/VC%d", index);
1113d861def3SAnthony Liguori 
1114d861def3SAnthony Liguori     vc->chr = vcs[index];
1115d861def3SAnthony Liguori 
1116d861def3SAnthony Liguori     if (vc->chr->label) {
1117d861def3SAnthony Liguori         label = vc->chr->label;
1118d861def3SAnthony Liguori     } else {
1119d861def3SAnthony Liguori         label = buffer;
1120d861def3SAnthony Liguori     }
1121d861def3SAnthony Liguori 
1122d861def3SAnthony Liguori     vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label);
1123d861def3SAnthony Liguori     group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
1124d861def3SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path);
1125d861def3SAnthony Liguori     gtk_accel_map_add_entry(path, GDK_KEY_2 + index, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1126d861def3SAnthony Liguori 
1127d861def3SAnthony Liguori     vc->terminal = vte_terminal_new();
1128d861def3SAnthony Liguori 
1129d861def3SAnthony Liguori     ret = openpty(&master_fd, &slave_fd, NULL, NULL, NULL);
1130d861def3SAnthony Liguori     g_assert(ret != -1);
1131d861def3SAnthony Liguori 
1132d861def3SAnthony Liguori     /* Set raw attributes on the pty. */
1133d861def3SAnthony Liguori     tcgetattr(slave_fd, &tty);
1134d861def3SAnthony Liguori     cfmakeraw(&tty);
1135d861def3SAnthony Liguori     tcsetattr(slave_fd, TCSAFLUSH, &tty);
1136d861def3SAnthony Liguori 
113728d2e5b2SStefan Weil #if VTE_CHECK_VERSION(0, 26, 0)
1138d861def3SAnthony Liguori     pty = vte_pty_new_foreign(master_fd, NULL);
1139d861def3SAnthony Liguori     vte_terminal_set_pty_object(VTE_TERMINAL(vc->terminal), pty);
114028d2e5b2SStefan Weil #else
114128d2e5b2SStefan Weil     vte_terminal_set_pty(VTE_TERMINAL(vc->terminal), master_fd);
114228d2e5b2SStefan Weil #endif
1143d861def3SAnthony Liguori 
1144d861def3SAnthony Liguori     vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1);
1145d861def3SAnthony Liguori 
1146d861def3SAnthony Liguori     vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal));
1147d861def3SAnthony Liguori 
1148d861def3SAnthony Liguori     scrolled_window = gtk_scrolled_window_new(NULL, vadjustment);
1149d861def3SAnthony Liguori     gtk_container_add(GTK_CONTAINER(scrolled_window), vc->terminal);
1150d861def3SAnthony Liguori 
1151d861def3SAnthony Liguori     vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25);
1152d861def3SAnthony Liguori 
1153d861def3SAnthony Liguori     vc->fd = slave_fd;
1154d861def3SAnthony Liguori     vc->chr->opaque = vc;
1155d861def3SAnthony Liguori     vc->scrolled_window = scrolled_window;
1156d861def3SAnthony Liguori 
1157d861def3SAnthony Liguori     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vc->scrolled_window),
1158d861def3SAnthony Liguori                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1159d861def3SAnthony Liguori 
1160d861def3SAnthony Liguori     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), scrolled_window, gtk_label_new(label));
1161d861def3SAnthony Liguori     g_signal_connect(vc->menu_item, "activate",
1162d861def3SAnthony Liguori                      G_CALLBACK(gd_menu_switch_vc), s);
1163d861def3SAnthony Liguori 
1164530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), vc->menu_item);
1165d861def3SAnthony Liguori 
1166fee204fdSHans de Goede     qemu_chr_be_generic_open(vc->chr);
1167d861def3SAnthony Liguori     if (vc->chr->init) {
1168d861def3SAnthony Liguori         vc->chr->init(vc->chr);
1169d861def3SAnthony Liguori     }
1170d861def3SAnthony Liguori 
1171d861def3SAnthony Liguori     chan = g_io_channel_unix_new(vc->fd);
1172d861def3SAnthony Liguori     g_io_add_watch(chan, G_IO_IN, gd_vc_in, vc);
1173d861def3SAnthony Liguori 
1174d861def3SAnthony Liguori     return group;
1175a4ccabcfSAnthony Liguori }
1176a4ccabcfSAnthony Liguori 
1177a4ccabcfSAnthony Liguori /** Window Creation **/
1178a4ccabcfSAnthony Liguori 
1179a4ccabcfSAnthony Liguori static void gd_connect_signals(GtkDisplayState *s)
1180a4ccabcfSAnthony Liguori {
1181a4ccabcfSAnthony Liguori     g_signal_connect(s->show_tabs_item, "activate",
1182a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_show_tabs), s);
1183a4ccabcfSAnthony Liguori 
118473d4dc71SAnthony Liguori     g_signal_connect(s->window, "key-press-event",
118573d4dc71SAnthony Liguori                      G_CALLBACK(gd_window_key_event), s);
1186a4ccabcfSAnthony Liguori     g_signal_connect(s->window, "delete-event",
1187a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_window_close), s);
1188a4ccabcfSAnthony Liguori 
1189fe43bca8SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0)
1190fe43bca8SDaniel P. Berrange     g_signal_connect(s->drawing_area, "draw",
1191fe43bca8SDaniel P. Berrange                      G_CALLBACK(gd_draw_event), s);
1192fe43bca8SDaniel P. Berrange #else
1193a4ccabcfSAnthony Liguori     g_signal_connect(s->drawing_area, "expose-event",
1194a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_expose_event), s);
1195fe43bca8SDaniel P. Berrange #endif
1196a4ccabcfSAnthony Liguori     g_signal_connect(s->drawing_area, "motion-notify-event",
1197a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_motion_event), s);
1198a4ccabcfSAnthony Liguori     g_signal_connect(s->drawing_area, "button-press-event",
1199a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_button_event), s);
1200a4ccabcfSAnthony Liguori     g_signal_connect(s->drawing_area, "button-release-event",
1201a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_button_event), s);
1202a4ccabcfSAnthony Liguori     g_signal_connect(s->drawing_area, "key-press-event",
1203a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_key_event), s);
1204a4ccabcfSAnthony Liguori     g_signal_connect(s->drawing_area, "key-release-event",
1205a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_key_event), s);
1206a4ccabcfSAnthony Liguori 
120730e8f22bSJan Kiszka     g_signal_connect(s->pause_item, "activate",
120830e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_pause), s);
120930e8f22bSJan Kiszka     g_signal_connect(s->reset_item, "activate",
121030e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_reset), s);
121130e8f22bSJan Kiszka     g_signal_connect(s->powerdown_item, "activate",
121230e8f22bSJan Kiszka                      G_CALLBACK(gd_menu_powerdown), s);
1213a4ccabcfSAnthony Liguori     g_signal_connect(s->quit_item, "activate",
1214a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_quit), s);
1215c6158483SAnthony Liguori     g_signal_connect(s->full_screen_item, "activate",
1216c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_full_screen), s);
1217c6158483SAnthony Liguori     g_signal_connect(s->zoom_in_item, "activate",
1218c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_in), s);
1219c6158483SAnthony Liguori     g_signal_connect(s->zoom_out_item, "activate",
1220c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_out), s);
1221c6158483SAnthony Liguori     g_signal_connect(s->zoom_fixed_item, "activate",
1222c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_fixed), s);
1223c6158483SAnthony Liguori     g_signal_connect(s->zoom_fit_item, "activate",
1224c6158483SAnthony Liguori                      G_CALLBACK(gd_menu_zoom_fit), s);
1225a4ccabcfSAnthony Liguori     g_signal_connect(s->vga_item, "activate",
1226a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_menu_switch_vc), s);
12275104a1f6SAnthony Liguori     g_signal_connect(s->grab_item, "activate",
12285104a1f6SAnthony Liguori                      G_CALLBACK(gd_menu_grab_input), s);
1229a4ccabcfSAnthony Liguori     g_signal_connect(s->notebook, "switch-page",
1230a4ccabcfSAnthony Liguori                      G_CALLBACK(gd_change_page), s);
12315104a1f6SAnthony Liguori     g_signal_connect(s->drawing_area, "enter-notify-event",
12325104a1f6SAnthony Liguori                      G_CALLBACK(gd_enter_event), s);
12335104a1f6SAnthony Liguori     g_signal_connect(s->drawing_area, "leave-notify-event",
12345104a1f6SAnthony Liguori                      G_CALLBACK(gd_leave_event), s);
12356db253caSJan Kiszka     g_signal_connect(s->drawing_area, "focus-out-event",
12366db253caSJan Kiszka                      G_CALLBACK(gd_focus_out_event), s);
1237a4ccabcfSAnthony Liguori }
1238a4ccabcfSAnthony Liguori 
1239a4ccabcfSAnthony Liguori static void gd_create_menus(GtkDisplayState *s)
1240a4ccabcfSAnthony Liguori {
1241a4ccabcfSAnthony Liguori     GtkStockItem item;
1242a4ccabcfSAnthony Liguori     GtkAccelGroup *accel_group;
1243a4ccabcfSAnthony Liguori     GSList *group = NULL;
1244a4ccabcfSAnthony Liguori     GtkWidget *separator;
1245d861def3SAnthony Liguori     int i;
1246a4ccabcfSAnthony Liguori 
1247a4ccabcfSAnthony Liguori     accel_group = gtk_accel_group_new();
124830e8f22bSJan Kiszka     s->machine_menu = gtk_menu_new();
124930e8f22bSJan Kiszka     gtk_menu_set_accel_group(GTK_MENU(s->machine_menu), accel_group);
125030e8f22bSJan Kiszka     s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
125130e8f22bSJan Kiszka 
125230e8f22bSJan Kiszka     s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
1253530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->machine_menu), s->pause_item);
125430e8f22bSJan Kiszka 
125530e8f22bSJan Kiszka     separator = gtk_separator_menu_item_new();
1256530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->machine_menu), separator);
125730e8f22bSJan Kiszka 
125830e8f22bSJan Kiszka     s->reset_item = gtk_image_menu_item_new_with_mnemonic(_("_Reset"));
1259530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->machine_menu), s->reset_item);
126030e8f22bSJan Kiszka 
126130e8f22bSJan Kiszka     s->powerdown_item = gtk_image_menu_item_new_with_mnemonic(_("Power _Down"));
1262530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->machine_menu), s->powerdown_item);
126330e8f22bSJan Kiszka 
126430e8f22bSJan Kiszka     separator = gtk_separator_menu_item_new();
1265530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->machine_menu), separator);
1266a4ccabcfSAnthony Liguori 
1267a4ccabcfSAnthony Liguori     s->quit_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
1268a4ccabcfSAnthony Liguori     gtk_stock_lookup(GTK_STOCK_QUIT, &item);
1269a4ccabcfSAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
127030e8f22bSJan Kiszka                                  "<QEMU>/Machine/Quit");
127130e8f22bSJan Kiszka     gtk_accel_map_add_entry("<QEMU>/Machine/Quit", item.keyval, item.modifier);
1272530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->machine_menu), s->quit_item);
1273a4ccabcfSAnthony Liguori 
1274a4ccabcfSAnthony Liguori     s->view_menu = gtk_menu_new();
1275a4ccabcfSAnthony Liguori     gtk_menu_set_accel_group(GTK_MENU(s->view_menu), accel_group);
1276834574eaSAnthony Liguori     s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
1277a4ccabcfSAnthony Liguori 
127810409282SStefan Weil     s->full_screen_item =
127910409282SStefan Weil         gtk_image_menu_item_new_from_stock(GTK_STOCK_FULLSCREEN, NULL);
1280c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->full_screen_item),
1281c6158483SAnthony Liguori                                  "<QEMU>/View/Full Screen");
1282c6158483SAnthony Liguori     gtk_accel_map_add_entry("<QEMU>/View/Full Screen", GDK_KEY_f, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1283530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->full_screen_item);
1284c6158483SAnthony Liguori 
1285c6158483SAnthony Liguori     separator = gtk_separator_menu_item_new();
1286530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), separator);
1287c6158483SAnthony Liguori 
1288c6158483SAnthony Liguori     s->zoom_in_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_IN, NULL);
1289c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
1290c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom In");
1291c6158483SAnthony Liguori     gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1292530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->zoom_in_item);
1293c6158483SAnthony Liguori 
1294c6158483SAnthony Liguori     s->zoom_out_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_OUT, NULL);
1295c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
1296c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom Out");
1297c6158483SAnthony Liguori     gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1298530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->zoom_out_item);
1299c6158483SAnthony Liguori 
1300c6158483SAnthony Liguori     s->zoom_fixed_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_100, NULL);
1301c6158483SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
1302c6158483SAnthony Liguori                                  "<QEMU>/View/Zoom Fixed");
1303c6158483SAnthony Liguori     gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1304530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->zoom_fixed_item);
1305c6158483SAnthony Liguori 
1306834574eaSAnthony Liguori     s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
1307530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->zoom_fit_item);
1308c6158483SAnthony Liguori 
1309c6158483SAnthony Liguori     separator = gtk_separator_menu_item_new();
1310530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), separator);
1311c6158483SAnthony Liguori 
1312834574eaSAnthony Liguori     s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
1313530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->grab_on_hover_item);
13145104a1f6SAnthony Liguori 
1315834574eaSAnthony Liguori     s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
13165104a1f6SAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
13175104a1f6SAnthony Liguori                                  "<QEMU>/View/Grab Input");
13185104a1f6SAnthony Liguori     gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1319530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->grab_item);
13205104a1f6SAnthony Liguori 
1321a4ccabcfSAnthony Liguori     separator = gtk_separator_menu_item_new();
1322530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), separator);
1323a4ccabcfSAnthony Liguori 
1324a4ccabcfSAnthony Liguori     s->vga_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA");
1325a4ccabcfSAnthony Liguori     group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s->vga_item));
1326a4ccabcfSAnthony Liguori     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->vga_item),
1327a4ccabcfSAnthony Liguori                                  "<QEMU>/View/VGA");
1328a4ccabcfSAnthony Liguori     gtk_accel_map_add_entry("<QEMU>/View/VGA", GDK_KEY_1, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1329530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->vga_item);
1330a4ccabcfSAnthony Liguori 
1331d861def3SAnthony Liguori     for (i = 0; i < nb_vcs; i++) {
1332d861def3SAnthony Liguori         VirtualConsole *vc = &s->vc[i];
1333d861def3SAnthony Liguori 
1334d861def3SAnthony Liguori         group = gd_vc_init(s, vc, i, group);
1335d861def3SAnthony Liguori         s->nb_vcs++;
1336d861def3SAnthony Liguori     }
1337d861def3SAnthony Liguori 
1338a4ccabcfSAnthony Liguori     separator = gtk_separator_menu_item_new();
1339530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), separator);
1340a4ccabcfSAnthony Liguori 
1341834574eaSAnthony Liguori     s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
1342530daf82SDaniel P. Berrange     gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->show_tabs_item);
1343a4ccabcfSAnthony Liguori 
1344a4ccabcfSAnthony Liguori     g_object_set_data(G_OBJECT(s->window), "accel_group", accel_group);
1345a4ccabcfSAnthony Liguori     gtk_window_add_accel_group(GTK_WINDOW(s->window), accel_group);
134673d4dc71SAnthony Liguori     s->accel_group = accel_group;
1347a4ccabcfSAnthony Liguori 
134830e8f22bSJan Kiszka     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
134930e8f22bSJan Kiszka                               s->machine_menu);
135030e8f22bSJan Kiszka     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
1351a4ccabcfSAnthony Liguori 
1352a4ccabcfSAnthony Liguori     gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
1353a4ccabcfSAnthony Liguori     gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
1354a4ccabcfSAnthony Liguori }
1355a4ccabcfSAnthony Liguori 
13567c20b4a3SGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = {
13577c20b4a3SGerd Hoffmann     .dpy_name          = "gtk",
13587c20b4a3SGerd Hoffmann     .dpy_gfx_update    = gd_update,
1359c12aeb86SGerd Hoffmann     .dpy_gfx_switch    = gd_switch,
13607c20b4a3SGerd Hoffmann     .dpy_refresh       = gd_refresh,
13617c20b4a3SGerd Hoffmann };
13627c20b4a3SGerd Hoffmann 
1363a4ccabcfSAnthony Liguori void gtk_display_init(DisplayState *ds)
1364a4ccabcfSAnthony Liguori {
1365a4ccabcfSAnthony Liguori     GtkDisplayState *s = g_malloc0(sizeof(*s));
1366*d819cdccSStefan Weil     char *filename;
1367a4ccabcfSAnthony Liguori 
1368a4ccabcfSAnthony Liguori     gtk_init(NULL, NULL);
1369a4ccabcfSAnthony Liguori 
13707c20b4a3SGerd Hoffmann     s->dcl.ops = &dcl_ops;
1371a4ccabcfSAnthony Liguori 
1372a4ccabcfSAnthony Liguori     s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
137351572ab0SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 2, 0)
137451572ab0SDaniel P. Berrange     s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
137551572ab0SDaniel P. Berrange #else
1376a4ccabcfSAnthony Liguori     s->vbox = gtk_vbox_new(FALSE, 0);
137751572ab0SDaniel P. Berrange #endif
1378a4ccabcfSAnthony Liguori     s->notebook = gtk_notebook_new();
1379a4ccabcfSAnthony Liguori     s->drawing_area = gtk_drawing_area_new();
1380a4ccabcfSAnthony Liguori     s->menu_bar = gtk_menu_bar_new();
1381a4ccabcfSAnthony Liguori 
1382a4ccabcfSAnthony Liguori     s->scale_x = 1.0;
1383a4ccabcfSAnthony Liguori     s->scale_y = 1.0;
1384c6158483SAnthony Liguori     s->free_scale = FALSE;
1385a4ccabcfSAnthony Liguori 
1386834574eaSAnthony Liguori     setlocale(LC_ALL, "");
1387834574eaSAnthony Liguori     bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
1388834574eaSAnthony Liguori     textdomain("qemu");
1389834574eaSAnthony Liguori 
1390a4ccabcfSAnthony Liguori     s->null_cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
1391a4ccabcfSAnthony Liguori 
1392a4ccabcfSAnthony Liguori     s->mouse_mode_notifier.notify = gd_mouse_mode_change;
1393a4ccabcfSAnthony Liguori     qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
1394a4ccabcfSAnthony Liguori     qemu_add_vm_change_state_handler(gd_change_runstate, s);
1395a4ccabcfSAnthony Liguori 
1396a4ccabcfSAnthony Liguori     gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), s->drawing_area, gtk_label_new("VGA"));
1397a4ccabcfSAnthony Liguori 
1398*d819cdccSStefan Weil     filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
1399*d819cdccSStefan Weil     if (filename) {
1400*d819cdccSStefan Weil         GError *error = NULL;
1401*d819cdccSStefan Weil         GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error);
1402*d819cdccSStefan Weil         if (pixbuf) {
1403*d819cdccSStefan Weil             gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf);
1404*d819cdccSStefan Weil         } else {
1405*d819cdccSStefan Weil             g_error_free(error);
1406*d819cdccSStefan Weil         }
1407*d819cdccSStefan Weil         g_free(filename);
1408*d819cdccSStefan Weil     }
1409*d819cdccSStefan Weil 
1410a4ccabcfSAnthony Liguori     gd_create_menus(s);
1411a4ccabcfSAnthony Liguori 
1412a4ccabcfSAnthony Liguori     gd_connect_signals(s);
1413a4ccabcfSAnthony Liguori 
1414a4ccabcfSAnthony Liguori     gtk_widget_add_events(s->drawing_area,
1415a4ccabcfSAnthony Liguori                           GDK_POINTER_MOTION_MASK |
1416a4ccabcfSAnthony Liguori                           GDK_BUTTON_PRESS_MASK |
1417a4ccabcfSAnthony Liguori                           GDK_BUTTON_RELEASE_MASK |
1418a4ccabcfSAnthony Liguori                           GDK_BUTTON_MOTION_MASK |
14195104a1f6SAnthony Liguori                           GDK_ENTER_NOTIFY_MASK |
14205104a1f6SAnthony Liguori                           GDK_LEAVE_NOTIFY_MASK |
1421a4ccabcfSAnthony Liguori                           GDK_SCROLL_MASK |
1422a4ccabcfSAnthony Liguori                           GDK_KEY_PRESS_MASK);
1423a4ccabcfSAnthony Liguori     gtk_widget_set_double_buffered(s->drawing_area, FALSE);
1424a4ccabcfSAnthony Liguori     gtk_widget_set_can_focus(s->drawing_area, TRUE);
1425a4ccabcfSAnthony Liguori 
1426a4ccabcfSAnthony Liguori     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1427a4ccabcfSAnthony Liguori     gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
1428a4ccabcfSAnthony Liguori 
1429a4ccabcfSAnthony Liguori     gd_update_caption(s);
1430a4ccabcfSAnthony Liguori 
1431a4ccabcfSAnthony Liguori     gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
1432a4ccabcfSAnthony Liguori     gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
1433a4ccabcfSAnthony Liguori 
1434a4ccabcfSAnthony Liguori     gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
1435a4ccabcfSAnthony Liguori 
1436a4ccabcfSAnthony Liguori     gtk_widget_show_all(s->window);
1437a4ccabcfSAnthony Liguori 
1438a4ccabcfSAnthony Liguori     register_displaychangelistener(ds, &s->dcl);
1439a4ccabcfSAnthony Liguori 
1440a4ccabcfSAnthony Liguori     global_state = s;
1441a4ccabcfSAnthony Liguori }
1442