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" 70a4ccabcfSAnthony Liguori 71d861def3SAnthony Liguori #define MAX_VCS 10 72d861def3SAnthony Liguori 73bbbf9bfbSStefan Weil #if !defined(CONFIG_VTE) 74bbbf9bfbSStefan Weil # define VTE_CHECK_VERSION(a, b, c) 0 75bbbf9bfbSStefan Weil #endif 76cba68834SDaniel P. Berrange 77cba68834SDaniel P. Berrange /* Compatibility define to let us build on both Gtk2 and Gtk3 */ 78cba68834SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0) 79cba68834SDaniel P. Berrange static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh) 80cba68834SDaniel P. Berrange { 81cba68834SDaniel P. Berrange *ww = gdk_window_get_width(w); 82cba68834SDaniel P. Berrange *wh = gdk_window_get_height(w); 83cba68834SDaniel P. Berrange } 84cba68834SDaniel P. Berrange #endif 85cba68834SDaniel P. Berrange 86ef6413a2SDaniel P. Berrange #if !GTK_CHECK_VERSION(2, 20, 0) 87ef6413a2SDaniel P. Berrange #define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget) 88ef6413a2SDaniel P. Berrange #endif 89ef6413a2SDaniel P. Berrange 90bc0477c7SDaniel P. Berrange #ifndef GDK_KEY_0 91bc0477c7SDaniel P. Berrange #define GDK_KEY_0 GDK_0 92bc0477c7SDaniel P. Berrange #define GDK_KEY_1 GDK_1 93bc0477c7SDaniel P. Berrange #define GDK_KEY_2 GDK_2 94bc0477c7SDaniel P. Berrange #define GDK_KEY_f GDK_f 95bc0477c7SDaniel P. Berrange #define GDK_KEY_g GDK_g 96db1da1f2SCole Robinson #define GDK_KEY_q GDK_q 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 101b1e749c0SJan Kiszka #define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK) 102b1e749c0SJan Kiszka 1036db253caSJan Kiszka static const int modifier_keycode[] = { 1046db253caSJan Kiszka /* shift, control, alt keys, meta keys, both left & right */ 1056db253caSJan Kiszka 0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd, 1066db253caSJan Kiszka }; 1076db253caSJan Kiszka 108a4ccabcfSAnthony Liguori typedef struct VirtualConsole 109a4ccabcfSAnthony Liguori { 110a4ccabcfSAnthony Liguori GtkWidget *menu_item; 111bbbf9bfbSStefan Weil #if defined(CONFIG_VTE) 1120fb20d1cSCole Robinson GtkWidget *box; 1130fb20d1cSCole Robinson GtkWidget *scrollbar; 114ee5f31e4SGerd Hoffmann GtkWidget *terminal; 115a4ccabcfSAnthony Liguori CharDriverState *chr; 116bbbf9bfbSStefan Weil #endif 117a4ccabcfSAnthony Liguori } VirtualConsole; 118a4ccabcfSAnthony Liguori 119a4ccabcfSAnthony Liguori typedef struct GtkDisplayState 120a4ccabcfSAnthony Liguori { 121a4ccabcfSAnthony Liguori GtkWidget *window; 122a4ccabcfSAnthony Liguori 123a4ccabcfSAnthony Liguori GtkWidget *menu_bar; 124a4ccabcfSAnthony Liguori 12573d4dc71SAnthony Liguori GtkAccelGroup *accel_group; 12673d4dc71SAnthony Liguori 12730e8f22bSJan Kiszka GtkWidget *machine_menu_item; 12830e8f22bSJan Kiszka GtkWidget *machine_menu; 12930e8f22bSJan Kiszka GtkWidget *pause_item; 13030e8f22bSJan Kiszka GtkWidget *reset_item; 13130e8f22bSJan Kiszka GtkWidget *powerdown_item; 132a4ccabcfSAnthony Liguori GtkWidget *quit_item; 133a4ccabcfSAnthony Liguori 134a4ccabcfSAnthony Liguori GtkWidget *view_menu_item; 135a4ccabcfSAnthony Liguori GtkWidget *view_menu; 136c6158483SAnthony Liguori GtkWidget *full_screen_item; 137c6158483SAnthony Liguori GtkWidget *zoom_in_item; 138c6158483SAnthony Liguori GtkWidget *zoom_out_item; 139c6158483SAnthony Liguori GtkWidget *zoom_fixed_item; 140c6158483SAnthony Liguori GtkWidget *zoom_fit_item; 1415104a1f6SAnthony Liguori GtkWidget *grab_item; 1425104a1f6SAnthony Liguori GtkWidget *grab_on_hover_item; 143a4ccabcfSAnthony Liguori GtkWidget *vga_item; 144a4ccabcfSAnthony Liguori 145d861def3SAnthony Liguori int nb_vcs; 146d861def3SAnthony Liguori VirtualConsole vc[MAX_VCS]; 147d861def3SAnthony Liguori 148a4ccabcfSAnthony Liguori GtkWidget *show_tabs_item; 149a4ccabcfSAnthony Liguori 150a4ccabcfSAnthony Liguori GtkWidget *vbox; 151a4ccabcfSAnthony Liguori GtkWidget *notebook; 152a4ccabcfSAnthony Liguori GtkWidget *drawing_area; 153a4ccabcfSAnthony Liguori cairo_surface_t *surface; 154f0875536SGerd Hoffmann pixman_image_t *convert; 155a4ccabcfSAnthony Liguori DisplayChangeListener dcl; 1569d9801cfSGerd Hoffmann DisplaySurface *ds; 157a4ccabcfSAnthony Liguori int button_mask; 158e61031cdSTakashi Iwai gboolean last_set; 159a4ccabcfSAnthony Liguori int last_x; 160a4ccabcfSAnthony Liguori int last_y; 161ecce1929STakashi Iwai int grab_x_root; 162ecce1929STakashi Iwai int grab_y_root; 163a4ccabcfSAnthony Liguori 164a4ccabcfSAnthony Liguori double scale_x; 165a4ccabcfSAnthony Liguori double scale_y; 166c6158483SAnthony Liguori gboolean full_screen; 167a4ccabcfSAnthony Liguori 168a4ccabcfSAnthony Liguori GdkCursor *null_cursor; 169a4ccabcfSAnthony Liguori Notifier mouse_mode_notifier; 170c6158483SAnthony Liguori gboolean free_scale; 17130e8f22bSJan Kiszka 17230e8f22bSJan Kiszka bool external_pause_update; 1736db253caSJan Kiszka 1746db253caSJan Kiszka bool modifier_pressed[ARRAY_SIZE(modifier_keycode)]; 175a4ccabcfSAnthony Liguori } GtkDisplayState; 176a4ccabcfSAnthony Liguori 177a4ccabcfSAnthony Liguori static GtkDisplayState *global_state; 178a4ccabcfSAnthony Liguori 179a4ccabcfSAnthony Liguori /** Utility Functions **/ 180a4ccabcfSAnthony Liguori 1815104a1f6SAnthony Liguori static bool gd_is_grab_active(GtkDisplayState *s) 1825104a1f6SAnthony Liguori { 1835104a1f6SAnthony Liguori return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item)); 1845104a1f6SAnthony Liguori } 1855104a1f6SAnthony Liguori 1865104a1f6SAnthony Liguori static bool gd_grab_on_hover(GtkDisplayState *s) 1875104a1f6SAnthony Liguori { 1885104a1f6SAnthony Liguori return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item)); 1895104a1f6SAnthony Liguori } 1905104a1f6SAnthony Liguori 1915104a1f6SAnthony Liguori static bool gd_on_vga(GtkDisplayState *s) 1925104a1f6SAnthony Liguori { 193*832189c9SGerd Hoffmann gint p1, p2; 194*832189c9SGerd Hoffmann 195*832189c9SGerd Hoffmann p1 = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)); 196*832189c9SGerd Hoffmann p2 = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), s->drawing_area); 197*832189c9SGerd Hoffmann return p1 == p2; 1985104a1f6SAnthony Liguori } 1995104a1f6SAnthony Liguori 200a4ccabcfSAnthony Liguori static void gd_update_cursor(GtkDisplayState *s, gboolean override) 201a4ccabcfSAnthony Liguori { 202a4ccabcfSAnthony Liguori GdkWindow *window; 203a4ccabcfSAnthony Liguori bool on_vga; 204a4ccabcfSAnthony Liguori 205a4ccabcfSAnthony Liguori window = gtk_widget_get_window(GTK_WIDGET(s->drawing_area)); 206a4ccabcfSAnthony Liguori 2075104a1f6SAnthony Liguori on_vga = gd_on_vga(s); 208a4ccabcfSAnthony Liguori 2095104a1f6SAnthony Liguori if ((override || on_vga) && 210192f81bfSGerd Hoffmann (s->full_screen || qemu_input_is_absolute() || gd_is_grab_active(s))) { 211a4ccabcfSAnthony Liguori gdk_window_set_cursor(window, s->null_cursor); 212a4ccabcfSAnthony Liguori } else { 213a4ccabcfSAnthony Liguori gdk_window_set_cursor(window, NULL); 214a4ccabcfSAnthony Liguori } 215a4ccabcfSAnthony Liguori } 216a4ccabcfSAnthony Liguori 217a4ccabcfSAnthony Liguori static void gd_update_caption(GtkDisplayState *s) 218a4ccabcfSAnthony Liguori { 219a4ccabcfSAnthony Liguori const char *status = ""; 220a4ccabcfSAnthony Liguori gchar *title; 2215104a1f6SAnthony Liguori const char *grab = ""; 22230e8f22bSJan Kiszka bool is_paused = !runstate_is_running(); 2235104a1f6SAnthony Liguori 2245104a1f6SAnthony Liguori if (gd_is_grab_active(s)) { 225d8da9ee8SAurelien Jarno grab = _(" - Press Ctrl+Alt+G to release grab"); 2265104a1f6SAnthony Liguori } 227a4ccabcfSAnthony Liguori 22830e8f22bSJan Kiszka if (is_paused) { 229d8da9ee8SAurelien Jarno status = _(" [Paused]"); 230a4ccabcfSAnthony Liguori } 23130e8f22bSJan Kiszka s->external_pause_update = true; 23230e8f22bSJan Kiszka gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item), 23330e8f22bSJan Kiszka is_paused); 23430e8f22bSJan Kiszka s->external_pause_update = false; 235a4ccabcfSAnthony Liguori 236a4ccabcfSAnthony Liguori if (qemu_name) { 2375104a1f6SAnthony Liguori title = g_strdup_printf("QEMU (%s)%s%s", qemu_name, status, grab); 238a4ccabcfSAnthony Liguori } else { 2395104a1f6SAnthony Liguori title = g_strdup_printf("QEMU%s%s", status, grab); 240a4ccabcfSAnthony Liguori } 241a4ccabcfSAnthony Liguori 242a4ccabcfSAnthony Liguori gtk_window_set_title(GTK_WINDOW(s->window), title); 243a4ccabcfSAnthony Liguori 244a4ccabcfSAnthony Liguori g_free(title); 245a4ccabcfSAnthony Liguori } 246a4ccabcfSAnthony Liguori 2479d9801cfSGerd Hoffmann static void gd_update_windowsize(GtkDisplayState *s) 248a4ccabcfSAnthony Liguori { 249c6158483SAnthony Liguori if (!s->full_screen) { 250c6158483SAnthony Liguori GtkRequisition req; 251c6158483SAnthony Liguori double sx, sy; 252c6158483SAnthony Liguori 253c6158483SAnthony Liguori if (s->free_scale) { 254c6158483SAnthony Liguori sx = s->scale_x; 255c6158483SAnthony Liguori sy = s->scale_y; 256c6158483SAnthony Liguori 257c6158483SAnthony Liguori s->scale_y = 1.0; 258c6158483SAnthony Liguori s->scale_x = 1.0; 259c6158483SAnthony Liguori } else { 260c6158483SAnthony Liguori sx = 1.0; 261c6158483SAnthony Liguori sy = 1.0; 262c6158483SAnthony Liguori } 263c6158483SAnthony Liguori 264a4ccabcfSAnthony Liguori gtk_widget_set_size_request(s->drawing_area, 2659d9801cfSGerd Hoffmann surface_width(s->ds) * s->scale_x, 2669d9801cfSGerd Hoffmann surface_height(s->ds) * s->scale_y); 2671ed76b59SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0) 2681ed76b59SDaniel P. Berrange gtk_widget_get_preferred_size(s->vbox, NULL, &req); 2691ed76b59SDaniel P. Berrange #else 270c6158483SAnthony Liguori gtk_widget_size_request(s->vbox, &req); 2711ed76b59SDaniel P. Berrange #endif 272c6158483SAnthony Liguori 273c6158483SAnthony Liguori gtk_window_resize(GTK_WINDOW(s->window), 274c6158483SAnthony Liguori req.width * sx, req.height * sy); 275c6158483SAnthony Liguori } 276a4ccabcfSAnthony Liguori } 277a4ccabcfSAnthony Liguori 2789d9801cfSGerd Hoffmann static void gd_update_full_redraw(GtkDisplayState *s) 2799d9801cfSGerd Hoffmann { 2809d9801cfSGerd Hoffmann int ww, wh; 2819d9801cfSGerd Hoffmann gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); 2829d9801cfSGerd Hoffmann gtk_widget_queue_draw_area(s->drawing_area, 0, 0, ww, wh); 2839d9801cfSGerd Hoffmann } 2849d9801cfSGerd Hoffmann 2856db253caSJan Kiszka static void gtk_release_modifiers(GtkDisplayState *s) 2866db253caSJan Kiszka { 2876db253caSJan Kiszka int i, keycode; 2886db253caSJan Kiszka 2896db253caSJan Kiszka if (!gd_on_vga(s)) { 2906db253caSJan Kiszka return; 2916db253caSJan Kiszka } 2926db253caSJan Kiszka for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { 2936db253caSJan Kiszka keycode = modifier_keycode[i]; 2946db253caSJan Kiszka if (!s->modifier_pressed[i]) { 2956db253caSJan Kiszka continue; 2966db253caSJan Kiszka } 297af98ba92SGerd Hoffmann qemu_input_event_send_key_number(s->dcl.con, keycode, false); 2986db253caSJan Kiszka s->modifier_pressed[i] = false; 2996db253caSJan Kiszka } 3006db253caSJan Kiszka } 3016db253caSJan Kiszka 3029d9801cfSGerd Hoffmann /** DisplayState Callbacks **/ 3039d9801cfSGerd Hoffmann 3049d9801cfSGerd Hoffmann static void gd_update(DisplayChangeListener *dcl, 305bc2ed970SGerd Hoffmann int x, int y, int w, int h) 3069d9801cfSGerd Hoffmann { 3079d9801cfSGerd Hoffmann GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); 3089d9801cfSGerd Hoffmann int x1, x2, y1, y2; 3099d9801cfSGerd Hoffmann int mx, my; 3109d9801cfSGerd Hoffmann int fbw, fbh; 3119d9801cfSGerd Hoffmann int ww, wh; 3129d9801cfSGerd Hoffmann 313ef0dd982SStefan Weil trace_gd_update(x, y, w, h); 3149d9801cfSGerd Hoffmann 315f0875536SGerd Hoffmann if (s->convert) { 316f0875536SGerd Hoffmann pixman_image_composite(PIXMAN_OP_SRC, s->ds->image, NULL, s->convert, 317f0875536SGerd Hoffmann x, y, 0, 0, x, y, w, h); 318f0875536SGerd Hoffmann } 319f0875536SGerd Hoffmann 3209d9801cfSGerd Hoffmann x1 = floor(x * s->scale_x); 3219d9801cfSGerd Hoffmann y1 = floor(y * s->scale_y); 3229d9801cfSGerd Hoffmann 3239d9801cfSGerd Hoffmann x2 = ceil(x * s->scale_x + w * s->scale_x); 3249d9801cfSGerd Hoffmann y2 = ceil(y * s->scale_y + h * s->scale_y); 3259d9801cfSGerd Hoffmann 3269d9801cfSGerd Hoffmann fbw = surface_width(s->ds) * s->scale_x; 3279d9801cfSGerd Hoffmann fbh = surface_height(s->ds) * s->scale_y; 3289d9801cfSGerd Hoffmann 3299d9801cfSGerd Hoffmann gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); 3309d9801cfSGerd Hoffmann 3319d9801cfSGerd Hoffmann mx = my = 0; 3329d9801cfSGerd Hoffmann if (ww > fbw) { 3339d9801cfSGerd Hoffmann mx = (ww - fbw) / 2; 3349d9801cfSGerd Hoffmann } 3359d9801cfSGerd Hoffmann if (wh > fbh) { 3369d9801cfSGerd Hoffmann my = (wh - fbh) / 2; 3379d9801cfSGerd Hoffmann } 3389d9801cfSGerd Hoffmann 3399d9801cfSGerd Hoffmann gtk_widget_queue_draw_area(s->drawing_area, mx + x1, my + y1, (x2 - x1), (y2 - y1)); 3409d9801cfSGerd Hoffmann } 3419d9801cfSGerd Hoffmann 342bc2ed970SGerd Hoffmann static void gd_refresh(DisplayChangeListener *dcl) 3439d9801cfSGerd Hoffmann { 344284d1c6bSGerd Hoffmann graphic_hw_update(dcl->con); 3459d9801cfSGerd Hoffmann } 3469d9801cfSGerd Hoffmann 347b087143bSIgor Mitsyanko #if GTK_CHECK_VERSION(3, 0, 0) 348b087143bSIgor Mitsyanko static void gd_mouse_set(DisplayChangeListener *dcl, 349b087143bSIgor Mitsyanko int x, int y, int visible) 350b087143bSIgor Mitsyanko { 351b087143bSIgor Mitsyanko GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); 352b087143bSIgor Mitsyanko GdkDisplay *dpy; 353b087143bSIgor Mitsyanko GdkDeviceManager *mgr; 354b087143bSIgor Mitsyanko gint x_root, y_root; 355b087143bSIgor Mitsyanko 3562bda6602SCole Robinson if (qemu_input_is_absolute()) { 3572bda6602SCole Robinson return; 3582bda6602SCole Robinson } 3592bda6602SCole Robinson 360b087143bSIgor Mitsyanko dpy = gtk_widget_get_display(s->drawing_area); 361b087143bSIgor Mitsyanko mgr = gdk_display_get_device_manager(dpy); 362b087143bSIgor Mitsyanko gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area), 363b087143bSIgor Mitsyanko x, y, &x_root, &y_root); 364b087143bSIgor Mitsyanko gdk_device_warp(gdk_device_manager_get_client_pointer(mgr), 365b087143bSIgor Mitsyanko gtk_widget_get_screen(s->drawing_area), 366298526feSCole Robinson x_root, y_root); 367b087143bSIgor Mitsyanko } 368b087143bSIgor Mitsyanko #else 3699697f5d2SGerd Hoffmann static void gd_mouse_set(DisplayChangeListener *dcl, 3709697f5d2SGerd Hoffmann int x, int y, int visible) 3719697f5d2SGerd Hoffmann { 3729697f5d2SGerd Hoffmann GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); 3739697f5d2SGerd Hoffmann gint x_root, y_root; 3749697f5d2SGerd Hoffmann 3752bda6602SCole Robinson if (qemu_input_is_absolute()) { 3762bda6602SCole Robinson return; 3772bda6602SCole Robinson } 3782bda6602SCole Robinson 379571253d4SOzan Çağlayan gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area), 3809697f5d2SGerd Hoffmann x, y, &x_root, &y_root); 3819697f5d2SGerd Hoffmann gdk_display_warp_pointer(gtk_widget_get_display(s->drawing_area), 3829697f5d2SGerd Hoffmann gtk_widget_get_screen(s->drawing_area), 3839697f5d2SGerd Hoffmann x_root, y_root); 3849697f5d2SGerd Hoffmann } 385b087143bSIgor Mitsyanko #endif 3869697f5d2SGerd Hoffmann 3879697f5d2SGerd Hoffmann static void gd_cursor_define(DisplayChangeListener *dcl, 3889697f5d2SGerd Hoffmann QEMUCursor *c) 3899697f5d2SGerd Hoffmann { 3909697f5d2SGerd Hoffmann GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); 3919697f5d2SGerd Hoffmann GdkPixbuf *pixbuf; 3929697f5d2SGerd Hoffmann GdkCursor *cursor; 3939697f5d2SGerd Hoffmann 3949697f5d2SGerd Hoffmann pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data), 3959697f5d2SGerd Hoffmann GDK_COLORSPACE_RGB, true, 8, 3969697f5d2SGerd Hoffmann c->width, c->height, c->width * 4, 3979697f5d2SGerd Hoffmann NULL, NULL); 3989697f5d2SGerd Hoffmann cursor = gdk_cursor_new_from_pixbuf(gtk_widget_get_display(s->drawing_area), 3999697f5d2SGerd Hoffmann pixbuf, c->hot_x, c->hot_y); 400571253d4SOzan Çağlayan gdk_window_set_cursor(gtk_widget_get_window(s->drawing_area), cursor); 4019697f5d2SGerd Hoffmann g_object_unref(pixbuf); 402030b4b7dSStefan Weil #if !GTK_CHECK_VERSION(3, 0, 0) 40317139240SAnthony Liguori gdk_cursor_unref(cursor); 404030b4b7dSStefan Weil #else 405030b4b7dSStefan Weil g_object_unref(cursor); 406030b4b7dSStefan Weil #endif 4079697f5d2SGerd Hoffmann } 4089697f5d2SGerd Hoffmann 4099d9801cfSGerd Hoffmann static void gd_switch(DisplayChangeListener *dcl, 4109d9801cfSGerd Hoffmann DisplaySurface *surface) 4119d9801cfSGerd Hoffmann { 4129d9801cfSGerd Hoffmann GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); 4139d9801cfSGerd Hoffmann bool resized = true; 4149d9801cfSGerd Hoffmann 415ef0dd982SStefan Weil trace_gd_switch(surface_width(surface), surface_height(surface)); 4169d9801cfSGerd Hoffmann 4179d9801cfSGerd Hoffmann if (s->surface) { 4189d9801cfSGerd Hoffmann cairo_surface_destroy(s->surface); 4199d9801cfSGerd Hoffmann } 4209d9801cfSGerd Hoffmann 4219d9801cfSGerd Hoffmann if (s->ds && 4229d9801cfSGerd Hoffmann surface_width(s->ds) == surface_width(surface) && 4239d9801cfSGerd Hoffmann surface_height(s->ds) == surface_height(surface)) { 4249d9801cfSGerd Hoffmann resized = false; 4259d9801cfSGerd Hoffmann } 4269d9801cfSGerd Hoffmann s->ds = surface; 427f0875536SGerd Hoffmann 428f0875536SGerd Hoffmann if (s->convert) { 429f0875536SGerd Hoffmann pixman_image_unref(s->convert); 430f0875536SGerd Hoffmann s->convert = NULL; 4319d9801cfSGerd Hoffmann } 4329d9801cfSGerd Hoffmann 433f0875536SGerd Hoffmann if (surface->format == PIXMAN_x8r8g8b8) { 434f0875536SGerd Hoffmann /* 435f0875536SGerd Hoffmann * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24 436f0875536SGerd Hoffmann * 437f0875536SGerd Hoffmann * No need to convert, use surface directly. Should be the 438f0875536SGerd Hoffmann * common case as this is qemu_default_pixelformat(32) too. 439f0875536SGerd Hoffmann */ 440f0875536SGerd Hoffmann s->surface = cairo_image_surface_create_for_data 441f0875536SGerd Hoffmann (surface_data(surface), 442f0875536SGerd Hoffmann CAIRO_FORMAT_RGB24, 4439d9801cfSGerd Hoffmann surface_width(surface), 4449d9801cfSGerd Hoffmann surface_height(surface), 4459d9801cfSGerd Hoffmann surface_stride(surface)); 446f0875536SGerd Hoffmann } else { 447f0875536SGerd Hoffmann /* Must convert surface, use pixman to do it. */ 448f0875536SGerd Hoffmann s->convert = pixman_image_create_bits(PIXMAN_x8r8g8b8, 449f0875536SGerd Hoffmann surface_width(surface), 450f0875536SGerd Hoffmann surface_height(surface), 451f0875536SGerd Hoffmann NULL, 0); 452f0875536SGerd Hoffmann s->surface = cairo_image_surface_create_for_data 453f0875536SGerd Hoffmann ((void *)pixman_image_get_data(s->convert), 454f0875536SGerd Hoffmann CAIRO_FORMAT_RGB24, 455f0875536SGerd Hoffmann pixman_image_get_width(s->convert), 456f0875536SGerd Hoffmann pixman_image_get_height(s->convert), 457f0875536SGerd Hoffmann pixman_image_get_stride(s->convert)); 458f0875536SGerd Hoffmann pixman_image_composite(PIXMAN_OP_SRC, s->ds->image, NULL, s->convert, 459f0875536SGerd Hoffmann 0, 0, 0, 0, 0, 0, 460f0875536SGerd Hoffmann pixman_image_get_width(s->convert), 461f0875536SGerd Hoffmann pixman_image_get_height(s->convert)); 462f0875536SGerd Hoffmann } 4639d9801cfSGerd Hoffmann 4649d9801cfSGerd Hoffmann if (resized) { 4659d9801cfSGerd Hoffmann gd_update_windowsize(s); 4669d9801cfSGerd Hoffmann } else { 4679d9801cfSGerd Hoffmann gd_update_full_redraw(s); 4689d9801cfSGerd Hoffmann } 4699d9801cfSGerd Hoffmann } 4709d9801cfSGerd Hoffmann 471a4ccabcfSAnthony Liguori /** QEMU Events **/ 472a4ccabcfSAnthony Liguori 473a4ccabcfSAnthony Liguori static void gd_change_runstate(void *opaque, int running, RunState state) 474a4ccabcfSAnthony Liguori { 475a4ccabcfSAnthony Liguori GtkDisplayState *s = opaque; 476a4ccabcfSAnthony Liguori 477a4ccabcfSAnthony Liguori gd_update_caption(s); 478a4ccabcfSAnthony Liguori } 479a4ccabcfSAnthony Liguori 480a4ccabcfSAnthony Liguori static void gd_mouse_mode_change(Notifier *notify, void *data) 481a4ccabcfSAnthony Liguori { 482800b0e81STakashi Iwai GtkDisplayState *s; 483800b0e81STakashi Iwai 484800b0e81STakashi Iwai s = container_of(notify, GtkDisplayState, mouse_mode_notifier); 485800b0e81STakashi Iwai /* release the grab at switching to absolute mode */ 486800b0e81STakashi Iwai if (qemu_input_is_absolute() && gd_is_grab_active(s)) { 487800b0e81STakashi Iwai gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 488a4ccabcfSAnthony Liguori FALSE); 489a4ccabcfSAnthony Liguori } 490800b0e81STakashi Iwai gd_update_cursor(s, FALSE); 491800b0e81STakashi Iwai } 492a4ccabcfSAnthony Liguori 493a4ccabcfSAnthony Liguori /** GTK Events **/ 494a4ccabcfSAnthony Liguori 495a4ccabcfSAnthony Liguori static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event, 496a4ccabcfSAnthony Liguori void *opaque) 497a4ccabcfSAnthony Liguori { 498a4ccabcfSAnthony Liguori GtkDisplayState *s = opaque; 499a4ccabcfSAnthony Liguori 500a4ccabcfSAnthony Liguori if (!no_quit) { 5017c20b4a3SGerd Hoffmann unregister_displaychangelistener(&s->dcl); 502a4ccabcfSAnthony Liguori qmp_quit(NULL); 503a4ccabcfSAnthony Liguori return FALSE; 504a4ccabcfSAnthony Liguori } 505a4ccabcfSAnthony Liguori 506a4ccabcfSAnthony Liguori return TRUE; 507a4ccabcfSAnthony Liguori } 508a4ccabcfSAnthony Liguori 509a4ccabcfSAnthony Liguori static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) 510a4ccabcfSAnthony Liguori { 511a4ccabcfSAnthony Liguori GtkDisplayState *s = opaque; 512c6158483SAnthony Liguori int mx, my; 513a4ccabcfSAnthony Liguori int ww, wh; 514a4ccabcfSAnthony Liguori int fbw, fbh; 515a4ccabcfSAnthony Liguori 516c6158483SAnthony Liguori if (!gtk_widget_get_realized(widget)) { 517c6158483SAnthony Liguori return FALSE; 518c6158483SAnthony Liguori } 519c6158483SAnthony Liguori 5209d9801cfSGerd Hoffmann fbw = surface_width(s->ds); 5219d9801cfSGerd Hoffmann fbh = surface_height(s->ds); 522a4ccabcfSAnthony Liguori 523a4ccabcfSAnthony Liguori gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh); 524a4ccabcfSAnthony Liguori 525c6158483SAnthony Liguori if (s->full_screen) { 526a4ccabcfSAnthony Liguori s->scale_x = (double)ww / fbw; 527a4ccabcfSAnthony Liguori s->scale_y = (double)wh / fbh; 528c6158483SAnthony Liguori } else if (s->free_scale) { 529c6158483SAnthony Liguori double sx, sy; 530c6158483SAnthony Liguori 531c6158483SAnthony Liguori sx = (double)ww / fbw; 532c6158483SAnthony Liguori sy = (double)wh / fbh; 533c6158483SAnthony Liguori 534c6158483SAnthony Liguori s->scale_x = s->scale_y = MIN(sx, sy); 535a4ccabcfSAnthony Liguori } 536a4ccabcfSAnthony Liguori 5375104a1f6SAnthony Liguori fbw *= s->scale_x; 5385104a1f6SAnthony Liguori fbh *= s->scale_y; 5395104a1f6SAnthony Liguori 540c6158483SAnthony Liguori mx = my = 0; 541c6158483SAnthony Liguori if (ww > fbw) { 542c6158483SAnthony Liguori mx = (ww - fbw) / 2; 543c6158483SAnthony Liguori } 544c6158483SAnthony Liguori if (wh > fbh) { 545c6158483SAnthony Liguori my = (wh - fbh) / 2; 546c6158483SAnthony Liguori } 547c6158483SAnthony Liguori 548c6158483SAnthony Liguori cairo_rectangle(cr, 0, 0, ww, wh); 549c6158483SAnthony Liguori 550c6158483SAnthony Liguori /* Optionally cut out the inner area where the pixmap 551c6158483SAnthony Liguori will be drawn. This avoids 'flashing' since we're 552c6158483SAnthony Liguori not double-buffering. Note we're using the undocumented 553c6158483SAnthony Liguori behaviour of drawing the rectangle from right to left 554c6158483SAnthony Liguori to cut out the whole */ 555c6158483SAnthony Liguori cairo_rectangle(cr, mx + fbw, my, 556c6158483SAnthony Liguori -1 * fbw, fbh); 557c6158483SAnthony Liguori cairo_fill(cr); 558c6158483SAnthony Liguori 559c6158483SAnthony Liguori cairo_scale(cr, s->scale_x, s->scale_y); 560c6158483SAnthony Liguori cairo_set_source_surface(cr, s->surface, mx / s->scale_x, my / s->scale_y); 561a4ccabcfSAnthony Liguori cairo_paint(cr); 562a4ccabcfSAnthony Liguori 563a4ccabcfSAnthony Liguori return TRUE; 564a4ccabcfSAnthony Liguori } 565a4ccabcfSAnthony Liguori 566fe43bca8SDaniel P. Berrange #if !GTK_CHECK_VERSION(3, 0, 0) 567a4ccabcfSAnthony Liguori static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose, 568a4ccabcfSAnthony Liguori void *opaque) 569a4ccabcfSAnthony Liguori { 570a4ccabcfSAnthony Liguori cairo_t *cr; 571a4ccabcfSAnthony Liguori gboolean ret; 572a4ccabcfSAnthony Liguori 573a4ccabcfSAnthony Liguori cr = gdk_cairo_create(gtk_widget_get_window(widget)); 574a4ccabcfSAnthony Liguori cairo_rectangle(cr, 575a4ccabcfSAnthony Liguori expose->area.x, 576a4ccabcfSAnthony Liguori expose->area.y, 577a4ccabcfSAnthony Liguori expose->area.width, 578a4ccabcfSAnthony Liguori expose->area.height); 579a4ccabcfSAnthony Liguori cairo_clip(cr); 580a4ccabcfSAnthony Liguori 581a4ccabcfSAnthony Liguori ret = gd_draw_event(widget, cr, opaque); 582a4ccabcfSAnthony Liguori 583a4ccabcfSAnthony Liguori cairo_destroy(cr); 584a4ccabcfSAnthony Liguori 585a4ccabcfSAnthony Liguori return ret; 586a4ccabcfSAnthony Liguori } 587fe43bca8SDaniel P. Berrange #endif 588a4ccabcfSAnthony Liguori 589a4ccabcfSAnthony Liguori static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, 590a4ccabcfSAnthony Liguori void *opaque) 591a4ccabcfSAnthony Liguori { 592a4ccabcfSAnthony Liguori GtkDisplayState *s = opaque; 593a4ccabcfSAnthony Liguori int x, y; 594c6158483SAnthony Liguori int mx, my; 595c6158483SAnthony Liguori int fbh, fbw; 596c6158483SAnthony Liguori int ww, wh; 597a4ccabcfSAnthony Liguori 5989d9801cfSGerd Hoffmann fbw = surface_width(s->ds) * s->scale_x; 5999d9801cfSGerd Hoffmann fbh = surface_height(s->ds) * s->scale_y; 600c6158483SAnthony Liguori 601c6158483SAnthony Liguori gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); 602c6158483SAnthony Liguori 603c6158483SAnthony Liguori mx = my = 0; 604c6158483SAnthony Liguori if (ww > fbw) { 605c6158483SAnthony Liguori mx = (ww - fbw) / 2; 606c6158483SAnthony Liguori } 607c6158483SAnthony Liguori if (wh > fbh) { 608c6158483SAnthony Liguori my = (wh - fbh) / 2; 609c6158483SAnthony Liguori } 610c6158483SAnthony Liguori 611c6158483SAnthony Liguori x = (motion->x - mx) / s->scale_x; 612c6158483SAnthony Liguori y = (motion->y - my) / s->scale_y; 613c6158483SAnthony Liguori 614e61031cdSTakashi Iwai if (qemu_input_is_absolute()) { 615c6158483SAnthony Liguori if (x < 0 || y < 0 || 6169d9801cfSGerd Hoffmann x >= surface_width(s->ds) || 6179d9801cfSGerd Hoffmann y >= surface_height(s->ds)) { 618c6158483SAnthony Liguori return TRUE; 619c6158483SAnthony Liguori } 620192f81bfSGerd Hoffmann qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_X, x, 621192f81bfSGerd Hoffmann surface_width(s->ds)); 622192f81bfSGerd Hoffmann qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_Y, y, 623192f81bfSGerd Hoffmann surface_height(s->ds)); 624192f81bfSGerd Hoffmann qemu_input_event_sync(); 625e61031cdSTakashi Iwai } else if (s->last_set && gd_is_grab_active(s)) { 626192f81bfSGerd Hoffmann qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_X, x - s->last_x); 627192f81bfSGerd Hoffmann qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_Y, y - s->last_y); 628192f81bfSGerd Hoffmann qemu_input_event_sync(); 629a4ccabcfSAnthony Liguori } 630a4ccabcfSAnthony Liguori s->last_x = x; 631a4ccabcfSAnthony Liguori s->last_y = y; 632e61031cdSTakashi Iwai s->last_set = TRUE; 633a4ccabcfSAnthony Liguori 634192f81bfSGerd Hoffmann if (!qemu_input_is_absolute() && gd_is_grab_active(s)) { 63566962f14SDaniel P. Berrange GdkScreen *screen = gtk_widget_get_screen(s->drawing_area); 6365104a1f6SAnthony Liguori int x = (int)motion->x_root; 6375104a1f6SAnthony Liguori int y = (int)motion->y_root; 6385104a1f6SAnthony Liguori 6395104a1f6SAnthony Liguori /* In relative mode check to see if client pointer hit 6405104a1f6SAnthony Liguori * one of the screen edges, and if so move it back by 6415104a1f6SAnthony Liguori * 200 pixels. This is important because the pointer 6425104a1f6SAnthony Liguori * in the server doesn't correspond 1-for-1, and so 6435104a1f6SAnthony Liguori * may still be only half way across the screen. Without 6445104a1f6SAnthony Liguori * this warp, the server pointer would thus appear to hit 6455104a1f6SAnthony Liguori * an invisible wall */ 6465104a1f6SAnthony Liguori if (x == 0) { 6475104a1f6SAnthony Liguori x += 200; 6485104a1f6SAnthony Liguori } 6495104a1f6SAnthony Liguori if (y == 0) { 6505104a1f6SAnthony Liguori y += 200; 6515104a1f6SAnthony Liguori } 6525104a1f6SAnthony Liguori if (x == (gdk_screen_get_width(screen) - 1)) { 6535104a1f6SAnthony Liguori x -= 200; 6545104a1f6SAnthony Liguori } 6555104a1f6SAnthony Liguori if (y == (gdk_screen_get_height(screen) - 1)) { 6565104a1f6SAnthony Liguori y -= 200; 6575104a1f6SAnthony Liguori } 6585104a1f6SAnthony Liguori 6595104a1f6SAnthony Liguori if (x != (int)motion->x_root || y != (int)motion->y_root) { 6608906de76SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0) 6618906de76SDaniel P. Berrange GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion); 6628906de76SDaniel P. Berrange gdk_device_warp(dev, screen, x, y); 6638906de76SDaniel P. Berrange #else 6648906de76SDaniel P. Berrange GdkDisplay *display = gtk_widget_get_display(widget); 6655104a1f6SAnthony Liguori gdk_display_warp_pointer(display, screen, x, y); 6668906de76SDaniel P. Berrange #endif 667e61031cdSTakashi Iwai s->last_set = FALSE; 6685104a1f6SAnthony Liguori return FALSE; 6695104a1f6SAnthony Liguori } 6705104a1f6SAnthony Liguori } 671a4ccabcfSAnthony Liguori return TRUE; 672a4ccabcfSAnthony Liguori } 673a4ccabcfSAnthony Liguori 674a4ccabcfSAnthony Liguori static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, 675a4ccabcfSAnthony Liguori void *opaque) 676a4ccabcfSAnthony Liguori { 677a4ccabcfSAnthony Liguori GtkDisplayState *s = opaque; 678192f81bfSGerd Hoffmann InputButton btn; 679a4ccabcfSAnthony Liguori 680800b0e81STakashi Iwai /* implicitly grab the input at the first click in the relative mode */ 681800b0e81STakashi Iwai if (button->button == 1 && button->type == GDK_BUTTON_PRESS && 682800b0e81STakashi Iwai !qemu_input_is_absolute() && !gd_is_grab_active(s)) { 683800b0e81STakashi Iwai gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 684800b0e81STakashi Iwai TRUE); 685800b0e81STakashi Iwai return TRUE; 686800b0e81STakashi Iwai } 687800b0e81STakashi Iwai 688a4ccabcfSAnthony Liguori if (button->button == 1) { 689192f81bfSGerd Hoffmann btn = INPUT_BUTTON_LEFT; 690a4ccabcfSAnthony Liguori } else if (button->button == 2) { 691192f81bfSGerd Hoffmann btn = INPUT_BUTTON_MIDDLE; 692a4ccabcfSAnthony Liguori } else if (button->button == 3) { 693192f81bfSGerd Hoffmann btn = INPUT_BUTTON_RIGHT; 694a4ccabcfSAnthony Liguori } else { 695192f81bfSGerd Hoffmann return TRUE; 696a4ccabcfSAnthony Liguori } 697a4ccabcfSAnthony Liguori 698192f81bfSGerd Hoffmann qemu_input_queue_btn(s->dcl.con, btn, button->type == GDK_BUTTON_PRESS); 699192f81bfSGerd Hoffmann qemu_input_event_sync(); 700a4ccabcfSAnthony Liguori return TRUE; 701a4ccabcfSAnthony Liguori } 702a4ccabcfSAnthony Liguori 703d58b9122SJan Kiszka static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll, 704d58b9122SJan Kiszka void *opaque) 705d58b9122SJan Kiszka { 706d58b9122SJan Kiszka GtkDisplayState *s = opaque; 707d58b9122SJan Kiszka InputButton btn; 708d58b9122SJan Kiszka 709d58b9122SJan Kiszka if (scroll->direction == GDK_SCROLL_UP) { 710d58b9122SJan Kiszka btn = INPUT_BUTTON_WHEEL_UP; 711d58b9122SJan Kiszka } else if (scroll->direction == GDK_SCROLL_DOWN) { 712d58b9122SJan Kiszka btn = INPUT_BUTTON_WHEEL_DOWN; 713d58b9122SJan Kiszka } else { 714d58b9122SJan Kiszka return TRUE; 715d58b9122SJan Kiszka } 716d58b9122SJan Kiszka 717d58b9122SJan Kiszka qemu_input_queue_btn(s->dcl.con, btn, true); 718d58b9122SJan Kiszka qemu_input_event_sync(); 719d58b9122SJan Kiszka qemu_input_queue_btn(s->dcl.con, btn, false); 720d58b9122SJan Kiszka qemu_input_event_sync(); 721d58b9122SJan Kiszka return TRUE; 722d58b9122SJan Kiszka } 723d58b9122SJan Kiszka 724a4ccabcfSAnthony Liguori static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) 725a4ccabcfSAnthony Liguori { 7266db253caSJan Kiszka GtkDisplayState *s = opaque; 7272777ccc5SStefan Weil int gdk_keycode = key->hardware_keycode; 7286db253caSJan Kiszka int i; 729a4ccabcfSAnthony Liguori 7302777ccc5SStefan Weil #ifdef _WIN32 7312777ccc5SStefan Weil UINT qemu_keycode = MapVirtualKey(gdk_keycode, MAPVK_VK_TO_VSC); 7322777ccc5SStefan Weil switch (qemu_keycode) { 7332777ccc5SStefan Weil case 103: /* alt gr */ 7342777ccc5SStefan Weil qemu_keycode = 56 | SCANCODE_GREY; 7352777ccc5SStefan Weil break; 7362777ccc5SStefan Weil } 7372777ccc5SStefan Weil #else 7382777ccc5SStefan Weil int qemu_keycode; 739a4ccabcfSAnthony Liguori 740a4ccabcfSAnthony Liguori if (gdk_keycode < 9) { 741a4ccabcfSAnthony Liguori qemu_keycode = 0; 742a4ccabcfSAnthony Liguori } else if (gdk_keycode < 97) { 743a4ccabcfSAnthony Liguori qemu_keycode = gdk_keycode - 8; 744a4ccabcfSAnthony Liguori } else if (gdk_keycode < 158) { 745a4ccabcfSAnthony Liguori qemu_keycode = translate_evdev_keycode(gdk_keycode - 97); 746a4ccabcfSAnthony Liguori } else if (gdk_keycode == 208) { /* Hiragana_Katakana */ 747a4ccabcfSAnthony Liguori qemu_keycode = 0x70; 748a4ccabcfSAnthony Liguori } else if (gdk_keycode == 211) { /* backslash */ 749a4ccabcfSAnthony Liguori qemu_keycode = 0x73; 750a4ccabcfSAnthony Liguori } else { 751a4ccabcfSAnthony Liguori qemu_keycode = 0; 752a4ccabcfSAnthony Liguori } 7532777ccc5SStefan Weil #endif 754a4ccabcfSAnthony Liguori 755ef0dd982SStefan Weil trace_gd_key_event(gdk_keycode, qemu_keycode, 756a4ccabcfSAnthony Liguori (key->type == GDK_KEY_PRESS) ? "down" : "up"); 757a4ccabcfSAnthony Liguori 7586db253caSJan Kiszka for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { 7596db253caSJan Kiszka if (qemu_keycode == modifier_keycode[i]) { 7606db253caSJan Kiszka s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS); 7616db253caSJan Kiszka } 7626db253caSJan Kiszka } 7636db253caSJan Kiszka 764af98ba92SGerd Hoffmann qemu_input_event_send_key_number(s->dcl.con, qemu_keycode, 765af98ba92SGerd Hoffmann key->type == GDK_KEY_PRESS); 766a4ccabcfSAnthony Liguori 767a4ccabcfSAnthony Liguori return TRUE; 768a4ccabcfSAnthony Liguori } 769a4ccabcfSAnthony Liguori 7700d0e044dSTakashi Iwai static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque) 7710d0e044dSTakashi Iwai { 7720d0e044dSTakashi Iwai if (event->type == GDK_MOTION_NOTIFY) { 7730d0e044dSTakashi Iwai return gd_motion_event(widget, &event->motion, opaque); 7740d0e044dSTakashi Iwai } 7750d0e044dSTakashi Iwai return FALSE; 7760d0e044dSTakashi Iwai } 7770d0e044dSTakashi Iwai 778a4ccabcfSAnthony Liguori /** Window Menu Actions **/ 779a4ccabcfSAnthony Liguori 78030e8f22bSJan Kiszka static void gd_menu_pause(GtkMenuItem *item, void *opaque) 78130e8f22bSJan Kiszka { 78230e8f22bSJan Kiszka GtkDisplayState *s = opaque; 78330e8f22bSJan Kiszka 78430e8f22bSJan Kiszka if (s->external_pause_update) { 78530e8f22bSJan Kiszka return; 78630e8f22bSJan Kiszka } 78730e8f22bSJan Kiszka if (runstate_is_running()) { 78830e8f22bSJan Kiszka qmp_stop(NULL); 78930e8f22bSJan Kiszka } else { 79030e8f22bSJan Kiszka qmp_cont(NULL); 79130e8f22bSJan Kiszka } 79230e8f22bSJan Kiszka } 79330e8f22bSJan Kiszka 79430e8f22bSJan Kiszka static void gd_menu_reset(GtkMenuItem *item, void *opaque) 79530e8f22bSJan Kiszka { 79630e8f22bSJan Kiszka qmp_system_reset(NULL); 79730e8f22bSJan Kiszka } 79830e8f22bSJan Kiszka 79930e8f22bSJan Kiszka static void gd_menu_powerdown(GtkMenuItem *item, void *opaque) 80030e8f22bSJan Kiszka { 80130e8f22bSJan Kiszka qmp_system_powerdown(NULL); 80230e8f22bSJan Kiszka } 80330e8f22bSJan Kiszka 804a4ccabcfSAnthony Liguori static void gd_menu_quit(GtkMenuItem *item, void *opaque) 805a4ccabcfSAnthony Liguori { 806a4ccabcfSAnthony Liguori qmp_quit(NULL); 807a4ccabcfSAnthony Liguori } 808a4ccabcfSAnthony Liguori 809a4ccabcfSAnthony Liguori static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque) 810a4ccabcfSAnthony Liguori { 811a4ccabcfSAnthony Liguori GtkDisplayState *s = opaque; 812*832189c9SGerd Hoffmann gint page; 813a4ccabcfSAnthony Liguori 814a4ccabcfSAnthony Liguori if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) { 815*832189c9SGerd Hoffmann page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), 816*832189c9SGerd Hoffmann s->drawing_area); 817*832189c9SGerd Hoffmann gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), page); 818d861def3SAnthony Liguori } else { 8196db253caSJan Kiszka gtk_release_modifiers(s); 820*832189c9SGerd Hoffmann #if defined(CONFIG_VTE) 821*832189c9SGerd Hoffmann gint i; 822d861def3SAnthony Liguori for (i = 0; i < s->nb_vcs; i++) { 823d861def3SAnthony Liguori if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) { 824*832189c9SGerd Hoffmann page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), 825*832189c9SGerd Hoffmann s->vc[i].box); 826*832189c9SGerd Hoffmann gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), page); 827*832189c9SGerd Hoffmann return; 828d861def3SAnthony Liguori } 829d861def3SAnthony Liguori } 830*832189c9SGerd Hoffmann #endif 831a4ccabcfSAnthony Liguori } 832a4ccabcfSAnthony Liguori } 833a4ccabcfSAnthony Liguori 834a4ccabcfSAnthony Liguori static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque) 835a4ccabcfSAnthony Liguori { 836a4ccabcfSAnthony Liguori GtkDisplayState *s = opaque; 837a4ccabcfSAnthony Liguori 838a4ccabcfSAnthony Liguori if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) { 839a4ccabcfSAnthony Liguori gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE); 840a4ccabcfSAnthony Liguori } else { 841a4ccabcfSAnthony Liguori gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 842a4ccabcfSAnthony Liguori } 843a4ccabcfSAnthony Liguori } 844a4ccabcfSAnthony Liguori 845c6158483SAnthony Liguori static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) 846c6158483SAnthony Liguori { 847c6158483SAnthony Liguori GtkDisplayState *s = opaque; 848c6158483SAnthony Liguori 84910409282SStefan Weil if (!s->full_screen) { 850c6158483SAnthony Liguori gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 851c6158483SAnthony Liguori gtk_widget_set_size_request(s->menu_bar, 0, 0); 852c6158483SAnthony Liguori gtk_widget_set_size_request(s->drawing_area, -1, -1); 853c6158483SAnthony Liguori gtk_window_fullscreen(GTK_WINDOW(s->window)); 854c6158483SAnthony Liguori if (gd_on_vga(s)) { 855c6158483SAnthony Liguori gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE); 856c6158483SAnthony Liguori } 857c6158483SAnthony Liguori s->full_screen = TRUE; 858c6158483SAnthony Liguori } else { 859c6158483SAnthony Liguori gtk_window_unfullscreen(GTK_WINDOW(s->window)); 860c6158483SAnthony Liguori gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s); 861c6158483SAnthony Liguori gtk_widget_set_size_request(s->menu_bar, -1, -1); 862c6158483SAnthony Liguori gtk_widget_set_size_request(s->drawing_area, 8639d9801cfSGerd Hoffmann surface_width(s->ds), 8649d9801cfSGerd Hoffmann surface_height(s->ds)); 865c6158483SAnthony Liguori gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE); 866c6158483SAnthony Liguori s->full_screen = FALSE; 867c6158483SAnthony Liguori s->scale_x = 1.0; 868c6158483SAnthony Liguori s->scale_y = 1.0; 869c6158483SAnthony Liguori } 870c6158483SAnthony Liguori 871c6158483SAnthony Liguori gd_update_cursor(s, FALSE); 872c6158483SAnthony Liguori } 873c6158483SAnthony Liguori 874c6158483SAnthony Liguori static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque) 875c6158483SAnthony Liguori { 876c6158483SAnthony Liguori GtkDisplayState *s = opaque; 877c6158483SAnthony Liguori 878c6158483SAnthony Liguori gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), 879c6158483SAnthony Liguori FALSE); 880c6158483SAnthony Liguori 881c6158483SAnthony Liguori s->scale_x += .25; 882c6158483SAnthony Liguori s->scale_y += .25; 883c6158483SAnthony Liguori 8849d9801cfSGerd Hoffmann gd_update_windowsize(s); 885c6158483SAnthony Liguori } 886c6158483SAnthony Liguori 887c6158483SAnthony Liguori static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque) 888c6158483SAnthony Liguori { 889c6158483SAnthony Liguori GtkDisplayState *s = opaque; 890c6158483SAnthony Liguori 891c6158483SAnthony Liguori gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), 892c6158483SAnthony Liguori FALSE); 893c6158483SAnthony Liguori 894c6158483SAnthony Liguori s->scale_x -= .25; 895c6158483SAnthony Liguori s->scale_y -= .25; 896c6158483SAnthony Liguori 897c6158483SAnthony Liguori s->scale_x = MAX(s->scale_x, .25); 898c6158483SAnthony Liguori s->scale_y = MAX(s->scale_y, .25); 899c6158483SAnthony Liguori 9009d9801cfSGerd Hoffmann gd_update_windowsize(s); 901c6158483SAnthony Liguori } 902c6158483SAnthony Liguori 903c6158483SAnthony Liguori static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque) 904c6158483SAnthony Liguori { 905c6158483SAnthony Liguori GtkDisplayState *s = opaque; 906c6158483SAnthony Liguori 907c6158483SAnthony Liguori s->scale_x = 1.0; 908c6158483SAnthony Liguori s->scale_y = 1.0; 909c6158483SAnthony Liguori 9109d9801cfSGerd Hoffmann gd_update_windowsize(s); 911c6158483SAnthony Liguori } 912c6158483SAnthony Liguori 913c6158483SAnthony Liguori static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque) 914c6158483SAnthony Liguori { 915c6158483SAnthony Liguori GtkDisplayState *s = opaque; 916c6158483SAnthony Liguori 917c6158483SAnthony Liguori if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) { 918c6158483SAnthony Liguori s->free_scale = TRUE; 919c6158483SAnthony Liguori } else { 920c6158483SAnthony Liguori s->free_scale = FALSE; 92141686a96SAnthony Liguori s->scale_x = 1.0; 92241686a96SAnthony Liguori s->scale_y = 1.0; 92341686a96SAnthony Liguori gd_update_windowsize(s); 924c6158483SAnthony Liguori } 925c6158483SAnthony Liguori 9269d9801cfSGerd Hoffmann gd_update_full_redraw(s); 927c6158483SAnthony Liguori } 928c6158483SAnthony Liguori 9295104a1f6SAnthony Liguori static void gd_grab_keyboard(GtkDisplayState *s) 9305104a1f6SAnthony Liguori { 931655199daSDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0) 932655199daSDaniel P. Berrange GdkDisplay *display = gtk_widget_get_display(s->drawing_area); 933655199daSDaniel P. Berrange GdkDeviceManager *mgr = gdk_display_get_device_manager(display); 934655199daSDaniel P. Berrange GList *devices = gdk_device_manager_list_devices(mgr, 935655199daSDaniel P. Berrange GDK_DEVICE_TYPE_MASTER); 936655199daSDaniel P. Berrange GList *tmp = devices; 937655199daSDaniel P. Berrange while (tmp) { 938655199daSDaniel P. Berrange GdkDevice *dev = tmp->data; 939655199daSDaniel P. Berrange if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) { 940655199daSDaniel P. Berrange gdk_device_grab(dev, 941655199daSDaniel P. Berrange gtk_widget_get_window(s->drawing_area), 942655199daSDaniel P. Berrange GDK_OWNERSHIP_NONE, 943655199daSDaniel P. Berrange FALSE, 944655199daSDaniel P. Berrange GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, 945655199daSDaniel P. Berrange NULL, 946655199daSDaniel P. Berrange GDK_CURRENT_TIME); 947655199daSDaniel P. Berrange } 948655199daSDaniel P. Berrange tmp = tmp->next; 949655199daSDaniel P. Berrange } 950655199daSDaniel P. Berrange g_list_free(devices); 951655199daSDaniel P. Berrange #else 952655199daSDaniel P. Berrange gdk_keyboard_grab(gtk_widget_get_window(s->drawing_area), 9535104a1f6SAnthony Liguori FALSE, 9545104a1f6SAnthony Liguori GDK_CURRENT_TIME); 955655199daSDaniel P. Berrange #endif 9565104a1f6SAnthony Liguori } 9575104a1f6SAnthony Liguori 9585104a1f6SAnthony Liguori static void gd_ungrab_keyboard(GtkDisplayState *s) 9595104a1f6SAnthony Liguori { 960655199daSDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0) 961655199daSDaniel P. Berrange GdkDisplay *display = gtk_widget_get_display(s->drawing_area); 962655199daSDaniel P. Berrange GdkDeviceManager *mgr = gdk_display_get_device_manager(display); 963655199daSDaniel P. Berrange GList *devices = gdk_device_manager_list_devices(mgr, 964655199daSDaniel P. Berrange GDK_DEVICE_TYPE_MASTER); 965655199daSDaniel P. Berrange GList *tmp = devices; 966655199daSDaniel P. Berrange while (tmp) { 967655199daSDaniel P. Berrange GdkDevice *dev = tmp->data; 968655199daSDaniel P. Berrange if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) { 969655199daSDaniel P. Berrange gdk_device_ungrab(dev, 970655199daSDaniel P. Berrange GDK_CURRENT_TIME); 971655199daSDaniel P. Berrange } 972655199daSDaniel P. Berrange tmp = tmp->next; 973655199daSDaniel P. Berrange } 974655199daSDaniel P. Berrange g_list_free(devices); 975655199daSDaniel P. Berrange #else 9765104a1f6SAnthony Liguori gdk_keyboard_ungrab(GDK_CURRENT_TIME); 977655199daSDaniel P. Berrange #endif 9785104a1f6SAnthony Liguori } 9795104a1f6SAnthony Liguori 9802a05485dSDaniel P. Berrange static void gd_grab_pointer(GtkDisplayState *s) 9815104a1f6SAnthony Liguori { 9822a05485dSDaniel P. Berrange GdkDisplay *display = gtk_widget_get_display(s->drawing_area); 983ecce1929STakashi Iwai #if GTK_CHECK_VERSION(3, 0, 0) 9842a05485dSDaniel P. Berrange GdkDeviceManager *mgr = gdk_display_get_device_manager(display); 9852a05485dSDaniel P. Berrange GList *devices = gdk_device_manager_list_devices(mgr, 9862a05485dSDaniel P. Berrange GDK_DEVICE_TYPE_MASTER); 9872a05485dSDaniel P. Berrange GList *tmp = devices; 9882a05485dSDaniel P. Berrange while (tmp) { 9892a05485dSDaniel P. Berrange GdkDevice *dev = tmp->data; 9902a05485dSDaniel P. Berrange if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) { 9912a05485dSDaniel P. Berrange gdk_device_grab(dev, 9922a05485dSDaniel P. Berrange gtk_widget_get_window(s->drawing_area), 9932a05485dSDaniel P. Berrange GDK_OWNERSHIP_NONE, 9942a05485dSDaniel P. Berrange FALSE, /* All events to come to our 9952a05485dSDaniel P. Berrange window directly */ 9962a05485dSDaniel P. Berrange GDK_POINTER_MOTION_MASK | 9972a05485dSDaniel P. Berrange GDK_BUTTON_PRESS_MASK | 9982a05485dSDaniel P. Berrange GDK_BUTTON_RELEASE_MASK | 9992a05485dSDaniel P. Berrange GDK_BUTTON_MOTION_MASK | 10002a05485dSDaniel P. Berrange GDK_SCROLL_MASK, 10012a05485dSDaniel P. Berrange s->null_cursor, 10022a05485dSDaniel P. Berrange GDK_CURRENT_TIME); 10032a05485dSDaniel P. Berrange } 10042a05485dSDaniel P. Berrange tmp = tmp->next; 10052a05485dSDaniel P. Berrange } 10062a05485dSDaniel P. Berrange g_list_free(devices); 1007ecce1929STakashi Iwai gdk_device_get_position(gdk_device_manager_get_client_pointer(mgr), 1008ecce1929STakashi Iwai NULL, &s->grab_x_root, &s->grab_y_root); 10092a05485dSDaniel P. Berrange #else 10102a05485dSDaniel P. Berrange gdk_pointer_grab(gtk_widget_get_window(s->drawing_area), 10115104a1f6SAnthony Liguori FALSE, /* All events to come to our window directly */ 10125104a1f6SAnthony Liguori GDK_POINTER_MOTION_MASK | 10135104a1f6SAnthony Liguori GDK_BUTTON_PRESS_MASK | 10145104a1f6SAnthony Liguori GDK_BUTTON_RELEASE_MASK | 10155104a1f6SAnthony Liguori GDK_BUTTON_MOTION_MASK | 10165104a1f6SAnthony Liguori GDK_SCROLL_MASK, 10175104a1f6SAnthony Liguori NULL, /* Allow cursor to move over entire desktop */ 10185104a1f6SAnthony Liguori s->null_cursor, 10195104a1f6SAnthony Liguori GDK_CURRENT_TIME); 1020ecce1929STakashi Iwai gdk_display_get_pointer(display, NULL, 1021ecce1929STakashi Iwai &s->grab_x_root, &s->grab_y_root, NULL); 10222a05485dSDaniel P. Berrange #endif 10232a05485dSDaniel P. Berrange } 10242a05485dSDaniel P. Berrange 10252a05485dSDaniel P. Berrange static void gd_ungrab_pointer(GtkDisplayState *s) 10262a05485dSDaniel P. Berrange { 10272a05485dSDaniel P. Berrange GdkDisplay *display = gtk_widget_get_display(s->drawing_area); 1028ecce1929STakashi Iwai #if GTK_CHECK_VERSION(3, 0, 0) 10292a05485dSDaniel P. Berrange GdkDeviceManager *mgr = gdk_display_get_device_manager(display); 10302a05485dSDaniel P. Berrange GList *devices = gdk_device_manager_list_devices(mgr, 10312a05485dSDaniel P. Berrange GDK_DEVICE_TYPE_MASTER); 10322a05485dSDaniel P. Berrange GList *tmp = devices; 10332a05485dSDaniel P. Berrange while (tmp) { 10342a05485dSDaniel P. Berrange GdkDevice *dev = tmp->data; 10352a05485dSDaniel P. Berrange if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) { 10362a05485dSDaniel P. Berrange gdk_device_ungrab(dev, 10372a05485dSDaniel P. Berrange GDK_CURRENT_TIME); 10382a05485dSDaniel P. Berrange } 10392a05485dSDaniel P. Berrange tmp = tmp->next; 10402a05485dSDaniel P. Berrange } 10412a05485dSDaniel P. Berrange g_list_free(devices); 1042ecce1929STakashi Iwai gdk_device_warp(gdk_device_manager_get_client_pointer(mgr), 1043ecce1929STakashi Iwai gtk_widget_get_screen(s->drawing_area), 1044ecce1929STakashi Iwai s->grab_x_root, s->grab_y_root); 10452a05485dSDaniel P. Berrange #else 10462a05485dSDaniel P. Berrange gdk_pointer_ungrab(GDK_CURRENT_TIME); 1047ecce1929STakashi Iwai gdk_display_warp_pointer(display, 1048ecce1929STakashi Iwai gtk_widget_get_screen(s->drawing_area), 1049ecce1929STakashi Iwai s->grab_x_root, s->grab_y_root); 10502a05485dSDaniel P. Berrange #endif 10512a05485dSDaniel P. Berrange } 10522a05485dSDaniel P. Berrange 10532a05485dSDaniel P. Berrange static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) 10542a05485dSDaniel P. Berrange { 10552a05485dSDaniel P. Berrange GtkDisplayState *s = opaque; 10562a05485dSDaniel P. Berrange 10572a05485dSDaniel P. Berrange if (gd_is_grab_active(s)) { 10582a05485dSDaniel P. Berrange gd_grab_keyboard(s); 10592a05485dSDaniel P. Berrange gd_grab_pointer(s); 10605104a1f6SAnthony Liguori } else { 10615104a1f6SAnthony Liguori gd_ungrab_keyboard(s); 10622a05485dSDaniel P. Berrange gd_ungrab_pointer(s); 10635104a1f6SAnthony Liguori } 10645104a1f6SAnthony Liguori 10655104a1f6SAnthony Liguori gd_update_caption(s); 10665104a1f6SAnthony Liguori gd_update_cursor(s, FALSE); 10675104a1f6SAnthony Liguori } 10685104a1f6SAnthony Liguori 1069a4ccabcfSAnthony Liguori static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, 1070a4ccabcfSAnthony Liguori gpointer data) 1071a4ccabcfSAnthony Liguori { 1072a4ccabcfSAnthony Liguori GtkDisplayState *s = data; 10735104a1f6SAnthony Liguori gboolean on_vga; 1074*832189c9SGerd Hoffmann gint page; 1075a4ccabcfSAnthony Liguori 1076a4ccabcfSAnthony Liguori if (!gtk_widget_get_realized(s->notebook)) { 1077a4ccabcfSAnthony Liguori return; 1078a4ccabcfSAnthony Liguori } 1079a4ccabcfSAnthony Liguori 1080*832189c9SGerd Hoffmann page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), s->drawing_area); 1081*832189c9SGerd Hoffmann on_vga = arg2 == page; 10825104a1f6SAnthony Liguori 10835104a1f6SAnthony Liguori if (!on_vga) { 10845104a1f6SAnthony Liguori gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 10855104a1f6SAnthony Liguori FALSE); 1086c6158483SAnthony Liguori } else if (s->full_screen) { 1087c6158483SAnthony Liguori gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 1088c6158483SAnthony Liguori TRUE); 10895104a1f6SAnthony Liguori } 10905104a1f6SAnthony Liguori 1091*832189c9SGerd Hoffmann if (on_vga) { 1092d861def3SAnthony Liguori gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE); 1093d861def3SAnthony Liguori } else { 1094bbbf9bfbSStefan Weil #if defined(CONFIG_VTE) 1095*832189c9SGerd Hoffmann VirtualConsole *vc; 1096*832189c9SGerd Hoffmann gint page, i; 1097*832189c9SGerd Hoffmann for (i = 0; i < s->nb_vcs; i++) { 1098*832189c9SGerd Hoffmann vc = &s->vc[i]; 1099*832189c9SGerd Hoffmann page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->box); 1100*832189c9SGerd Hoffmann if (page == arg2) { 1101*832189c9SGerd Hoffmann gtk_check_menu_item_set_active 1102*832189c9SGerd Hoffmann (GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); 1103*832189c9SGerd Hoffmann } 1104*832189c9SGerd Hoffmann } 1105bbbf9bfbSStefan Weil #else 1106bbbf9bfbSStefan Weil g_assert_not_reached(); 1107bbbf9bfbSStefan Weil #endif 1108d861def3SAnthony Liguori } 1109d861def3SAnthony Liguori 11105104a1f6SAnthony Liguori gtk_widget_set_sensitive(s->grab_item, on_vga); 11115104a1f6SAnthony Liguori 1112a4ccabcfSAnthony Liguori gd_update_cursor(s, TRUE); 1113a4ccabcfSAnthony Liguori } 1114a4ccabcfSAnthony Liguori 11155104a1f6SAnthony Liguori static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data) 11165104a1f6SAnthony Liguori { 11175104a1f6SAnthony Liguori GtkDisplayState *s = data; 11185104a1f6SAnthony Liguori 11195104a1f6SAnthony Liguori if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) { 11205104a1f6SAnthony Liguori gd_grab_keyboard(s); 11215104a1f6SAnthony Liguori } 11225104a1f6SAnthony Liguori 11235104a1f6SAnthony Liguori return TRUE; 11245104a1f6SAnthony Liguori } 11255104a1f6SAnthony Liguori 11265104a1f6SAnthony Liguori static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data) 11275104a1f6SAnthony Liguori { 11285104a1f6SAnthony Liguori GtkDisplayState *s = data; 11295104a1f6SAnthony Liguori 11305104a1f6SAnthony Liguori if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) { 11315104a1f6SAnthony Liguori gd_ungrab_keyboard(s); 11325104a1f6SAnthony Liguori } 11335104a1f6SAnthony Liguori 11345104a1f6SAnthony Liguori return TRUE; 11355104a1f6SAnthony Liguori } 11365104a1f6SAnthony Liguori 11376db253caSJan Kiszka static gboolean gd_focus_out_event(GtkWidget *widget, 11386db253caSJan Kiszka GdkEventCrossing *crossing, gpointer data) 11396db253caSJan Kiszka { 11406db253caSJan Kiszka GtkDisplayState *s = data; 11416db253caSJan Kiszka 11426db253caSJan Kiszka gtk_release_modifiers(s); 11436db253caSJan Kiszka 11446db253caSJan Kiszka return TRUE; 11456db253caSJan Kiszka } 11466db253caSJan Kiszka 1147d861def3SAnthony Liguori /** Virtual Console Callbacks **/ 1148d861def3SAnthony Liguori 1149ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE) 11500fb20d1cSCole Robinson static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque) 11510fb20d1cSCole Robinson { 11520fb20d1cSCole Robinson VirtualConsole *vc = opaque; 11530fb20d1cSCole Robinson 11540fb20d1cSCole Robinson if (gtk_adjustment_get_upper(adjustment) > 11550fb20d1cSCole Robinson gtk_adjustment_get_page_size(adjustment)) { 11560fb20d1cSCole Robinson gtk_widget_show(vc->scrollbar); 11570fb20d1cSCole Robinson } else { 11580fb20d1cSCole Robinson gtk_widget_hide(vc->scrollbar); 11590fb20d1cSCole Robinson } 11600fb20d1cSCole Robinson } 11610fb20d1cSCole Robinson 1162d861def3SAnthony Liguori static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len) 1163d861def3SAnthony Liguori { 1164d861def3SAnthony Liguori VirtualConsole *vc = chr->opaque; 1165d861def3SAnthony Liguori 1166d4370741SCole Robinson vte_terminal_feed(VTE_TERMINAL(vc->terminal), (const char *)buf, len); 1167d4370741SCole Robinson return len; 1168d861def3SAnthony Liguori } 1169d861def3SAnthony Liguori 1170d861def3SAnthony Liguori static int nb_vcs; 1171d861def3SAnthony Liguori static CharDriverState *vcs[MAX_VCS]; 1172d861def3SAnthony Liguori 1173702ec69cSGerd Hoffmann static CharDriverState *gd_vc_handler(ChardevVC *unused) 1174d861def3SAnthony Liguori { 1175d861def3SAnthony Liguori CharDriverState *chr; 1176d861def3SAnthony Liguori 1177d861def3SAnthony Liguori chr = g_malloc0(sizeof(*chr)); 1178d861def3SAnthony Liguori chr->chr_write = gd_vc_chr_write; 1179bd5c51eeSMichael Roth /* defer OPENED events until our vc is fully initialized */ 1180bd5c51eeSMichael Roth chr->explicit_be_open = true; 1181d861def3SAnthony Liguori 1182d861def3SAnthony Liguori vcs[nb_vcs++] = chr; 1183d861def3SAnthony Liguori 1184d861def3SAnthony Liguori return chr; 1185d861def3SAnthony Liguori } 1186d861def3SAnthony Liguori 1187d4370741SCole Robinson static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size, 1188d4370741SCole Robinson gpointer user_data) 1189d861def3SAnthony Liguori { 1190d4370741SCole Robinson VirtualConsole *vc = user_data; 1191d861def3SAnthony Liguori 1192d4370741SCole Robinson qemu_chr_be_write(vc->chr, (uint8_t *)text, (unsigned int)size); 1193d861def3SAnthony Liguori return TRUE; 1194d861def3SAnthony Liguori } 1195d861def3SAnthony Liguori 1196bf9b255fSAnthony Liguori static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group, 1197bf9b255fSAnthony Liguori GtkWidget *view_menu) 1198d861def3SAnthony Liguori { 1199d861def3SAnthony Liguori const char *label; 1200d861def3SAnthony Liguori char buffer[32]; 1201d861def3SAnthony Liguori char path[32]; 12020fb20d1cSCole Robinson GtkWidget *box; 12030fb20d1cSCole Robinson GtkWidget *scrollbar; 12040fb20d1cSCole Robinson GtkAdjustment *vadjustment; 1205d861def3SAnthony Liguori 1206d861def3SAnthony Liguori snprintf(buffer, sizeof(buffer), "vc%d", index); 1207d861def3SAnthony Liguori snprintf(path, sizeof(path), "<QEMU>/View/VC%d", index); 1208d861def3SAnthony Liguori 1209d861def3SAnthony Liguori vc->chr = vcs[index]; 1210d861def3SAnthony Liguori 1211d861def3SAnthony Liguori if (vc->chr->label) { 1212d861def3SAnthony Liguori label = vc->chr->label; 1213d861def3SAnthony Liguori } else { 1214d861def3SAnthony Liguori label = buffer; 1215d861def3SAnthony Liguori } 1216d861def3SAnthony Liguori 1217d861def3SAnthony Liguori vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label); 1218d861def3SAnthony Liguori group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); 1219d861def3SAnthony Liguori gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path); 1220b1e749c0SJan Kiszka gtk_accel_map_add_entry(path, GDK_KEY_2 + index, HOTKEY_MODIFIERS); 1221d861def3SAnthony Liguori 1222d861def3SAnthony Liguori vc->terminal = vte_terminal_new(); 1223d4370741SCole Robinson g_signal_connect(vc->terminal, "commit", G_CALLBACK(gd_vc_in), vc); 1224d861def3SAnthony Liguori 1225d861def3SAnthony Liguori vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1); 1226d861def3SAnthony Liguori vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25); 1227d861def3SAnthony Liguori 12280fb20d1cSCole Robinson #if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0) 12290fb20d1cSCole Robinson vadjustment = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(vc->terminal)); 12300fb20d1cSCole Robinson #else 12310fb20d1cSCole Robinson vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal)); 12320fb20d1cSCole Robinson #endif 1233d861def3SAnthony Liguori 12340fb20d1cSCole Robinson #if GTK_CHECK_VERSION(3, 0, 0) 12350fb20d1cSCole Robinson box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2); 12360fb20d1cSCole Robinson scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment); 12370fb20d1cSCole Robinson #else 12380fb20d1cSCole Robinson box = gtk_hbox_new(false, 2); 12390fb20d1cSCole Robinson scrollbar = gtk_vscrollbar_new(vadjustment); 12400fb20d1cSCole Robinson #endif 12410fb20d1cSCole Robinson 12420fb20d1cSCole Robinson gtk_box_pack_start(GTK_BOX(box), vc->terminal, TRUE, TRUE, 0); 12430fb20d1cSCole Robinson gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0); 12440fb20d1cSCole Robinson 12450fb20d1cSCole Robinson vc->chr->opaque = vc; 12460fb20d1cSCole Robinson vc->box = box; 12470fb20d1cSCole Robinson vc->scrollbar = scrollbar; 12480fb20d1cSCole Robinson 12490fb20d1cSCole Robinson g_signal_connect(vadjustment, "changed", 12500fb20d1cSCole Robinson G_CALLBACK(gd_vc_adjustment_changed), vc); 12510fb20d1cSCole Robinson 12520fb20d1cSCole Robinson gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), box, 1253fd07d07bSGerd Hoffmann gtk_label_new(label)); 1254d861def3SAnthony Liguori g_signal_connect(vc->menu_item, "activate", 1255d861def3SAnthony Liguori G_CALLBACK(gd_menu_switch_vc), s); 1256d861def3SAnthony Liguori 1257bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item); 1258d861def3SAnthony Liguori 1259fee204fdSHans de Goede qemu_chr_be_generic_open(vc->chr); 1260d861def3SAnthony Liguori if (vc->chr->init) { 1261d861def3SAnthony Liguori vc->chr->init(vc->chr); 1262d861def3SAnthony Liguori } 1263d861def3SAnthony Liguori 1264d861def3SAnthony Liguori return group; 1265a4ccabcfSAnthony Liguori } 1266a4ccabcfSAnthony Liguori 1267ee5f31e4SGerd Hoffmann static void gd_vcs_init(GtkDisplayState *s, GSList *group, 1268ee5f31e4SGerd Hoffmann GtkWidget *view_menu) 1269ee5f31e4SGerd Hoffmann { 1270ee5f31e4SGerd Hoffmann int i; 1271ee5f31e4SGerd Hoffmann 1272ee5f31e4SGerd Hoffmann for (i = 0; i < nb_vcs; i++) { 1273ee5f31e4SGerd Hoffmann VirtualConsole *vc = &s->vc[i]; 1274ee5f31e4SGerd Hoffmann 1275ee5f31e4SGerd Hoffmann group = gd_vc_init(s, vc, i, group, view_menu); 1276ee5f31e4SGerd Hoffmann s->nb_vcs++; 1277ee5f31e4SGerd Hoffmann } 1278ee5f31e4SGerd Hoffmann } 1279ee5f31e4SGerd Hoffmann #endif /* CONFIG_VTE */ 1280ee5f31e4SGerd Hoffmann 1281a4ccabcfSAnthony Liguori /** Window Creation **/ 1282a4ccabcfSAnthony Liguori 1283a4ccabcfSAnthony Liguori static void gd_connect_signals(GtkDisplayState *s) 1284a4ccabcfSAnthony Liguori { 1285a4ccabcfSAnthony Liguori g_signal_connect(s->show_tabs_item, "activate", 1286a4ccabcfSAnthony Liguori G_CALLBACK(gd_menu_show_tabs), s); 1287a4ccabcfSAnthony Liguori 1288a4ccabcfSAnthony Liguori g_signal_connect(s->window, "delete-event", 1289a4ccabcfSAnthony Liguori G_CALLBACK(gd_window_close), s); 1290a4ccabcfSAnthony Liguori 1291fe43bca8SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0) 1292fe43bca8SDaniel P. Berrange g_signal_connect(s->drawing_area, "draw", 1293fe43bca8SDaniel P. Berrange G_CALLBACK(gd_draw_event), s); 1294fe43bca8SDaniel P. Berrange #else 1295a4ccabcfSAnthony Liguori g_signal_connect(s->drawing_area, "expose-event", 1296a4ccabcfSAnthony Liguori G_CALLBACK(gd_expose_event), s); 1297fe43bca8SDaniel P. Berrange #endif 12980d0e044dSTakashi Iwai g_signal_connect(s->drawing_area, "event", 12990d0e044dSTakashi Iwai G_CALLBACK(gd_event), s); 1300a4ccabcfSAnthony Liguori g_signal_connect(s->drawing_area, "button-press-event", 1301a4ccabcfSAnthony Liguori G_CALLBACK(gd_button_event), s); 1302a4ccabcfSAnthony Liguori g_signal_connect(s->drawing_area, "button-release-event", 1303a4ccabcfSAnthony Liguori G_CALLBACK(gd_button_event), s); 1304d58b9122SJan Kiszka g_signal_connect(s->drawing_area, "scroll-event", 1305d58b9122SJan Kiszka G_CALLBACK(gd_scroll_event), s); 1306a4ccabcfSAnthony Liguori g_signal_connect(s->drawing_area, "key-press-event", 1307a4ccabcfSAnthony Liguori G_CALLBACK(gd_key_event), s); 1308a4ccabcfSAnthony Liguori g_signal_connect(s->drawing_area, "key-release-event", 1309a4ccabcfSAnthony Liguori G_CALLBACK(gd_key_event), s); 1310a4ccabcfSAnthony Liguori 131130e8f22bSJan Kiszka g_signal_connect(s->pause_item, "activate", 131230e8f22bSJan Kiszka G_CALLBACK(gd_menu_pause), s); 131330e8f22bSJan Kiszka g_signal_connect(s->reset_item, "activate", 131430e8f22bSJan Kiszka G_CALLBACK(gd_menu_reset), s); 131530e8f22bSJan Kiszka g_signal_connect(s->powerdown_item, "activate", 131630e8f22bSJan Kiszka G_CALLBACK(gd_menu_powerdown), s); 1317a4ccabcfSAnthony Liguori g_signal_connect(s->quit_item, "activate", 1318a4ccabcfSAnthony Liguori G_CALLBACK(gd_menu_quit), s); 1319c6158483SAnthony Liguori g_signal_connect(s->full_screen_item, "activate", 1320c6158483SAnthony Liguori G_CALLBACK(gd_menu_full_screen), s); 1321c6158483SAnthony Liguori g_signal_connect(s->zoom_in_item, "activate", 1322c6158483SAnthony Liguori G_CALLBACK(gd_menu_zoom_in), s); 1323c6158483SAnthony Liguori g_signal_connect(s->zoom_out_item, "activate", 1324c6158483SAnthony Liguori G_CALLBACK(gd_menu_zoom_out), s); 1325c6158483SAnthony Liguori g_signal_connect(s->zoom_fixed_item, "activate", 1326c6158483SAnthony Liguori G_CALLBACK(gd_menu_zoom_fixed), s); 1327c6158483SAnthony Liguori g_signal_connect(s->zoom_fit_item, "activate", 1328c6158483SAnthony Liguori G_CALLBACK(gd_menu_zoom_fit), s); 1329a4ccabcfSAnthony Liguori g_signal_connect(s->vga_item, "activate", 1330a4ccabcfSAnthony Liguori G_CALLBACK(gd_menu_switch_vc), s); 13315104a1f6SAnthony Liguori g_signal_connect(s->grab_item, "activate", 13325104a1f6SAnthony Liguori G_CALLBACK(gd_menu_grab_input), s); 1333a4ccabcfSAnthony Liguori g_signal_connect(s->notebook, "switch-page", 1334a4ccabcfSAnthony Liguori G_CALLBACK(gd_change_page), s); 13355104a1f6SAnthony Liguori g_signal_connect(s->drawing_area, "enter-notify-event", 13365104a1f6SAnthony Liguori G_CALLBACK(gd_enter_event), s); 13375104a1f6SAnthony Liguori g_signal_connect(s->drawing_area, "leave-notify-event", 13385104a1f6SAnthony Liguori G_CALLBACK(gd_leave_event), s); 13396db253caSJan Kiszka g_signal_connect(s->drawing_area, "focus-out-event", 13406db253caSJan Kiszka G_CALLBACK(gd_focus_out_event), s); 1341a4ccabcfSAnthony Liguori } 1342a4ccabcfSAnthony Liguori 1343bf9b255fSAnthony Liguori static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *accel_group) 1344a4ccabcfSAnthony Liguori { 1345bf9b255fSAnthony Liguori GtkWidget *machine_menu; 1346a4ccabcfSAnthony Liguori GtkWidget *separator; 1347a4ccabcfSAnthony Liguori 1348bf9b255fSAnthony Liguori machine_menu = gtk_menu_new(); 1349bf9b255fSAnthony Liguori gtk_menu_set_accel_group(GTK_MENU(machine_menu), accel_group); 135030e8f22bSJan Kiszka 135130e8f22bSJan Kiszka s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause")); 1352bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item); 135330e8f22bSJan Kiszka 135430e8f22bSJan Kiszka separator = gtk_separator_menu_item_new(); 1355bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator); 135630e8f22bSJan Kiszka 13579068f20dSCole Robinson s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset")); 1358bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item); 135930e8f22bSJan Kiszka 13609068f20dSCole Robinson s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down")); 1361bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item); 136230e8f22bSJan Kiszka 136330e8f22bSJan Kiszka separator = gtk_separator_menu_item_new(); 1364bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator); 1365a4ccabcfSAnthony Liguori 13663d914488SCole Robinson s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit")); 1367a4ccabcfSAnthony Liguori gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item), 136830e8f22bSJan Kiszka "<QEMU>/Machine/Quit"); 13693d914488SCole Robinson gtk_accel_map_add_entry("<QEMU>/Machine/Quit", 1370db1da1f2SCole Robinson GDK_KEY_q, HOTKEY_MODIFIERS); 1371bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item); 1372a4ccabcfSAnthony Liguori 1373bf9b255fSAnthony Liguori return machine_menu; 1374bf9b255fSAnthony Liguori } 1375bf9b255fSAnthony Liguori 1376bf9b255fSAnthony Liguori static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_group) 1377bf9b255fSAnthony Liguori { 1378bf9b255fSAnthony Liguori GSList *group = NULL; 1379bf9b255fSAnthony Liguori GtkWidget *view_menu; 1380bf9b255fSAnthony Liguori GtkWidget *separator; 1381bf9b255fSAnthony Liguori 1382bf9b255fSAnthony Liguori view_menu = gtk_menu_new(); 1383bf9b255fSAnthony Liguori gtk_menu_set_accel_group(GTK_MENU(view_menu), accel_group); 1384a4ccabcfSAnthony Liguori 13853d914488SCole Robinson s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen")); 1386c6158483SAnthony Liguori gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->full_screen_item), 1387c6158483SAnthony Liguori "<QEMU>/View/Full Screen"); 1388b1e749c0SJan Kiszka gtk_accel_map_add_entry("<QEMU>/View/Full Screen", GDK_KEY_f, 1389b1e749c0SJan Kiszka HOTKEY_MODIFIERS); 1390bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item); 1391c6158483SAnthony Liguori 1392c6158483SAnthony Liguori separator = gtk_separator_menu_item_new(); 1393bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 1394c6158483SAnthony Liguori 13953d914488SCole Robinson s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In")); 1396c6158483SAnthony Liguori gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item), 1397c6158483SAnthony Liguori "<QEMU>/View/Zoom In"); 1398b1e749c0SJan Kiszka gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus, 1399b1e749c0SJan Kiszka HOTKEY_MODIFIERS); 1400bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item); 1401c6158483SAnthony Liguori 14023d914488SCole Robinson s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out")); 1403c6158483SAnthony Liguori gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item), 1404c6158483SAnthony Liguori "<QEMU>/View/Zoom Out"); 1405b1e749c0SJan Kiszka gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus, 1406b1e749c0SJan Kiszka HOTKEY_MODIFIERS); 1407bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item); 1408c6158483SAnthony Liguori 14093d914488SCole Robinson s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit")); 1410c6158483SAnthony Liguori gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item), 1411c6158483SAnthony Liguori "<QEMU>/View/Zoom Fixed"); 1412b1e749c0SJan Kiszka gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0, 1413b1e749c0SJan Kiszka HOTKEY_MODIFIERS); 1414bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item); 1415c6158483SAnthony Liguori 1416834574eaSAnthony Liguori s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit")); 1417bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item); 1418c6158483SAnthony Liguori 1419c6158483SAnthony Liguori separator = gtk_separator_menu_item_new(); 1420bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 1421c6158483SAnthony Liguori 1422834574eaSAnthony Liguori s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover")); 1423bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item); 14245104a1f6SAnthony Liguori 1425834574eaSAnthony Liguori s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input")); 14265104a1f6SAnthony Liguori gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item), 14275104a1f6SAnthony Liguori "<QEMU>/View/Grab Input"); 1428b1e749c0SJan Kiszka gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g, 1429b1e749c0SJan Kiszka HOTKEY_MODIFIERS); 1430bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item); 14315104a1f6SAnthony Liguori 1432a4ccabcfSAnthony Liguori separator = gtk_separator_menu_item_new(); 1433bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 1434a4ccabcfSAnthony Liguori 1435a4ccabcfSAnthony Liguori s->vga_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA"); 1436a4ccabcfSAnthony Liguori group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s->vga_item)); 1437a4ccabcfSAnthony Liguori gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->vga_item), 1438a4ccabcfSAnthony Liguori "<QEMU>/View/VGA"); 1439b1e749c0SJan Kiszka gtk_accel_map_add_entry("<QEMU>/View/VGA", GDK_KEY_1, HOTKEY_MODIFIERS); 1440bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->vga_item); 1441a4ccabcfSAnthony Liguori 1442ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE) 1443ee5f31e4SGerd Hoffmann gd_vcs_init(s, group, view_menu); 1444ee5f31e4SGerd Hoffmann #endif 1445d861def3SAnthony Liguori 1446a4ccabcfSAnthony Liguori separator = gtk_separator_menu_item_new(); 1447bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 1448a4ccabcfSAnthony Liguori 1449834574eaSAnthony Liguori s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs")); 1450bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item); 1451a4ccabcfSAnthony Liguori 1452bf9b255fSAnthony Liguori return view_menu; 1453bf9b255fSAnthony Liguori } 1454a4ccabcfSAnthony Liguori 1455bf9b255fSAnthony Liguori static void gd_create_menus(GtkDisplayState *s) 1456bf9b255fSAnthony Liguori { 1457bf9b255fSAnthony Liguori GtkAccelGroup *accel_group; 1458bf9b255fSAnthony Liguori 1459bf9b255fSAnthony Liguori accel_group = gtk_accel_group_new(); 1460bf9b255fSAnthony Liguori s->machine_menu = gd_create_menu_machine(s, accel_group); 1461bf9b255fSAnthony Liguori s->view_menu = gd_create_menu_view(s, accel_group); 1462bf9b255fSAnthony Liguori 1463bf9b255fSAnthony Liguori s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine")); 146430e8f22bSJan Kiszka gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item), 146530e8f22bSJan Kiszka s->machine_menu); 146630e8f22bSJan Kiszka gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item); 1467a4ccabcfSAnthony Liguori 1468bf9b255fSAnthony Liguori s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View")); 1469a4ccabcfSAnthony Liguori gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu); 1470a4ccabcfSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item); 1471bf9b255fSAnthony Liguori 1472bf9b255fSAnthony Liguori g_object_set_data(G_OBJECT(s->window), "accel_group", accel_group); 1473bf9b255fSAnthony Liguori gtk_window_add_accel_group(GTK_WINDOW(s->window), accel_group); 1474bf9b255fSAnthony Liguori s->accel_group = accel_group; 1475a4ccabcfSAnthony Liguori } 1476a4ccabcfSAnthony Liguori 14777c20b4a3SGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = { 14787c20b4a3SGerd Hoffmann .dpy_name = "gtk", 14797c20b4a3SGerd Hoffmann .dpy_gfx_update = gd_update, 1480c12aeb86SGerd Hoffmann .dpy_gfx_switch = gd_switch, 14817c20b4a3SGerd Hoffmann .dpy_refresh = gd_refresh, 14829697f5d2SGerd Hoffmann .dpy_mouse_set = gd_mouse_set, 14839697f5d2SGerd Hoffmann .dpy_cursor_define = gd_cursor_define, 14847c20b4a3SGerd Hoffmann }; 14857c20b4a3SGerd Hoffmann 1486881249c7SJan Kiszka void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) 1487a4ccabcfSAnthony Liguori { 1488a4ccabcfSAnthony Liguori GtkDisplayState *s = g_malloc0(sizeof(*s)); 1489d819cdccSStefan Weil char *filename; 1490a4ccabcfSAnthony Liguori 1491a4ccabcfSAnthony Liguori gtk_init(NULL, NULL); 1492a4ccabcfSAnthony Liguori 14937c20b4a3SGerd Hoffmann s->dcl.ops = &dcl_ops; 1494284d1c6bSGerd Hoffmann s->dcl.con = qemu_console_lookup_by_index(0); 1495a4ccabcfSAnthony Liguori 1496a4ccabcfSAnthony Liguori s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 149751572ab0SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 2, 0) 149851572ab0SDaniel P. Berrange s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 149951572ab0SDaniel P. Berrange #else 1500a4ccabcfSAnthony Liguori s->vbox = gtk_vbox_new(FALSE, 0); 150151572ab0SDaniel P. Berrange #endif 1502a4ccabcfSAnthony Liguori s->notebook = gtk_notebook_new(); 1503a4ccabcfSAnthony Liguori s->drawing_area = gtk_drawing_area_new(); 1504a4ccabcfSAnthony Liguori s->menu_bar = gtk_menu_bar_new(); 1505a4ccabcfSAnthony Liguori 1506a4ccabcfSAnthony Liguori s->scale_x = 1.0; 1507a4ccabcfSAnthony Liguori s->scale_y = 1.0; 1508c6158483SAnthony Liguori s->free_scale = FALSE; 1509a4ccabcfSAnthony Liguori 1510834574eaSAnthony Liguori setlocale(LC_ALL, ""); 1511834574eaSAnthony Liguori bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR); 1512834574eaSAnthony Liguori textdomain("qemu"); 1513834574eaSAnthony Liguori 1514a4ccabcfSAnthony Liguori s->null_cursor = gdk_cursor_new(GDK_BLANK_CURSOR); 1515a4ccabcfSAnthony Liguori 1516a4ccabcfSAnthony Liguori s->mouse_mode_notifier.notify = gd_mouse_mode_change; 1517a4ccabcfSAnthony Liguori qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier); 1518a4ccabcfSAnthony Liguori qemu_add_vm_change_state_handler(gd_change_runstate, s); 1519a4ccabcfSAnthony Liguori 1520a4ccabcfSAnthony Liguori gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), s->drawing_area, gtk_label_new("VGA")); 1521a4ccabcfSAnthony Liguori 1522f7da9c17SAnthony Liguori filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg"); 1523d819cdccSStefan Weil if (filename) { 1524d819cdccSStefan Weil GError *error = NULL; 1525d819cdccSStefan Weil GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error); 1526d819cdccSStefan Weil if (pixbuf) { 1527d819cdccSStefan Weil gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf); 1528d819cdccSStefan Weil } else { 1529d819cdccSStefan Weil g_error_free(error); 1530d819cdccSStefan Weil } 1531d819cdccSStefan Weil g_free(filename); 1532d819cdccSStefan Weil } 1533d819cdccSStefan Weil 1534a4ccabcfSAnthony Liguori gd_create_menus(s); 1535a4ccabcfSAnthony Liguori 1536a4ccabcfSAnthony Liguori gd_connect_signals(s); 1537a4ccabcfSAnthony Liguori 1538a4ccabcfSAnthony Liguori gtk_widget_add_events(s->drawing_area, 1539a4ccabcfSAnthony Liguori GDK_POINTER_MOTION_MASK | 1540a4ccabcfSAnthony Liguori GDK_BUTTON_PRESS_MASK | 1541a4ccabcfSAnthony Liguori GDK_BUTTON_RELEASE_MASK | 1542a4ccabcfSAnthony Liguori GDK_BUTTON_MOTION_MASK | 15435104a1f6SAnthony Liguori GDK_ENTER_NOTIFY_MASK | 15445104a1f6SAnthony Liguori GDK_LEAVE_NOTIFY_MASK | 1545a4ccabcfSAnthony Liguori GDK_SCROLL_MASK | 1546a4ccabcfSAnthony Liguori GDK_KEY_PRESS_MASK); 1547a4ccabcfSAnthony Liguori gtk_widget_set_double_buffered(s->drawing_area, FALSE); 1548a4ccabcfSAnthony Liguori gtk_widget_set_can_focus(s->drawing_area, TRUE); 1549a4ccabcfSAnthony Liguori 1550a4ccabcfSAnthony Liguori gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 1551a4ccabcfSAnthony Liguori gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE); 1552a4ccabcfSAnthony Liguori 1553a4ccabcfSAnthony Liguori gd_update_caption(s); 1554a4ccabcfSAnthony Liguori 1555a4ccabcfSAnthony Liguori gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0); 1556a4ccabcfSAnthony Liguori gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0); 1557a4ccabcfSAnthony Liguori 1558a4ccabcfSAnthony Liguori gtk_container_add(GTK_CONTAINER(s->window), s->vbox); 1559a4ccabcfSAnthony Liguori 1560a4ccabcfSAnthony Liguori gtk_widget_show_all(s->window); 1561a4ccabcfSAnthony Liguori 1562787ba4f0SPeter Wu if (full_screen) { 1563787ba4f0SPeter Wu gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item)); 1564787ba4f0SPeter Wu } 1565881249c7SJan Kiszka if (grab_on_hover) { 1566881249c7SJan Kiszka gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item)); 1567881249c7SJan Kiszka } 1568787ba4f0SPeter Wu 15695209089fSGerd Hoffmann register_displaychangelistener(&s->dcl); 1570a4ccabcfSAnthony Liguori 1571a4ccabcfSAnthony Liguori global_state = s; 1572a4ccabcfSAnthony Liguori } 1573ee5f31e4SGerd Hoffmann 1574ee5f31e4SGerd Hoffmann void early_gtk_display_init(void) 1575ee5f31e4SGerd Hoffmann { 1576ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE) 1577ee5f31e4SGerd Hoffmann register_vc_handler(gd_vc_handler); 1578ee5f31e4SGerd Hoffmann #endif 1579ee5f31e4SGerd Hoffmann } 1580