1 /* 2 * GTK UI 3 * 4 * Copyright IBM, Corp. 2012 5 * 6 * Authors: 7 * Anthony Liguori <aliguori@us.ibm.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 * See the COPYING file in the top-level directory. 11 * 12 * Portions from gtk-vnc: 13 * 14 * GTK VNC Widget 15 * 16 * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws> 17 * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com> 18 * 19 * This library is free software; you can redistribute it and/or 20 * modify it under the terms of the GNU Lesser General Public 21 * License as published by the Free Software Foundation; either 22 * version 2.0 of the License, or (at your option) any later version. 23 * 24 * This library is distributed in the hope that it will be useful, 25 * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 27 * Lesser General Public License for more details. 28 * 29 * You should have received a copy of the GNU Lesser General Public 30 * License along with this library; if not, write to the Free Software 31 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 32 */ 33 34 #define GETTEXT_PACKAGE "qemu" 35 #define LOCALEDIR "po" 36 37 #ifdef _WIN32 38 # define _WIN32_WINNT 0x0601 /* needed to get definition of MAPVK_VK_TO_VSC */ 39 #endif 40 41 #include "qemu-common.h" 42 43 #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE 44 /* Work around an -Wstrict-prototypes warning in GTK headers */ 45 #pragma GCC diagnostic push 46 #pragma GCC diagnostic ignored "-Wstrict-prototypes" 47 #endif 48 #include <gtk/gtk.h> 49 #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE 50 #pragma GCC diagnostic pop 51 #endif 52 53 54 #include <gdk/gdkkeysyms.h> 55 #include <glib/gi18n.h> 56 #include <locale.h> 57 #if defined(CONFIG_VTE) 58 #include <vte/vte.h> 59 #endif 60 #include <math.h> 61 62 #include "trace.h" 63 #include "ui/console.h" 64 #include "ui/input.h" 65 #include "sysemu/sysemu.h" 66 #include "qmp-commands.h" 67 #include "x_keymap.h" 68 #include "keymaps.h" 69 #include "sysemu/char.h" 70 #include "qom/object.h" 71 #ifdef GDK_WINDOWING_X11 72 #include <gdk/gdkx.h> 73 #include <X11/XKBlib.h> 74 #endif 75 76 #define MAX_VCS 10 77 #define VC_WINDOW_X_MIN 320 78 #define VC_WINDOW_Y_MIN 240 79 #define VC_TERM_X_MIN 80 80 #define VC_TERM_Y_MIN 25 81 #define VC_SCALE_MIN 0.25 82 #define VC_SCALE_STEP 0.25 83 84 #if !defined(CONFIG_VTE) 85 # define VTE_CHECK_VERSION(a, b, c) 0 86 #endif 87 88 #if defined(CONFIG_VTE) && !GTK_CHECK_VERSION(3, 0, 0) 89 /* 90 * The gtk2 vte terminal widget seriously messes up the window resize 91 * for some reason. You basically can't make the qemu window smaller 92 * any more because the toplevel window geoemtry hints are overridden. 93 * 94 * Workaround that by hiding all vte widgets, except the one in the 95 * current tab. 96 * 97 * Luckily everything works smooth in gtk3. 98 */ 99 # define VTE_RESIZE_HACK 1 100 #endif 101 102 /* Compatibility define to let us build on both Gtk2 and Gtk3 */ 103 #if GTK_CHECK_VERSION(3, 0, 0) 104 static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh) 105 { 106 *ww = gdk_window_get_width(w); 107 *wh = gdk_window_get_height(w); 108 } 109 #endif 110 111 #if !GTK_CHECK_VERSION(2, 20, 0) 112 #define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget) 113 #endif 114 115 #ifndef GDK_IS_X11_DISPLAY 116 #define GDK_IS_X11_DISPLAY(dpy) (dpy == dpy) 117 #endif 118 #ifndef GDK_IS_WIN32_DISPLAY 119 #define GDK_IS_WIN32_DISPLAY(dpy) (dpy == dpy) 120 #endif 121 122 #ifndef GDK_KEY_0 123 #define GDK_KEY_0 GDK_0 124 #define GDK_KEY_1 GDK_1 125 #define GDK_KEY_2 GDK_2 126 #define GDK_KEY_f GDK_f 127 #define GDK_KEY_g GDK_g 128 #define GDK_KEY_q GDK_q 129 #define GDK_KEY_plus GDK_plus 130 #define GDK_KEY_minus GDK_minus 131 #define GDK_KEY_Pause GDK_Pause 132 #endif 133 134 #define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK) 135 136 static const int modifier_keycode[] = { 137 /* shift, control, alt keys, meta keys, both left & right */ 138 0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd, 139 }; 140 141 typedef struct GtkDisplayState GtkDisplayState; 142 143 typedef struct VirtualGfxConsole { 144 GtkWidget *drawing_area; 145 DisplayChangeListener dcl; 146 DisplaySurface *ds; 147 pixman_image_t *convert; 148 cairo_surface_t *surface; 149 double scale_x; 150 double scale_y; 151 } VirtualGfxConsole; 152 153 #if defined(CONFIG_VTE) 154 typedef struct VirtualVteConsole { 155 GtkWidget *box; 156 GtkWidget *scrollbar; 157 GtkWidget *terminal; 158 CharDriverState *chr; 159 } VirtualVteConsole; 160 #endif 161 162 typedef enum VirtualConsoleType { 163 GD_VC_GFX, 164 GD_VC_VTE, 165 } VirtualConsoleType; 166 167 typedef struct VirtualConsole { 168 GtkDisplayState *s; 169 char *label; 170 GtkWidget *window; 171 GtkWidget *menu_item; 172 GtkWidget *tab_item; 173 VirtualConsoleType type; 174 union { 175 VirtualGfxConsole gfx; 176 #if defined(CONFIG_VTE) 177 VirtualVteConsole vte; 178 #endif 179 }; 180 } VirtualConsole; 181 182 struct GtkDisplayState { 183 GtkWidget *window; 184 185 GtkWidget *menu_bar; 186 187 GtkAccelGroup *accel_group; 188 189 GtkWidget *machine_menu_item; 190 GtkWidget *machine_menu; 191 GtkWidget *pause_item; 192 GtkWidget *reset_item; 193 GtkWidget *powerdown_item; 194 GtkWidget *quit_item; 195 196 GtkWidget *view_menu_item; 197 GtkWidget *view_menu; 198 GtkWidget *full_screen_item; 199 GtkWidget *zoom_in_item; 200 GtkWidget *zoom_out_item; 201 GtkWidget *zoom_fixed_item; 202 GtkWidget *zoom_fit_item; 203 GtkWidget *grab_item; 204 GtkWidget *grab_on_hover_item; 205 206 int nb_vcs; 207 VirtualConsole vc[MAX_VCS]; 208 209 GtkWidget *show_tabs_item; 210 GtkWidget *untabify_item; 211 212 GtkWidget *vbox; 213 GtkWidget *notebook; 214 int button_mask; 215 gboolean last_set; 216 int last_x; 217 int last_y; 218 int grab_x_root; 219 int grab_y_root; 220 VirtualConsole *kbd_owner; 221 VirtualConsole *ptr_owner; 222 223 gboolean full_screen; 224 225 GdkCursor *null_cursor; 226 Notifier mouse_mode_notifier; 227 gboolean free_scale; 228 229 bool external_pause_update; 230 231 bool modifier_pressed[ARRAY_SIZE(modifier_keycode)]; 232 bool has_evdev; 233 }; 234 235 static void gd_grab_pointer(VirtualConsole *vc); 236 static void gd_ungrab_pointer(GtkDisplayState *s); 237 238 /** Utility Functions **/ 239 240 static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s) 241 { 242 VirtualConsole *vc; 243 gint i; 244 245 for (i = 0; i < s->nb_vcs; i++) { 246 vc = &s->vc[i]; 247 if (gtk_check_menu_item_get_active 248 (GTK_CHECK_MENU_ITEM(vc->menu_item))) { 249 return vc; 250 } 251 } 252 return NULL; 253 } 254 255 static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page) 256 { 257 VirtualConsole *vc; 258 gint i, p; 259 260 for (i = 0; i < s->nb_vcs; i++) { 261 vc = &s->vc[i]; 262 p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item); 263 if (p == page) { 264 return vc; 265 } 266 } 267 return NULL; 268 } 269 270 static VirtualConsole *gd_vc_find_current(GtkDisplayState *s) 271 { 272 gint page; 273 274 page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)); 275 return gd_vc_find_by_page(s, page); 276 } 277 278 static bool gd_is_grab_active(GtkDisplayState *s) 279 { 280 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item)); 281 } 282 283 static bool gd_grab_on_hover(GtkDisplayState *s) 284 { 285 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item)); 286 } 287 288 static void gd_update_cursor(VirtualConsole *vc) 289 { 290 GtkDisplayState *s = vc->s; 291 GdkWindow *window; 292 293 if (vc->type != GD_VC_GFX) { 294 return; 295 } 296 297 if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { 298 return; 299 } 300 301 window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area)); 302 if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) { 303 gdk_window_set_cursor(window, s->null_cursor); 304 } else { 305 gdk_window_set_cursor(window, NULL); 306 } 307 } 308 309 static void gd_update_caption(GtkDisplayState *s) 310 { 311 const char *status = ""; 312 gchar *prefix; 313 gchar *title; 314 const char *grab = ""; 315 bool is_paused = !runstate_is_running(); 316 int i; 317 318 if (qemu_name) { 319 prefix = g_strdup_printf("QEMU (%s)", qemu_name); 320 } else { 321 prefix = g_strdup_printf("QEMU"); 322 } 323 324 if (s->ptr_owner != NULL && 325 s->ptr_owner->window == NULL) { 326 grab = _(" - Press Ctrl+Alt+G to release grab"); 327 } 328 329 if (is_paused) { 330 status = _(" [Paused]"); 331 } 332 s->external_pause_update = true; 333 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item), 334 is_paused); 335 s->external_pause_update = false; 336 337 title = g_strdup_printf("%s%s%s", prefix, status, grab); 338 gtk_window_set_title(GTK_WINDOW(s->window), title); 339 g_free(title); 340 341 for (i = 0; i < s->nb_vcs; i++) { 342 VirtualConsole *vc = &s->vc[i]; 343 344 if (!vc->window) { 345 continue; 346 } 347 title = g_strdup_printf("%s: %s%s%s", prefix, vc->label, 348 vc == s->kbd_owner ? " +kbd" : "", 349 vc == s->ptr_owner ? " +ptr" : ""); 350 gtk_window_set_title(GTK_WINDOW(vc->window), title); 351 g_free(title); 352 } 353 354 g_free(prefix); 355 } 356 357 static void gd_update_geometry_hints(VirtualConsole *vc) 358 { 359 GtkDisplayState *s = vc->s; 360 GdkWindowHints mask = 0; 361 GdkGeometry geo = {}; 362 GtkWidget *geo_widget = NULL; 363 GtkWindow *geo_window; 364 365 if (vc->type == GD_VC_GFX) { 366 if (s->free_scale) { 367 geo.min_width = surface_width(vc->gfx.ds) * VC_SCALE_MIN; 368 geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN; 369 mask |= GDK_HINT_MIN_SIZE; 370 } else { 371 geo.min_width = surface_width(vc->gfx.ds) * vc->gfx.scale_x; 372 geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y; 373 mask |= GDK_HINT_MIN_SIZE; 374 } 375 geo_widget = vc->gfx.drawing_area; 376 gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height); 377 378 #if defined(CONFIG_VTE) 379 } else if (vc->type == GD_VC_VTE) { 380 VteTerminal *term = VTE_TERMINAL(vc->vte.terminal); 381 GtkBorder *ib; 382 383 geo.width_inc = vte_terminal_get_char_width(term); 384 geo.height_inc = vte_terminal_get_char_height(term); 385 mask |= GDK_HINT_RESIZE_INC; 386 geo.base_width = geo.width_inc; 387 geo.base_height = geo.height_inc; 388 mask |= GDK_HINT_BASE_SIZE; 389 geo.min_width = geo.width_inc * VC_TERM_X_MIN; 390 geo.min_height = geo.height_inc * VC_TERM_Y_MIN; 391 mask |= GDK_HINT_MIN_SIZE; 392 gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL); 393 geo.base_width += ib->left + ib->right; 394 geo.base_height += ib->top + ib->bottom; 395 geo.min_width += ib->left + ib->right; 396 geo.min_height += ib->top + ib->bottom; 397 geo_widget = vc->vte.terminal; 398 #endif 399 } 400 401 geo_window = GTK_WINDOW(vc->window ? vc->window : s->window); 402 gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask); 403 } 404 405 static void gd_update_windowsize(VirtualConsole *vc) 406 { 407 GtkDisplayState *s = vc->s; 408 409 gd_update_geometry_hints(vc); 410 411 if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) { 412 gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window), 413 VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN); 414 } 415 } 416 417 static void gd_update_full_redraw(VirtualConsole *vc) 418 { 419 GtkWidget *area = vc->gfx.drawing_area; 420 int ww, wh; 421 gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh); 422 gtk_widget_queue_draw_area(area, 0, 0, ww, wh); 423 } 424 425 static void gtk_release_modifiers(GtkDisplayState *s) 426 { 427 VirtualConsole *vc = gd_vc_find_current(s); 428 int i, keycode; 429 430 if (vc->type != GD_VC_GFX) { 431 return; 432 } 433 for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { 434 keycode = modifier_keycode[i]; 435 if (!s->modifier_pressed[i]) { 436 continue; 437 } 438 qemu_input_event_send_key_number(vc->gfx.dcl.con, keycode, false); 439 s->modifier_pressed[i] = false; 440 } 441 } 442 443 static void gd_widget_reparent(GtkWidget *from, GtkWidget *to, 444 GtkWidget *widget) 445 { 446 g_object_ref(G_OBJECT(widget)); 447 gtk_container_remove(GTK_CONTAINER(from), widget); 448 gtk_container_add(GTK_CONTAINER(to), widget); 449 g_object_unref(G_OBJECT(widget)); 450 } 451 452 /** DisplayState Callbacks **/ 453 454 static void gd_update(DisplayChangeListener *dcl, 455 int x, int y, int w, int h) 456 { 457 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 458 int x1, x2, y1, y2; 459 int mx, my; 460 int fbw, fbh; 461 int ww, wh; 462 463 trace_gd_update(vc->label, x, y, w, h); 464 465 if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { 466 return; 467 } 468 469 if (vc->gfx.convert) { 470 pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, 471 NULL, vc->gfx.convert, 472 x, y, 0, 0, x, y, w, h); 473 } 474 475 x1 = floor(x * vc->gfx.scale_x); 476 y1 = floor(y * vc->gfx.scale_y); 477 478 x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x); 479 y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y); 480 481 fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; 482 fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; 483 484 gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area), 485 &ww, &wh); 486 487 mx = my = 0; 488 if (ww > fbw) { 489 mx = (ww - fbw) / 2; 490 } 491 if (wh > fbh) { 492 my = (wh - fbh) / 2; 493 } 494 495 gtk_widget_queue_draw_area(vc->gfx.drawing_area, 496 mx + x1, my + y1, (x2 - x1), (y2 - y1)); 497 } 498 499 static void gd_refresh(DisplayChangeListener *dcl) 500 { 501 graphic_hw_update(dcl->con); 502 } 503 504 #if GTK_CHECK_VERSION(3, 0, 0) 505 static void gd_mouse_set(DisplayChangeListener *dcl, 506 int x, int y, int visible) 507 { 508 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 509 GdkDisplay *dpy; 510 GdkDeviceManager *mgr; 511 gint x_root, y_root; 512 513 if (qemu_input_is_absolute()) { 514 return; 515 } 516 517 dpy = gtk_widget_get_display(vc->gfx.drawing_area); 518 mgr = gdk_display_get_device_manager(dpy); 519 gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area), 520 x, y, &x_root, &y_root); 521 gdk_device_warp(gdk_device_manager_get_client_pointer(mgr), 522 gtk_widget_get_screen(vc->gfx.drawing_area), 523 x_root, y_root); 524 } 525 #else 526 static void gd_mouse_set(DisplayChangeListener *dcl, 527 int x, int y, int visible) 528 { 529 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 530 gint x_root, y_root; 531 532 if (qemu_input_is_absolute()) { 533 return; 534 } 535 536 gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area), 537 x, y, &x_root, &y_root); 538 gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area), 539 gtk_widget_get_screen(vc->gfx.drawing_area), 540 x_root, y_root); 541 } 542 #endif 543 544 static void gd_cursor_define(DisplayChangeListener *dcl, 545 QEMUCursor *c) 546 { 547 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 548 GdkPixbuf *pixbuf; 549 GdkCursor *cursor; 550 551 if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { 552 return; 553 } 554 555 pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data), 556 GDK_COLORSPACE_RGB, true, 8, 557 c->width, c->height, c->width * 4, 558 NULL, NULL); 559 cursor = gdk_cursor_new_from_pixbuf 560 (gtk_widget_get_display(vc->gfx.drawing_area), 561 pixbuf, c->hot_x, c->hot_y); 562 gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor); 563 g_object_unref(pixbuf); 564 #if !GTK_CHECK_VERSION(3, 0, 0) 565 gdk_cursor_unref(cursor); 566 #else 567 g_object_unref(cursor); 568 #endif 569 } 570 571 static void gd_switch(DisplayChangeListener *dcl, 572 DisplaySurface *surface) 573 { 574 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 575 bool resized = true; 576 577 trace_gd_switch(vc->label, surface_width(surface), surface_height(surface)); 578 579 if (vc->gfx.surface) { 580 cairo_surface_destroy(vc->gfx.surface); 581 } 582 583 if (vc->gfx.ds && 584 surface_width(vc->gfx.ds) == surface_width(surface) && 585 surface_height(vc->gfx.ds) == surface_height(surface)) { 586 resized = false; 587 } 588 vc->gfx.ds = surface; 589 590 if (vc->gfx.convert) { 591 pixman_image_unref(vc->gfx.convert); 592 vc->gfx.convert = NULL; 593 } 594 595 if (surface->format == PIXMAN_x8r8g8b8) { 596 /* 597 * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24 598 * 599 * No need to convert, use surface directly. Should be the 600 * common case as this is qemu_default_pixelformat(32) too. 601 */ 602 vc->gfx.surface = cairo_image_surface_create_for_data 603 (surface_data(surface), 604 CAIRO_FORMAT_RGB24, 605 surface_width(surface), 606 surface_height(surface), 607 surface_stride(surface)); 608 } else { 609 /* Must convert surface, use pixman to do it. */ 610 vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8, 611 surface_width(surface), 612 surface_height(surface), 613 NULL, 0); 614 vc->gfx.surface = cairo_image_surface_create_for_data 615 ((void *)pixman_image_get_data(vc->gfx.convert), 616 CAIRO_FORMAT_RGB24, 617 pixman_image_get_width(vc->gfx.convert), 618 pixman_image_get_height(vc->gfx.convert), 619 pixman_image_get_stride(vc->gfx.convert)); 620 pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, 621 NULL, vc->gfx.convert, 622 0, 0, 0, 0, 0, 0, 623 pixman_image_get_width(vc->gfx.convert), 624 pixman_image_get_height(vc->gfx.convert)); 625 } 626 627 if (resized) { 628 gd_update_windowsize(vc); 629 } else { 630 gd_update_full_redraw(vc); 631 } 632 } 633 634 /** QEMU Events **/ 635 636 static void gd_change_runstate(void *opaque, int running, RunState state) 637 { 638 GtkDisplayState *s = opaque; 639 640 gd_update_caption(s); 641 } 642 643 static void gd_mouse_mode_change(Notifier *notify, void *data) 644 { 645 GtkDisplayState *s; 646 int i; 647 648 s = container_of(notify, GtkDisplayState, mouse_mode_notifier); 649 /* release the grab at switching to absolute mode */ 650 if (qemu_input_is_absolute() && gd_is_grab_active(s)) { 651 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 652 FALSE); 653 } 654 for (i = 0; i < s->nb_vcs; i++) { 655 VirtualConsole *vc = &s->vc[i]; 656 gd_update_cursor(vc); 657 } 658 } 659 660 /** GTK Events **/ 661 662 static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event, 663 void *opaque) 664 { 665 GtkDisplayState *s = opaque; 666 int i; 667 668 if (!no_quit) { 669 for (i = 0; i < s->nb_vcs; i++) { 670 if (s->vc[i].type != GD_VC_GFX) { 671 continue; 672 } 673 unregister_displaychangelistener(&s->vc[i].gfx.dcl); 674 } 675 qmp_quit(NULL); 676 return FALSE; 677 } 678 679 return TRUE; 680 } 681 682 static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) 683 { 684 VirtualConsole *vc = opaque; 685 GtkDisplayState *s = vc->s; 686 int mx, my; 687 int ww, wh; 688 int fbw, fbh; 689 690 if (!gtk_widget_get_realized(widget)) { 691 return FALSE; 692 } 693 694 fbw = surface_width(vc->gfx.ds); 695 fbh = surface_height(vc->gfx.ds); 696 697 gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh); 698 699 if (s->full_screen) { 700 vc->gfx.scale_x = (double)ww / fbw; 701 vc->gfx.scale_y = (double)wh / fbh; 702 } else if (s->free_scale) { 703 double sx, sy; 704 705 sx = (double)ww / fbw; 706 sy = (double)wh / fbh; 707 708 vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); 709 } 710 711 fbw *= vc->gfx.scale_x; 712 fbh *= vc->gfx.scale_y; 713 714 mx = my = 0; 715 if (ww > fbw) { 716 mx = (ww - fbw) / 2; 717 } 718 if (wh > fbh) { 719 my = (wh - fbh) / 2; 720 } 721 722 cairo_rectangle(cr, 0, 0, ww, wh); 723 724 /* Optionally cut out the inner area where the pixmap 725 will be drawn. This avoids 'flashing' since we're 726 not double-buffering. Note we're using the undocumented 727 behaviour of drawing the rectangle from right to left 728 to cut out the whole */ 729 cairo_rectangle(cr, mx + fbw, my, 730 -1 * fbw, fbh); 731 cairo_fill(cr); 732 733 cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y); 734 cairo_set_source_surface(cr, vc->gfx.surface, 735 mx / vc->gfx.scale_x, my / vc->gfx.scale_y); 736 cairo_paint(cr); 737 738 return TRUE; 739 } 740 741 #if !GTK_CHECK_VERSION(3, 0, 0) 742 static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose, 743 void *opaque) 744 { 745 cairo_t *cr; 746 gboolean ret; 747 748 cr = gdk_cairo_create(gtk_widget_get_window(widget)); 749 cairo_rectangle(cr, 750 expose->area.x, 751 expose->area.y, 752 expose->area.width, 753 expose->area.height); 754 cairo_clip(cr); 755 756 ret = gd_draw_event(widget, cr, opaque); 757 758 cairo_destroy(cr); 759 760 return ret; 761 } 762 #endif 763 764 static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, 765 void *opaque) 766 { 767 VirtualConsole *vc = opaque; 768 GtkDisplayState *s = vc->s; 769 int x, y; 770 int mx, my; 771 int fbh, fbw; 772 int ww, wh; 773 774 fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; 775 fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; 776 777 gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area), 778 &ww, &wh); 779 780 mx = my = 0; 781 if (ww > fbw) { 782 mx = (ww - fbw) / 2; 783 } 784 if (wh > fbh) { 785 my = (wh - fbh) / 2; 786 } 787 788 x = (motion->x - mx) / vc->gfx.scale_x; 789 y = (motion->y - my) / vc->gfx.scale_y; 790 791 if (qemu_input_is_absolute()) { 792 if (x < 0 || y < 0 || 793 x >= surface_width(vc->gfx.ds) || 794 y >= surface_height(vc->gfx.ds)) { 795 return TRUE; 796 } 797 qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x, 798 surface_width(vc->gfx.ds)); 799 qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y, 800 surface_height(vc->gfx.ds)); 801 qemu_input_event_sync(); 802 } else if (s->last_set && s->ptr_owner == vc) { 803 qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x); 804 qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y); 805 qemu_input_event_sync(); 806 } 807 s->last_x = x; 808 s->last_y = y; 809 s->last_set = TRUE; 810 811 if (!qemu_input_is_absolute() && s->ptr_owner == vc) { 812 GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area); 813 int x = (int)motion->x_root; 814 int y = (int)motion->y_root; 815 816 /* In relative mode check to see if client pointer hit 817 * one of the screen edges, and if so move it back by 818 * 200 pixels. This is important because the pointer 819 * in the server doesn't correspond 1-for-1, and so 820 * may still be only half way across the screen. Without 821 * this warp, the server pointer would thus appear to hit 822 * an invisible wall */ 823 if (x == 0) { 824 x += 200; 825 } 826 if (y == 0) { 827 y += 200; 828 } 829 if (x == (gdk_screen_get_width(screen) - 1)) { 830 x -= 200; 831 } 832 if (y == (gdk_screen_get_height(screen) - 1)) { 833 y -= 200; 834 } 835 836 if (x != (int)motion->x_root || y != (int)motion->y_root) { 837 #if GTK_CHECK_VERSION(3, 0, 0) 838 GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion); 839 gdk_device_warp(dev, screen, x, y); 840 #else 841 GdkDisplay *display = gtk_widget_get_display(widget); 842 gdk_display_warp_pointer(display, screen, x, y); 843 #endif 844 s->last_set = FALSE; 845 return FALSE; 846 } 847 } 848 return TRUE; 849 } 850 851 static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, 852 void *opaque) 853 { 854 VirtualConsole *vc = opaque; 855 GtkDisplayState *s = vc->s; 856 InputButton btn; 857 858 /* implicitly grab the input at the first click in the relative mode */ 859 if (button->button == 1 && button->type == GDK_BUTTON_PRESS && 860 !qemu_input_is_absolute() && s->ptr_owner != vc) { 861 gd_ungrab_pointer(s); 862 if (!vc->window) { 863 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 864 TRUE); 865 } else { 866 gd_grab_pointer(vc); 867 gd_update_caption(s); 868 } 869 return TRUE; 870 } 871 872 if (button->button == 1) { 873 btn = INPUT_BUTTON_LEFT; 874 } else if (button->button == 2) { 875 btn = INPUT_BUTTON_MIDDLE; 876 } else if (button->button == 3) { 877 btn = INPUT_BUTTON_RIGHT; 878 } else { 879 return TRUE; 880 } 881 882 qemu_input_queue_btn(vc->gfx.dcl.con, btn, 883 button->type == GDK_BUTTON_PRESS); 884 qemu_input_event_sync(); 885 return TRUE; 886 } 887 888 static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll, 889 void *opaque) 890 { 891 VirtualConsole *vc = opaque; 892 InputButton btn; 893 894 if (scroll->direction == GDK_SCROLL_UP) { 895 btn = INPUT_BUTTON_WHEEL_UP; 896 } else if (scroll->direction == GDK_SCROLL_DOWN) { 897 btn = INPUT_BUTTON_WHEEL_DOWN; 898 } else { 899 return TRUE; 900 } 901 902 qemu_input_queue_btn(vc->gfx.dcl.con, btn, true); 903 qemu_input_event_sync(); 904 qemu_input_queue_btn(vc->gfx.dcl.con, btn, false); 905 qemu_input_event_sync(); 906 return TRUE; 907 } 908 909 static int gd_map_keycode(GtkDisplayState *s, GdkDisplay *dpy, int gdk_keycode) 910 { 911 int qemu_keycode; 912 913 #ifdef GDK_WINDOWING_WIN32 914 if (GDK_IS_WIN32_DISPLAY(dpy)) { 915 qemu_keycode = MapVirtualKey(gdk_keycode, MAPVK_VK_TO_VSC); 916 switch (qemu_keycode) { 917 case 103: /* alt gr */ 918 qemu_keycode = 56 | SCANCODE_GREY; 919 break; 920 } 921 return qemu_keycode; 922 } 923 #endif 924 925 if (gdk_keycode < 9) { 926 qemu_keycode = 0; 927 } else if (gdk_keycode < 97) { 928 qemu_keycode = gdk_keycode - 8; 929 #ifdef GDK_WINDOWING_X11 930 } else if (GDK_IS_X11_DISPLAY(dpy) && gdk_keycode < 158) { 931 if (s->has_evdev) { 932 qemu_keycode = translate_evdev_keycode(gdk_keycode - 97); 933 } else { 934 qemu_keycode = translate_xfree86_keycode(gdk_keycode - 97); 935 } 936 #endif 937 } else if (gdk_keycode == 208) { /* Hiragana_Katakana */ 938 qemu_keycode = 0x70; 939 } else if (gdk_keycode == 211) { /* backslash */ 940 qemu_keycode = 0x73; 941 } else { 942 qemu_keycode = 0; 943 } 944 945 return qemu_keycode; 946 } 947 948 static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) 949 { 950 VirtualConsole *vc = opaque; 951 GtkDisplayState *s = vc->s; 952 int gdk_keycode = key->hardware_keycode; 953 int qemu_keycode; 954 int i; 955 956 if (key->keyval == GDK_KEY_Pause) { 957 qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE, 958 key->type == GDK_KEY_PRESS); 959 return TRUE; 960 } 961 962 qemu_keycode = gd_map_keycode(s, gtk_widget_get_display(widget), 963 gdk_keycode); 964 965 trace_gd_key_event(vc->label, gdk_keycode, qemu_keycode, 966 (key->type == GDK_KEY_PRESS) ? "down" : "up"); 967 968 for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { 969 if (qemu_keycode == modifier_keycode[i]) { 970 s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS); 971 } 972 } 973 974 qemu_input_event_send_key_number(vc->gfx.dcl.con, qemu_keycode, 975 key->type == GDK_KEY_PRESS); 976 977 return TRUE; 978 } 979 980 static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque) 981 { 982 if (event->type == GDK_MOTION_NOTIFY) { 983 return gd_motion_event(widget, &event->motion, opaque); 984 } 985 return FALSE; 986 } 987 988 /** Window Menu Actions **/ 989 990 static void gd_menu_pause(GtkMenuItem *item, void *opaque) 991 { 992 GtkDisplayState *s = opaque; 993 994 if (s->external_pause_update) { 995 return; 996 } 997 if (runstate_is_running()) { 998 qmp_stop(NULL); 999 } else { 1000 qmp_cont(NULL); 1001 } 1002 } 1003 1004 static void gd_menu_reset(GtkMenuItem *item, void *opaque) 1005 { 1006 qmp_system_reset(NULL); 1007 } 1008 1009 static void gd_menu_powerdown(GtkMenuItem *item, void *opaque) 1010 { 1011 qmp_system_powerdown(NULL); 1012 } 1013 1014 static void gd_menu_quit(GtkMenuItem *item, void *opaque) 1015 { 1016 qmp_quit(NULL); 1017 } 1018 1019 static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque) 1020 { 1021 GtkDisplayState *s = opaque; 1022 VirtualConsole *vc = gd_vc_find_by_menu(s); 1023 GtkNotebook *nb = GTK_NOTEBOOK(s->notebook); 1024 GtkWidget *child; 1025 gint page; 1026 1027 gtk_release_modifiers(s); 1028 if (vc) { 1029 page = gtk_notebook_page_num(nb, vc->tab_item); 1030 gtk_notebook_set_current_page(nb, page); 1031 child = gtk_notebook_get_nth_page(nb, page); 1032 gtk_widget_grab_focus(child); 1033 } 1034 } 1035 1036 static void gd_accel_switch_vc(void *opaque) 1037 { 1038 VirtualConsole *vc = opaque; 1039 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); 1040 } 1041 1042 static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque) 1043 { 1044 GtkDisplayState *s = opaque; 1045 VirtualConsole *vc = gd_vc_find_current(s); 1046 1047 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) { 1048 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE); 1049 } else { 1050 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 1051 } 1052 gd_update_windowsize(vc); 1053 } 1054 1055 static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event, 1056 void *opaque) 1057 { 1058 VirtualConsole *vc = opaque; 1059 GtkDisplayState *s = vc->s; 1060 1061 gtk_widget_set_sensitive(vc->menu_item, true); 1062 gd_widget_reparent(vc->window, s->notebook, vc->tab_item); 1063 gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook), 1064 vc->tab_item, vc->label); 1065 gtk_widget_destroy(vc->window); 1066 vc->window = NULL; 1067 return TRUE; 1068 } 1069 1070 static gboolean gd_win_grab(void *opaque) 1071 { 1072 VirtualConsole *vc = opaque; 1073 1074 fprintf(stderr, "%s: %s\n", __func__, vc->label); 1075 if (vc->s->ptr_owner) { 1076 gd_ungrab_pointer(vc->s); 1077 } else { 1078 gd_grab_pointer(vc); 1079 } 1080 gd_update_caption(vc->s); 1081 return TRUE; 1082 } 1083 1084 static void gd_menu_untabify(GtkMenuItem *item, void *opaque) 1085 { 1086 GtkDisplayState *s = opaque; 1087 VirtualConsole *vc = gd_vc_find_current(s); 1088 1089 if (vc->type == GD_VC_GFX) { 1090 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 1091 FALSE); 1092 } 1093 if (!vc->window) { 1094 gtk_widget_set_sensitive(vc->menu_item, false); 1095 vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1096 gd_widget_reparent(s->notebook, vc->window, vc->tab_item); 1097 1098 g_signal_connect(vc->window, "delete-event", 1099 G_CALLBACK(gd_tab_window_close), vc); 1100 gtk_widget_show_all(vc->window); 1101 1102 GtkAccelGroup *ag = gtk_accel_group_new(); 1103 gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag); 1104 1105 GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab), vc, NULL); 1106 gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb); 1107 1108 gd_update_geometry_hints(vc); 1109 gd_update_caption(s); 1110 } 1111 } 1112 1113 static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) 1114 { 1115 GtkDisplayState *s = opaque; 1116 VirtualConsole *vc = gd_vc_find_current(s); 1117 1118 if (!s->full_screen) { 1119 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 1120 gtk_widget_hide(s->menu_bar); 1121 if (vc->type == GD_VC_GFX) { 1122 gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1); 1123 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 1124 TRUE); 1125 } 1126 gtk_window_fullscreen(GTK_WINDOW(s->window)); 1127 s->full_screen = TRUE; 1128 } else { 1129 gtk_window_unfullscreen(GTK_WINDOW(s->window)); 1130 gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s); 1131 gtk_widget_show(s->menu_bar); 1132 s->full_screen = FALSE; 1133 if (vc->type == GD_VC_GFX) { 1134 vc->gfx.scale_x = 1.0; 1135 vc->gfx.scale_y = 1.0; 1136 gd_update_windowsize(vc); 1137 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 1138 FALSE); 1139 } 1140 } 1141 1142 gd_update_cursor(vc); 1143 } 1144 1145 static void gd_accel_full_screen(void *opaque) 1146 { 1147 GtkDisplayState *s = opaque; 1148 gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item)); 1149 } 1150 1151 static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque) 1152 { 1153 GtkDisplayState *s = opaque; 1154 VirtualConsole *vc = gd_vc_find_current(s); 1155 1156 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), 1157 FALSE); 1158 1159 vc->gfx.scale_x += VC_SCALE_STEP; 1160 vc->gfx.scale_y += VC_SCALE_STEP; 1161 1162 gd_update_windowsize(vc); 1163 } 1164 1165 static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque) 1166 { 1167 GtkDisplayState *s = opaque; 1168 VirtualConsole *vc = gd_vc_find_current(s); 1169 1170 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), 1171 FALSE); 1172 1173 vc->gfx.scale_x -= VC_SCALE_STEP; 1174 vc->gfx.scale_y -= VC_SCALE_STEP; 1175 1176 vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN); 1177 vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN); 1178 1179 gd_update_windowsize(vc); 1180 } 1181 1182 static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque) 1183 { 1184 GtkDisplayState *s = opaque; 1185 VirtualConsole *vc = gd_vc_find_current(s); 1186 1187 vc->gfx.scale_x = 1.0; 1188 vc->gfx.scale_y = 1.0; 1189 1190 gd_update_windowsize(vc); 1191 } 1192 1193 static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque) 1194 { 1195 GtkDisplayState *s = opaque; 1196 VirtualConsole *vc = gd_vc_find_current(s); 1197 1198 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) { 1199 s->free_scale = TRUE; 1200 } else { 1201 s->free_scale = FALSE; 1202 vc->gfx.scale_x = 1.0; 1203 vc->gfx.scale_y = 1.0; 1204 } 1205 1206 gd_update_windowsize(vc); 1207 gd_update_full_redraw(vc); 1208 } 1209 1210 #if GTK_CHECK_VERSION(3, 0, 0) 1211 static void gd_grab_devices(VirtualConsole *vc, bool grab, 1212 GdkInputSource source, GdkEventMask mask, 1213 GdkCursor *cursor) 1214 { 1215 GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); 1216 GdkDeviceManager *mgr = gdk_display_get_device_manager(display); 1217 GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER); 1218 GList *tmp = devs; 1219 1220 for (tmp = devs; tmp; tmp = tmp->next) { 1221 GdkDevice *dev = tmp->data; 1222 if (gdk_device_get_source(dev) != source) { 1223 continue; 1224 } 1225 if (grab) { 1226 GdkWindow *win = gtk_widget_get_window(vc->gfx.drawing_area); 1227 gdk_device_grab(dev, win, GDK_OWNERSHIP_NONE, FALSE, 1228 mask, cursor, GDK_CURRENT_TIME); 1229 } else { 1230 gdk_device_ungrab(dev, GDK_CURRENT_TIME); 1231 } 1232 } 1233 g_list_free(devs); 1234 } 1235 #endif 1236 1237 static void gd_grab_keyboard(VirtualConsole *vc) 1238 { 1239 #if GTK_CHECK_VERSION(3, 0, 0) 1240 gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD, 1241 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, 1242 NULL); 1243 #else 1244 gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area), 1245 FALSE, 1246 GDK_CURRENT_TIME); 1247 #endif 1248 vc->s->kbd_owner = vc; 1249 trace_gd_grab(vc->label, "kbd", true); 1250 } 1251 1252 static void gd_ungrab_keyboard(GtkDisplayState *s) 1253 { 1254 VirtualConsole *vc = s->kbd_owner; 1255 1256 if (vc == NULL) { 1257 return; 1258 } 1259 s->kbd_owner = NULL; 1260 1261 #if GTK_CHECK_VERSION(3, 0, 0) 1262 gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL); 1263 #else 1264 gdk_keyboard_ungrab(GDK_CURRENT_TIME); 1265 #endif 1266 trace_gd_grab(vc->label, "kbd", false); 1267 } 1268 1269 static void gd_grab_pointer(VirtualConsole *vc) 1270 { 1271 GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); 1272 #if GTK_CHECK_VERSION(3, 0, 0) 1273 GdkDeviceManager *mgr = gdk_display_get_device_manager(display); 1274 gd_grab_devices(vc, true, GDK_SOURCE_MOUSE, 1275 GDK_POINTER_MOTION_MASK | 1276 GDK_BUTTON_PRESS_MASK | 1277 GDK_BUTTON_RELEASE_MASK | 1278 GDK_BUTTON_MOTION_MASK | 1279 GDK_SCROLL_MASK, 1280 vc->s->null_cursor); 1281 gdk_device_get_position(gdk_device_manager_get_client_pointer(mgr), 1282 NULL, &vc->s->grab_x_root, &vc->s->grab_y_root); 1283 #else 1284 gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area), 1285 FALSE, /* All events to come to our window directly */ 1286 GDK_POINTER_MOTION_MASK | 1287 GDK_BUTTON_PRESS_MASK | 1288 GDK_BUTTON_RELEASE_MASK | 1289 GDK_BUTTON_MOTION_MASK | 1290 GDK_SCROLL_MASK, 1291 NULL, /* Allow cursor to move over entire desktop */ 1292 vc->s->null_cursor, 1293 GDK_CURRENT_TIME); 1294 gdk_display_get_pointer(display, NULL, 1295 &vc->s->grab_x_root, &vc->s->grab_y_root, NULL); 1296 #endif 1297 vc->s->ptr_owner = vc; 1298 trace_gd_grab(vc->label, "ptr", true); 1299 } 1300 1301 static void gd_ungrab_pointer(GtkDisplayState *s) 1302 { 1303 VirtualConsole *vc = s->ptr_owner; 1304 1305 if (vc == NULL) { 1306 return; 1307 } 1308 s->ptr_owner = NULL; 1309 1310 GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); 1311 #if GTK_CHECK_VERSION(3, 0, 0) 1312 GdkDeviceManager *mgr = gdk_display_get_device_manager(display); 1313 gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL); 1314 gdk_device_warp(gdk_device_manager_get_client_pointer(mgr), 1315 gtk_widget_get_screen(vc->gfx.drawing_area), 1316 vc->s->grab_x_root, vc->s->grab_y_root); 1317 #else 1318 gdk_pointer_ungrab(GDK_CURRENT_TIME); 1319 gdk_display_warp_pointer(display, 1320 gtk_widget_get_screen(vc->gfx.drawing_area), 1321 vc->s->grab_x_root, vc->s->grab_y_root); 1322 #endif 1323 trace_gd_grab(vc->label, "ptr", false); 1324 } 1325 1326 static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) 1327 { 1328 GtkDisplayState *s = opaque; 1329 VirtualConsole *vc = gd_vc_find_current(s); 1330 1331 if (gd_is_grab_active(s)) { 1332 if (!gd_grab_on_hover(s)) { 1333 gd_grab_keyboard(vc); 1334 } 1335 gd_grab_pointer(vc); 1336 } else { 1337 gd_ungrab_keyboard(s); 1338 gd_ungrab_pointer(s); 1339 } 1340 1341 gd_update_caption(s); 1342 gd_update_cursor(vc); 1343 } 1344 1345 static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, 1346 gpointer data) 1347 { 1348 GtkDisplayState *s = data; 1349 VirtualConsole *vc; 1350 gboolean on_vga; 1351 1352 if (!gtk_widget_get_realized(s->notebook)) { 1353 return; 1354 } 1355 1356 #ifdef VTE_RESIZE_HACK 1357 vc = gd_vc_find_current(s); 1358 if (vc && vc->type == GD_VC_VTE) { 1359 gtk_widget_hide(vc->vte.terminal); 1360 } 1361 #endif 1362 vc = gd_vc_find_by_page(s, arg2); 1363 if (!vc) { 1364 return; 1365 } 1366 #ifdef VTE_RESIZE_HACK 1367 if (vc->type == GD_VC_VTE) { 1368 gtk_widget_show(vc->vte.terminal); 1369 } 1370 #endif 1371 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), 1372 TRUE); 1373 on_vga = (vc->type == GD_VC_GFX); 1374 if (!on_vga) { 1375 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 1376 FALSE); 1377 } else if (s->full_screen) { 1378 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 1379 TRUE); 1380 } 1381 gtk_widget_set_sensitive(s->grab_item, on_vga); 1382 1383 gd_update_windowsize(vc); 1384 gd_update_cursor(vc); 1385 } 1386 1387 static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, 1388 gpointer opaque) 1389 { 1390 VirtualConsole *vc = opaque; 1391 GtkDisplayState *s = vc->s; 1392 1393 if (gd_grab_on_hover(s)) { 1394 gd_ungrab_keyboard(s); 1395 gd_grab_keyboard(vc); 1396 gd_update_caption(s); 1397 } 1398 return TRUE; 1399 } 1400 1401 static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, 1402 gpointer opaque) 1403 { 1404 VirtualConsole *vc = opaque; 1405 GtkDisplayState *s = vc->s; 1406 1407 if (gd_grab_on_hover(s)) { 1408 gd_ungrab_keyboard(s); 1409 gd_update_caption(s); 1410 } 1411 return TRUE; 1412 } 1413 1414 static gboolean gd_focus_out_event(GtkWidget *widget, 1415 GdkEventCrossing *crossing, gpointer opaque) 1416 { 1417 VirtualConsole *vc = opaque; 1418 GtkDisplayState *s = vc->s; 1419 1420 gtk_release_modifiers(s); 1421 return TRUE; 1422 } 1423 1424 /** Virtual Console Callbacks **/ 1425 1426 static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc, 1427 int idx, GSList *group, GtkWidget *view_menu) 1428 { 1429 vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label); 1430 gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx, 1431 HOTKEY_MODIFIERS, 0, 1432 g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL)); 1433 #if GTK_CHECK_VERSION(3, 8, 0) 1434 gtk_accel_label_set_accel( 1435 GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))), 1436 GDK_KEY_1 + idx, HOTKEY_MODIFIERS); 1437 #endif 1438 1439 g_signal_connect(vc->menu_item, "activate", 1440 G_CALLBACK(gd_menu_switch_vc), s); 1441 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item); 1442 1443 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); 1444 return group; 1445 } 1446 1447 #if defined(CONFIG_VTE) 1448 static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque) 1449 { 1450 VirtualConsole *vc = opaque; 1451 1452 if (gtk_adjustment_get_upper(adjustment) > 1453 gtk_adjustment_get_page_size(adjustment)) { 1454 gtk_widget_show(vc->vte.scrollbar); 1455 } else { 1456 gtk_widget_hide(vc->vte.scrollbar); 1457 } 1458 } 1459 1460 static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len) 1461 { 1462 VirtualConsole *vc = chr->opaque; 1463 1464 vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len); 1465 return len; 1466 } 1467 1468 static int nb_vcs; 1469 static CharDriverState *vcs[MAX_VCS]; 1470 1471 static CharDriverState *gd_vc_handler(ChardevVC *unused) 1472 { 1473 CharDriverState *chr; 1474 1475 chr = g_malloc0(sizeof(*chr)); 1476 chr->chr_write = gd_vc_chr_write; 1477 /* defer OPENED events until our vc is fully initialized */ 1478 chr->explicit_be_open = true; 1479 1480 vcs[nb_vcs++] = chr; 1481 1482 return chr; 1483 } 1484 1485 static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size, 1486 gpointer user_data) 1487 { 1488 VirtualConsole *vc = user_data; 1489 1490 qemu_chr_be_write(vc->vte.chr, (uint8_t *)text, (unsigned int)size); 1491 return TRUE; 1492 } 1493 1494 static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, 1495 CharDriverState *chr, int idx, 1496 GSList *group, GtkWidget *view_menu) 1497 { 1498 char buffer[32]; 1499 GtkWidget *box; 1500 GtkWidget *scrollbar; 1501 GtkAdjustment *vadjustment; 1502 1503 vc->s = s; 1504 vc->vte.chr = chr; 1505 1506 snprintf(buffer, sizeof(buffer), "vc%d", idx); 1507 vc->label = g_strdup_printf("%s", vc->vte.chr->label 1508 ? vc->vte.chr->label : buffer); 1509 group = gd_vc_menu_init(s, vc, idx, group, view_menu); 1510 1511 vc->vte.terminal = vte_terminal_new(); 1512 g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc); 1513 1514 vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1); 1515 vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal), 1516 VC_TERM_X_MIN, VC_TERM_Y_MIN); 1517 1518 #if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0) 1519 vadjustment = gtk_scrollable_get_vadjustment 1520 (GTK_SCROLLABLE(vc->vte.terminal)); 1521 #else 1522 vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal)); 1523 #endif 1524 1525 #if GTK_CHECK_VERSION(3, 0, 0) 1526 box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2); 1527 scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment); 1528 #else 1529 box = gtk_hbox_new(false, 2); 1530 scrollbar = gtk_vscrollbar_new(vadjustment); 1531 #endif 1532 1533 gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0); 1534 gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0); 1535 1536 vc->vte.chr->opaque = vc; 1537 vc->vte.box = box; 1538 vc->vte.scrollbar = scrollbar; 1539 1540 g_signal_connect(vadjustment, "changed", 1541 G_CALLBACK(gd_vc_adjustment_changed), vc); 1542 1543 vc->type = GD_VC_VTE; 1544 vc->tab_item = box; 1545 gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item, 1546 gtk_label_new(vc->label)); 1547 1548 qemu_chr_be_generic_open(vc->vte.chr); 1549 if (vc->vte.chr->init) { 1550 vc->vte.chr->init(vc->vte.chr); 1551 } 1552 1553 return group; 1554 } 1555 1556 static void gd_vcs_init(GtkDisplayState *s, GSList *group, 1557 GtkWidget *view_menu) 1558 { 1559 int i; 1560 1561 for (i = 0; i < nb_vcs; i++) { 1562 VirtualConsole *vc = &s->vc[s->nb_vcs]; 1563 group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu); 1564 s->nb_vcs++; 1565 } 1566 } 1567 #endif /* CONFIG_VTE */ 1568 1569 /** Window Creation **/ 1570 1571 static void gd_connect_vc_gfx_signals(VirtualConsole *vc) 1572 { 1573 #if GTK_CHECK_VERSION(3, 0, 0) 1574 g_signal_connect(vc->gfx.drawing_area, "draw", 1575 G_CALLBACK(gd_draw_event), vc); 1576 #else 1577 g_signal_connect(vc->gfx.drawing_area, "expose-event", 1578 G_CALLBACK(gd_expose_event), vc); 1579 #endif 1580 g_signal_connect(vc->gfx.drawing_area, "event", 1581 G_CALLBACK(gd_event), vc); 1582 g_signal_connect(vc->gfx.drawing_area, "button-press-event", 1583 G_CALLBACK(gd_button_event), vc); 1584 g_signal_connect(vc->gfx.drawing_area, "button-release-event", 1585 G_CALLBACK(gd_button_event), vc); 1586 g_signal_connect(vc->gfx.drawing_area, "scroll-event", 1587 G_CALLBACK(gd_scroll_event), vc); 1588 g_signal_connect(vc->gfx.drawing_area, "key-press-event", 1589 G_CALLBACK(gd_key_event), vc); 1590 g_signal_connect(vc->gfx.drawing_area, "key-release-event", 1591 G_CALLBACK(gd_key_event), vc); 1592 1593 g_signal_connect(vc->gfx.drawing_area, "enter-notify-event", 1594 G_CALLBACK(gd_enter_event), vc); 1595 g_signal_connect(vc->gfx.drawing_area, "leave-notify-event", 1596 G_CALLBACK(gd_leave_event), vc); 1597 g_signal_connect(vc->gfx.drawing_area, "focus-out-event", 1598 G_CALLBACK(gd_focus_out_event), vc); 1599 } 1600 1601 static void gd_connect_signals(GtkDisplayState *s) 1602 { 1603 g_signal_connect(s->show_tabs_item, "activate", 1604 G_CALLBACK(gd_menu_show_tabs), s); 1605 g_signal_connect(s->untabify_item, "activate", 1606 G_CALLBACK(gd_menu_untabify), s); 1607 1608 g_signal_connect(s->window, "delete-event", 1609 G_CALLBACK(gd_window_close), s); 1610 1611 g_signal_connect(s->pause_item, "activate", 1612 G_CALLBACK(gd_menu_pause), s); 1613 g_signal_connect(s->reset_item, "activate", 1614 G_CALLBACK(gd_menu_reset), s); 1615 g_signal_connect(s->powerdown_item, "activate", 1616 G_CALLBACK(gd_menu_powerdown), s); 1617 g_signal_connect(s->quit_item, "activate", 1618 G_CALLBACK(gd_menu_quit), s); 1619 g_signal_connect(s->full_screen_item, "activate", 1620 G_CALLBACK(gd_menu_full_screen), s); 1621 g_signal_connect(s->zoom_in_item, "activate", 1622 G_CALLBACK(gd_menu_zoom_in), s); 1623 g_signal_connect(s->zoom_out_item, "activate", 1624 G_CALLBACK(gd_menu_zoom_out), s); 1625 g_signal_connect(s->zoom_fixed_item, "activate", 1626 G_CALLBACK(gd_menu_zoom_fixed), s); 1627 g_signal_connect(s->zoom_fit_item, "activate", 1628 G_CALLBACK(gd_menu_zoom_fit), s); 1629 g_signal_connect(s->grab_item, "activate", 1630 G_CALLBACK(gd_menu_grab_input), s); 1631 g_signal_connect(s->notebook, "switch-page", 1632 G_CALLBACK(gd_change_page), s); 1633 } 1634 1635 static GtkWidget *gd_create_menu_machine(GtkDisplayState *s) 1636 { 1637 GtkWidget *machine_menu; 1638 GtkWidget *separator; 1639 1640 machine_menu = gtk_menu_new(); 1641 gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group); 1642 1643 s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause")); 1644 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item); 1645 1646 separator = gtk_separator_menu_item_new(); 1647 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator); 1648 1649 s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset")); 1650 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item); 1651 1652 s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down")); 1653 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item); 1654 1655 separator = gtk_separator_menu_item_new(); 1656 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator); 1657 1658 s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit")); 1659 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item), 1660 "<QEMU>/Machine/Quit"); 1661 gtk_accel_map_add_entry("<QEMU>/Machine/Quit", 1662 GDK_KEY_q, HOTKEY_MODIFIERS); 1663 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item); 1664 1665 return machine_menu; 1666 } 1667 1668 static const DisplayChangeListenerOps dcl_ops = { 1669 .dpy_name = "gtk", 1670 .dpy_gfx_update = gd_update, 1671 .dpy_gfx_switch = gd_switch, 1672 .dpy_gfx_check_format = qemu_pixman_check_format, 1673 .dpy_refresh = gd_refresh, 1674 .dpy_mouse_set = gd_mouse_set, 1675 .dpy_cursor_define = gd_cursor_define, 1676 }; 1677 1678 static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, 1679 QemuConsole *con, int idx, 1680 GSList *group, GtkWidget *view_menu) 1681 { 1682 Object *obj; 1683 1684 obj = object_property_get_link(OBJECT(con), "device", NULL); 1685 if (obj) { 1686 vc->label = g_strdup_printf("%s", object_get_typename(obj)); 1687 } else { 1688 vc->label = g_strdup_printf("VGA"); 1689 } 1690 1691 vc->s = s; 1692 vc->gfx.scale_x = 1.0; 1693 vc->gfx.scale_y = 1.0; 1694 1695 vc->gfx.drawing_area = gtk_drawing_area_new(); 1696 gtk_widget_add_events(vc->gfx.drawing_area, 1697 GDK_POINTER_MOTION_MASK | 1698 GDK_BUTTON_PRESS_MASK | 1699 GDK_BUTTON_RELEASE_MASK | 1700 GDK_BUTTON_MOTION_MASK | 1701 GDK_ENTER_NOTIFY_MASK | 1702 GDK_LEAVE_NOTIFY_MASK | 1703 GDK_SCROLL_MASK | 1704 GDK_KEY_PRESS_MASK); 1705 gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE); 1706 1707 vc->type = GD_VC_GFX; 1708 vc->tab_item = vc->gfx.drawing_area; 1709 gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), 1710 vc->tab_item, gtk_label_new(vc->label)); 1711 gd_connect_vc_gfx_signals(vc); 1712 1713 group = gd_vc_menu_init(s, vc, idx, group, view_menu); 1714 1715 vc->gfx.dcl.ops = &dcl_ops; 1716 vc->gfx.dcl.con = con; 1717 register_displaychangelistener(&vc->gfx.dcl); 1718 1719 return group; 1720 } 1721 1722 static GtkWidget *gd_create_menu_view(GtkDisplayState *s) 1723 { 1724 GSList *group = NULL; 1725 GtkWidget *view_menu; 1726 GtkWidget *separator; 1727 QemuConsole *con; 1728 int vc; 1729 1730 view_menu = gtk_menu_new(); 1731 gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group); 1732 1733 s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen")); 1734 1735 gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0, 1736 g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL)); 1737 #if GTK_CHECK_VERSION(3, 8, 0) 1738 gtk_accel_label_set_accel( 1739 GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))), 1740 GDK_KEY_f, HOTKEY_MODIFIERS); 1741 #endif 1742 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item); 1743 1744 separator = gtk_separator_menu_item_new(); 1745 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 1746 1747 s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In")); 1748 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item), 1749 "<QEMU>/View/Zoom In"); 1750 gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus, 1751 HOTKEY_MODIFIERS); 1752 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item); 1753 1754 s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out")); 1755 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item), 1756 "<QEMU>/View/Zoom Out"); 1757 gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus, 1758 HOTKEY_MODIFIERS); 1759 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item); 1760 1761 s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit")); 1762 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item), 1763 "<QEMU>/View/Zoom Fixed"); 1764 gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0, 1765 HOTKEY_MODIFIERS); 1766 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item); 1767 1768 s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit")); 1769 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item); 1770 1771 separator = gtk_separator_menu_item_new(); 1772 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 1773 1774 s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover")); 1775 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item); 1776 1777 s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input")); 1778 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item), 1779 "<QEMU>/View/Grab Input"); 1780 gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g, 1781 HOTKEY_MODIFIERS); 1782 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item); 1783 1784 separator = gtk_separator_menu_item_new(); 1785 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 1786 1787 /* gfx */ 1788 for (vc = 0;; vc++) { 1789 con = qemu_console_lookup_by_index(vc); 1790 if (!con || !qemu_console_is_graphic(con)) { 1791 break; 1792 } 1793 group = gd_vc_gfx_init(s, &s->vc[vc], con, 1794 vc, group, view_menu); 1795 s->nb_vcs++; 1796 } 1797 1798 #if defined(CONFIG_VTE) 1799 /* vte */ 1800 gd_vcs_init(s, group, view_menu); 1801 #endif 1802 1803 separator = gtk_separator_menu_item_new(); 1804 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 1805 1806 s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs")); 1807 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item); 1808 1809 s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab")); 1810 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item); 1811 1812 return view_menu; 1813 } 1814 1815 static void gd_create_menus(GtkDisplayState *s) 1816 { 1817 s->accel_group = gtk_accel_group_new(); 1818 s->machine_menu = gd_create_menu_machine(s); 1819 s->view_menu = gd_create_menu_view(s); 1820 1821 s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine")); 1822 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item), 1823 s->machine_menu); 1824 gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item); 1825 1826 s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View")); 1827 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu); 1828 gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item); 1829 1830 g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group); 1831 gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group); 1832 } 1833 1834 static void gd_set_keycode_type(GtkDisplayState *s) 1835 { 1836 #ifdef GDK_WINDOWING_X11 1837 GdkDisplay *display = gtk_widget_get_display(s->window); 1838 if (GDK_IS_X11_DISPLAY(display)) { 1839 Display *x11_display = gdk_x11_display_get_xdisplay(display); 1840 XkbDescPtr desc = XkbGetKeyboard(x11_display, XkbGBN_AllComponentsMask, 1841 XkbUseCoreKbd); 1842 char *keycodes = NULL; 1843 1844 if (desc && desc->names) { 1845 keycodes = XGetAtomName(x11_display, desc->names->keycodes); 1846 } 1847 if (keycodes == NULL) { 1848 fprintf(stderr, "could not lookup keycode name\n"); 1849 } else if (strstart(keycodes, "evdev", NULL)) { 1850 s->has_evdev = true; 1851 } else if (!strstart(keycodes, "xfree86", NULL)) { 1852 fprintf(stderr, "unknown keycodes `%s', please report to " 1853 "qemu-devel@nongnu.org\n", keycodes); 1854 } 1855 1856 if (desc) { 1857 XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True); 1858 } 1859 if (keycodes) { 1860 XFree(keycodes); 1861 } 1862 } 1863 #endif 1864 } 1865 1866 void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) 1867 { 1868 GtkDisplayState *s = g_malloc0(sizeof(*s)); 1869 char *filename; 1870 1871 gtk_init(NULL, NULL); 1872 1873 s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1874 #if GTK_CHECK_VERSION(3, 2, 0) 1875 s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 1876 #else 1877 s->vbox = gtk_vbox_new(FALSE, 0); 1878 #endif 1879 s->notebook = gtk_notebook_new(); 1880 s->menu_bar = gtk_menu_bar_new(); 1881 1882 s->free_scale = FALSE; 1883 1884 setlocale(LC_ALL, ""); 1885 bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR); 1886 textdomain("qemu"); 1887 1888 s->null_cursor = gdk_cursor_new(GDK_BLANK_CURSOR); 1889 1890 s->mouse_mode_notifier.notify = gd_mouse_mode_change; 1891 qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier); 1892 qemu_add_vm_change_state_handler(gd_change_runstate, s); 1893 1894 filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg"); 1895 if (filename) { 1896 GError *error = NULL; 1897 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error); 1898 if (pixbuf) { 1899 gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf); 1900 } else { 1901 g_error_free(error); 1902 } 1903 g_free(filename); 1904 } 1905 1906 gd_create_menus(s); 1907 1908 gd_connect_signals(s); 1909 1910 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 1911 gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE); 1912 1913 gd_update_caption(s); 1914 1915 gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0); 1916 gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0); 1917 1918 gtk_container_add(GTK_CONTAINER(s->window), s->vbox); 1919 1920 gtk_widget_show_all(s->window); 1921 1922 #ifdef VTE_RESIZE_HACK 1923 { 1924 VirtualConsole *cur = gd_vc_find_current(s); 1925 if (cur) { 1926 int i; 1927 1928 for (i = 0; i < s->nb_vcs; i++) { 1929 VirtualConsole *vc = &s->vc[i]; 1930 if (vc && vc->type == GD_VC_VTE && vc != cur) { 1931 gtk_widget_hide(vc->vte.terminal); 1932 } 1933 } 1934 gd_update_windowsize(cur); 1935 } 1936 } 1937 #endif 1938 1939 if (full_screen) { 1940 gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item)); 1941 } 1942 if (grab_on_hover) { 1943 gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item)); 1944 } 1945 1946 gd_set_keycode_type(s); 1947 } 1948 1949 void early_gtk_display_init(void) 1950 { 1951 #if defined(CONFIG_VTE) 1952 register_vc_handler(gd_vc_handler); 1953 #endif 1954 } 1955