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