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); 33282fc1809SGerd Hoffmann GtkBorder *ib; 33382fc1809SGerd Hoffmann 33484e2dc4bSCole Robinson #if VTE_CHECK_VERSION(0, 37, 0) 33584e2dc4bSCole Robinson GtkBorder padding; 33684e2dc4bSCole Robinson gtk_style_context_get_padding( 33784e2dc4bSCole Robinson gtk_widget_get_style_context(vc->vte.terminal), 33884e2dc4bSCole Robinson gtk_widget_get_state_flags(vc->vte.terminal), 33984e2dc4bSCole Robinson &padding); 34084e2dc4bSCole Robinson ib = &padding; 34184e2dc4bSCole Robinson #else 34284e2dc4bSCole Robinson gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL); 34384e2dc4bSCole Robinson #endif 34484e2dc4bSCole Robinson 34582fc1809SGerd Hoffmann geo.width_inc = vte_terminal_get_char_width(term); 34682fc1809SGerd Hoffmann geo.height_inc = vte_terminal_get_char_height(term); 34782fc1809SGerd Hoffmann mask |= GDK_HINT_RESIZE_INC; 34882fc1809SGerd Hoffmann geo.base_width = geo.width_inc; 34982fc1809SGerd Hoffmann geo.base_height = geo.height_inc; 35082fc1809SGerd Hoffmann mask |= GDK_HINT_BASE_SIZE; 35182fc1809SGerd Hoffmann geo.min_width = geo.width_inc * VC_TERM_X_MIN; 35282fc1809SGerd Hoffmann geo.min_height = geo.height_inc * VC_TERM_Y_MIN; 35382fc1809SGerd Hoffmann mask |= GDK_HINT_MIN_SIZE; 35484e2dc4bSCole Robinson 3554fd811a6SCole Robinson if (ib) { 35682fc1809SGerd Hoffmann geo.base_width += ib->left + ib->right; 35782fc1809SGerd Hoffmann geo.base_height += ib->top + ib->bottom; 35882fc1809SGerd Hoffmann geo.min_width += ib->left + ib->right; 35982fc1809SGerd Hoffmann geo.min_height += ib->top + ib->bottom; 3604fd811a6SCole Robinson } 36182fc1809SGerd Hoffmann geo_widget = vc->vte.terminal; 36282fc1809SGerd Hoffmann #endif 36382fc1809SGerd Hoffmann } 36482fc1809SGerd Hoffmann 36582fc1809SGerd Hoffmann geo_window = GTK_WINDOW(vc->window ? vc->window : s->window); 36682fc1809SGerd Hoffmann gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask); 36782fc1809SGerd Hoffmann } 36882fc1809SGerd Hoffmann 36997edf3bdSGerd Hoffmann void gd_update_windowsize(VirtualConsole *vc) 370a4ccabcfSAnthony Liguori { 371e3500d1fSGerd Hoffmann GtkDisplayState *s = vc->s; 372c6158483SAnthony Liguori 37382fc1809SGerd Hoffmann gd_update_geometry_hints(vc); 374c6158483SAnthony Liguori 37582fc1809SGerd Hoffmann if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) { 37682fc1809SGerd Hoffmann gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window), 37782fc1809SGerd Hoffmann VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN); 378a4ccabcfSAnthony Liguori } 379aa0a55d4SGerd Hoffmann } 380a4ccabcfSAnthony Liguori 381e3500d1fSGerd Hoffmann static void gd_update_full_redraw(VirtualConsole *vc) 3829d9801cfSGerd Hoffmann { 383e3500d1fSGerd Hoffmann GtkWidget *area = vc->gfx.drawing_area; 3849d9801cfSGerd Hoffmann int ww, wh; 385e3500d1fSGerd Hoffmann gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh); 386925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL) 387925a0400SGerd Hoffmann if (vc->gfx.gls) { 388925a0400SGerd Hoffmann gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); 389925a0400SGerd Hoffmann return; 390925a0400SGerd Hoffmann } 391925a0400SGerd Hoffmann #endif 392e3500d1fSGerd Hoffmann gtk_widget_queue_draw_area(area, 0, 0, ww, wh); 3939d9801cfSGerd Hoffmann } 3949d9801cfSGerd Hoffmann 3956db253caSJan Kiszka static void gtk_release_modifiers(GtkDisplayState *s) 3966db253caSJan Kiszka { 397e3500d1fSGerd Hoffmann VirtualConsole *vc = gd_vc_find_current(s); 3986db253caSJan Kiszka int i, keycode; 3996db253caSJan Kiszka 400f8c223f6SGerd Hoffmann if (vc->type != GD_VC_GFX || 401f8c223f6SGerd Hoffmann !qemu_console_is_graphic(vc->gfx.dcl.con)) { 4026db253caSJan Kiszka return; 4036db253caSJan Kiszka } 4046db253caSJan Kiszka for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { 4056db253caSJan Kiszka keycode = modifier_keycode[i]; 4066db253caSJan Kiszka if (!s->modifier_pressed[i]) { 4076db253caSJan Kiszka continue; 4086db253caSJan Kiszka } 409e3500d1fSGerd Hoffmann qemu_input_event_send_key_number(vc->gfx.dcl.con, keycode, false); 4106db253caSJan Kiszka s->modifier_pressed[i] = false; 4116db253caSJan Kiszka } 4126db253caSJan Kiszka } 4136db253caSJan Kiszka 414316cb068SGerd Hoffmann static void gd_widget_reparent(GtkWidget *from, GtkWidget *to, 415316cb068SGerd Hoffmann GtkWidget *widget) 416316cb068SGerd Hoffmann { 417316cb068SGerd Hoffmann g_object_ref(G_OBJECT(widget)); 418316cb068SGerd Hoffmann gtk_container_remove(GTK_CONTAINER(from), widget); 419316cb068SGerd Hoffmann gtk_container_add(GTK_CONTAINER(to), widget); 420316cb068SGerd Hoffmann g_object_unref(G_OBJECT(widget)); 421316cb068SGerd Hoffmann } 422316cb068SGerd Hoffmann 4239d9801cfSGerd Hoffmann /** DisplayState Callbacks **/ 4249d9801cfSGerd Hoffmann 4259d9801cfSGerd Hoffmann static void gd_update(DisplayChangeListener *dcl, 426bc2ed970SGerd Hoffmann int x, int y, int w, int h) 4279d9801cfSGerd Hoffmann { 428e3500d1fSGerd Hoffmann VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 429f8c223f6SGerd Hoffmann GdkWindow *win; 4309d9801cfSGerd Hoffmann int x1, x2, y1, y2; 4319d9801cfSGerd Hoffmann int mx, my; 4329d9801cfSGerd Hoffmann int fbw, fbh; 4339d9801cfSGerd Hoffmann int ww, wh; 4349d9801cfSGerd Hoffmann 43574444bc1SGerd Hoffmann trace_gd_update(vc->label, x, y, w, h); 4369d9801cfSGerd Hoffmann 4374cdfc935SHervé Poussineau if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { 4384cdfc935SHervé Poussineau return; 4394cdfc935SHervé Poussineau } 4404cdfc935SHervé Poussineau 441e3500d1fSGerd Hoffmann if (vc->gfx.convert) { 442e3500d1fSGerd Hoffmann pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, 443e3500d1fSGerd Hoffmann NULL, vc->gfx.convert, 444f0875536SGerd Hoffmann x, y, 0, 0, x, y, w, h); 445f0875536SGerd Hoffmann } 446f0875536SGerd Hoffmann 447e3500d1fSGerd Hoffmann x1 = floor(x * vc->gfx.scale_x); 448e3500d1fSGerd Hoffmann y1 = floor(y * vc->gfx.scale_y); 4499d9801cfSGerd Hoffmann 450e3500d1fSGerd Hoffmann x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x); 451e3500d1fSGerd Hoffmann y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y); 4529d9801cfSGerd Hoffmann 453e3500d1fSGerd Hoffmann fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; 454e3500d1fSGerd Hoffmann fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; 4559d9801cfSGerd Hoffmann 456f8c223f6SGerd Hoffmann win = gtk_widget_get_window(vc->gfx.drawing_area); 457f8c223f6SGerd Hoffmann if (!win) { 458f8c223f6SGerd Hoffmann return; 459f8c223f6SGerd Hoffmann } 460f8c223f6SGerd Hoffmann gdk_drawable_get_size(win, &ww, &wh); 4619d9801cfSGerd Hoffmann 4629d9801cfSGerd Hoffmann mx = my = 0; 4639d9801cfSGerd Hoffmann if (ww > fbw) { 4649d9801cfSGerd Hoffmann mx = (ww - fbw) / 2; 4659d9801cfSGerd Hoffmann } 4669d9801cfSGerd Hoffmann if (wh > fbh) { 4679d9801cfSGerd Hoffmann my = (wh - fbh) / 2; 4689d9801cfSGerd Hoffmann } 4699d9801cfSGerd Hoffmann 470e3500d1fSGerd Hoffmann gtk_widget_queue_draw_area(vc->gfx.drawing_area, 471e3500d1fSGerd Hoffmann mx + x1, my + y1, (x2 - x1), (y2 - y1)); 4729d9801cfSGerd Hoffmann } 4739d9801cfSGerd Hoffmann 474bc2ed970SGerd Hoffmann static void gd_refresh(DisplayChangeListener *dcl) 4759d9801cfSGerd Hoffmann { 476284d1c6bSGerd Hoffmann graphic_hw_update(dcl->con); 4779d9801cfSGerd Hoffmann } 4789d9801cfSGerd Hoffmann 479b087143bSIgor Mitsyanko #if GTK_CHECK_VERSION(3, 0, 0) 480bb732ee7SCole Robinson static GdkDevice *gd_get_pointer(GdkDisplay *dpy) 481bb732ee7SCole Robinson { 482bb732ee7SCole Robinson #if GTK_CHECK_VERSION(3, 20, 0) 483bb732ee7SCole Robinson return gdk_seat_get_pointer(gdk_display_get_default_seat(dpy)); 484bb732ee7SCole Robinson #else 485bb732ee7SCole Robinson return gdk_device_manager_get_client_pointer( 486bb732ee7SCole Robinson gdk_display_get_device_manager(dpy)); 487bb732ee7SCole Robinson #endif 488bb732ee7SCole Robinson } 489bb732ee7SCole Robinson 490b087143bSIgor Mitsyanko static void gd_mouse_set(DisplayChangeListener *dcl, 491b087143bSIgor Mitsyanko int x, int y, int visible) 492b087143bSIgor Mitsyanko { 493e3500d1fSGerd Hoffmann VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 494b087143bSIgor Mitsyanko GdkDisplay *dpy; 495b087143bSIgor Mitsyanko gint x_root, y_root; 496b087143bSIgor Mitsyanko 4972bda6602SCole Robinson if (qemu_input_is_absolute()) { 4982bda6602SCole Robinson return; 4992bda6602SCole Robinson } 5002bda6602SCole Robinson 501e3500d1fSGerd Hoffmann dpy = gtk_widget_get_display(vc->gfx.drawing_area); 502e3500d1fSGerd Hoffmann gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area), 503b087143bSIgor Mitsyanko x, y, &x_root, &y_root); 504bb732ee7SCole Robinson gdk_device_warp(gd_get_pointer(dpy), 505e3500d1fSGerd Hoffmann gtk_widget_get_screen(vc->gfx.drawing_area), 506298526feSCole Robinson x_root, y_root); 5071271f7f7SGerd Hoffmann vc->s->last_x = x; 5081271f7f7SGerd Hoffmann vc->s->last_y = y; 509b087143bSIgor Mitsyanko } 510b087143bSIgor Mitsyanko #else 5119697f5d2SGerd Hoffmann static void gd_mouse_set(DisplayChangeListener *dcl, 5129697f5d2SGerd Hoffmann int x, int y, int visible) 5139697f5d2SGerd Hoffmann { 514e3500d1fSGerd Hoffmann VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 5159697f5d2SGerd Hoffmann gint x_root, y_root; 5169697f5d2SGerd Hoffmann 5172bda6602SCole Robinson if (qemu_input_is_absolute()) { 5182bda6602SCole Robinson return; 5192bda6602SCole Robinson } 5202bda6602SCole Robinson 521e3500d1fSGerd Hoffmann gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area), 5229697f5d2SGerd Hoffmann x, y, &x_root, &y_root); 523e3500d1fSGerd Hoffmann gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area), 524e3500d1fSGerd Hoffmann gtk_widget_get_screen(vc->gfx.drawing_area), 5259697f5d2SGerd Hoffmann x_root, y_root); 5269697f5d2SGerd Hoffmann } 527b087143bSIgor Mitsyanko #endif 5289697f5d2SGerd Hoffmann 5299697f5d2SGerd Hoffmann static void gd_cursor_define(DisplayChangeListener *dcl, 5309697f5d2SGerd Hoffmann QEMUCursor *c) 5319697f5d2SGerd Hoffmann { 532e3500d1fSGerd Hoffmann VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 5339697f5d2SGerd Hoffmann GdkPixbuf *pixbuf; 5349697f5d2SGerd Hoffmann GdkCursor *cursor; 5359697f5d2SGerd Hoffmann 5364cdfc935SHervé Poussineau if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { 5374cdfc935SHervé Poussineau return; 5384cdfc935SHervé Poussineau } 5394cdfc935SHervé Poussineau 5409697f5d2SGerd Hoffmann pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data), 5419697f5d2SGerd Hoffmann GDK_COLORSPACE_RGB, true, 8, 5429697f5d2SGerd Hoffmann c->width, c->height, c->width * 4, 5439697f5d2SGerd Hoffmann NULL, NULL); 544e3500d1fSGerd Hoffmann cursor = gdk_cursor_new_from_pixbuf 545e3500d1fSGerd Hoffmann (gtk_widget_get_display(vc->gfx.drawing_area), 5469697f5d2SGerd Hoffmann pixbuf, c->hot_x, c->hot_y); 547e3500d1fSGerd Hoffmann gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor); 5489697f5d2SGerd Hoffmann g_object_unref(pixbuf); 549030b4b7dSStefan Weil #if !GTK_CHECK_VERSION(3, 0, 0) 55017139240SAnthony Liguori gdk_cursor_unref(cursor); 551030b4b7dSStefan Weil #else 552030b4b7dSStefan Weil g_object_unref(cursor); 553030b4b7dSStefan Weil #endif 5549697f5d2SGerd Hoffmann } 5559697f5d2SGerd Hoffmann 5569d9801cfSGerd Hoffmann static void gd_switch(DisplayChangeListener *dcl, 5579d9801cfSGerd Hoffmann DisplaySurface *surface) 5589d9801cfSGerd Hoffmann { 559e3500d1fSGerd Hoffmann VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 5609d9801cfSGerd Hoffmann bool resized = true; 5619d9801cfSGerd Hoffmann 562f98f43eaSGerd Hoffmann trace_gd_switch(vc->label, 563f98f43eaSGerd Hoffmann surface ? surface_width(surface) : 0, 564f98f43eaSGerd Hoffmann surface ? surface_height(surface) : 0); 5659d9801cfSGerd Hoffmann 566e3500d1fSGerd Hoffmann if (vc->gfx.surface) { 567e3500d1fSGerd Hoffmann cairo_surface_destroy(vc->gfx.surface); 568f98f43eaSGerd Hoffmann vc->gfx.surface = NULL; 569f98f43eaSGerd Hoffmann } 570f98f43eaSGerd Hoffmann if (vc->gfx.convert) { 571f98f43eaSGerd Hoffmann pixman_image_unref(vc->gfx.convert); 572f98f43eaSGerd Hoffmann vc->gfx.convert = NULL; 5739d9801cfSGerd Hoffmann } 5749d9801cfSGerd Hoffmann 575f98f43eaSGerd Hoffmann if (vc->gfx.ds && surface && 576e3500d1fSGerd Hoffmann surface_width(vc->gfx.ds) == surface_width(surface) && 577e3500d1fSGerd Hoffmann surface_height(vc->gfx.ds) == surface_height(surface)) { 5789d9801cfSGerd Hoffmann resized = false; 5799d9801cfSGerd Hoffmann } 580e3500d1fSGerd Hoffmann vc->gfx.ds = surface; 581f0875536SGerd Hoffmann 582f98f43eaSGerd Hoffmann if (!surface) { 583f98f43eaSGerd Hoffmann return; 5849d9801cfSGerd Hoffmann } 5859d9801cfSGerd Hoffmann 586f0875536SGerd Hoffmann if (surface->format == PIXMAN_x8r8g8b8) { 587f0875536SGerd Hoffmann /* 588f0875536SGerd Hoffmann * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24 589f0875536SGerd Hoffmann * 590f0875536SGerd Hoffmann * No need to convert, use surface directly. Should be the 591f0875536SGerd Hoffmann * common case as this is qemu_default_pixelformat(32) too. 592f0875536SGerd Hoffmann */ 593e3500d1fSGerd Hoffmann vc->gfx.surface = cairo_image_surface_create_for_data 594f0875536SGerd Hoffmann (surface_data(surface), 595f0875536SGerd Hoffmann CAIRO_FORMAT_RGB24, 5969d9801cfSGerd Hoffmann surface_width(surface), 5979d9801cfSGerd Hoffmann surface_height(surface), 5989d9801cfSGerd Hoffmann surface_stride(surface)); 599f0875536SGerd Hoffmann } else { 600f0875536SGerd Hoffmann /* Must convert surface, use pixman to do it. */ 601e3500d1fSGerd Hoffmann vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8, 602f0875536SGerd Hoffmann surface_width(surface), 603f0875536SGerd Hoffmann surface_height(surface), 604f0875536SGerd Hoffmann NULL, 0); 605e3500d1fSGerd Hoffmann vc->gfx.surface = cairo_image_surface_create_for_data 606e3500d1fSGerd Hoffmann ((void *)pixman_image_get_data(vc->gfx.convert), 607f0875536SGerd Hoffmann CAIRO_FORMAT_RGB24, 608e3500d1fSGerd Hoffmann pixman_image_get_width(vc->gfx.convert), 609e3500d1fSGerd Hoffmann pixman_image_get_height(vc->gfx.convert), 610e3500d1fSGerd Hoffmann pixman_image_get_stride(vc->gfx.convert)); 611e3500d1fSGerd Hoffmann pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, 612e3500d1fSGerd Hoffmann NULL, vc->gfx.convert, 613f0875536SGerd Hoffmann 0, 0, 0, 0, 0, 0, 614e3500d1fSGerd Hoffmann pixman_image_get_width(vc->gfx.convert), 615e3500d1fSGerd Hoffmann pixman_image_get_height(vc->gfx.convert)); 616f0875536SGerd Hoffmann } 6179d9801cfSGerd Hoffmann 6189d9801cfSGerd Hoffmann if (resized) { 619e3500d1fSGerd Hoffmann gd_update_windowsize(vc); 6209d9801cfSGerd Hoffmann } else { 621e3500d1fSGerd Hoffmann gd_update_full_redraw(vc); 6229d9801cfSGerd Hoffmann } 6239d9801cfSGerd Hoffmann } 6249d9801cfSGerd Hoffmann 62597edf3bdSGerd Hoffmann static const DisplayChangeListenerOps dcl_ops = { 62697edf3bdSGerd Hoffmann .dpy_name = "gtk", 62797edf3bdSGerd Hoffmann .dpy_gfx_update = gd_update, 62897edf3bdSGerd Hoffmann .dpy_gfx_switch = gd_switch, 62997edf3bdSGerd Hoffmann .dpy_gfx_check_format = qemu_pixman_check_format, 63097edf3bdSGerd Hoffmann .dpy_refresh = gd_refresh, 63197edf3bdSGerd Hoffmann .dpy_mouse_set = gd_mouse_set, 63297edf3bdSGerd Hoffmann .dpy_cursor_define = gd_cursor_define, 63397edf3bdSGerd Hoffmann }; 63497edf3bdSGerd Hoffmann 63597edf3bdSGerd Hoffmann 63697edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL) 63797edf3bdSGerd Hoffmann 63897edf3bdSGerd Hoffmann /** DisplayState Callbacks (opengl version) **/ 63997edf3bdSGerd Hoffmann 640925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL) 641925a0400SGerd Hoffmann 642925a0400SGerd Hoffmann static const DisplayChangeListenerOps dcl_gl_area_ops = { 643925a0400SGerd Hoffmann .dpy_name = "gtk-egl", 644925a0400SGerd Hoffmann .dpy_gfx_update = gd_gl_area_update, 645925a0400SGerd Hoffmann .dpy_gfx_switch = gd_gl_area_switch, 646925a0400SGerd Hoffmann .dpy_gfx_check_format = console_gl_check_format, 647925a0400SGerd Hoffmann .dpy_refresh = gd_gl_area_refresh, 648925a0400SGerd Hoffmann .dpy_mouse_set = gd_mouse_set, 649925a0400SGerd Hoffmann .dpy_cursor_define = gd_cursor_define, 650925a0400SGerd Hoffmann 651925a0400SGerd Hoffmann .dpy_gl_ctx_create = gd_gl_area_create_context, 652925a0400SGerd Hoffmann .dpy_gl_ctx_destroy = gd_gl_area_destroy_context, 653925a0400SGerd Hoffmann .dpy_gl_ctx_make_current = gd_gl_area_make_current, 654925a0400SGerd Hoffmann .dpy_gl_ctx_get_current = gd_gl_area_get_current_context, 655925a0400SGerd Hoffmann .dpy_gl_scanout = gd_gl_area_scanout, 656925a0400SGerd Hoffmann .dpy_gl_update = gd_gl_area_scanout_flush, 657925a0400SGerd Hoffmann }; 658925a0400SGerd Hoffmann 659925a0400SGerd Hoffmann #else 660925a0400SGerd Hoffmann 66197edf3bdSGerd Hoffmann static const DisplayChangeListenerOps dcl_egl_ops = { 66297edf3bdSGerd Hoffmann .dpy_name = "gtk-egl", 66397edf3bdSGerd Hoffmann .dpy_gfx_update = gd_egl_update, 66497edf3bdSGerd Hoffmann .dpy_gfx_switch = gd_egl_switch, 66597edf3bdSGerd Hoffmann .dpy_gfx_check_format = console_gl_check_format, 66697edf3bdSGerd Hoffmann .dpy_refresh = gd_egl_refresh, 66797edf3bdSGerd Hoffmann .dpy_mouse_set = gd_mouse_set, 66897edf3bdSGerd Hoffmann .dpy_cursor_define = gd_cursor_define, 6694782aeb7SGerd Hoffmann 6704782aeb7SGerd Hoffmann .dpy_gl_ctx_create = gd_egl_create_context, 6714782aeb7SGerd Hoffmann .dpy_gl_ctx_destroy = qemu_egl_destroy_context, 6724782aeb7SGerd Hoffmann .dpy_gl_ctx_make_current = gd_egl_make_current, 6734782aeb7SGerd Hoffmann .dpy_gl_ctx_get_current = qemu_egl_get_current_context, 6744782aeb7SGerd Hoffmann .dpy_gl_scanout = gd_egl_scanout, 6754782aeb7SGerd Hoffmann .dpy_gl_update = gd_egl_scanout_flush, 67697edf3bdSGerd Hoffmann }; 67797edf3bdSGerd Hoffmann 678925a0400SGerd Hoffmann #endif /* CONFIG_GTK_GL */ 679925a0400SGerd Hoffmann #endif /* CONFIG_OPENGL */ 68097edf3bdSGerd Hoffmann 681a4ccabcfSAnthony Liguori /** QEMU Events **/ 682a4ccabcfSAnthony Liguori 683a4ccabcfSAnthony Liguori static void gd_change_runstate(void *opaque, int running, RunState state) 684a4ccabcfSAnthony Liguori { 685a4ccabcfSAnthony Liguori GtkDisplayState *s = opaque; 686a4ccabcfSAnthony Liguori 687a4ccabcfSAnthony Liguori gd_update_caption(s); 688a4ccabcfSAnthony Liguori } 689a4ccabcfSAnthony Liguori 690a4ccabcfSAnthony Liguori static void gd_mouse_mode_change(Notifier *notify, void *data) 691a4ccabcfSAnthony Liguori { 692800b0e81STakashi Iwai GtkDisplayState *s; 69399623c90SGerd Hoffmann int i; 694800b0e81STakashi Iwai 695800b0e81STakashi Iwai s = container_of(notify, GtkDisplayState, mouse_mode_notifier); 696800b0e81STakashi Iwai /* release the grab at switching to absolute mode */ 697800b0e81STakashi Iwai if (qemu_input_is_absolute() && gd_is_grab_active(s)) { 698800b0e81STakashi Iwai gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 699a4ccabcfSAnthony Liguori FALSE); 700a4ccabcfSAnthony Liguori } 70199623c90SGerd Hoffmann for (i = 0; i < s->nb_vcs; i++) { 70299623c90SGerd Hoffmann VirtualConsole *vc = &s->vc[i]; 70399623c90SGerd Hoffmann gd_update_cursor(vc); 70499623c90SGerd Hoffmann } 705800b0e81STakashi Iwai } 706a4ccabcfSAnthony Liguori 707a4ccabcfSAnthony Liguori /** GTK Events **/ 708a4ccabcfSAnthony Liguori 709a4ccabcfSAnthony Liguori static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event, 710a4ccabcfSAnthony Liguori void *opaque) 711a4ccabcfSAnthony Liguori { 712a4ccabcfSAnthony Liguori GtkDisplayState *s = opaque; 713e3500d1fSGerd Hoffmann int i; 714a4ccabcfSAnthony Liguori 715a4ccabcfSAnthony Liguori if (!no_quit) { 716e3500d1fSGerd Hoffmann for (i = 0; i < s->nb_vcs; i++) { 717e3500d1fSGerd Hoffmann if (s->vc[i].type != GD_VC_GFX) { 718e3500d1fSGerd Hoffmann continue; 719e3500d1fSGerd Hoffmann } 720e3500d1fSGerd Hoffmann unregister_displaychangelistener(&s->vc[i].gfx.dcl); 721e3500d1fSGerd Hoffmann } 722a4ccabcfSAnthony Liguori qmp_quit(NULL); 723a4ccabcfSAnthony Liguori return FALSE; 724a4ccabcfSAnthony Liguori } 725a4ccabcfSAnthony Liguori 726a4ccabcfSAnthony Liguori return TRUE; 727a4ccabcfSAnthony Liguori } 728a4ccabcfSAnthony Liguori 729925a0400SGerd Hoffmann static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height) 730925a0400SGerd Hoffmann { 731925a0400SGerd Hoffmann QemuUIInfo info; 732925a0400SGerd Hoffmann 733925a0400SGerd Hoffmann memset(&info, 0, sizeof(info)); 734925a0400SGerd Hoffmann info.width = width; 735925a0400SGerd Hoffmann info.height = height; 736925a0400SGerd Hoffmann dpy_set_ui_info(vc->gfx.dcl.con, &info); 737925a0400SGerd Hoffmann } 738925a0400SGerd Hoffmann 739925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL) 740925a0400SGerd Hoffmann 741925a0400SGerd Hoffmann static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context, 742925a0400SGerd Hoffmann void *opaque) 743925a0400SGerd Hoffmann { 744925a0400SGerd Hoffmann VirtualConsole *vc = opaque; 745925a0400SGerd Hoffmann 746925a0400SGerd Hoffmann if (vc->gfx.gls) { 747925a0400SGerd Hoffmann gd_gl_area_draw(vc); 748925a0400SGerd Hoffmann } 749925a0400SGerd Hoffmann return TRUE; 750925a0400SGerd Hoffmann } 751925a0400SGerd Hoffmann 752925a0400SGerd Hoffmann static void gd_resize_event(GtkGLArea *area, 753925a0400SGerd Hoffmann gint width, gint height, gpointer *opaque) 754925a0400SGerd Hoffmann { 755925a0400SGerd Hoffmann VirtualConsole *vc = (void *)opaque; 756925a0400SGerd Hoffmann 757925a0400SGerd Hoffmann gd_set_ui_info(vc, width, height); 758925a0400SGerd Hoffmann } 759925a0400SGerd Hoffmann 760925a0400SGerd Hoffmann #endif 761925a0400SGerd Hoffmann 762a4ccabcfSAnthony Liguori static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) 763a4ccabcfSAnthony Liguori { 764e3500d1fSGerd Hoffmann VirtualConsole *vc = opaque; 765e3500d1fSGerd Hoffmann GtkDisplayState *s = vc->s; 766c6158483SAnthony Liguori int mx, my; 767a4ccabcfSAnthony Liguori int ww, wh; 768a4ccabcfSAnthony Liguori int fbw, fbh; 769a4ccabcfSAnthony Liguori 77097edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL) 77197edf3bdSGerd Hoffmann if (vc->gfx.gls) { 772925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL) 773925a0400SGerd Hoffmann /* invoke render callback please */ 774925a0400SGerd Hoffmann return FALSE; 775925a0400SGerd Hoffmann #else 77697edf3bdSGerd Hoffmann gd_egl_draw(vc); 77797edf3bdSGerd Hoffmann return TRUE; 778925a0400SGerd Hoffmann #endif 77997edf3bdSGerd Hoffmann } 78097edf3bdSGerd Hoffmann #endif 78197edf3bdSGerd Hoffmann 782c6158483SAnthony Liguori if (!gtk_widget_get_realized(widget)) { 783c6158483SAnthony Liguori return FALSE; 784c6158483SAnthony Liguori } 785f98f43eaSGerd Hoffmann if (!vc->gfx.ds) { 786f98f43eaSGerd Hoffmann return FALSE; 787f98f43eaSGerd Hoffmann } 788c6158483SAnthony Liguori 789e3500d1fSGerd Hoffmann fbw = surface_width(vc->gfx.ds); 790e3500d1fSGerd Hoffmann fbh = surface_height(vc->gfx.ds); 791a4ccabcfSAnthony Liguori 792a4ccabcfSAnthony Liguori gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh); 793a4ccabcfSAnthony Liguori 794c6158483SAnthony Liguori if (s->full_screen) { 795e3500d1fSGerd Hoffmann vc->gfx.scale_x = (double)ww / fbw; 796e3500d1fSGerd Hoffmann vc->gfx.scale_y = (double)wh / fbh; 797c6158483SAnthony Liguori } else if (s->free_scale) { 798c6158483SAnthony Liguori double sx, sy; 799c6158483SAnthony Liguori 800c6158483SAnthony Liguori sx = (double)ww / fbw; 801c6158483SAnthony Liguori sy = (double)wh / fbh; 802c6158483SAnthony Liguori 803e3500d1fSGerd Hoffmann vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); 804a4ccabcfSAnthony Liguori } 805a4ccabcfSAnthony Liguori 806e3500d1fSGerd Hoffmann fbw *= vc->gfx.scale_x; 807e3500d1fSGerd Hoffmann fbh *= vc->gfx.scale_y; 8085104a1f6SAnthony Liguori 809c6158483SAnthony Liguori mx = my = 0; 810c6158483SAnthony Liguori if (ww > fbw) { 811c6158483SAnthony Liguori mx = (ww - fbw) / 2; 812c6158483SAnthony Liguori } 813c6158483SAnthony Liguori if (wh > fbh) { 814c6158483SAnthony Liguori my = (wh - fbh) / 2; 815c6158483SAnthony Liguori } 816c6158483SAnthony Liguori 817c6158483SAnthony Liguori cairo_rectangle(cr, 0, 0, ww, wh); 818c6158483SAnthony Liguori 819c6158483SAnthony Liguori /* Optionally cut out the inner area where the pixmap 820c6158483SAnthony Liguori will be drawn. This avoids 'flashing' since we're 821c6158483SAnthony Liguori not double-buffering. Note we're using the undocumented 822c6158483SAnthony Liguori behaviour of drawing the rectangle from right to left 823c6158483SAnthony Liguori to cut out the whole */ 824c6158483SAnthony Liguori cairo_rectangle(cr, mx + fbw, my, 825c6158483SAnthony Liguori -1 * fbw, fbh); 826c6158483SAnthony Liguori cairo_fill(cr); 827c6158483SAnthony Liguori 828e3500d1fSGerd Hoffmann cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y); 829e3500d1fSGerd Hoffmann cairo_set_source_surface(cr, vc->gfx.surface, 830e3500d1fSGerd Hoffmann mx / vc->gfx.scale_x, my / vc->gfx.scale_y); 831a4ccabcfSAnthony Liguori cairo_paint(cr); 832a4ccabcfSAnthony Liguori 833a4ccabcfSAnthony Liguori return TRUE; 834a4ccabcfSAnthony Liguori } 835a4ccabcfSAnthony Liguori 836fe43bca8SDaniel P. Berrange #if !GTK_CHECK_VERSION(3, 0, 0) 837a4ccabcfSAnthony Liguori static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose, 838a4ccabcfSAnthony Liguori void *opaque) 839a4ccabcfSAnthony Liguori { 840a4ccabcfSAnthony Liguori cairo_t *cr; 841a4ccabcfSAnthony Liguori gboolean ret; 842a4ccabcfSAnthony Liguori 843a4ccabcfSAnthony Liguori cr = gdk_cairo_create(gtk_widget_get_window(widget)); 844a4ccabcfSAnthony Liguori cairo_rectangle(cr, 845a4ccabcfSAnthony Liguori expose->area.x, 846a4ccabcfSAnthony Liguori expose->area.y, 847a4ccabcfSAnthony Liguori expose->area.width, 848a4ccabcfSAnthony Liguori expose->area.height); 849a4ccabcfSAnthony Liguori cairo_clip(cr); 850a4ccabcfSAnthony Liguori 851a4ccabcfSAnthony Liguori ret = gd_draw_event(widget, cr, opaque); 852a4ccabcfSAnthony Liguori 853a4ccabcfSAnthony Liguori cairo_destroy(cr); 854a4ccabcfSAnthony Liguori 855a4ccabcfSAnthony Liguori return ret; 856a4ccabcfSAnthony Liguori } 857fe43bca8SDaniel P. Berrange #endif 858a4ccabcfSAnthony Liguori 859a4ccabcfSAnthony Liguori static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, 860a4ccabcfSAnthony Liguori void *opaque) 861a4ccabcfSAnthony Liguori { 862e3500d1fSGerd Hoffmann VirtualConsole *vc = opaque; 863e3500d1fSGerd Hoffmann GtkDisplayState *s = vc->s; 864a4ccabcfSAnthony Liguori int x, y; 865c6158483SAnthony Liguori int mx, my; 866c6158483SAnthony Liguori int fbh, fbw; 867c6158483SAnthony Liguori int ww, wh; 868a4ccabcfSAnthony Liguori 869f98f43eaSGerd Hoffmann if (!vc->gfx.ds) { 870f98f43eaSGerd Hoffmann return TRUE; 871f98f43eaSGerd Hoffmann } 872f98f43eaSGerd Hoffmann 873e3500d1fSGerd Hoffmann fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; 874e3500d1fSGerd Hoffmann fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; 875c6158483SAnthony Liguori 876e3500d1fSGerd Hoffmann gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area), 877e3500d1fSGerd Hoffmann &ww, &wh); 878c6158483SAnthony Liguori 879c6158483SAnthony Liguori mx = my = 0; 880c6158483SAnthony Liguori if (ww > fbw) { 881c6158483SAnthony Liguori mx = (ww - fbw) / 2; 882c6158483SAnthony Liguori } 883c6158483SAnthony Liguori if (wh > fbh) { 884c6158483SAnthony Liguori my = (wh - fbh) / 2; 885c6158483SAnthony Liguori } 886c6158483SAnthony Liguori 887e3500d1fSGerd Hoffmann x = (motion->x - mx) / vc->gfx.scale_x; 888e3500d1fSGerd Hoffmann y = (motion->y - my) / vc->gfx.scale_y; 889c6158483SAnthony Liguori 890e61031cdSTakashi Iwai if (qemu_input_is_absolute()) { 891c6158483SAnthony Liguori if (x < 0 || y < 0 || 892e3500d1fSGerd Hoffmann x >= surface_width(vc->gfx.ds) || 893e3500d1fSGerd Hoffmann y >= surface_height(vc->gfx.ds)) { 894c6158483SAnthony Liguori return TRUE; 895c6158483SAnthony Liguori } 896e3500d1fSGerd Hoffmann qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x, 897e3500d1fSGerd Hoffmann surface_width(vc->gfx.ds)); 898e3500d1fSGerd Hoffmann qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y, 899e3500d1fSGerd Hoffmann surface_height(vc->gfx.ds)); 900192f81bfSGerd Hoffmann qemu_input_event_sync(); 9012884cf5bSGerd Hoffmann } else if (s->last_set && s->ptr_owner == vc) { 902e3500d1fSGerd Hoffmann qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x); 903e3500d1fSGerd Hoffmann qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y); 904192f81bfSGerd Hoffmann qemu_input_event_sync(); 905a4ccabcfSAnthony Liguori } 906a4ccabcfSAnthony Liguori s->last_x = x; 907a4ccabcfSAnthony Liguori s->last_y = y; 908e61031cdSTakashi Iwai s->last_set = TRUE; 909a4ccabcfSAnthony Liguori 9102884cf5bSGerd Hoffmann if (!qemu_input_is_absolute() && s->ptr_owner == vc) { 911e3500d1fSGerd Hoffmann GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area); 9125104a1f6SAnthony Liguori int x = (int)motion->x_root; 9135104a1f6SAnthony Liguori int y = (int)motion->y_root; 9145104a1f6SAnthony Liguori 9155104a1f6SAnthony Liguori /* In relative mode check to see if client pointer hit 9165104a1f6SAnthony Liguori * one of the screen edges, and if so move it back by 9175104a1f6SAnthony Liguori * 200 pixels. This is important because the pointer 9185104a1f6SAnthony Liguori * in the server doesn't correspond 1-for-1, and so 9195104a1f6SAnthony Liguori * may still be only half way across the screen. Without 9205104a1f6SAnthony Liguori * this warp, the server pointer would thus appear to hit 9215104a1f6SAnthony Liguori * an invisible wall */ 9225104a1f6SAnthony Liguori if (x == 0) { 9235104a1f6SAnthony Liguori x += 200; 9245104a1f6SAnthony Liguori } 9255104a1f6SAnthony Liguori if (y == 0) { 9265104a1f6SAnthony Liguori y += 200; 9275104a1f6SAnthony Liguori } 9285104a1f6SAnthony Liguori if (x == (gdk_screen_get_width(screen) - 1)) { 9295104a1f6SAnthony Liguori x -= 200; 9305104a1f6SAnthony Liguori } 9315104a1f6SAnthony Liguori if (y == (gdk_screen_get_height(screen) - 1)) { 9325104a1f6SAnthony Liguori y -= 200; 9335104a1f6SAnthony Liguori } 9345104a1f6SAnthony Liguori 9355104a1f6SAnthony Liguori if (x != (int)motion->x_root || y != (int)motion->y_root) { 9368906de76SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 0, 0) 9378906de76SDaniel P. Berrange GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion); 9388906de76SDaniel P. Berrange gdk_device_warp(dev, screen, x, y); 9398906de76SDaniel P. Berrange #else 9408906de76SDaniel P. Berrange GdkDisplay *display = gtk_widget_get_display(widget); 9415104a1f6SAnthony Liguori gdk_display_warp_pointer(display, screen, x, y); 9428906de76SDaniel P. Berrange #endif 943e61031cdSTakashi Iwai s->last_set = FALSE; 9445104a1f6SAnthony Liguori return FALSE; 9455104a1f6SAnthony Liguori } 9465104a1f6SAnthony Liguori } 947a4ccabcfSAnthony Liguori return TRUE; 948a4ccabcfSAnthony Liguori } 949a4ccabcfSAnthony Liguori 950a4ccabcfSAnthony Liguori static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, 951a4ccabcfSAnthony Liguori void *opaque) 952a4ccabcfSAnthony Liguori { 953e3500d1fSGerd Hoffmann VirtualConsole *vc = opaque; 954e3500d1fSGerd Hoffmann GtkDisplayState *s = vc->s; 955192f81bfSGerd Hoffmann InputButton btn; 956a4ccabcfSAnthony Liguori 957800b0e81STakashi Iwai /* implicitly grab the input at the first click in the relative mode */ 958800b0e81STakashi Iwai if (button->button == 1 && button->type == GDK_BUTTON_PRESS && 9592884cf5bSGerd Hoffmann !qemu_input_is_absolute() && s->ptr_owner != vc) { 9602884cf5bSGerd Hoffmann if (!vc->window) { 961800b0e81STakashi Iwai gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 962800b0e81STakashi Iwai TRUE); 9632884cf5bSGerd Hoffmann } else { 964d531deefSGerd Hoffmann gd_grab_pointer(vc, "relative-mode-click"); 9652884cf5bSGerd Hoffmann } 966800b0e81STakashi Iwai return TRUE; 967800b0e81STakashi Iwai } 968800b0e81STakashi Iwai 969a4ccabcfSAnthony Liguori if (button->button == 1) { 970192f81bfSGerd Hoffmann btn = INPUT_BUTTON_LEFT; 971a4ccabcfSAnthony Liguori } else if (button->button == 2) { 972192f81bfSGerd Hoffmann btn = INPUT_BUTTON_MIDDLE; 973a4ccabcfSAnthony Liguori } else if (button->button == 3) { 974192f81bfSGerd Hoffmann btn = INPUT_BUTTON_RIGHT; 975a4ccabcfSAnthony Liguori } else { 976192f81bfSGerd Hoffmann return TRUE; 977a4ccabcfSAnthony Liguori } 978a4ccabcfSAnthony Liguori 979e3500d1fSGerd Hoffmann qemu_input_queue_btn(vc->gfx.dcl.con, btn, 980e3500d1fSGerd Hoffmann button->type == GDK_BUTTON_PRESS); 981192f81bfSGerd Hoffmann qemu_input_event_sync(); 982a4ccabcfSAnthony Liguori return TRUE; 983a4ccabcfSAnthony Liguori } 984a4ccabcfSAnthony Liguori 985d58b9122SJan Kiszka static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll, 986d58b9122SJan Kiszka void *opaque) 987d58b9122SJan Kiszka { 988e3500d1fSGerd Hoffmann VirtualConsole *vc = opaque; 989d58b9122SJan Kiszka InputButton btn; 990d58b9122SJan Kiszka 991d58b9122SJan Kiszka if (scroll->direction == GDK_SCROLL_UP) { 992f22d0af0SGerd Hoffmann btn = INPUT_BUTTON_WHEEL_UP; 993d58b9122SJan Kiszka } else if (scroll->direction == GDK_SCROLL_DOWN) { 994f22d0af0SGerd Hoffmann btn = INPUT_BUTTON_WHEEL_DOWN; 995d58b9122SJan Kiszka } else { 996d58b9122SJan Kiszka return TRUE; 997d58b9122SJan Kiszka } 998d58b9122SJan Kiszka 999e3500d1fSGerd Hoffmann qemu_input_queue_btn(vc->gfx.dcl.con, btn, true); 1000d58b9122SJan Kiszka qemu_input_event_sync(); 1001e3500d1fSGerd Hoffmann qemu_input_queue_btn(vc->gfx.dcl.con, btn, false); 1002d58b9122SJan Kiszka qemu_input_event_sync(); 1003d58b9122SJan Kiszka return TRUE; 1004d58b9122SJan Kiszka } 1005d58b9122SJan Kiszka 10060a337ed0SGerd Hoffmann static int gd_map_keycode(GtkDisplayState *s, GdkDisplay *dpy, int gdk_keycode) 1007a4ccabcfSAnthony Liguori { 1008932f2d7eSGerd Hoffmann int qemu_keycode; 1009a4ccabcfSAnthony Liguori 10100a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_WIN32 10110a337ed0SGerd Hoffmann if (GDK_IS_WIN32_DISPLAY(dpy)) { 1012932f2d7eSGerd Hoffmann qemu_keycode = MapVirtualKey(gdk_keycode, MAPVK_VK_TO_VSC); 10132777ccc5SStefan Weil switch (qemu_keycode) { 10142777ccc5SStefan Weil case 103: /* alt gr */ 10152777ccc5SStefan Weil qemu_keycode = 56 | SCANCODE_GREY; 10162777ccc5SStefan Weil break; 10172777ccc5SStefan Weil } 10180a337ed0SGerd Hoffmann return qemu_keycode; 10190a337ed0SGerd Hoffmann } 10200a337ed0SGerd Hoffmann #endif 1021a4ccabcfSAnthony Liguori 1022a4ccabcfSAnthony Liguori if (gdk_keycode < 9) { 1023a4ccabcfSAnthony Liguori qemu_keycode = 0; 1024a4ccabcfSAnthony Liguori } else if (gdk_keycode < 97) { 1025a4ccabcfSAnthony Liguori qemu_keycode = gdk_keycode - 8; 10260a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_X11 10270a337ed0SGerd Hoffmann } else if (GDK_IS_X11_DISPLAY(dpy) && gdk_keycode < 158) { 10283158a348SBruce Rogers if (s->has_evdev) { 1029a4ccabcfSAnthony Liguori qemu_keycode = translate_evdev_keycode(gdk_keycode - 97); 10303158a348SBruce Rogers } else { 10313158a348SBruce Rogers qemu_keycode = translate_xfree86_keycode(gdk_keycode - 97); 10323158a348SBruce Rogers } 10330a337ed0SGerd Hoffmann #endif 1034a4ccabcfSAnthony Liguori } else if (gdk_keycode == 208) { /* Hiragana_Katakana */ 1035a4ccabcfSAnthony Liguori qemu_keycode = 0x70; 1036a4ccabcfSAnthony Liguori } else if (gdk_keycode == 211) { /* backslash */ 1037a4ccabcfSAnthony Liguori qemu_keycode = 0x73; 1038a4ccabcfSAnthony Liguori } else { 1039a4ccabcfSAnthony Liguori qemu_keycode = 0; 1040a4ccabcfSAnthony Liguori } 1041a4ccabcfSAnthony Liguori 1042932f2d7eSGerd Hoffmann return qemu_keycode; 1043932f2d7eSGerd Hoffmann } 1044932f2d7eSGerd Hoffmann 1045f8c223f6SGerd Hoffmann static gboolean gd_text_key_down(GtkWidget *widget, 1046f8c223f6SGerd Hoffmann GdkEventKey *key, void *opaque) 1047f8c223f6SGerd Hoffmann { 1048f8c223f6SGerd Hoffmann VirtualConsole *vc = opaque; 1049f8c223f6SGerd Hoffmann QemuConsole *con = vc->gfx.dcl.con; 1050f8c223f6SGerd Hoffmann 1051f8c223f6SGerd Hoffmann if (key->length) { 1052f8c223f6SGerd Hoffmann kbd_put_string_console(con, key->string, key->length); 1053f8c223f6SGerd Hoffmann } else { 1054f8c223f6SGerd Hoffmann int num = gd_map_keycode(vc->s, gtk_widget_get_display(widget), 1055f8c223f6SGerd Hoffmann key->hardware_keycode); 1056f8c223f6SGerd Hoffmann int qcode = qemu_input_key_number_to_qcode(num); 1057f8c223f6SGerd Hoffmann kbd_put_qcode_console(con, qcode); 1058f8c223f6SGerd Hoffmann } 1059f8c223f6SGerd Hoffmann return TRUE; 1060f8c223f6SGerd Hoffmann } 1061f8c223f6SGerd Hoffmann 1062932f2d7eSGerd Hoffmann static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) 1063932f2d7eSGerd Hoffmann { 1064932f2d7eSGerd Hoffmann VirtualConsole *vc = opaque; 1065932f2d7eSGerd Hoffmann GtkDisplayState *s = vc->s; 1066932f2d7eSGerd Hoffmann int gdk_keycode = key->hardware_keycode; 1067932f2d7eSGerd Hoffmann int qemu_keycode; 1068932f2d7eSGerd Hoffmann int i; 1069932f2d7eSGerd Hoffmann 10701a01716aSJan Kiszka if (s->ignore_keys) { 10711a01716aSJan Kiszka s->ignore_keys = (key->type == GDK_KEY_PRESS); 10721a01716aSJan Kiszka return TRUE; 10731a01716aSJan Kiszka } 10741a01716aSJan Kiszka 10755c960521SMartin Decky if (key->keyval == GDK_KEY_Pause) { 10765c960521SMartin Decky qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE, 10775c960521SMartin Decky key->type == GDK_KEY_PRESS); 10785c960521SMartin Decky return TRUE; 10795c960521SMartin Decky } 10805c960521SMartin Decky 10810a337ed0SGerd Hoffmann qemu_keycode = gd_map_keycode(s, gtk_widget_get_display(widget), 10820a337ed0SGerd Hoffmann gdk_keycode); 1083932f2d7eSGerd Hoffmann 108474444bc1SGerd Hoffmann trace_gd_key_event(vc->label, gdk_keycode, qemu_keycode, 1085a4ccabcfSAnthony Liguori (key->type == GDK_KEY_PRESS) ? "down" : "up"); 1086a4ccabcfSAnthony Liguori 10876db253caSJan Kiszka for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { 10886db253caSJan Kiszka if (qemu_keycode == modifier_keycode[i]) { 10896db253caSJan Kiszka s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS); 10906db253caSJan Kiszka } 10916db253caSJan Kiszka } 10926db253caSJan Kiszka 1093e3500d1fSGerd Hoffmann qemu_input_event_send_key_number(vc->gfx.dcl.con, qemu_keycode, 1094af98ba92SGerd Hoffmann key->type == GDK_KEY_PRESS); 1095a4ccabcfSAnthony Liguori 1096a4ccabcfSAnthony Liguori return TRUE; 1097a4ccabcfSAnthony Liguori } 1098a4ccabcfSAnthony Liguori 10990d0e044dSTakashi Iwai static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque) 11000d0e044dSTakashi Iwai { 11010d0e044dSTakashi Iwai if (event->type == GDK_MOTION_NOTIFY) { 11020d0e044dSTakashi Iwai return gd_motion_event(widget, &event->motion, opaque); 11030d0e044dSTakashi Iwai } 11040d0e044dSTakashi Iwai return FALSE; 11050d0e044dSTakashi Iwai } 11060d0e044dSTakashi Iwai 1107a4ccabcfSAnthony Liguori /** Window Menu Actions **/ 1108a4ccabcfSAnthony Liguori 110930e8f22bSJan Kiszka static void gd_menu_pause(GtkMenuItem *item, void *opaque) 111030e8f22bSJan Kiszka { 111130e8f22bSJan Kiszka GtkDisplayState *s = opaque; 111230e8f22bSJan Kiszka 111330e8f22bSJan Kiszka if (s->external_pause_update) { 111430e8f22bSJan Kiszka return; 111530e8f22bSJan Kiszka } 111630e8f22bSJan Kiszka if (runstate_is_running()) { 111730e8f22bSJan Kiszka qmp_stop(NULL); 111830e8f22bSJan Kiszka } else { 111930e8f22bSJan Kiszka qmp_cont(NULL); 112030e8f22bSJan Kiszka } 112130e8f22bSJan Kiszka } 112230e8f22bSJan Kiszka 112330e8f22bSJan Kiszka static void gd_menu_reset(GtkMenuItem *item, void *opaque) 112430e8f22bSJan Kiszka { 112530e8f22bSJan Kiszka qmp_system_reset(NULL); 112630e8f22bSJan Kiszka } 112730e8f22bSJan Kiszka 112830e8f22bSJan Kiszka static void gd_menu_powerdown(GtkMenuItem *item, void *opaque) 112930e8f22bSJan Kiszka { 113030e8f22bSJan Kiszka qmp_system_powerdown(NULL); 113130e8f22bSJan Kiszka } 113230e8f22bSJan Kiszka 1133a4ccabcfSAnthony Liguori static void gd_menu_quit(GtkMenuItem *item, void *opaque) 1134a4ccabcfSAnthony Liguori { 1135a4ccabcfSAnthony Liguori qmp_quit(NULL); 1136a4ccabcfSAnthony Liguori } 1137a4ccabcfSAnthony Liguori 1138a4ccabcfSAnthony Liguori static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque) 1139a4ccabcfSAnthony Liguori { 1140a4ccabcfSAnthony Liguori GtkDisplayState *s = opaque; 1141e3500d1fSGerd Hoffmann VirtualConsole *vc = gd_vc_find_by_menu(s); 1142e72b59faSJohn Snow GtkNotebook *nb = GTK_NOTEBOOK(s->notebook); 1143832189c9SGerd Hoffmann gint page; 1144a4ccabcfSAnthony Liguori 11456db253caSJan Kiszka gtk_release_modifiers(s); 1146271a25c0SGerd Hoffmann if (vc) { 1147e72b59faSJohn Snow page = gtk_notebook_page_num(nb, vc->tab_item); 1148e72b59faSJohn Snow gtk_notebook_set_current_page(nb, page); 11499d677e1cSJan Kiszka gtk_widget_grab_focus(vc->focus); 1150d861def3SAnthony Liguori } 11511a01716aSJan Kiszka s->ignore_keys = false; 1152d861def3SAnthony Liguori } 1153a4ccabcfSAnthony Liguori 1154277836c8SCole Robinson static void gd_accel_switch_vc(void *opaque) 1155277836c8SCole Robinson { 1156277836c8SCole Robinson VirtualConsole *vc = opaque; 11571a01716aSJan Kiszka 1158277836c8SCole Robinson gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); 11591a01716aSJan Kiszka #if !GTK_CHECK_VERSION(3, 0, 0) 11601a01716aSJan Kiszka /* GTK2 sends the accel key to the target console - ignore this until */ 11611a01716aSJan Kiszka vc->s->ignore_keys = true; 11621a01716aSJan Kiszka #endif 1163277836c8SCole Robinson } 1164277836c8SCole Robinson 1165a4ccabcfSAnthony Liguori static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque) 1166a4ccabcfSAnthony Liguori { 1167a4ccabcfSAnthony Liguori GtkDisplayState *s = opaque; 1168fa7a1e52SGerd Hoffmann VirtualConsole *vc = gd_vc_find_current(s); 1169a4ccabcfSAnthony Liguori 1170a4ccabcfSAnthony Liguori if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) { 1171a4ccabcfSAnthony Liguori gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE); 1172a4ccabcfSAnthony Liguori } else { 1173a4ccabcfSAnthony Liguori gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 1174a4ccabcfSAnthony Liguori } 1175fa7a1e52SGerd Hoffmann gd_update_windowsize(vc); 1176a4ccabcfSAnthony Liguori } 1177a4ccabcfSAnthony Liguori 1178cdeb7090SGerd Hoffmann static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event, 1179cdeb7090SGerd Hoffmann void *opaque) 1180cdeb7090SGerd Hoffmann { 1181cdeb7090SGerd Hoffmann VirtualConsole *vc = opaque; 1182cdeb7090SGerd Hoffmann GtkDisplayState *s = vc->s; 1183cdeb7090SGerd Hoffmann 1184cdeb7090SGerd Hoffmann gtk_widget_set_sensitive(vc->menu_item, true); 1185316cb068SGerd Hoffmann gd_widget_reparent(vc->window, s->notebook, vc->tab_item); 1186cdeb7090SGerd Hoffmann gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook), 1187cdeb7090SGerd Hoffmann vc->tab_item, vc->label); 1188cdeb7090SGerd Hoffmann gtk_widget_destroy(vc->window); 1189cdeb7090SGerd Hoffmann vc->window = NULL; 1190cdeb7090SGerd Hoffmann return TRUE; 1191cdeb7090SGerd Hoffmann } 1192cdeb7090SGerd Hoffmann 11930c77a37fSGerd Hoffmann static gboolean gd_win_grab(void *opaque) 11940c77a37fSGerd Hoffmann { 11950c77a37fSGerd Hoffmann VirtualConsole *vc = opaque; 11960c77a37fSGerd Hoffmann 11970c77a37fSGerd Hoffmann fprintf(stderr, "%s: %s\n", __func__, vc->label); 11980c77a37fSGerd Hoffmann if (vc->s->ptr_owner) { 11990c77a37fSGerd Hoffmann gd_ungrab_pointer(vc->s); 12000c77a37fSGerd Hoffmann } else { 1201d531deefSGerd Hoffmann gd_grab_pointer(vc, "user-request-detached-tab"); 12020c77a37fSGerd Hoffmann } 12030c77a37fSGerd Hoffmann return TRUE; 12040c77a37fSGerd Hoffmann } 12050c77a37fSGerd Hoffmann 1206cdeb7090SGerd Hoffmann static void gd_menu_untabify(GtkMenuItem *item, void *opaque) 1207cdeb7090SGerd Hoffmann { 1208cdeb7090SGerd Hoffmann GtkDisplayState *s = opaque; 1209cdeb7090SGerd Hoffmann VirtualConsole *vc = gd_vc_find_current(s); 1210cdeb7090SGerd Hoffmann 1211f8c223f6SGerd Hoffmann if (vc->type == GD_VC_GFX && 1212f8c223f6SGerd Hoffmann qemu_console_is_graphic(vc->gfx.dcl.con)) { 1213aa0a55d4SGerd Hoffmann gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 1214aa0a55d4SGerd Hoffmann FALSE); 1215cdeb7090SGerd Hoffmann } 1216cdeb7090SGerd Hoffmann if (!vc->window) { 1217cdeb7090SGerd Hoffmann gtk_widget_set_sensitive(vc->menu_item, false); 1218cdeb7090SGerd Hoffmann vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1219316cb068SGerd Hoffmann gd_widget_reparent(s->notebook, vc->window, vc->tab_item); 1220cdeb7090SGerd Hoffmann 1221cdeb7090SGerd Hoffmann g_signal_connect(vc->window, "delete-event", 1222cdeb7090SGerd Hoffmann G_CALLBACK(gd_tab_window_close), vc); 1223cdeb7090SGerd Hoffmann gtk_widget_show_all(vc->window); 12244eeaa3a8SGerd Hoffmann 1225f8c223f6SGerd Hoffmann if (qemu_console_is_graphic(vc->gfx.dcl.con)) { 12260c77a37fSGerd Hoffmann GtkAccelGroup *ag = gtk_accel_group_new(); 12270c77a37fSGerd Hoffmann gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag); 12280c77a37fSGerd Hoffmann 1229f8c223f6SGerd Hoffmann GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab), 1230f8c223f6SGerd Hoffmann vc, NULL); 12310c77a37fSGerd Hoffmann gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb); 1232f8c223f6SGerd Hoffmann } 12330c77a37fSGerd Hoffmann 123482fc1809SGerd Hoffmann gd_update_geometry_hints(vc); 12354eeaa3a8SGerd Hoffmann gd_update_caption(s); 1236cdeb7090SGerd Hoffmann } 1237cdeb7090SGerd Hoffmann } 1238cdeb7090SGerd Hoffmann 1239c6158483SAnthony Liguori static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) 1240c6158483SAnthony Liguori { 1241c6158483SAnthony Liguori GtkDisplayState *s = opaque; 1242e3500d1fSGerd Hoffmann VirtualConsole *vc = gd_vc_find_current(s); 1243c6158483SAnthony Liguori 124410409282SStefan Weil if (!s->full_screen) { 1245c6158483SAnthony Liguori gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 1246b0f31820SCole Robinson gtk_widget_hide(s->menu_bar); 1247e3500d1fSGerd Hoffmann if (vc->type == GD_VC_GFX) { 1248e3500d1fSGerd Hoffmann gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1); 1249c6158483SAnthony Liguori } 1250e3500d1fSGerd Hoffmann gtk_window_fullscreen(GTK_WINDOW(s->window)); 1251c6158483SAnthony Liguori s->full_screen = TRUE; 1252c6158483SAnthony Liguori } else { 1253c6158483SAnthony Liguori gtk_window_unfullscreen(GTK_WINDOW(s->window)); 1254c6158483SAnthony Liguori gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s); 1255b0f31820SCole Robinson gtk_widget_show(s->menu_bar); 1256c6158483SAnthony Liguori s->full_screen = FALSE; 1257e3500d1fSGerd Hoffmann if (vc->type == GD_VC_GFX) { 1258e3500d1fSGerd Hoffmann vc->gfx.scale_x = 1.0; 1259e3500d1fSGerd Hoffmann vc->gfx.scale_y = 1.0; 126082fc1809SGerd Hoffmann gd_update_windowsize(vc); 1261e3500d1fSGerd Hoffmann } 1262c6158483SAnthony Liguori } 1263c6158483SAnthony Liguori 1264e3500d1fSGerd Hoffmann gd_update_cursor(vc); 1265c6158483SAnthony Liguori } 1266c6158483SAnthony Liguori 126795414914SCole Robinson static void gd_accel_full_screen(void *opaque) 126895414914SCole Robinson { 126995414914SCole Robinson GtkDisplayState *s = opaque; 127095414914SCole Robinson gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item)); 127195414914SCole Robinson } 127295414914SCole Robinson 1273c6158483SAnthony Liguori static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque) 1274c6158483SAnthony Liguori { 1275c6158483SAnthony Liguori GtkDisplayState *s = opaque; 1276e3500d1fSGerd Hoffmann VirtualConsole *vc = gd_vc_find_current(s); 1277c6158483SAnthony Liguori 1278c6158483SAnthony Liguori gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), 1279c6158483SAnthony Liguori FALSE); 1280c6158483SAnthony Liguori 128182fc1809SGerd Hoffmann vc->gfx.scale_x += VC_SCALE_STEP; 128282fc1809SGerd Hoffmann vc->gfx.scale_y += VC_SCALE_STEP; 1283c6158483SAnthony Liguori 1284e3500d1fSGerd Hoffmann gd_update_windowsize(vc); 1285c6158483SAnthony Liguori } 1286c6158483SAnthony Liguori 1287c6158483SAnthony Liguori static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque) 1288c6158483SAnthony Liguori { 1289c6158483SAnthony Liguori GtkDisplayState *s = opaque; 1290e3500d1fSGerd Hoffmann VirtualConsole *vc = gd_vc_find_current(s); 1291c6158483SAnthony Liguori 1292c6158483SAnthony Liguori gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), 1293c6158483SAnthony Liguori FALSE); 1294c6158483SAnthony Liguori 129582fc1809SGerd Hoffmann vc->gfx.scale_x -= VC_SCALE_STEP; 129682fc1809SGerd Hoffmann vc->gfx.scale_y -= VC_SCALE_STEP; 1297c6158483SAnthony Liguori 129882fc1809SGerd Hoffmann vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN); 129982fc1809SGerd Hoffmann vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN); 1300c6158483SAnthony Liguori 1301e3500d1fSGerd Hoffmann gd_update_windowsize(vc); 1302c6158483SAnthony Liguori } 1303c6158483SAnthony Liguori 1304c6158483SAnthony Liguori static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque) 1305c6158483SAnthony Liguori { 1306c6158483SAnthony Liguori GtkDisplayState *s = opaque; 1307e3500d1fSGerd Hoffmann VirtualConsole *vc = gd_vc_find_current(s); 1308c6158483SAnthony Liguori 1309e3500d1fSGerd Hoffmann vc->gfx.scale_x = 1.0; 1310e3500d1fSGerd Hoffmann vc->gfx.scale_y = 1.0; 1311c6158483SAnthony Liguori 1312e3500d1fSGerd Hoffmann gd_update_windowsize(vc); 1313c6158483SAnthony Liguori } 1314c6158483SAnthony Liguori 1315c6158483SAnthony Liguori static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque) 1316c6158483SAnthony Liguori { 1317c6158483SAnthony Liguori GtkDisplayState *s = opaque; 1318e3500d1fSGerd Hoffmann VirtualConsole *vc = gd_vc_find_current(s); 1319c6158483SAnthony Liguori 1320c6158483SAnthony Liguori if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) { 1321c6158483SAnthony Liguori s->free_scale = TRUE; 1322c6158483SAnthony Liguori } else { 1323c6158483SAnthony Liguori s->free_scale = FALSE; 1324e3500d1fSGerd Hoffmann vc->gfx.scale_x = 1.0; 1325e3500d1fSGerd Hoffmann vc->gfx.scale_y = 1.0; 1326c6158483SAnthony Liguori } 1327c6158483SAnthony Liguori 132882fc1809SGerd Hoffmann gd_update_windowsize(vc); 1329e3500d1fSGerd Hoffmann gd_update_full_redraw(vc); 1330c6158483SAnthony Liguori } 1331c6158483SAnthony Liguori 1332*a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0) 1333*a69fc693SGerd Hoffmann static void gd_grab_update(VirtualConsole *vc, bool kbd, bool ptr) 1334*a69fc693SGerd Hoffmann { 1335*a69fc693SGerd Hoffmann GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); 1336*a69fc693SGerd Hoffmann GdkSeat *seat = gdk_display_get_default_seat(display); 1337*a69fc693SGerd Hoffmann GdkWindow *window = gtk_widget_get_window(vc->gfx.drawing_area); 1338*a69fc693SGerd Hoffmann GdkSeatCapabilities caps = 0; 1339*a69fc693SGerd Hoffmann GdkCursor *cursor = NULL; 1340*a69fc693SGerd Hoffmann 1341*a69fc693SGerd Hoffmann if (kbd) { 1342*a69fc693SGerd Hoffmann caps |= GDK_SEAT_CAPABILITY_KEYBOARD; 1343*a69fc693SGerd Hoffmann } 1344*a69fc693SGerd Hoffmann if (ptr) { 1345*a69fc693SGerd Hoffmann caps |= GDK_SEAT_CAPABILITY_ALL_POINTING; 1346*a69fc693SGerd Hoffmann cursor = vc->s->null_cursor; 1347*a69fc693SGerd Hoffmann } 1348*a69fc693SGerd Hoffmann 1349*a69fc693SGerd Hoffmann if (caps) { 1350*a69fc693SGerd Hoffmann gdk_seat_grab(seat, window, caps, false, cursor, 1351*a69fc693SGerd Hoffmann NULL, NULL, NULL); 1352*a69fc693SGerd Hoffmann } else { 1353*a69fc693SGerd Hoffmann gdk_seat_ungrab(seat); 1354*a69fc693SGerd Hoffmann } 1355*a69fc693SGerd Hoffmann } 1356*a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0) 1357f50def91SGerd Hoffmann static void gd_grab_devices(VirtualConsole *vc, bool grab, 1358f50def91SGerd Hoffmann GdkInputSource source, GdkEventMask mask, 1359f50def91SGerd Hoffmann GdkCursor *cursor) 1360f50def91SGerd Hoffmann { 1361f50def91SGerd Hoffmann GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); 1362f50def91SGerd Hoffmann GdkDeviceManager *mgr = gdk_display_get_device_manager(display); 1363f50def91SGerd Hoffmann GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER); 1364f50def91SGerd Hoffmann GList *tmp = devs; 1365f50def91SGerd Hoffmann 1366f50def91SGerd Hoffmann for (tmp = devs; tmp; tmp = tmp->next) { 1367f50def91SGerd Hoffmann GdkDevice *dev = tmp->data; 1368f50def91SGerd Hoffmann if (gdk_device_get_source(dev) != source) { 1369f50def91SGerd Hoffmann continue; 1370f50def91SGerd Hoffmann } 1371f50def91SGerd Hoffmann if (grab) { 1372f50def91SGerd Hoffmann GdkWindow *win = gtk_widget_get_window(vc->gfx.drawing_area); 1373f50def91SGerd Hoffmann gdk_device_grab(dev, win, GDK_OWNERSHIP_NONE, FALSE, 1374f50def91SGerd Hoffmann mask, cursor, GDK_CURRENT_TIME); 1375f50def91SGerd Hoffmann } else { 1376f50def91SGerd Hoffmann gdk_device_ungrab(dev, GDK_CURRENT_TIME); 1377f50def91SGerd Hoffmann } 1378f50def91SGerd Hoffmann } 1379f50def91SGerd Hoffmann g_list_free(devs); 1380f50def91SGerd Hoffmann } 1381f50def91SGerd Hoffmann #endif 1382f50def91SGerd Hoffmann 1383d531deefSGerd Hoffmann static void gd_grab_keyboard(VirtualConsole *vc, const char *reason) 13845104a1f6SAnthony Liguori { 1385aa4f4058SGerd Hoffmann if (vc->s->kbd_owner) { 1386aa4f4058SGerd Hoffmann if (vc->s->kbd_owner == vc) { 1387aa4f4058SGerd Hoffmann return; 1388aa4f4058SGerd Hoffmann } else { 1389aa4f4058SGerd Hoffmann gd_ungrab_keyboard(vc->s); 1390aa4f4058SGerd Hoffmann } 1391aa4f4058SGerd Hoffmann } 1392aa4f4058SGerd Hoffmann 1393*a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0) 1394*a69fc693SGerd Hoffmann gd_grab_update(vc, true, vc->s->ptr_owner == vc); 1395*a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0) 1396f50def91SGerd Hoffmann gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD, 1397655199daSDaniel P. Berrange GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, 1398f50def91SGerd Hoffmann NULL); 1399655199daSDaniel P. Berrange #else 1400e3500d1fSGerd Hoffmann gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area), 14015104a1f6SAnthony Liguori FALSE, 14025104a1f6SAnthony Liguori GDK_CURRENT_TIME); 1403655199daSDaniel P. Berrange #endif 14044c638e2eSGerd Hoffmann vc->s->kbd_owner = vc; 1405695cc59dSGerd Hoffmann gd_update_caption(vc->s); 1406d531deefSGerd Hoffmann trace_gd_grab(vc->label, "kbd", reason); 14075104a1f6SAnthony Liguori } 14085104a1f6SAnthony Liguori 14094c638e2eSGerd Hoffmann static void gd_ungrab_keyboard(GtkDisplayState *s) 14105104a1f6SAnthony Liguori { 14114c638e2eSGerd Hoffmann VirtualConsole *vc = s->kbd_owner; 14124c638e2eSGerd Hoffmann 14134c638e2eSGerd Hoffmann if (vc == NULL) { 14144c638e2eSGerd Hoffmann return; 14154c638e2eSGerd Hoffmann } 14164c638e2eSGerd Hoffmann s->kbd_owner = NULL; 14174c638e2eSGerd Hoffmann 1418*a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0) 1419*a69fc693SGerd Hoffmann gd_grab_update(vc, false, vc->s->ptr_owner == vc); 1420*a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0) 1421f50def91SGerd Hoffmann gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL); 1422655199daSDaniel P. Berrange #else 14235104a1f6SAnthony Liguori gdk_keyboard_ungrab(GDK_CURRENT_TIME); 1424655199daSDaniel P. Berrange #endif 1425695cc59dSGerd Hoffmann gd_update_caption(s); 1426d531deefSGerd Hoffmann trace_gd_ungrab(vc->label, "kbd"); 14275104a1f6SAnthony Liguori } 14285104a1f6SAnthony Liguori 1429d531deefSGerd Hoffmann static void gd_grab_pointer(VirtualConsole *vc, const char *reason) 14305104a1f6SAnthony Liguori { 1431e3500d1fSGerd Hoffmann GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); 1432aa4f4058SGerd Hoffmann 1433aa4f4058SGerd Hoffmann if (vc->s->ptr_owner) { 1434aa4f4058SGerd Hoffmann if (vc->s->ptr_owner == vc) { 1435aa4f4058SGerd Hoffmann return; 1436aa4f4058SGerd Hoffmann } else { 1437aa4f4058SGerd Hoffmann gd_ungrab_pointer(vc->s); 1438aa4f4058SGerd Hoffmann } 1439aa4f4058SGerd Hoffmann } 1440aa4f4058SGerd Hoffmann 1441*a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0) 1442*a69fc693SGerd Hoffmann gd_grab_update(vc, vc->s->kbd_owner == vc, true); 1443*a69fc693SGerd Hoffmann gdk_device_get_position(gd_get_pointer(display), 1444*a69fc693SGerd Hoffmann NULL, &vc->s->grab_x_root, &vc->s->grab_y_root); 1445*a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0) 1446f50def91SGerd Hoffmann gd_grab_devices(vc, true, GDK_SOURCE_MOUSE, 14472a05485dSDaniel P. Berrange GDK_POINTER_MOTION_MASK | 14482a05485dSDaniel P. Berrange GDK_BUTTON_PRESS_MASK | 14492a05485dSDaniel P. Berrange GDK_BUTTON_RELEASE_MASK | 14502a05485dSDaniel P. Berrange GDK_BUTTON_MOTION_MASK | 14512a05485dSDaniel P. Berrange GDK_SCROLL_MASK, 1452f50def91SGerd Hoffmann vc->s->null_cursor); 1453bb732ee7SCole Robinson gdk_device_get_position(gd_get_pointer(display), 1454e3500d1fSGerd Hoffmann NULL, &vc->s->grab_x_root, &vc->s->grab_y_root); 14552a05485dSDaniel P. Berrange #else 1456e3500d1fSGerd Hoffmann gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area), 14575104a1f6SAnthony Liguori FALSE, /* All events to come to our window directly */ 14585104a1f6SAnthony Liguori GDK_POINTER_MOTION_MASK | 14595104a1f6SAnthony Liguori GDK_BUTTON_PRESS_MASK | 14605104a1f6SAnthony Liguori GDK_BUTTON_RELEASE_MASK | 14615104a1f6SAnthony Liguori GDK_BUTTON_MOTION_MASK | 14625104a1f6SAnthony Liguori GDK_SCROLL_MASK, 14635104a1f6SAnthony Liguori NULL, /* Allow cursor to move over entire desktop */ 1464e3500d1fSGerd Hoffmann vc->s->null_cursor, 14655104a1f6SAnthony Liguori GDK_CURRENT_TIME); 1466ecce1929STakashi Iwai gdk_display_get_pointer(display, NULL, 1467e3500d1fSGerd Hoffmann &vc->s->grab_x_root, &vc->s->grab_y_root, NULL); 14682a05485dSDaniel P. Berrange #endif 14694c638e2eSGerd Hoffmann vc->s->ptr_owner = vc; 1470695cc59dSGerd Hoffmann gd_update_caption(vc->s); 1471d531deefSGerd Hoffmann trace_gd_grab(vc->label, "ptr", reason); 14722a05485dSDaniel P. Berrange } 14732a05485dSDaniel P. Berrange 14744c638e2eSGerd Hoffmann static void gd_ungrab_pointer(GtkDisplayState *s) 14752a05485dSDaniel P. Berrange { 14764c638e2eSGerd Hoffmann VirtualConsole *vc = s->ptr_owner; 1477*a69fc693SGerd Hoffmann GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); 14784c638e2eSGerd Hoffmann 14794c638e2eSGerd Hoffmann if (vc == NULL) { 14804c638e2eSGerd Hoffmann return; 14814c638e2eSGerd Hoffmann } 14824c638e2eSGerd Hoffmann s->ptr_owner = NULL; 14834c638e2eSGerd Hoffmann 1484*a69fc693SGerd Hoffmann #if GTK_CHECK_VERSION(3, 20, 0) 1485*a69fc693SGerd Hoffmann gd_grab_update(vc, vc->s->kbd_owner == vc, false); 1486*a69fc693SGerd Hoffmann gdk_device_warp(gd_get_pointer(display), 1487*a69fc693SGerd Hoffmann gtk_widget_get_screen(vc->gfx.drawing_area), 1488*a69fc693SGerd Hoffmann vc->s->grab_x_root, vc->s->grab_y_root); 1489*a69fc693SGerd Hoffmann #elif GTK_CHECK_VERSION(3, 0, 0) 1490f50def91SGerd Hoffmann gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL); 1491bb732ee7SCole Robinson gdk_device_warp(gd_get_pointer(display), 1492e3500d1fSGerd Hoffmann gtk_widget_get_screen(vc->gfx.drawing_area), 1493e3500d1fSGerd Hoffmann vc->s->grab_x_root, vc->s->grab_y_root); 14942a05485dSDaniel P. Berrange #else 14952a05485dSDaniel P. Berrange gdk_pointer_ungrab(GDK_CURRENT_TIME); 1496ecce1929STakashi Iwai gdk_display_warp_pointer(display, 1497e3500d1fSGerd Hoffmann gtk_widget_get_screen(vc->gfx.drawing_area), 1498e3500d1fSGerd Hoffmann vc->s->grab_x_root, vc->s->grab_y_root); 14992a05485dSDaniel P. Berrange #endif 1500695cc59dSGerd Hoffmann gd_update_caption(s); 1501d531deefSGerd Hoffmann trace_gd_ungrab(vc->label, "ptr"); 15022a05485dSDaniel P. Berrange } 15032a05485dSDaniel P. Berrange 15042a05485dSDaniel P. Berrange static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) 15052a05485dSDaniel P. Berrange { 15062a05485dSDaniel P. Berrange GtkDisplayState *s = opaque; 1507e3500d1fSGerd Hoffmann VirtualConsole *vc = gd_vc_find_current(s); 15082a05485dSDaniel P. Berrange 15092a05485dSDaniel P. Berrange if (gd_is_grab_active(s)) { 1510d531deefSGerd Hoffmann gd_grab_keyboard(vc, "user-request-main-window"); 1511d531deefSGerd Hoffmann gd_grab_pointer(vc, "user-request-main-window"); 15125104a1f6SAnthony Liguori } else { 15134c638e2eSGerd Hoffmann gd_ungrab_keyboard(s); 15144c638e2eSGerd Hoffmann gd_ungrab_pointer(s); 15155104a1f6SAnthony Liguori } 15165104a1f6SAnthony Liguori 1517e3500d1fSGerd Hoffmann gd_update_cursor(vc); 15185104a1f6SAnthony Liguori } 15195104a1f6SAnthony Liguori 1520a4ccabcfSAnthony Liguori static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, 1521a4ccabcfSAnthony Liguori gpointer data) 1522a4ccabcfSAnthony Liguori { 1523a4ccabcfSAnthony Liguori GtkDisplayState *s = data; 1524e3500d1fSGerd Hoffmann VirtualConsole *vc; 15255104a1f6SAnthony Liguori gboolean on_vga; 1526a4ccabcfSAnthony Liguori 1527a4ccabcfSAnthony Liguori if (!gtk_widget_get_realized(s->notebook)) { 1528a4ccabcfSAnthony Liguori return; 1529a4ccabcfSAnthony Liguori } 1530a4ccabcfSAnthony Liguori 15316fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK 15326fa27697SGerd Hoffmann vc = gd_vc_find_current(s); 15336fa27697SGerd Hoffmann if (vc && vc->type == GD_VC_VTE) { 15346fa27697SGerd Hoffmann gtk_widget_hide(vc->vte.terminal); 15356fa27697SGerd Hoffmann } 15366fa27697SGerd Hoffmann #endif 1537e3500d1fSGerd Hoffmann vc = gd_vc_find_by_page(s, arg2); 1538e3500d1fSGerd Hoffmann if (!vc) { 1539e3500d1fSGerd Hoffmann return; 1540e3500d1fSGerd Hoffmann } 15416fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK 15426fa27697SGerd Hoffmann if (vc->type == GD_VC_VTE) { 15436fa27697SGerd Hoffmann gtk_widget_show(vc->vte.terminal); 15446fa27697SGerd Hoffmann } 15456fa27697SGerd Hoffmann #endif 1546e3500d1fSGerd Hoffmann gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), 1547e3500d1fSGerd Hoffmann TRUE); 1548f8c223f6SGerd Hoffmann on_vga = (vc->type == GD_VC_GFX && 1549f8c223f6SGerd Hoffmann qemu_console_is_graphic(vc->gfx.dcl.con)); 15505104a1f6SAnthony Liguori if (!on_vga) { 15515104a1f6SAnthony Liguori gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 15525104a1f6SAnthony Liguori FALSE); 1553c6158483SAnthony Liguori } else if (s->full_screen) { 1554c6158483SAnthony Liguori gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 1555c6158483SAnthony Liguori TRUE); 15565104a1f6SAnthony Liguori } 15575104a1f6SAnthony Liguori gtk_widget_set_sensitive(s->grab_item, on_vga); 15585104a1f6SAnthony Liguori 155982fc1809SGerd Hoffmann gd_update_windowsize(vc); 1560e3500d1fSGerd Hoffmann gd_update_cursor(vc); 1561a4ccabcfSAnthony Liguori } 1562a4ccabcfSAnthony Liguori 1563e3500d1fSGerd Hoffmann static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, 1564e3500d1fSGerd Hoffmann gpointer opaque) 15655104a1f6SAnthony Liguori { 1566e3500d1fSGerd Hoffmann VirtualConsole *vc = opaque; 1567e3500d1fSGerd Hoffmann GtkDisplayState *s = vc->s; 15685104a1f6SAnthony Liguori 15692884cf5bSGerd Hoffmann if (gd_grab_on_hover(s)) { 1570d531deefSGerd Hoffmann gd_grab_keyboard(vc, "grab-on-hover"); 15715104a1f6SAnthony Liguori } 15725104a1f6SAnthony Liguori return TRUE; 15735104a1f6SAnthony Liguori } 15745104a1f6SAnthony Liguori 1575e3500d1fSGerd Hoffmann static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, 1576e3500d1fSGerd Hoffmann gpointer opaque) 15775104a1f6SAnthony Liguori { 1578e3500d1fSGerd Hoffmann VirtualConsole *vc = opaque; 1579e3500d1fSGerd Hoffmann GtkDisplayState *s = vc->s; 15805104a1f6SAnthony Liguori 15812884cf5bSGerd Hoffmann if (gd_grab_on_hover(s)) { 15824c638e2eSGerd Hoffmann gd_ungrab_keyboard(s); 15835104a1f6SAnthony Liguori } 15845104a1f6SAnthony Liguori return TRUE; 15855104a1f6SAnthony Liguori } 15865104a1f6SAnthony Liguori 15876db253caSJan Kiszka static gboolean gd_focus_out_event(GtkWidget *widget, 1588e3500d1fSGerd Hoffmann GdkEventCrossing *crossing, gpointer opaque) 15896db253caSJan Kiszka { 1590e3500d1fSGerd Hoffmann VirtualConsole *vc = opaque; 1591e3500d1fSGerd Hoffmann GtkDisplayState *s = vc->s; 15926db253caSJan Kiszka 15936db253caSJan Kiszka gtk_release_modifiers(s); 15946db253caSJan Kiszka return TRUE; 15956db253caSJan Kiszka } 15966db253caSJan Kiszka 15971301e515SGerd Hoffmann static gboolean gd_configure(GtkWidget *widget, 15981301e515SGerd Hoffmann GdkEventConfigure *cfg, gpointer opaque) 15991301e515SGerd Hoffmann { 16001301e515SGerd Hoffmann VirtualConsole *vc = opaque; 16011301e515SGerd Hoffmann 1602925a0400SGerd Hoffmann gd_set_ui_info(vc, cfg->width, cfg->height); 16031301e515SGerd Hoffmann return FALSE; 16041301e515SGerd Hoffmann } 16051301e515SGerd Hoffmann 1606d861def3SAnthony Liguori /** Virtual Console Callbacks **/ 1607d861def3SAnthony Liguori 1608ed1132e4SGerd Hoffmann static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc, 1609cdeb7090SGerd Hoffmann int idx, GSList *group, GtkWidget *view_menu) 1610ed1132e4SGerd Hoffmann { 1611cdeb7090SGerd Hoffmann vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label); 1612277836c8SCole Robinson gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx, 1613277836c8SCole Robinson HOTKEY_MODIFIERS, 0, 1614277836c8SCole Robinson g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL)); 1615277836c8SCole Robinson #if GTK_CHECK_VERSION(3, 8, 0) 1616277836c8SCole Robinson gtk_accel_label_set_accel( 1617277836c8SCole Robinson GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))), 1618277836c8SCole Robinson GDK_KEY_1 + idx, HOTKEY_MODIFIERS); 1619277836c8SCole Robinson #endif 1620ed1132e4SGerd Hoffmann 1621ed1132e4SGerd Hoffmann g_signal_connect(vc->menu_item, "activate", 1622ed1132e4SGerd Hoffmann G_CALLBACK(gd_menu_switch_vc), s); 1623ed1132e4SGerd Hoffmann gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item); 1624ed1132e4SGerd Hoffmann 1625277836c8SCole Robinson group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); 1626ed1132e4SGerd Hoffmann return group; 1627ed1132e4SGerd Hoffmann } 1628ed1132e4SGerd Hoffmann 1629ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE) 163044b31e0bSMichael S. Tsirkin static void gd_menu_copy(GtkMenuItem *item, void *opaque) 163144b31e0bSMichael S. Tsirkin { 163244b31e0bSMichael S. Tsirkin GtkDisplayState *s = opaque; 163344b31e0bSMichael S. Tsirkin VirtualConsole *vc = gd_vc_find_current(s); 163444b31e0bSMichael S. Tsirkin 163544b31e0bSMichael S. Tsirkin vte_terminal_copy_clipboard(VTE_TERMINAL(vc->vte.terminal)); 163644b31e0bSMichael S. Tsirkin } 163744b31e0bSMichael S. Tsirkin 16380fb20d1cSCole Robinson static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque) 16390fb20d1cSCole Robinson { 16400fb20d1cSCole Robinson VirtualConsole *vc = opaque; 16410fb20d1cSCole Robinson 16420fb20d1cSCole Robinson if (gtk_adjustment_get_upper(adjustment) > 16430fb20d1cSCole Robinson gtk_adjustment_get_page_size(adjustment)) { 1644271a25c0SGerd Hoffmann gtk_widget_show(vc->vte.scrollbar); 16450fb20d1cSCole Robinson } else { 1646271a25c0SGerd Hoffmann gtk_widget_hide(vc->vte.scrollbar); 16470fb20d1cSCole Robinson } 16480fb20d1cSCole Robinson } 16490fb20d1cSCole Robinson 1650d861def3SAnthony Liguori static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len) 1651d861def3SAnthony Liguori { 1652d861def3SAnthony Liguori VirtualConsole *vc = chr->opaque; 1653d861def3SAnthony Liguori 1654271a25c0SGerd Hoffmann vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len); 1655d4370741SCole Robinson return len; 1656d861def3SAnthony Liguori } 1657d861def3SAnthony Liguori 1658fba958c6SPaolo Bonzini static void gd_vc_chr_set_echo(CharDriverState *chr, bool echo) 1659fba958c6SPaolo Bonzini { 1660fba958c6SPaolo Bonzini VirtualConsole *vc = chr->opaque; 1661fba958c6SPaolo Bonzini 1662fba958c6SPaolo Bonzini vc->vte.echo = echo; 1663fba958c6SPaolo Bonzini } 1664fba958c6SPaolo Bonzini 1665d861def3SAnthony Liguori static int nb_vcs; 1666d861def3SAnthony Liguori static CharDriverState *vcs[MAX_VCS]; 1667d861def3SAnthony Liguori 1668919e11f3SDaniel P. Berrange static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp) 1669d861def3SAnthony Liguori { 1670919e11f3SDaniel P. Berrange ChardevCommon *common = qapi_ChardevVC_base(vc); 1671d861def3SAnthony Liguori CharDriverState *chr; 1672d861def3SAnthony Liguori 1673919e11f3SDaniel P. Berrange chr = qemu_chr_alloc(common, errp); 1674919e11f3SDaniel P. Berrange if (!chr) { 1675919e11f3SDaniel P. Berrange return NULL; 1676919e11f3SDaniel P. Berrange } 1677919e11f3SDaniel P. Berrange 1678d861def3SAnthony Liguori chr->chr_write = gd_vc_chr_write; 1679fba958c6SPaolo Bonzini chr->chr_set_echo = gd_vc_chr_set_echo; 1680fba958c6SPaolo Bonzini 1681fba958c6SPaolo Bonzini /* Temporary, until gd_vc_vte_init runs. */ 1682327d83baSPaolo Bonzini chr->opaque = g_new0(VirtualConsole, 1); 1683fba958c6SPaolo Bonzini 1684bd5c51eeSMichael Roth /* defer OPENED events until our vc is fully initialized */ 1685bd5c51eeSMichael Roth chr->explicit_be_open = true; 1686d861def3SAnthony Liguori 1687d861def3SAnthony Liguori vcs[nb_vcs++] = chr; 1688d861def3SAnthony Liguori 1689d861def3SAnthony Liguori return chr; 1690d861def3SAnthony Liguori } 1691d861def3SAnthony Liguori 1692d4370741SCole Robinson static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size, 1693d4370741SCole Robinson gpointer user_data) 1694d861def3SAnthony Liguori { 1695d4370741SCole Robinson VirtualConsole *vc = user_data; 1696d861def3SAnthony Liguori 1697fba958c6SPaolo Bonzini if (vc->vte.echo) { 1698fba958c6SPaolo Bonzini VteTerminal *term = VTE_TERMINAL(vc->vte.terminal); 1699fba958c6SPaolo Bonzini int i; 1700fba958c6SPaolo Bonzini for (i = 0; i < size; i++) { 1701fba958c6SPaolo Bonzini uint8_t c = text[i]; 1702fba958c6SPaolo Bonzini if (c >= 128 || isprint(c)) { 1703fba958c6SPaolo Bonzini /* 8-bit characters are considered printable. */ 1704fba958c6SPaolo Bonzini vte_terminal_feed(term, &text[i], 1); 1705fba958c6SPaolo Bonzini } else if (c == '\r' || c == '\n') { 1706fba958c6SPaolo Bonzini vte_terminal_feed(term, "\r\n", 2); 1707fba958c6SPaolo Bonzini } else { 1708fba958c6SPaolo Bonzini char ctrl[2] = { '^', 0}; 1709fba958c6SPaolo Bonzini ctrl[1] = text[i] ^ 64; 1710fba958c6SPaolo Bonzini vte_terminal_feed(term, ctrl, 2); 1711fba958c6SPaolo Bonzini } 1712fba958c6SPaolo Bonzini } 1713fba958c6SPaolo Bonzini } 1714fba958c6SPaolo Bonzini 1715271a25c0SGerd Hoffmann qemu_chr_be_write(vc->vte.chr, (uint8_t *)text, (unsigned int)size); 1716d861def3SAnthony Liguori return TRUE; 1717d861def3SAnthony Liguori } 1718d861def3SAnthony Liguori 1719ed1132e4SGerd Hoffmann static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, 1720ed1132e4SGerd Hoffmann CharDriverState *chr, int idx, 1721e3500d1fSGerd Hoffmann GSList *group, GtkWidget *view_menu) 1722d861def3SAnthony Liguori { 1723d861def3SAnthony Liguori char buffer[32]; 17240fb20d1cSCole Robinson GtkWidget *box; 17250fb20d1cSCole Robinson GtkWidget *scrollbar; 17260fb20d1cSCole Robinson GtkAdjustment *vadjustment; 1727fba958c6SPaolo Bonzini VirtualConsole *tmp_vc = chr->opaque; 1728d861def3SAnthony Liguori 1729e3500d1fSGerd Hoffmann vc->s = s; 1730fba958c6SPaolo Bonzini vc->vte.echo = tmp_vc->vte.echo; 1731fba958c6SPaolo Bonzini 1732ed1132e4SGerd Hoffmann vc->vte.chr = chr; 1733fba958c6SPaolo Bonzini chr->opaque = vc; 1734fba958c6SPaolo Bonzini g_free(tmp_vc); 1735d861def3SAnthony Liguori 1736ed1132e4SGerd Hoffmann snprintf(buffer, sizeof(buffer), "vc%d", idx); 1737cdeb7090SGerd Hoffmann vc->label = g_strdup_printf("%s", vc->vte.chr->label 1738cdeb7090SGerd Hoffmann ? vc->vte.chr->label : buffer); 1739cdeb7090SGerd Hoffmann group = gd_vc_menu_init(s, vc, idx, group, view_menu); 1740d861def3SAnthony Liguori 1741271a25c0SGerd Hoffmann vc->vte.terminal = vte_terminal_new(); 1742271a25c0SGerd Hoffmann g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc); 1743d861def3SAnthony Liguori 1744fba958c6SPaolo Bonzini /* The documentation says that the default is UTF-8, but actually it is 1745fba958c6SPaolo Bonzini * 7-bit ASCII at least in VTE 0.38. 1746fba958c6SPaolo Bonzini */ 1747fba958c6SPaolo Bonzini #if VTE_CHECK_VERSION(0, 40, 0) 1748fba958c6SPaolo Bonzini vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8", NULL); 1749fba958c6SPaolo Bonzini #else 1750fba958c6SPaolo Bonzini vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8"); 1751fba958c6SPaolo Bonzini #endif 1752fba958c6SPaolo Bonzini 1753271a25c0SGerd Hoffmann vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1); 175482fc1809SGerd Hoffmann vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal), 175582fc1809SGerd Hoffmann VC_TERM_X_MIN, VC_TERM_Y_MIN); 1756d861def3SAnthony Liguori 17570fb20d1cSCole Robinson #if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0) 1758271a25c0SGerd Hoffmann vadjustment = gtk_scrollable_get_vadjustment 1759271a25c0SGerd Hoffmann (GTK_SCROLLABLE(vc->vte.terminal)); 17600fb20d1cSCole Robinson #else 1761271a25c0SGerd Hoffmann vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal)); 17620fb20d1cSCole Robinson #endif 1763d861def3SAnthony Liguori 17640fb20d1cSCole Robinson #if GTK_CHECK_VERSION(3, 0, 0) 17650fb20d1cSCole Robinson box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2); 17660fb20d1cSCole Robinson scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment); 17670fb20d1cSCole Robinson #else 17680fb20d1cSCole Robinson box = gtk_hbox_new(false, 2); 17690fb20d1cSCole Robinson scrollbar = gtk_vscrollbar_new(vadjustment); 17700fb20d1cSCole Robinson #endif 17710fb20d1cSCole Robinson 1772271a25c0SGerd Hoffmann gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0); 17730fb20d1cSCole Robinson gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0); 17740fb20d1cSCole Robinson 1775271a25c0SGerd Hoffmann vc->vte.box = box; 1776271a25c0SGerd Hoffmann vc->vte.scrollbar = scrollbar; 17770fb20d1cSCole Robinson 17780fb20d1cSCole Robinson g_signal_connect(vadjustment, "changed", 17790fb20d1cSCole Robinson G_CALLBACK(gd_vc_adjustment_changed), vc); 17800fb20d1cSCole Robinson 1781e3500d1fSGerd Hoffmann vc->type = GD_VC_VTE; 1782271a25c0SGerd Hoffmann vc->tab_item = box; 17839d677e1cSJan Kiszka vc->focus = vc->vte.terminal; 1784271a25c0SGerd Hoffmann gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item, 1785cdeb7090SGerd Hoffmann gtk_label_new(vc->label)); 1786d861def3SAnthony Liguori 1787271a25c0SGerd Hoffmann qemu_chr_be_generic_open(vc->vte.chr); 1788271a25c0SGerd Hoffmann if (vc->vte.chr->init) { 1789271a25c0SGerd Hoffmann vc->vte.chr->init(vc->vte.chr); 1790d861def3SAnthony Liguori } 1791d861def3SAnthony Liguori 1792d861def3SAnthony Liguori return group; 1793a4ccabcfSAnthony Liguori } 1794a4ccabcfSAnthony Liguori 1795ed1132e4SGerd Hoffmann static void gd_vcs_init(GtkDisplayState *s, GSList *group, 1796ee5f31e4SGerd Hoffmann GtkWidget *view_menu) 1797ee5f31e4SGerd Hoffmann { 1798ee5f31e4SGerd Hoffmann int i; 1799ee5f31e4SGerd Hoffmann 1800ee5f31e4SGerd Hoffmann for (i = 0; i < nb_vcs; i++) { 1801ed1132e4SGerd Hoffmann VirtualConsole *vc = &s->vc[s->nb_vcs]; 1802ed1132e4SGerd Hoffmann group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu); 1803ee5f31e4SGerd Hoffmann s->nb_vcs++; 1804ee5f31e4SGerd Hoffmann } 1805ee5f31e4SGerd Hoffmann } 1806ee5f31e4SGerd Hoffmann #endif /* CONFIG_VTE */ 1807ee5f31e4SGerd Hoffmann 1808a4ccabcfSAnthony Liguori /** Window Creation **/ 1809a4ccabcfSAnthony Liguori 1810e3500d1fSGerd Hoffmann static void gd_connect_vc_gfx_signals(VirtualConsole *vc) 1811e3500d1fSGerd Hoffmann { 1812e3500d1fSGerd Hoffmann #if GTK_CHECK_VERSION(3, 0, 0) 1813e3500d1fSGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "draw", 1814e3500d1fSGerd Hoffmann G_CALLBACK(gd_draw_event), vc); 1815925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL) 1816925a0400SGerd Hoffmann if (display_opengl) { 1817925a0400SGerd Hoffmann /* wire up GtkGlArea events */ 1818925a0400SGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "render", 1819925a0400SGerd Hoffmann G_CALLBACK(gd_render_event), vc); 1820925a0400SGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "resize", 1821925a0400SGerd Hoffmann G_CALLBACK(gd_resize_event), vc); 1822925a0400SGerd Hoffmann } 1823925a0400SGerd Hoffmann #endif 1824e3500d1fSGerd Hoffmann #else 1825e3500d1fSGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "expose-event", 1826e3500d1fSGerd Hoffmann G_CALLBACK(gd_expose_event), vc); 1827e3500d1fSGerd Hoffmann #endif 1828f8c223f6SGerd Hoffmann if (qemu_console_is_graphic(vc->gfx.dcl.con)) { 1829e3500d1fSGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "event", 1830e3500d1fSGerd Hoffmann G_CALLBACK(gd_event), vc); 1831e3500d1fSGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "button-press-event", 1832e3500d1fSGerd Hoffmann G_CALLBACK(gd_button_event), vc); 1833e3500d1fSGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "button-release-event", 1834e3500d1fSGerd Hoffmann G_CALLBACK(gd_button_event), vc); 1835e3500d1fSGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "scroll-event", 1836e3500d1fSGerd Hoffmann G_CALLBACK(gd_scroll_event), vc); 1837e3500d1fSGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "key-press-event", 1838e3500d1fSGerd Hoffmann G_CALLBACK(gd_key_event), vc); 1839e3500d1fSGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "key-release-event", 1840e3500d1fSGerd Hoffmann G_CALLBACK(gd_key_event), vc); 1841e3500d1fSGerd Hoffmann 1842e3500d1fSGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "enter-notify-event", 1843e3500d1fSGerd Hoffmann G_CALLBACK(gd_enter_event), vc); 1844e3500d1fSGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "leave-notify-event", 1845e3500d1fSGerd Hoffmann G_CALLBACK(gd_leave_event), vc); 1846e3500d1fSGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "focus-out-event", 1847e3500d1fSGerd Hoffmann G_CALLBACK(gd_focus_out_event), vc); 18481301e515SGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "configure-event", 18491301e515SGerd Hoffmann G_CALLBACK(gd_configure), vc); 1850f8c223f6SGerd Hoffmann } else { 1851f8c223f6SGerd Hoffmann g_signal_connect(vc->gfx.drawing_area, "key-press-event", 1852f8c223f6SGerd Hoffmann G_CALLBACK(gd_text_key_down), vc); 1853f8c223f6SGerd Hoffmann } 1854e3500d1fSGerd Hoffmann } 1855e3500d1fSGerd Hoffmann 1856a4ccabcfSAnthony Liguori static void gd_connect_signals(GtkDisplayState *s) 1857a4ccabcfSAnthony Liguori { 1858a4ccabcfSAnthony Liguori g_signal_connect(s->show_tabs_item, "activate", 1859a4ccabcfSAnthony Liguori G_CALLBACK(gd_menu_show_tabs), s); 1860cdeb7090SGerd Hoffmann g_signal_connect(s->untabify_item, "activate", 1861cdeb7090SGerd Hoffmann G_CALLBACK(gd_menu_untabify), s); 1862a4ccabcfSAnthony Liguori 1863a4ccabcfSAnthony Liguori g_signal_connect(s->window, "delete-event", 1864a4ccabcfSAnthony Liguori G_CALLBACK(gd_window_close), s); 1865a4ccabcfSAnthony Liguori 186630e8f22bSJan Kiszka g_signal_connect(s->pause_item, "activate", 186730e8f22bSJan Kiszka G_CALLBACK(gd_menu_pause), s); 186830e8f22bSJan Kiszka g_signal_connect(s->reset_item, "activate", 186930e8f22bSJan Kiszka G_CALLBACK(gd_menu_reset), s); 187030e8f22bSJan Kiszka g_signal_connect(s->powerdown_item, "activate", 187130e8f22bSJan Kiszka G_CALLBACK(gd_menu_powerdown), s); 1872a4ccabcfSAnthony Liguori g_signal_connect(s->quit_item, "activate", 1873a4ccabcfSAnthony Liguori G_CALLBACK(gd_menu_quit), s); 187444b31e0bSMichael S. Tsirkin #if defined(CONFIG_VTE) 187544b31e0bSMichael S. Tsirkin g_signal_connect(s->copy_item, "activate", 187644b31e0bSMichael S. Tsirkin G_CALLBACK(gd_menu_copy), s); 187744b31e0bSMichael S. Tsirkin #endif 1878c6158483SAnthony Liguori g_signal_connect(s->full_screen_item, "activate", 1879c6158483SAnthony Liguori G_CALLBACK(gd_menu_full_screen), s); 1880c6158483SAnthony Liguori g_signal_connect(s->zoom_in_item, "activate", 1881c6158483SAnthony Liguori G_CALLBACK(gd_menu_zoom_in), s); 1882c6158483SAnthony Liguori g_signal_connect(s->zoom_out_item, "activate", 1883c6158483SAnthony Liguori G_CALLBACK(gd_menu_zoom_out), s); 1884c6158483SAnthony Liguori g_signal_connect(s->zoom_fixed_item, "activate", 1885c6158483SAnthony Liguori G_CALLBACK(gd_menu_zoom_fixed), s); 1886c6158483SAnthony Liguori g_signal_connect(s->zoom_fit_item, "activate", 1887c6158483SAnthony Liguori G_CALLBACK(gd_menu_zoom_fit), s); 18885104a1f6SAnthony Liguori g_signal_connect(s->grab_item, "activate", 18895104a1f6SAnthony Liguori G_CALLBACK(gd_menu_grab_input), s); 1890a4ccabcfSAnthony Liguori g_signal_connect(s->notebook, "switch-page", 1891a4ccabcfSAnthony Liguori G_CALLBACK(gd_change_page), s); 1892a4ccabcfSAnthony Liguori } 1893a4ccabcfSAnthony Liguori 1894400519d2SCole Robinson static GtkWidget *gd_create_menu_machine(GtkDisplayState *s) 1895a4ccabcfSAnthony Liguori { 1896bf9b255fSAnthony Liguori GtkWidget *machine_menu; 1897a4ccabcfSAnthony Liguori GtkWidget *separator; 1898a4ccabcfSAnthony Liguori 1899bf9b255fSAnthony Liguori machine_menu = gtk_menu_new(); 1900400519d2SCole Robinson gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group); 190130e8f22bSJan Kiszka 190230e8f22bSJan Kiszka s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause")); 1903bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item); 190430e8f22bSJan Kiszka 190530e8f22bSJan Kiszka separator = gtk_separator_menu_item_new(); 1906bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator); 190730e8f22bSJan Kiszka 19089068f20dSCole Robinson s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset")); 1909bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item); 191030e8f22bSJan Kiszka 19119068f20dSCole Robinson s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down")); 1912bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item); 191330e8f22bSJan Kiszka 191430e8f22bSJan Kiszka separator = gtk_separator_menu_item_new(); 1915bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator); 1916a4ccabcfSAnthony Liguori 19173d914488SCole Robinson s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit")); 1918a4ccabcfSAnthony Liguori gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item), 191930e8f22bSJan Kiszka "<QEMU>/Machine/Quit"); 19203d914488SCole Robinson gtk_accel_map_add_entry("<QEMU>/Machine/Quit", 1921db1da1f2SCole Robinson GDK_KEY_q, HOTKEY_MODIFIERS); 1922bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item); 1923a4ccabcfSAnthony Liguori 1924bf9b255fSAnthony Liguori return machine_menu; 1925bf9b255fSAnthony Liguori } 1926bf9b255fSAnthony Liguori 1927e3500d1fSGerd Hoffmann static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, 1928ed1132e4SGerd Hoffmann QemuConsole *con, int idx, 1929e3500d1fSGerd Hoffmann GSList *group, GtkWidget *view_menu) 1930e3500d1fSGerd Hoffmann { 1931779ce88fSGerd Hoffmann vc->label = qemu_console_get_label(con); 1932e3500d1fSGerd Hoffmann vc->s = s; 1933e3500d1fSGerd Hoffmann vc->gfx.scale_x = 1.0; 1934e3500d1fSGerd Hoffmann vc->gfx.scale_y = 1.0; 1935e3500d1fSGerd Hoffmann 1936925a0400SGerd Hoffmann #if defined(CONFIG_OPENGL) 1937925a0400SGerd Hoffmann if (display_opengl) { 1938925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL) 1939925a0400SGerd Hoffmann vc->gfx.drawing_area = gtk_gl_area_new(); 1940925a0400SGerd Hoffmann vc->gfx.dcl.ops = &dcl_gl_area_ops; 1941925a0400SGerd Hoffmann #else 1942e3500d1fSGerd Hoffmann vc->gfx.drawing_area = gtk_drawing_area_new(); 1943925a0400SGerd Hoffmann /* 1944925a0400SGerd Hoffmann * gtk_widget_set_double_buffered() was deprecated in 3.14. 1945925a0400SGerd Hoffmann * It is required for opengl rendering on X11 though. A 1946925a0400SGerd Hoffmann * proper replacement (native opengl support) is only 1947925a0400SGerd Hoffmann * available in 3.16+. Silence the warning if possible. 1948925a0400SGerd Hoffmann */ 1949925a0400SGerd Hoffmann #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE 1950925a0400SGerd Hoffmann #pragma GCC diagnostic push 1951925a0400SGerd Hoffmann #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 1952925a0400SGerd Hoffmann #endif 1953925a0400SGerd Hoffmann gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE); 1954925a0400SGerd Hoffmann #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE 1955925a0400SGerd Hoffmann #pragma GCC diagnostic pop 1956925a0400SGerd Hoffmann #endif 1957925a0400SGerd Hoffmann vc->gfx.dcl.ops = &dcl_egl_ops; 1958925a0400SGerd Hoffmann #endif /* CONFIG_GTK_GL */ 1959925a0400SGerd Hoffmann } else 1960925a0400SGerd Hoffmann #endif 1961925a0400SGerd Hoffmann { 1962925a0400SGerd Hoffmann vc->gfx.drawing_area = gtk_drawing_area_new(); 1963925a0400SGerd Hoffmann vc->gfx.dcl.ops = &dcl_ops; 1964925a0400SGerd Hoffmann } 1965925a0400SGerd Hoffmann 1966925a0400SGerd Hoffmann 1967e3500d1fSGerd Hoffmann gtk_widget_add_events(vc->gfx.drawing_area, 1968e3500d1fSGerd Hoffmann GDK_POINTER_MOTION_MASK | 1969e3500d1fSGerd Hoffmann GDK_BUTTON_PRESS_MASK | 1970e3500d1fSGerd Hoffmann GDK_BUTTON_RELEASE_MASK | 1971e3500d1fSGerd Hoffmann GDK_BUTTON_MOTION_MASK | 1972e3500d1fSGerd Hoffmann GDK_ENTER_NOTIFY_MASK | 1973e3500d1fSGerd Hoffmann GDK_LEAVE_NOTIFY_MASK | 1974e3500d1fSGerd Hoffmann GDK_SCROLL_MASK | 1975e3500d1fSGerd Hoffmann GDK_KEY_PRESS_MASK); 1976e3500d1fSGerd Hoffmann gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE); 1977e3500d1fSGerd Hoffmann 1978e3500d1fSGerd Hoffmann vc->type = GD_VC_GFX; 1979e3500d1fSGerd Hoffmann vc->tab_item = vc->gfx.drawing_area; 19809d677e1cSJan Kiszka vc->focus = vc->gfx.drawing_area; 1981e3500d1fSGerd Hoffmann gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), 1982cdeb7090SGerd Hoffmann vc->tab_item, gtk_label_new(vc->label)); 1983e3500d1fSGerd Hoffmann 1984e3500d1fSGerd Hoffmann vc->gfx.dcl.con = con; 1985e3500d1fSGerd Hoffmann register_displaychangelistener(&vc->gfx.dcl); 1986e3500d1fSGerd Hoffmann 1987f8c223f6SGerd Hoffmann gd_connect_vc_gfx_signals(vc); 1988f8c223f6SGerd Hoffmann group = gd_vc_menu_init(s, vc, idx, group, view_menu); 1989f8c223f6SGerd Hoffmann 19901301e515SGerd Hoffmann if (dpy_ui_info_supported(vc->gfx.dcl.con)) { 19911301e515SGerd Hoffmann gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item)); 19921d73cd78SGerd Hoffmann s->free_scale = true; 19931301e515SGerd Hoffmann } 19941301e515SGerd Hoffmann 1995e3500d1fSGerd Hoffmann return group; 1996e3500d1fSGerd Hoffmann } 1997e3500d1fSGerd Hoffmann 1998400519d2SCole Robinson static GtkWidget *gd_create_menu_view(GtkDisplayState *s) 1999bf9b255fSAnthony Liguori { 2000bf9b255fSAnthony Liguori GSList *group = NULL; 2001bf9b255fSAnthony Liguori GtkWidget *view_menu; 2002bf9b255fSAnthony Liguori GtkWidget *separator; 2003ed1132e4SGerd Hoffmann QemuConsole *con; 2004ed1132e4SGerd Hoffmann int vc; 2005bf9b255fSAnthony Liguori 2006bf9b255fSAnthony Liguori view_menu = gtk_menu_new(); 2007400519d2SCole Robinson gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group); 2008a4ccabcfSAnthony Liguori 20093d914488SCole Robinson s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen")); 201095414914SCole Robinson 201144b31e0bSMichael S. Tsirkin #if defined(CONFIG_VTE) 201244b31e0bSMichael S. Tsirkin s->copy_item = gtk_menu_item_new_with_mnemonic(_("_Copy")); 201344b31e0bSMichael S. Tsirkin gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->copy_item); 201444b31e0bSMichael S. Tsirkin #endif 201544b31e0bSMichael S. Tsirkin 201695414914SCole Robinson gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0, 201795414914SCole Robinson g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL)); 201895414914SCole Robinson #if GTK_CHECK_VERSION(3, 8, 0) 201995414914SCole Robinson gtk_accel_label_set_accel( 202095414914SCole Robinson GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))), 202195414914SCole Robinson GDK_KEY_f, HOTKEY_MODIFIERS); 202295414914SCole Robinson #endif 2023bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item); 2024c6158483SAnthony Liguori 2025c6158483SAnthony Liguori separator = gtk_separator_menu_item_new(); 2026bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 2027c6158483SAnthony Liguori 20283d914488SCole Robinson s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In")); 2029c6158483SAnthony Liguori gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item), 2030c6158483SAnthony Liguori "<QEMU>/View/Zoom In"); 2031b1e749c0SJan Kiszka gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus, 2032b1e749c0SJan Kiszka HOTKEY_MODIFIERS); 2033bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item); 2034c6158483SAnthony Liguori 20353d914488SCole Robinson s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out")); 2036c6158483SAnthony Liguori gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item), 2037c6158483SAnthony Liguori "<QEMU>/View/Zoom Out"); 2038b1e749c0SJan Kiszka gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus, 2039b1e749c0SJan Kiszka HOTKEY_MODIFIERS); 2040bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item); 2041c6158483SAnthony Liguori 20423d914488SCole Robinson s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit")); 2043c6158483SAnthony Liguori gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item), 2044c6158483SAnthony Liguori "<QEMU>/View/Zoom Fixed"); 2045b1e749c0SJan Kiszka gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0, 2046b1e749c0SJan Kiszka HOTKEY_MODIFIERS); 2047bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item); 2048c6158483SAnthony Liguori 2049834574eaSAnthony Liguori s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit")); 2050bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item); 2051c6158483SAnthony Liguori 2052c6158483SAnthony Liguori separator = gtk_separator_menu_item_new(); 2053bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 2054c6158483SAnthony Liguori 2055834574eaSAnthony Liguori s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover")); 2056bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item); 20575104a1f6SAnthony Liguori 2058834574eaSAnthony Liguori s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input")); 20595104a1f6SAnthony Liguori gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item), 20605104a1f6SAnthony Liguori "<QEMU>/View/Grab Input"); 2061b1e749c0SJan Kiszka gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g, 2062b1e749c0SJan Kiszka HOTKEY_MODIFIERS); 2063bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item); 20645104a1f6SAnthony Liguori 2065a4ccabcfSAnthony Liguori separator = gtk_separator_menu_item_new(); 2066bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 2067a4ccabcfSAnthony Liguori 2068e3500d1fSGerd Hoffmann /* gfx */ 2069ed1132e4SGerd Hoffmann for (vc = 0;; vc++) { 2070ed1132e4SGerd Hoffmann con = qemu_console_lookup_by_index(vc); 2071f8c223f6SGerd Hoffmann if (!con) { 2072ed1132e4SGerd Hoffmann break; 2073ed1132e4SGerd Hoffmann } 2074ed1132e4SGerd Hoffmann group = gd_vc_gfx_init(s, &s->vc[vc], con, 2075ed1132e4SGerd Hoffmann vc, group, view_menu); 2076ed1132e4SGerd Hoffmann s->nb_vcs++; 2077ed1132e4SGerd Hoffmann } 2078a4ccabcfSAnthony Liguori 2079ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE) 2080e3500d1fSGerd Hoffmann /* vte */ 2081ed1132e4SGerd Hoffmann gd_vcs_init(s, group, view_menu); 2082ee5f31e4SGerd Hoffmann #endif 2083d861def3SAnthony Liguori 2084a4ccabcfSAnthony Liguori separator = gtk_separator_menu_item_new(); 2085bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 2086a4ccabcfSAnthony Liguori 2087834574eaSAnthony Liguori s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs")); 2088bf9b255fSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item); 2089a4ccabcfSAnthony Liguori 2090cdeb7090SGerd Hoffmann s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab")); 2091cdeb7090SGerd Hoffmann gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item); 2092cdeb7090SGerd Hoffmann 2093bf9b255fSAnthony Liguori return view_menu; 2094bf9b255fSAnthony Liguori } 2095a4ccabcfSAnthony Liguori 2096bf9b255fSAnthony Liguori static void gd_create_menus(GtkDisplayState *s) 2097bf9b255fSAnthony Liguori { 2098400519d2SCole Robinson s->accel_group = gtk_accel_group_new(); 2099400519d2SCole Robinson s->machine_menu = gd_create_menu_machine(s); 2100400519d2SCole Robinson s->view_menu = gd_create_menu_view(s); 2101bf9b255fSAnthony Liguori 2102bf9b255fSAnthony Liguori s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine")); 210330e8f22bSJan Kiszka gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item), 210430e8f22bSJan Kiszka s->machine_menu); 210530e8f22bSJan Kiszka gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item); 2106a4ccabcfSAnthony Liguori 2107bf9b255fSAnthony Liguori s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View")); 2108a4ccabcfSAnthony Liguori gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu); 2109a4ccabcfSAnthony Liguori gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item); 2110bf9b255fSAnthony Liguori 2111400519d2SCole Robinson g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group); 2112400519d2SCole Robinson gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group); 2113a4ccabcfSAnthony Liguori } 2114a4ccabcfSAnthony Liguori 21153158a348SBruce Rogers static void gd_set_keycode_type(GtkDisplayState *s) 21163158a348SBruce Rogers { 21170a337ed0SGerd Hoffmann #ifdef GDK_WINDOWING_X11 21183158a348SBruce Rogers GdkDisplay *display = gtk_widget_get_display(s->window); 21190a337ed0SGerd Hoffmann if (GDK_IS_X11_DISPLAY(display)) { 21203158a348SBruce Rogers Display *x11_display = gdk_x11_display_get_xdisplay(display); 21213158a348SBruce Rogers XkbDescPtr desc = XkbGetKeyboard(x11_display, XkbGBN_AllComponentsMask, 21223158a348SBruce Rogers XkbUseCoreKbd); 21230a337ed0SGerd Hoffmann char *keycodes = NULL; 21243158a348SBruce Rogers 21253158a348SBruce Rogers if (desc && desc->names) { 21263158a348SBruce Rogers keycodes = XGetAtomName(x11_display, desc->names->keycodes); 21273158a348SBruce Rogers } 21283158a348SBruce Rogers if (keycodes == NULL) { 21293158a348SBruce Rogers fprintf(stderr, "could not lookup keycode name\n"); 21303158a348SBruce Rogers } else if (strstart(keycodes, "evdev", NULL)) { 21313158a348SBruce Rogers s->has_evdev = true; 21323158a348SBruce Rogers } else if (!strstart(keycodes, "xfree86", NULL)) { 21333158a348SBruce Rogers fprintf(stderr, "unknown keycodes `%s', please report to " 21343158a348SBruce Rogers "qemu-devel@nongnu.org\n", keycodes); 21353158a348SBruce Rogers } 213684961407SChen Fan 213784961407SChen Fan if (desc) { 213884961407SChen Fan XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True); 213984961407SChen Fan } 214084961407SChen Fan if (keycodes) { 214184961407SChen Fan XFree(keycodes); 214284961407SChen Fan } 21430a337ed0SGerd Hoffmann } 21443158a348SBruce Rogers #endif 21453158a348SBruce Rogers } 21463158a348SBruce Rogers 2147060ab763SGerd Hoffmann static gboolean gtkinit; 2148060ab763SGerd Hoffmann 2149881249c7SJan Kiszka void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) 2150a4ccabcfSAnthony Liguori { 2151a4ccabcfSAnthony Liguori GtkDisplayState *s = g_malloc0(sizeof(*s)); 2152d819cdccSStefan Weil char *filename; 215363c67b6dSMax Reitz GdkDisplay *window_display; 2154a4ccabcfSAnthony Liguori 2155060ab763SGerd Hoffmann if (!gtkinit) { 2156060ab763SGerd Hoffmann fprintf(stderr, "gtk initialization failed\n"); 2157060ab763SGerd Hoffmann exit(1); 2158060ab763SGerd Hoffmann } 2159060ab763SGerd Hoffmann 2160a4ccabcfSAnthony Liguori s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 216151572ab0SDaniel P. Berrange #if GTK_CHECK_VERSION(3, 2, 0) 216251572ab0SDaniel P. Berrange s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 216351572ab0SDaniel P. Berrange #else 2164a4ccabcfSAnthony Liguori s->vbox = gtk_vbox_new(FALSE, 0); 216551572ab0SDaniel P. Berrange #endif 2166a4ccabcfSAnthony Liguori s->notebook = gtk_notebook_new(); 2167a4ccabcfSAnthony Liguori s->menu_bar = gtk_menu_bar_new(); 2168a4ccabcfSAnthony Liguori 2169c6158483SAnthony Liguori s->free_scale = FALSE; 2170a4ccabcfSAnthony Liguori 21712cb5d2a4SAlberto Garcia /* LC_MESSAGES only. See early_gtk_display_init() for details */ 21722cb5d2a4SAlberto Garcia setlocale(LC_MESSAGES, ""); 2173834574eaSAnthony Liguori bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR); 2174834574eaSAnthony Liguori textdomain("qemu"); 2175834574eaSAnthony Liguori 217663c67b6dSMax Reitz window_display = gtk_widget_get_display(s->window); 217763c67b6dSMax Reitz s->null_cursor = gdk_cursor_new_for_display(window_display, 217863c67b6dSMax Reitz GDK_BLANK_CURSOR); 2179a4ccabcfSAnthony Liguori 2180a4ccabcfSAnthony Liguori s->mouse_mode_notifier.notify = gd_mouse_mode_change; 2181a4ccabcfSAnthony Liguori qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier); 2182a4ccabcfSAnthony Liguori qemu_add_vm_change_state_handler(gd_change_runstate, s); 2183a4ccabcfSAnthony Liguori 2184f7da9c17SAnthony Liguori filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg"); 2185d819cdccSStefan Weil if (filename) { 2186d819cdccSStefan Weil GError *error = NULL; 2187d819cdccSStefan Weil GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error); 2188d819cdccSStefan Weil if (pixbuf) { 2189d819cdccSStefan Weil gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf); 2190d819cdccSStefan Weil } else { 2191d819cdccSStefan Weil g_error_free(error); 2192d819cdccSStefan Weil } 2193d819cdccSStefan Weil g_free(filename); 2194d819cdccSStefan Weil } 2195d819cdccSStefan Weil 2196a4ccabcfSAnthony Liguori gd_create_menus(s); 2197a4ccabcfSAnthony Liguori 2198a4ccabcfSAnthony Liguori gd_connect_signals(s); 2199a4ccabcfSAnthony Liguori 2200a4ccabcfSAnthony Liguori gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 2201a4ccabcfSAnthony Liguori gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE); 2202a4ccabcfSAnthony Liguori 2203a4ccabcfSAnthony Liguori gd_update_caption(s); 2204a4ccabcfSAnthony Liguori 2205a4ccabcfSAnthony Liguori gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0); 2206a4ccabcfSAnthony Liguori gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0); 2207a4ccabcfSAnthony Liguori 2208a4ccabcfSAnthony Liguori gtk_container_add(GTK_CONTAINER(s->window), s->vbox); 2209a4ccabcfSAnthony Liguori 2210a4ccabcfSAnthony Liguori gtk_widget_show_all(s->window); 2211a4ccabcfSAnthony Liguori 22126fa27697SGerd Hoffmann #ifdef VTE_RESIZE_HACK 22136fa27697SGerd Hoffmann { 22146fa27697SGerd Hoffmann VirtualConsole *cur = gd_vc_find_current(s); 2215b310a2a6SFam Zheng if (cur) { 22166fa27697SGerd Hoffmann int i; 22176fa27697SGerd Hoffmann 22186fa27697SGerd Hoffmann for (i = 0; i < s->nb_vcs; i++) { 22196fa27697SGerd Hoffmann VirtualConsole *vc = &s->vc[i]; 22206fa27697SGerd Hoffmann if (vc && vc->type == GD_VC_VTE && vc != cur) { 22216fa27697SGerd Hoffmann gtk_widget_hide(vc->vte.terminal); 22226fa27697SGerd Hoffmann } 22236fa27697SGerd Hoffmann } 22246fa27697SGerd Hoffmann gd_update_windowsize(cur); 22256fa27697SGerd Hoffmann } 2226b310a2a6SFam Zheng } 22276fa27697SGerd Hoffmann #endif 22286fa27697SGerd Hoffmann 2229787ba4f0SPeter Wu if (full_screen) { 2230787ba4f0SPeter Wu gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item)); 2231787ba4f0SPeter Wu } 2232881249c7SJan Kiszka if (grab_on_hover) { 2233881249c7SJan Kiszka gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item)); 2234881249c7SJan Kiszka } 2235787ba4f0SPeter Wu 22363158a348SBruce Rogers gd_set_keycode_type(s); 2237a4ccabcfSAnthony Liguori } 2238ee5f31e4SGerd Hoffmann 223997edf3bdSGerd Hoffmann void early_gtk_display_init(int opengl) 2240ee5f31e4SGerd Hoffmann { 22412cb5d2a4SAlberto Garcia /* The QEMU code relies on the assumption that it's always run in 22422cb5d2a4SAlberto Garcia * the C locale. Therefore it is not prepared to deal with 22432cb5d2a4SAlberto Garcia * operations that produce different results depending on the 22442cb5d2a4SAlberto Garcia * locale, such as printf's formatting of decimal numbers, and 22452cb5d2a4SAlberto Garcia * possibly others. 22462cb5d2a4SAlberto Garcia * 22472cb5d2a4SAlberto Garcia * Since GTK+ calls setlocale() by default -importing the locale 22482cb5d2a4SAlberto Garcia * settings from the environment- we must prevent it from doing so 22492cb5d2a4SAlberto Garcia * using gtk_disable_setlocale(). 22502cb5d2a4SAlberto Garcia * 22512cb5d2a4SAlberto Garcia * QEMU's GTK+ UI, however, _does_ have translations for some of 22522cb5d2a4SAlberto Garcia * the menu items. As a trade-off between a functionally correct 22532cb5d2a4SAlberto Garcia * QEMU and a fully internationalized UI we support importing 22542cb5d2a4SAlberto Garcia * LC_MESSAGES from the environment (see the setlocale() call 22552cb5d2a4SAlberto Garcia * earlier in this file). This allows us to display translated 22562cb5d2a4SAlberto Garcia * messages leaving everything else untouched. 22572cb5d2a4SAlberto Garcia */ 22582cb5d2a4SAlberto Garcia gtk_disable_setlocale(); 2259060ab763SGerd Hoffmann gtkinit = gtk_init_check(NULL, NULL); 2260060ab763SGerd Hoffmann if (!gtkinit) { 2261060ab763SGerd Hoffmann /* don't exit yet, that'll break -help */ 2262060ab763SGerd Hoffmann return; 2263060ab763SGerd Hoffmann } 226497edf3bdSGerd Hoffmann 226597edf3bdSGerd Hoffmann switch (opengl) { 226697edf3bdSGerd Hoffmann case -1: /* default */ 226797edf3bdSGerd Hoffmann case 0: /* off */ 226897edf3bdSGerd Hoffmann break; 226997edf3bdSGerd Hoffmann case 1: /* on */ 227097edf3bdSGerd Hoffmann #if defined(CONFIG_OPENGL) 2271925a0400SGerd Hoffmann #if defined(CONFIG_GTK_GL) 2272925a0400SGerd Hoffmann gtk_gl_area_init(); 2273925a0400SGerd Hoffmann #else 227497edf3bdSGerd Hoffmann gtk_egl_init(); 227597edf3bdSGerd Hoffmann #endif 2276925a0400SGerd Hoffmann #endif 227797edf3bdSGerd Hoffmann break; 227897edf3bdSGerd Hoffmann default: 227997edf3bdSGerd Hoffmann g_assert_not_reached(); 228097edf3bdSGerd Hoffmann break; 228197edf3bdSGerd Hoffmann } 228297edf3bdSGerd Hoffmann 2283ee5f31e4SGerd Hoffmann #if defined(CONFIG_VTE) 2284ee5f31e4SGerd Hoffmann register_vc_handler(gd_vc_handler); 2285ee5f31e4SGerd Hoffmann #endif 2286ee5f31e4SGerd Hoffmann } 2287