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