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 #include "qemu/osdep.h" 38 #include "qemu-common.h" 39 #include "qapi/error.h" 40 #include "qapi/qapi-commands-misc.h" 41 #include "qemu/cutils.h" 42 43 #include "ui/console.h" 44 #include "ui/gtk.h" 45 46 #include <glib/gi18n.h> 47 #include <locale.h> 48 #if defined(CONFIG_VTE) 49 #include <vte/vte.h> 50 #endif 51 #include <math.h> 52 53 #include "trace.h" 54 #include "ui/input.h" 55 #include "sysemu/sysemu.h" 56 #include "keymaps.h" 57 #include "chardev/char.h" 58 #include "qom/object.h" 59 60 #define MAX_VCS 10 61 #define VC_WINDOW_X_MIN 320 62 #define VC_WINDOW_Y_MIN 240 63 #define VC_TERM_X_MIN 80 64 #define VC_TERM_Y_MIN 25 65 #define VC_SCALE_MIN 0.25 66 #define VC_SCALE_STEP 0.25 67 68 #ifdef GDK_WINDOWING_X11 69 #include "ui/x_keymap.h" 70 71 /* Gtk2 compat */ 72 #ifndef GDK_IS_X11_DISPLAY 73 #define GDK_IS_X11_DISPLAY(dpy) (dpy != NULL) 74 #endif 75 #endif 76 77 78 #ifdef GDK_WINDOWING_WAYLAND 79 /* Gtk2 compat */ 80 #ifndef GDK_IS_WAYLAND_DISPLAY 81 #define GDK_IS_WAYLAND_DISPLAY(dpy) (dpy != NULL) 82 #endif 83 #endif 84 85 86 #ifdef GDK_WINDOWING_WIN32 87 /* Gtk2 compat */ 88 #ifndef GDK_IS_WIN32_DISPLAY 89 #define GDK_IS_WIN32_DISPLAY(dpy) (dpy != NULL) 90 #endif 91 #endif 92 93 94 #ifdef GDK_WINDOWING_BROADWAY 95 /* Gtk2 compat */ 96 #ifndef GDK_IS_BROADWAY_DISPLAY 97 #define GDK_IS_BROADWAY_DISPLAY(dpy) (dpy != NULL) 98 #endif 99 #endif 100 101 102 #ifdef GDK_WINDOWING_QUARTZ 103 /* Gtk2 compat */ 104 #ifndef GDK_IS_QUARTZ_DISPLAY 105 #define GDK_IS_QUARTZ_DISPLAY(dpy) (dpy != NULL) 106 #endif 107 #endif 108 109 110 #if !defined(CONFIG_VTE) 111 # define VTE_CHECK_VERSION(a, b, c) 0 112 #endif 113 114 #if defined(CONFIG_VTE) && !GTK_CHECK_VERSION(3, 0, 0) 115 /* 116 * The gtk2 vte terminal widget seriously messes up the window resize 117 * for some reason. You basically can't make the qemu window smaller 118 * any more because the toplevel window geoemtry hints are overridden. 119 * 120 * Workaround that by hiding all vte widgets, except the one in the 121 * current tab. 122 * 123 * Luckily everything works smooth in gtk3. 124 */ 125 # define VTE_RESIZE_HACK 1 126 #endif 127 128 #if !GTK_CHECK_VERSION(2, 20, 0) 129 #define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget) 130 #endif 131 132 #ifndef GDK_IS_X11_DISPLAY 133 #define GDK_IS_X11_DISPLAY(dpy) (dpy == dpy) 134 #endif 135 #ifndef GDK_IS_WAYLAND_DISPLAY 136 #define GDK_IS_WAYLAND_DISPLAY(dpy) (dpy == dpy) 137 #endif 138 #ifndef GDK_IS_WIN32_DISPLAY 139 #define GDK_IS_WIN32_DISPLAY(dpy) (dpy == dpy) 140 #endif 141 142 #if !GTK_CHECK_VERSION(2, 22, 0) 143 #define GDK_KEY_0 GDK_0 144 #define GDK_KEY_1 GDK_1 145 #define GDK_KEY_2 GDK_2 146 #define GDK_KEY_f GDK_f 147 #define GDK_KEY_g GDK_g 148 #define GDK_KEY_m GDK_m 149 #define GDK_KEY_q GDK_q 150 #define GDK_KEY_plus GDK_plus 151 #define GDK_KEY_equal GDK_equal 152 #define GDK_KEY_minus GDK_minus 153 #define GDK_KEY_Pause GDK_Pause 154 #define GDK_KEY_Delete GDK_Delete 155 #endif 156 157 /* Some older mingw versions lack this constant or have 158 * it conditionally defined */ 159 #ifdef _WIN32 160 # ifndef MAPVK_VK_TO_VSC 161 # define MAPVK_VK_TO_VSC 0 162 # endif 163 #endif 164 165 166 #define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK) 167 168 static const int modifier_keycode[] = { 169 Q_KEY_CODE_SHIFT, 170 Q_KEY_CODE_SHIFT_R, 171 Q_KEY_CODE_CTRL, 172 Q_KEY_CODE_CTRL_R, 173 Q_KEY_CODE_ALT, 174 Q_KEY_CODE_ALT_R, 175 Q_KEY_CODE_META_L, 176 Q_KEY_CODE_META_R, 177 }; 178 179 static const guint16 *keycode_map; 180 static size_t keycode_maplen; 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 *copy_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 GtkWidget *show_menubar_item; 213 214 GtkWidget *vbox; 215 GtkWidget *notebook; 216 int button_mask; 217 gboolean last_set; 218 int last_x; 219 int last_y; 220 int grab_x_root; 221 int grab_y_root; 222 VirtualConsole *kbd_owner; 223 VirtualConsole *ptr_owner; 224 225 gboolean full_screen; 226 227 GdkCursor *null_cursor; 228 Notifier mouse_mode_notifier; 229 gboolean free_scale; 230 231 bool external_pause_update; 232 233 bool modifier_pressed[ARRAY_SIZE(modifier_keycode)]; 234 bool ignore_keys; 235 236 DisplayOptions *opts; 237 }; 238 239 typedef struct VCChardev { 240 Chardev parent; 241 VirtualConsole *console; 242 bool echo; 243 } VCChardev; 244 245 #define TYPE_CHARDEV_VC "chardev-vc" 246 #define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC) 247 248 bool gtk_use_gl_area; 249 250 static void gd_grab_pointer(VirtualConsole *vc, const char *reason); 251 static void gd_ungrab_pointer(GtkDisplayState *s); 252 static void gd_grab_keyboard(VirtualConsole *vc, const char *reason); 253 static void gd_ungrab_keyboard(GtkDisplayState *s); 254 255 /** Utility Functions **/ 256 257 static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s) 258 { 259 VirtualConsole *vc; 260 gint i; 261 262 for (i = 0; i < s->nb_vcs; i++) { 263 vc = &s->vc[i]; 264 if (gtk_check_menu_item_get_active 265 (GTK_CHECK_MENU_ITEM(vc->menu_item))) { 266 return vc; 267 } 268 } 269 return NULL; 270 } 271 272 static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page) 273 { 274 VirtualConsole *vc; 275 gint i, p; 276 277 for (i = 0; i < s->nb_vcs; i++) { 278 vc = &s->vc[i]; 279 p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item); 280 if (p == page) { 281 return vc; 282 } 283 } 284 return NULL; 285 } 286 287 static VirtualConsole *gd_vc_find_current(GtkDisplayState *s) 288 { 289 gint page; 290 291 page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)); 292 return gd_vc_find_by_page(s, page); 293 } 294 295 static bool gd_is_grab_active(GtkDisplayState *s) 296 { 297 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item)); 298 } 299 300 static bool gd_grab_on_hover(GtkDisplayState *s) 301 { 302 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item)); 303 } 304 305 static void gd_update_cursor(VirtualConsole *vc) 306 { 307 GtkDisplayState *s = vc->s; 308 GdkWindow *window; 309 310 if (vc->type != GD_VC_GFX || 311 !qemu_console_is_graphic(vc->gfx.dcl.con)) { 312 return; 313 } 314 315 if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { 316 return; 317 } 318 319 window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area)); 320 if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) { 321 gdk_window_set_cursor(window, s->null_cursor); 322 } else { 323 gdk_window_set_cursor(window, NULL); 324 } 325 } 326 327 static void gd_update_caption(GtkDisplayState *s) 328 { 329 const char *status = ""; 330 gchar *prefix; 331 gchar *title; 332 const char *grab = ""; 333 bool is_paused = !runstate_is_running(); 334 int i; 335 336 if (qemu_name) { 337 prefix = g_strdup_printf("QEMU (%s)", qemu_name); 338 } else { 339 prefix = g_strdup_printf("QEMU"); 340 } 341 342 if (s->ptr_owner != NULL && 343 s->ptr_owner->window == NULL) { 344 grab = _(" - Press Ctrl+Alt+G to release grab"); 345 } 346 347 if (is_paused) { 348 status = _(" [Paused]"); 349 } 350 s->external_pause_update = true; 351 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item), 352 is_paused); 353 s->external_pause_update = false; 354 355 title = g_strdup_printf("%s%s%s", prefix, status, grab); 356 gtk_window_set_title(GTK_WINDOW(s->window), title); 357 g_free(title); 358 359 for (i = 0; i < s->nb_vcs; i++) { 360 VirtualConsole *vc = &s->vc[i]; 361 362 if (!vc->window) { 363 continue; 364 } 365 title = g_strdup_printf("%s: %s%s%s", prefix, vc->label, 366 vc == s->kbd_owner ? " +kbd" : "", 367 vc == s->ptr_owner ? " +ptr" : ""); 368 gtk_window_set_title(GTK_WINDOW(vc->window), title); 369 g_free(title); 370 } 371 372 g_free(prefix); 373 } 374 375 static void gd_update_geometry_hints(VirtualConsole *vc) 376 { 377 GtkDisplayState *s = vc->s; 378 GdkWindowHints mask = 0; 379 GdkGeometry geo = {}; 380 GtkWidget *geo_widget = NULL; 381 GtkWindow *geo_window; 382 383 if (vc->type == GD_VC_GFX) { 384 if (!vc->gfx.ds) { 385 return; 386 } 387 if (s->free_scale) { 388 geo.min_width = surface_width(vc->gfx.ds) * VC_SCALE_MIN; 389 geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN; 390 mask |= GDK_HINT_MIN_SIZE; 391 } else { 392 geo.min_width = surface_width(vc->gfx.ds) * vc->gfx.scale_x; 393 geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y; 394 mask |= GDK_HINT_MIN_SIZE; 395 } 396 geo_widget = vc->gfx.drawing_area; 397 gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height); 398 399 #if defined(CONFIG_VTE) 400 } else if (vc->type == GD_VC_VTE) { 401 VteTerminal *term = VTE_TERMINAL(vc->vte.terminal); 402 GtkBorder padding = { 0 }; 403 404 #if VTE_CHECK_VERSION(0, 37, 0) 405 gtk_style_context_get_padding( 406 gtk_widget_get_style_context(vc->vte.terminal), 407 gtk_widget_get_state_flags(vc->vte.terminal), 408 &padding); 409 #else 410 { 411 GtkBorder *ib = NULL; 412 gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL); 413 if (ib) { 414 padding = *ib; 415 gtk_border_free(ib); 416 } 417 } 418 #endif 419 420 geo.width_inc = vte_terminal_get_char_width(term); 421 geo.height_inc = vte_terminal_get_char_height(term); 422 mask |= GDK_HINT_RESIZE_INC; 423 geo.base_width = geo.width_inc; 424 geo.base_height = geo.height_inc; 425 mask |= GDK_HINT_BASE_SIZE; 426 geo.min_width = geo.width_inc * VC_TERM_X_MIN; 427 geo.min_height = geo.height_inc * VC_TERM_Y_MIN; 428 mask |= GDK_HINT_MIN_SIZE; 429 430 geo.base_width += padding.left + padding.right; 431 geo.base_height += padding.top + padding.bottom; 432 geo.min_width += padding.left + padding.right; 433 geo.min_height += padding.top + padding.bottom; 434 geo_widget = vc->vte.terminal; 435 #endif 436 } 437 438 geo_window = GTK_WINDOW(vc->window ? vc->window : s->window); 439 gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask); 440 } 441 442 void gd_update_windowsize(VirtualConsole *vc) 443 { 444 GtkDisplayState *s = vc->s; 445 446 gd_update_geometry_hints(vc); 447 448 if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) { 449 gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window), 450 VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN); 451 } 452 } 453 454 static void gd_update_full_redraw(VirtualConsole *vc) 455 { 456 GtkWidget *area = vc->gfx.drawing_area; 457 int ww, wh; 458 gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh); 459 #if defined(CONFIG_GTK_GL) 460 if (vc->gfx.gls && gtk_use_gl_area) { 461 gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); 462 return; 463 } 464 #endif 465 gtk_widget_queue_draw_area(area, 0, 0, ww, wh); 466 } 467 468 static void gtk_release_modifiers(GtkDisplayState *s) 469 { 470 VirtualConsole *vc = gd_vc_find_current(s); 471 int i, qcode; 472 473 if (vc->type != GD_VC_GFX || 474 !qemu_console_is_graphic(vc->gfx.dcl.con)) { 475 return; 476 } 477 for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { 478 qcode = modifier_keycode[i]; 479 if (!s->modifier_pressed[i]) { 480 continue; 481 } 482 qemu_input_event_send_key_qcode(vc->gfx.dcl.con, qcode, false); 483 s->modifier_pressed[i] = false; 484 } 485 } 486 487 static void gd_widget_reparent(GtkWidget *from, GtkWidget *to, 488 GtkWidget *widget) 489 { 490 g_object_ref(G_OBJECT(widget)); 491 gtk_container_remove(GTK_CONTAINER(from), widget); 492 gtk_container_add(GTK_CONTAINER(to), widget); 493 g_object_unref(G_OBJECT(widget)); 494 } 495 496 /** DisplayState Callbacks **/ 497 498 static void gd_update(DisplayChangeListener *dcl, 499 int x, int y, int w, int h) 500 { 501 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 502 GdkWindow *win; 503 int x1, x2, y1, y2; 504 int mx, my; 505 int fbw, fbh; 506 int ww, wh; 507 508 trace_gd_update(vc->label, x, y, w, h); 509 510 if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { 511 return; 512 } 513 514 if (vc->gfx.convert) { 515 pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, 516 NULL, vc->gfx.convert, 517 x, y, 0, 0, x, y, w, h); 518 } 519 520 x1 = floor(x * vc->gfx.scale_x); 521 y1 = floor(y * vc->gfx.scale_y); 522 523 x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x); 524 y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y); 525 526 fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; 527 fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; 528 529 win = gtk_widget_get_window(vc->gfx.drawing_area); 530 if (!win) { 531 return; 532 } 533 gdk_drawable_get_size(win, &ww, &wh); 534 535 mx = my = 0; 536 if (ww > fbw) { 537 mx = (ww - fbw) / 2; 538 } 539 if (wh > fbh) { 540 my = (wh - fbh) / 2; 541 } 542 543 gtk_widget_queue_draw_area(vc->gfx.drawing_area, 544 mx + x1, my + y1, (x2 - x1), (y2 - y1)); 545 } 546 547 static void gd_refresh(DisplayChangeListener *dcl) 548 { 549 graphic_hw_update(dcl->con); 550 } 551 552 #if GTK_CHECK_VERSION(3, 0, 0) 553 static GdkDevice *gd_get_pointer(GdkDisplay *dpy) 554 { 555 #if GTK_CHECK_VERSION(3, 20, 0) 556 return gdk_seat_get_pointer(gdk_display_get_default_seat(dpy)); 557 #else 558 return gdk_device_manager_get_client_pointer( 559 gdk_display_get_device_manager(dpy)); 560 #endif 561 } 562 563 static void gd_mouse_set(DisplayChangeListener *dcl, 564 int x, int y, int visible) 565 { 566 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 567 GdkDisplay *dpy; 568 gint x_root, y_root; 569 570 if (qemu_input_is_absolute()) { 571 return; 572 } 573 574 dpy = gtk_widget_get_display(vc->gfx.drawing_area); 575 gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area), 576 x, y, &x_root, &y_root); 577 gdk_device_warp(gd_get_pointer(dpy), 578 gtk_widget_get_screen(vc->gfx.drawing_area), 579 x_root, y_root); 580 vc->s->last_x = x; 581 vc->s->last_y = y; 582 } 583 #else 584 static void gd_mouse_set(DisplayChangeListener *dcl, 585 int x, int y, int visible) 586 { 587 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 588 gint x_root, y_root; 589 590 if (qemu_input_is_absolute()) { 591 return; 592 } 593 594 gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area), 595 x, y, &x_root, &y_root); 596 gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area), 597 gtk_widget_get_screen(vc->gfx.drawing_area), 598 x_root, y_root); 599 } 600 #endif 601 602 static void gd_cursor_define(DisplayChangeListener *dcl, 603 QEMUCursor *c) 604 { 605 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 606 GdkPixbuf *pixbuf; 607 GdkCursor *cursor; 608 609 if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { 610 return; 611 } 612 613 pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data), 614 GDK_COLORSPACE_RGB, true, 8, 615 c->width, c->height, c->width * 4, 616 NULL, NULL); 617 cursor = gdk_cursor_new_from_pixbuf 618 (gtk_widget_get_display(vc->gfx.drawing_area), 619 pixbuf, c->hot_x, c->hot_y); 620 gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor); 621 g_object_unref(pixbuf); 622 #if !GTK_CHECK_VERSION(3, 0, 0) 623 gdk_cursor_unref(cursor); 624 #else 625 g_object_unref(cursor); 626 #endif 627 } 628 629 static void gd_switch(DisplayChangeListener *dcl, 630 DisplaySurface *surface) 631 { 632 VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); 633 bool resized = true; 634 635 trace_gd_switch(vc->label, 636 surface ? surface_width(surface) : 0, 637 surface ? surface_height(surface) : 0); 638 639 if (vc->gfx.surface) { 640 cairo_surface_destroy(vc->gfx.surface); 641 vc->gfx.surface = NULL; 642 } 643 if (vc->gfx.convert) { 644 pixman_image_unref(vc->gfx.convert); 645 vc->gfx.convert = NULL; 646 } 647 648 if (vc->gfx.ds && surface && 649 surface_width(vc->gfx.ds) == surface_width(surface) && 650 surface_height(vc->gfx.ds) == surface_height(surface)) { 651 resized = false; 652 } 653 vc->gfx.ds = surface; 654 655 if (!surface) { 656 return; 657 } 658 659 if (surface->format == PIXMAN_x8r8g8b8) { 660 /* 661 * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24 662 * 663 * No need to convert, use surface directly. Should be the 664 * common case as this is qemu_default_pixelformat(32) too. 665 */ 666 vc->gfx.surface = cairo_image_surface_create_for_data 667 (surface_data(surface), 668 CAIRO_FORMAT_RGB24, 669 surface_width(surface), 670 surface_height(surface), 671 surface_stride(surface)); 672 } else { 673 /* Must convert surface, use pixman to do it. */ 674 vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8, 675 surface_width(surface), 676 surface_height(surface), 677 NULL, 0); 678 vc->gfx.surface = cairo_image_surface_create_for_data 679 ((void *)pixman_image_get_data(vc->gfx.convert), 680 CAIRO_FORMAT_RGB24, 681 pixman_image_get_width(vc->gfx.convert), 682 pixman_image_get_height(vc->gfx.convert), 683 pixman_image_get_stride(vc->gfx.convert)); 684 pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, 685 NULL, vc->gfx.convert, 686 0, 0, 0, 0, 0, 0, 687 pixman_image_get_width(vc->gfx.convert), 688 pixman_image_get_height(vc->gfx.convert)); 689 } 690 691 if (resized) { 692 gd_update_windowsize(vc); 693 } else { 694 gd_update_full_redraw(vc); 695 } 696 } 697 698 static const DisplayChangeListenerOps dcl_ops = { 699 .dpy_name = "gtk", 700 .dpy_gfx_update = gd_update, 701 .dpy_gfx_switch = gd_switch, 702 .dpy_gfx_check_format = qemu_pixman_check_format, 703 .dpy_refresh = gd_refresh, 704 .dpy_mouse_set = gd_mouse_set, 705 .dpy_cursor_define = gd_cursor_define, 706 }; 707 708 709 #if defined(CONFIG_OPENGL) 710 711 /** DisplayState Callbacks (opengl version) **/ 712 713 #if defined(CONFIG_GTK_GL) 714 715 static const DisplayChangeListenerOps dcl_gl_area_ops = { 716 .dpy_name = "gtk-egl", 717 .dpy_gfx_update = gd_gl_area_update, 718 .dpy_gfx_switch = gd_gl_area_switch, 719 .dpy_gfx_check_format = console_gl_check_format, 720 .dpy_refresh = gd_gl_area_refresh, 721 .dpy_mouse_set = gd_mouse_set, 722 .dpy_cursor_define = gd_cursor_define, 723 724 .dpy_gl_ctx_create = gd_gl_area_create_context, 725 .dpy_gl_ctx_destroy = gd_gl_area_destroy_context, 726 .dpy_gl_ctx_make_current = gd_gl_area_make_current, 727 .dpy_gl_ctx_get_current = gd_gl_area_get_current_context, 728 .dpy_gl_scanout_texture = gd_gl_area_scanout_texture, 729 .dpy_gl_update = gd_gl_area_scanout_flush, 730 }; 731 732 #endif /* CONFIG_GTK_GL */ 733 734 static const DisplayChangeListenerOps dcl_egl_ops = { 735 .dpy_name = "gtk-egl", 736 .dpy_gfx_update = gd_egl_update, 737 .dpy_gfx_switch = gd_egl_switch, 738 .dpy_gfx_check_format = console_gl_check_format, 739 .dpy_refresh = gd_egl_refresh, 740 .dpy_mouse_set = gd_mouse_set, 741 .dpy_cursor_define = gd_cursor_define, 742 743 .dpy_gl_ctx_create = gd_egl_create_context, 744 .dpy_gl_ctx_destroy = qemu_egl_destroy_context, 745 .dpy_gl_ctx_make_current = gd_egl_make_current, 746 .dpy_gl_ctx_get_current = qemu_egl_get_current_context, 747 .dpy_gl_scanout_disable = gd_egl_scanout_disable, 748 .dpy_gl_scanout_texture = gd_egl_scanout_texture, 749 .dpy_gl_scanout_dmabuf = gd_egl_scanout_dmabuf, 750 .dpy_gl_cursor_dmabuf = gd_egl_cursor_dmabuf, 751 .dpy_gl_cursor_position = gd_egl_cursor_position, 752 .dpy_gl_release_dmabuf = gd_egl_release_dmabuf, 753 .dpy_gl_update = gd_egl_scanout_flush, 754 }; 755 756 #endif /* CONFIG_OPENGL */ 757 758 /** QEMU Events **/ 759 760 static void gd_change_runstate(void *opaque, int running, RunState state) 761 { 762 GtkDisplayState *s = opaque; 763 764 gd_update_caption(s); 765 } 766 767 static void gd_mouse_mode_change(Notifier *notify, void *data) 768 { 769 GtkDisplayState *s; 770 int i; 771 772 s = container_of(notify, GtkDisplayState, mouse_mode_notifier); 773 /* release the grab at switching to absolute mode */ 774 if (qemu_input_is_absolute() && gd_is_grab_active(s)) { 775 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 776 FALSE); 777 } 778 for (i = 0; i < s->nb_vcs; i++) { 779 VirtualConsole *vc = &s->vc[i]; 780 gd_update_cursor(vc); 781 } 782 } 783 784 /** GTK Events **/ 785 786 static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event, 787 void *opaque) 788 { 789 GtkDisplayState *s = opaque; 790 bool allow_close = true; 791 792 if (s->opts->has_window_close && !s->opts->window_close) { 793 allow_close = false; 794 } 795 796 if (allow_close) { 797 qmp_quit(NULL); 798 } 799 800 return TRUE; 801 } 802 803 static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height) 804 { 805 QemuUIInfo info; 806 807 memset(&info, 0, sizeof(info)); 808 info.width = width; 809 info.height = height; 810 dpy_set_ui_info(vc->gfx.dcl.con, &info); 811 } 812 813 #if defined(CONFIG_GTK_GL) 814 815 static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context, 816 void *opaque) 817 { 818 VirtualConsole *vc = opaque; 819 820 if (vc->gfx.gls) { 821 gd_gl_area_draw(vc); 822 } 823 return TRUE; 824 } 825 826 static void gd_resize_event(GtkGLArea *area, 827 gint width, gint height, gpointer *opaque) 828 { 829 VirtualConsole *vc = (void *)opaque; 830 831 gd_set_ui_info(vc, width, height); 832 } 833 834 #endif 835 836 static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) 837 { 838 VirtualConsole *vc = opaque; 839 GtkDisplayState *s = vc->s; 840 int mx, my; 841 int ww, wh; 842 int fbw, fbh; 843 844 #if defined(CONFIG_OPENGL) 845 if (vc->gfx.gls) { 846 if (gtk_use_gl_area) { 847 /* invoke render callback please */ 848 return FALSE; 849 } else { 850 gd_egl_draw(vc); 851 return TRUE; 852 } 853 } 854 #endif 855 856 if (!gtk_widget_get_realized(widget)) { 857 return FALSE; 858 } 859 if (!vc->gfx.ds) { 860 return FALSE; 861 } 862 863 fbw = surface_width(vc->gfx.ds); 864 fbh = surface_height(vc->gfx.ds); 865 866 gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh); 867 868 if (s->full_screen) { 869 vc->gfx.scale_x = (double)ww / fbw; 870 vc->gfx.scale_y = (double)wh / fbh; 871 } else if (s->free_scale) { 872 double sx, sy; 873 874 sx = (double)ww / fbw; 875 sy = (double)wh / fbh; 876 877 vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); 878 } 879 880 fbw *= vc->gfx.scale_x; 881 fbh *= vc->gfx.scale_y; 882 883 mx = my = 0; 884 if (ww > fbw) { 885 mx = (ww - fbw) / 2; 886 } 887 if (wh > fbh) { 888 my = (wh - fbh) / 2; 889 } 890 891 cairo_rectangle(cr, 0, 0, ww, wh); 892 893 /* Optionally cut out the inner area where the pixmap 894 will be drawn. This avoids 'flashing' since we're 895 not double-buffering. Note we're using the undocumented 896 behaviour of drawing the rectangle from right to left 897 to cut out the whole */ 898 cairo_rectangle(cr, mx + fbw, my, 899 -1 * fbw, fbh); 900 cairo_fill(cr); 901 902 cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y); 903 cairo_set_source_surface(cr, vc->gfx.surface, 904 mx / vc->gfx.scale_x, my / vc->gfx.scale_y); 905 cairo_paint(cr); 906 907 return TRUE; 908 } 909 910 #if !GTK_CHECK_VERSION(3, 0, 0) 911 static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose, 912 void *opaque) 913 { 914 cairo_t *cr; 915 gboolean ret; 916 917 cr = gdk_cairo_create(gtk_widget_get_window(widget)); 918 cairo_rectangle(cr, 919 expose->area.x, 920 expose->area.y, 921 expose->area.width, 922 expose->area.height); 923 cairo_clip(cr); 924 925 ret = gd_draw_event(widget, cr, opaque); 926 927 cairo_destroy(cr); 928 929 return ret; 930 } 931 #endif 932 933 static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, 934 void *opaque) 935 { 936 VirtualConsole *vc = opaque; 937 GtkDisplayState *s = vc->s; 938 int x, y; 939 int mx, my; 940 int fbh, fbw; 941 int ww, wh; 942 943 if (!vc->gfx.ds) { 944 return TRUE; 945 } 946 947 fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; 948 fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; 949 950 gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area), 951 &ww, &wh); 952 953 mx = my = 0; 954 if (ww > fbw) { 955 mx = (ww - fbw) / 2; 956 } 957 if (wh > fbh) { 958 my = (wh - fbh) / 2; 959 } 960 961 x = (motion->x - mx) / vc->gfx.scale_x; 962 y = (motion->y - my) / vc->gfx.scale_y; 963 964 if (qemu_input_is_absolute()) { 965 if (x < 0 || y < 0 || 966 x >= surface_width(vc->gfx.ds) || 967 y >= surface_height(vc->gfx.ds)) { 968 return TRUE; 969 } 970 qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x, 971 0, surface_width(vc->gfx.ds)); 972 qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y, 973 0, surface_height(vc->gfx.ds)); 974 qemu_input_event_sync(); 975 } else if (s->last_set && s->ptr_owner == vc) { 976 qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x); 977 qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y); 978 qemu_input_event_sync(); 979 } 980 s->last_x = x; 981 s->last_y = y; 982 s->last_set = TRUE; 983 984 if (!qemu_input_is_absolute() && s->ptr_owner == vc) { 985 GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area); 986 int screen_width, screen_height; 987 988 int x = (int)motion->x_root; 989 int y = (int)motion->y_root; 990 991 #if GTK_CHECK_VERSION(3, 22, 0) 992 { 993 GdkDisplay *dpy = gtk_widget_get_display(widget); 994 GdkWindow *win = gtk_widget_get_window(widget); 995 GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win); 996 GdkRectangle geometry; 997 gdk_monitor_get_geometry(monitor, &geometry); 998 screen_width = geometry.width; 999 screen_height = geometry.height; 1000 } 1001 #else 1002 { 1003 screen_width = gdk_screen_get_width(screen); 1004 screen_height = gdk_screen_get_height(screen); 1005 } 1006 #endif 1007 1008 /* In relative mode check to see if client pointer hit 1009 * one of the screen edges, and if so move it back by 1010 * 200 pixels. This is important because the pointer 1011 * in the server doesn't correspond 1-for-1, and so 1012 * may still be only half way across the screen. Without 1013 * this warp, the server pointer would thus appear to hit 1014 * an invisible wall */ 1015 if (x == 0) { 1016 x += 200; 1017 } 1018 if (y == 0) { 1019 y += 200; 1020 } 1021 if (x == (screen_width - 1)) { 1022 x -= 200; 1023 } 1024 if (y == (screen_height - 1)) { 1025 y -= 200; 1026 } 1027 1028 if (x != (int)motion->x_root || y != (int)motion->y_root) { 1029 #if GTK_CHECK_VERSION(3, 0, 0) 1030 GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion); 1031 gdk_device_warp(dev, screen, x, y); 1032 #else 1033 GdkDisplay *display = gtk_widget_get_display(widget); 1034 gdk_display_warp_pointer(display, screen, x, y); 1035 #endif 1036 s->last_set = FALSE; 1037 return FALSE; 1038 } 1039 } 1040 return TRUE; 1041 } 1042 1043 static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, 1044 void *opaque) 1045 { 1046 VirtualConsole *vc = opaque; 1047 GtkDisplayState *s = vc->s; 1048 InputButton btn; 1049 1050 /* implicitly grab the input at the first click in the relative mode */ 1051 if (button->button == 1 && button->type == GDK_BUTTON_PRESS && 1052 !qemu_input_is_absolute() && s->ptr_owner != vc) { 1053 if (!vc->window) { 1054 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 1055 TRUE); 1056 } else { 1057 gd_grab_pointer(vc, "relative-mode-click"); 1058 } 1059 return TRUE; 1060 } 1061 1062 if (button->button == 1) { 1063 btn = INPUT_BUTTON_LEFT; 1064 } else if (button->button == 2) { 1065 btn = INPUT_BUTTON_MIDDLE; 1066 } else if (button->button == 3) { 1067 btn = INPUT_BUTTON_RIGHT; 1068 } else if (button->button == 8) { 1069 btn = INPUT_BUTTON_SIDE; 1070 } else if (button->button == 9) { 1071 btn = INPUT_BUTTON_EXTRA; 1072 } else { 1073 return TRUE; 1074 } 1075 1076 qemu_input_queue_btn(vc->gfx.dcl.con, btn, 1077 button->type == GDK_BUTTON_PRESS); 1078 qemu_input_event_sync(); 1079 return TRUE; 1080 } 1081 1082 static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll, 1083 void *opaque) 1084 { 1085 VirtualConsole *vc = opaque; 1086 InputButton btn; 1087 1088 if (scroll->direction == GDK_SCROLL_UP) { 1089 btn = INPUT_BUTTON_WHEEL_UP; 1090 } else if (scroll->direction == GDK_SCROLL_DOWN) { 1091 btn = INPUT_BUTTON_WHEEL_DOWN; 1092 #if GTK_CHECK_VERSION(3, 4, 0) 1093 } else if (scroll->direction == GDK_SCROLL_SMOOTH) { 1094 gdouble delta_x, delta_y; 1095 if (!gdk_event_get_scroll_deltas((GdkEvent *)scroll, 1096 &delta_x, &delta_y)) { 1097 return TRUE; 1098 } 1099 if (delta_y > 0) { 1100 btn = INPUT_BUTTON_WHEEL_DOWN; 1101 } else { 1102 btn = INPUT_BUTTON_WHEEL_UP; 1103 } 1104 #endif 1105 } else { 1106 return TRUE; 1107 } 1108 1109 qemu_input_queue_btn(vc->gfx.dcl.con, btn, true); 1110 qemu_input_event_sync(); 1111 qemu_input_queue_btn(vc->gfx.dcl.con, btn, false); 1112 qemu_input_event_sync(); 1113 return TRUE; 1114 } 1115 1116 1117 static const guint16 *gd_get_keymap(size_t *maplen) 1118 { 1119 GdkDisplay *dpy = gdk_display_get_default(); 1120 1121 #ifdef GDK_WINDOWING_X11 1122 if (GDK_IS_X11_DISPLAY(dpy)) { 1123 trace_gd_keymap_windowing("x11"); 1124 return qemu_xkeymap_mapping_table( 1125 gdk_x11_display_get_xdisplay(dpy), maplen); 1126 } 1127 #endif 1128 1129 #ifdef GDK_WINDOWING_WAYLAND 1130 if (GDK_IS_WAYLAND_DISPLAY(dpy)) { 1131 trace_gd_keymap_windowing("wayland"); 1132 *maplen = qemu_input_map_xorgevdev_to_qcode_len; 1133 return qemu_input_map_xorgevdev_to_qcode; 1134 } 1135 #endif 1136 1137 #ifdef GDK_WINDOWING_WIN32 1138 if (GDK_IS_WIN32_DISPLAY(dpy)) { 1139 trace_gd_keymap_windowing("win32"); 1140 *maplen = qemu_input_map_win32_to_qcode_len; 1141 return qemu_input_map_win32_to_qcode; 1142 } 1143 #endif 1144 1145 #ifdef GDK_WINDOWING_QUARTZ 1146 if (GDK_IS_QUARTZ_DISPLAY(dpy)) { 1147 trace_gd_keymap_windowing("quartz"); 1148 *maplen = qemu_input_map_osx_to_qcode_len; 1149 return qemu_input_map_osx_to_qcode; 1150 } 1151 #endif 1152 1153 #ifdef GDK_WINDOWING_BROADWAY 1154 if (GDK_IS_BROADWAY_DISPLAY(dpy)) { 1155 trace_gd_keymap_windowing("broadway"); 1156 g_warning("experimental: using broadway, x11 virtual keysym\n" 1157 "mapping - with very limited support. See also\n" 1158 "https://bugzilla.gnome.org/show_bug.cgi?id=700105"); 1159 *maplen = qemu_input_map_x11_to_qcode_len; 1160 return qemu_input_map_x11_to_qcode; 1161 } 1162 #endif 1163 1164 g_warning("Unsupported GDK Windowing platform.\n" 1165 "Disabling extended keycode tables.\n" 1166 "Please report to qemu-devel@nongnu.org\n" 1167 "including the following information:\n" 1168 "\n" 1169 " - Operating system\n" 1170 " - GDK Windowing system build\n"); 1171 return NULL; 1172 } 1173 1174 1175 static int gd_map_keycode(int scancode) 1176 { 1177 if (!keycode_map) { 1178 return 0; 1179 } 1180 if (scancode > keycode_maplen) { 1181 return 0; 1182 } 1183 1184 return keycode_map[scancode]; 1185 } 1186 1187 static gboolean gd_text_key_down(GtkWidget *widget, 1188 GdkEventKey *key, void *opaque) 1189 { 1190 VirtualConsole *vc = opaque; 1191 QemuConsole *con = vc->gfx.dcl.con; 1192 1193 if (key->keyval == GDK_KEY_Delete) { 1194 kbd_put_qcode_console(con, Q_KEY_CODE_DELETE, false); 1195 } else if (key->length) { 1196 kbd_put_string_console(con, key->string, key->length); 1197 } else { 1198 int qcode = gd_map_keycode(key->hardware_keycode); 1199 kbd_put_qcode_console(con, qcode, false); 1200 } 1201 return TRUE; 1202 } 1203 1204 static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) 1205 { 1206 VirtualConsole *vc = opaque; 1207 GtkDisplayState *s = vc->s; 1208 int qcode; 1209 int i; 1210 1211 if (s->ignore_keys) { 1212 s->ignore_keys = (key->type == GDK_KEY_PRESS); 1213 return TRUE; 1214 } 1215 1216 #ifdef WIN32 1217 /* on windows, we ought to ignore the reserved key event? */ 1218 if (key->hardware_keycode == 0xff) 1219 return false; 1220 #endif 1221 1222 if (key->keyval == GDK_KEY_Pause 1223 #ifdef G_OS_WIN32 1224 /* for some reason GDK does not fill keyval for VK_PAUSE 1225 * See https://bugzilla.gnome.org/show_bug.cgi?id=769214 1226 */ 1227 || key->hardware_keycode == VK_PAUSE 1228 #endif 1229 ) { 1230 qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE, 1231 key->type == GDK_KEY_PRESS); 1232 return TRUE; 1233 } 1234 1235 qcode = gd_map_keycode(key->hardware_keycode); 1236 1237 trace_gd_key_event(vc->label, key->hardware_keycode, qcode, 1238 (key->type == GDK_KEY_PRESS) ? "down" : "up"); 1239 1240 for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { 1241 if (qcode == modifier_keycode[i]) { 1242 s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS); 1243 } 1244 } 1245 1246 qemu_input_event_send_key_qcode(vc->gfx.dcl.con, qcode, 1247 key->type == GDK_KEY_PRESS); 1248 1249 return TRUE; 1250 } 1251 1252 static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque) 1253 { 1254 if (event->type == GDK_MOTION_NOTIFY) { 1255 return gd_motion_event(widget, &event->motion, opaque); 1256 } 1257 return FALSE; 1258 } 1259 1260 /** Window Menu Actions **/ 1261 1262 static void gd_menu_pause(GtkMenuItem *item, void *opaque) 1263 { 1264 GtkDisplayState *s = opaque; 1265 1266 if (s->external_pause_update) { 1267 return; 1268 } 1269 if (runstate_is_running()) { 1270 qmp_stop(NULL); 1271 } else { 1272 qmp_cont(NULL); 1273 } 1274 } 1275 1276 static void gd_menu_reset(GtkMenuItem *item, void *opaque) 1277 { 1278 qmp_system_reset(NULL); 1279 } 1280 1281 static void gd_menu_powerdown(GtkMenuItem *item, void *opaque) 1282 { 1283 qmp_system_powerdown(NULL); 1284 } 1285 1286 static void gd_menu_quit(GtkMenuItem *item, void *opaque) 1287 { 1288 qmp_quit(NULL); 1289 } 1290 1291 static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque) 1292 { 1293 GtkDisplayState *s = opaque; 1294 VirtualConsole *vc = gd_vc_find_by_menu(s); 1295 GtkNotebook *nb = GTK_NOTEBOOK(s->notebook); 1296 gint page; 1297 1298 gtk_release_modifiers(s); 1299 if (vc) { 1300 page = gtk_notebook_page_num(nb, vc->tab_item); 1301 gtk_notebook_set_current_page(nb, page); 1302 gtk_widget_grab_focus(vc->focus); 1303 } 1304 s->ignore_keys = false; 1305 } 1306 1307 static void gd_accel_switch_vc(void *opaque) 1308 { 1309 VirtualConsole *vc = opaque; 1310 1311 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); 1312 #if !GTK_CHECK_VERSION(3, 0, 0) 1313 /* GTK2 sends the accel key to the target console - ignore this until */ 1314 vc->s->ignore_keys = true; 1315 #endif 1316 } 1317 1318 static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque) 1319 { 1320 GtkDisplayState *s = opaque; 1321 VirtualConsole *vc = gd_vc_find_current(s); 1322 1323 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) { 1324 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE); 1325 } else { 1326 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 1327 } 1328 gd_update_windowsize(vc); 1329 } 1330 1331 static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event, 1332 void *opaque) 1333 { 1334 VirtualConsole *vc = opaque; 1335 GtkDisplayState *s = vc->s; 1336 1337 gtk_widget_set_sensitive(vc->menu_item, true); 1338 gd_widget_reparent(vc->window, s->notebook, vc->tab_item); 1339 gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook), 1340 vc->tab_item, vc->label); 1341 gtk_widget_destroy(vc->window); 1342 vc->window = NULL; 1343 return TRUE; 1344 } 1345 1346 static gboolean gd_win_grab(void *opaque) 1347 { 1348 VirtualConsole *vc = opaque; 1349 1350 fprintf(stderr, "%s: %s\n", __func__, vc->label); 1351 if (vc->s->ptr_owner) { 1352 gd_ungrab_pointer(vc->s); 1353 } else { 1354 gd_grab_pointer(vc, "user-request-detached-tab"); 1355 } 1356 return TRUE; 1357 } 1358 1359 static void gd_menu_untabify(GtkMenuItem *item, void *opaque) 1360 { 1361 GtkDisplayState *s = opaque; 1362 VirtualConsole *vc = gd_vc_find_current(s); 1363 1364 if (vc->type == GD_VC_GFX && 1365 qemu_console_is_graphic(vc->gfx.dcl.con)) { 1366 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 1367 FALSE); 1368 } 1369 if (!vc->window) { 1370 gtk_widget_set_sensitive(vc->menu_item, false); 1371 vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1372 gd_widget_reparent(s->notebook, vc->window, vc->tab_item); 1373 1374 g_signal_connect(vc->window, "delete-event", 1375 G_CALLBACK(gd_tab_window_close), vc); 1376 gtk_widget_show_all(vc->window); 1377 1378 if (qemu_console_is_graphic(vc->gfx.dcl.con)) { 1379 GtkAccelGroup *ag = gtk_accel_group_new(); 1380 gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag); 1381 1382 GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab), 1383 vc, NULL); 1384 gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb); 1385 } 1386 1387 gd_update_geometry_hints(vc); 1388 gd_update_caption(s); 1389 } 1390 } 1391 1392 static void gd_menu_show_menubar(GtkMenuItem *item, void *opaque) 1393 { 1394 GtkDisplayState *s = opaque; 1395 VirtualConsole *vc = gd_vc_find_current(s); 1396 1397 if (s->full_screen) { 1398 return; 1399 } 1400 1401 if (gtk_check_menu_item_get_active( 1402 GTK_CHECK_MENU_ITEM(s->show_menubar_item))) { 1403 gtk_widget_show(s->menu_bar); 1404 } else { 1405 gtk_widget_hide(s->menu_bar); 1406 } 1407 gd_update_windowsize(vc); 1408 } 1409 1410 static void gd_accel_show_menubar(void *opaque) 1411 { 1412 GtkDisplayState *s = opaque; 1413 gtk_menu_item_activate(GTK_MENU_ITEM(s->show_menubar_item)); 1414 } 1415 1416 static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) 1417 { 1418 GtkDisplayState *s = opaque; 1419 VirtualConsole *vc = gd_vc_find_current(s); 1420 1421 if (!s->full_screen) { 1422 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 1423 gtk_widget_hide(s->menu_bar); 1424 if (vc->type == GD_VC_GFX) { 1425 gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1); 1426 } 1427 gtk_window_fullscreen(GTK_WINDOW(s->window)); 1428 s->full_screen = TRUE; 1429 } else { 1430 gtk_window_unfullscreen(GTK_WINDOW(s->window)); 1431 gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s); 1432 if (gtk_check_menu_item_get_active( 1433 GTK_CHECK_MENU_ITEM(s->show_menubar_item))) { 1434 gtk_widget_show(s->menu_bar); 1435 } 1436 s->full_screen = FALSE; 1437 if (vc->type == GD_VC_GFX) { 1438 vc->gfx.scale_x = 1.0; 1439 vc->gfx.scale_y = 1.0; 1440 gd_update_windowsize(vc); 1441 } 1442 } 1443 1444 gd_update_cursor(vc); 1445 } 1446 1447 static void gd_accel_full_screen(void *opaque) 1448 { 1449 GtkDisplayState *s = opaque; 1450 gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item)); 1451 } 1452 1453 static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque) 1454 { 1455 GtkDisplayState *s = opaque; 1456 VirtualConsole *vc = gd_vc_find_current(s); 1457 1458 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), 1459 FALSE); 1460 1461 vc->gfx.scale_x += VC_SCALE_STEP; 1462 vc->gfx.scale_y += VC_SCALE_STEP; 1463 1464 gd_update_windowsize(vc); 1465 } 1466 1467 static void gd_accel_zoom_in(void *opaque) 1468 { 1469 GtkDisplayState *s = opaque; 1470 gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_in_item)); 1471 } 1472 1473 static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque) 1474 { 1475 GtkDisplayState *s = opaque; 1476 VirtualConsole *vc = gd_vc_find_current(s); 1477 1478 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), 1479 FALSE); 1480 1481 vc->gfx.scale_x -= VC_SCALE_STEP; 1482 vc->gfx.scale_y -= VC_SCALE_STEP; 1483 1484 vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN); 1485 vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN); 1486 1487 gd_update_windowsize(vc); 1488 } 1489 1490 static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque) 1491 { 1492 GtkDisplayState *s = opaque; 1493 VirtualConsole *vc = gd_vc_find_current(s); 1494 1495 vc->gfx.scale_x = 1.0; 1496 vc->gfx.scale_y = 1.0; 1497 1498 gd_update_windowsize(vc); 1499 } 1500 1501 static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque) 1502 { 1503 GtkDisplayState *s = opaque; 1504 VirtualConsole *vc = gd_vc_find_current(s); 1505 1506 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) { 1507 s->free_scale = TRUE; 1508 } else { 1509 s->free_scale = FALSE; 1510 vc->gfx.scale_x = 1.0; 1511 vc->gfx.scale_y = 1.0; 1512 } 1513 1514 gd_update_windowsize(vc); 1515 gd_update_full_redraw(vc); 1516 } 1517 1518 #if GTK_CHECK_VERSION(3, 20, 0) 1519 static void gd_grab_update(VirtualConsole *vc, bool kbd, bool ptr) 1520 { 1521 GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); 1522 GdkSeat *seat = gdk_display_get_default_seat(display); 1523 GdkWindow *window = gtk_widget_get_window(vc->gfx.drawing_area); 1524 GdkSeatCapabilities caps = 0; 1525 GdkCursor *cursor = NULL; 1526 1527 if (kbd) { 1528 caps |= GDK_SEAT_CAPABILITY_KEYBOARD; 1529 } 1530 if (ptr) { 1531 caps |= GDK_SEAT_CAPABILITY_ALL_POINTING; 1532 cursor = vc->s->null_cursor; 1533 } 1534 1535 if (caps) { 1536 gdk_seat_grab(seat, window, caps, false, cursor, 1537 NULL, NULL, NULL); 1538 } else { 1539 gdk_seat_ungrab(seat); 1540 } 1541 } 1542 #elif GTK_CHECK_VERSION(3, 0, 0) 1543 static void gd_grab_devices(VirtualConsole *vc, bool grab, 1544 GdkInputSource source, GdkEventMask mask, 1545 GdkCursor *cursor) 1546 { 1547 GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); 1548 GdkDeviceManager *mgr = gdk_display_get_device_manager(display); 1549 GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER); 1550 GList *tmp = devs; 1551 1552 for (tmp = devs; tmp; tmp = tmp->next) { 1553 GdkDevice *dev = tmp->data; 1554 if (gdk_device_get_source(dev) != source) { 1555 continue; 1556 } 1557 if (grab) { 1558 GdkWindow *win = gtk_widget_get_window(vc->gfx.drawing_area); 1559 gdk_device_grab(dev, win, GDK_OWNERSHIP_NONE, FALSE, 1560 mask, cursor, GDK_CURRENT_TIME); 1561 } else { 1562 gdk_device_ungrab(dev, GDK_CURRENT_TIME); 1563 } 1564 } 1565 g_list_free(devs); 1566 } 1567 #endif 1568 1569 static void gd_grab_keyboard(VirtualConsole *vc, const char *reason) 1570 { 1571 if (vc->s->kbd_owner) { 1572 if (vc->s->kbd_owner == vc) { 1573 return; 1574 } else { 1575 gd_ungrab_keyboard(vc->s); 1576 } 1577 } 1578 1579 #if GTK_CHECK_VERSION(3, 20, 0) 1580 gd_grab_update(vc, true, vc->s->ptr_owner == vc); 1581 #elif GTK_CHECK_VERSION(3, 0, 0) 1582 gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD, 1583 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, 1584 NULL); 1585 #else 1586 gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area), 1587 FALSE, 1588 GDK_CURRENT_TIME); 1589 #endif 1590 vc->s->kbd_owner = vc; 1591 gd_update_caption(vc->s); 1592 trace_gd_grab(vc->label, "kbd", reason); 1593 } 1594 1595 static void gd_ungrab_keyboard(GtkDisplayState *s) 1596 { 1597 VirtualConsole *vc = s->kbd_owner; 1598 1599 if (vc == NULL) { 1600 return; 1601 } 1602 s->kbd_owner = NULL; 1603 1604 #if GTK_CHECK_VERSION(3, 20, 0) 1605 gd_grab_update(vc, false, vc->s->ptr_owner == vc); 1606 #elif GTK_CHECK_VERSION(3, 0, 0) 1607 gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL); 1608 #else 1609 gdk_keyboard_ungrab(GDK_CURRENT_TIME); 1610 #endif 1611 gd_update_caption(s); 1612 trace_gd_ungrab(vc->label, "kbd"); 1613 } 1614 1615 static void gd_grab_pointer(VirtualConsole *vc, const char *reason) 1616 { 1617 GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); 1618 1619 if (vc->s->ptr_owner) { 1620 if (vc->s->ptr_owner == vc) { 1621 return; 1622 } else { 1623 gd_ungrab_pointer(vc->s); 1624 } 1625 } 1626 1627 #if GTK_CHECK_VERSION(3, 20, 0) 1628 gd_grab_update(vc, vc->s->kbd_owner == vc, true); 1629 gdk_device_get_position(gd_get_pointer(display), 1630 NULL, &vc->s->grab_x_root, &vc->s->grab_y_root); 1631 #elif GTK_CHECK_VERSION(3, 0, 0) 1632 gd_grab_devices(vc, true, GDK_SOURCE_MOUSE, 1633 GDK_POINTER_MOTION_MASK | 1634 GDK_BUTTON_PRESS_MASK | 1635 GDK_BUTTON_RELEASE_MASK | 1636 GDK_BUTTON_MOTION_MASK | 1637 GDK_SCROLL_MASK, 1638 vc->s->null_cursor); 1639 gdk_device_get_position(gd_get_pointer(display), 1640 NULL, &vc->s->grab_x_root, &vc->s->grab_y_root); 1641 #else 1642 gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area), 1643 FALSE, /* All events to come to our window directly */ 1644 GDK_POINTER_MOTION_MASK | 1645 GDK_BUTTON_PRESS_MASK | 1646 GDK_BUTTON_RELEASE_MASK | 1647 GDK_BUTTON_MOTION_MASK | 1648 GDK_SCROLL_MASK, 1649 NULL, /* Allow cursor to move over entire desktop */ 1650 vc->s->null_cursor, 1651 GDK_CURRENT_TIME); 1652 gdk_display_get_pointer(display, NULL, 1653 &vc->s->grab_x_root, &vc->s->grab_y_root, NULL); 1654 #endif 1655 vc->s->ptr_owner = vc; 1656 gd_update_caption(vc->s); 1657 trace_gd_grab(vc->label, "ptr", reason); 1658 } 1659 1660 static void gd_ungrab_pointer(GtkDisplayState *s) 1661 { 1662 VirtualConsole *vc = s->ptr_owner; 1663 GdkDisplay *display; 1664 1665 if (vc == NULL) { 1666 return; 1667 } 1668 s->ptr_owner = NULL; 1669 1670 display = gtk_widget_get_display(vc->gfx.drawing_area); 1671 #if GTK_CHECK_VERSION(3, 20, 0) 1672 gd_grab_update(vc, vc->s->kbd_owner == vc, false); 1673 gdk_device_warp(gd_get_pointer(display), 1674 gtk_widget_get_screen(vc->gfx.drawing_area), 1675 vc->s->grab_x_root, vc->s->grab_y_root); 1676 #elif GTK_CHECK_VERSION(3, 0, 0) 1677 gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL); 1678 gdk_device_warp(gd_get_pointer(display), 1679 gtk_widget_get_screen(vc->gfx.drawing_area), 1680 vc->s->grab_x_root, vc->s->grab_y_root); 1681 #else 1682 gdk_pointer_ungrab(GDK_CURRENT_TIME); 1683 gdk_display_warp_pointer(display, 1684 gtk_widget_get_screen(vc->gfx.drawing_area), 1685 vc->s->grab_x_root, vc->s->grab_y_root); 1686 #endif 1687 gd_update_caption(s); 1688 trace_gd_ungrab(vc->label, "ptr"); 1689 } 1690 1691 static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) 1692 { 1693 GtkDisplayState *s = opaque; 1694 VirtualConsole *vc = gd_vc_find_current(s); 1695 1696 if (gd_is_grab_active(s)) { 1697 gd_grab_keyboard(vc, "user-request-main-window"); 1698 gd_grab_pointer(vc, "user-request-main-window"); 1699 } else { 1700 gd_ungrab_keyboard(s); 1701 gd_ungrab_pointer(s); 1702 } 1703 1704 gd_update_cursor(vc); 1705 } 1706 1707 static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, 1708 gpointer data) 1709 { 1710 GtkDisplayState *s = data; 1711 VirtualConsole *vc; 1712 gboolean on_vga; 1713 1714 if (!gtk_widget_get_realized(s->notebook)) { 1715 return; 1716 } 1717 1718 #ifdef VTE_RESIZE_HACK 1719 vc = gd_vc_find_current(s); 1720 if (vc && vc->type == GD_VC_VTE) { 1721 gtk_widget_hide(vc->vte.terminal); 1722 } 1723 #endif 1724 vc = gd_vc_find_by_page(s, arg2); 1725 if (!vc) { 1726 return; 1727 } 1728 #ifdef VTE_RESIZE_HACK 1729 if (vc->type == GD_VC_VTE) { 1730 gtk_widget_show(vc->vte.terminal); 1731 } 1732 #endif 1733 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), 1734 TRUE); 1735 on_vga = (vc->type == GD_VC_GFX && 1736 qemu_console_is_graphic(vc->gfx.dcl.con)); 1737 if (!on_vga) { 1738 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 1739 FALSE); 1740 } else if (s->full_screen) { 1741 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 1742 TRUE); 1743 } 1744 gtk_widget_set_sensitive(s->grab_item, on_vga); 1745 #ifdef CONFIG_VTE 1746 gtk_widget_set_sensitive(s->copy_item, vc->type == GD_VC_VTE); 1747 #endif 1748 1749 gd_update_windowsize(vc); 1750 gd_update_cursor(vc); 1751 } 1752 1753 static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, 1754 gpointer opaque) 1755 { 1756 VirtualConsole *vc = opaque; 1757 GtkDisplayState *s = vc->s; 1758 1759 if (gd_grab_on_hover(s)) { 1760 gd_grab_keyboard(vc, "grab-on-hover"); 1761 } 1762 return TRUE; 1763 } 1764 1765 static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, 1766 gpointer opaque) 1767 { 1768 VirtualConsole *vc = opaque; 1769 GtkDisplayState *s = vc->s; 1770 1771 if (gd_grab_on_hover(s)) { 1772 gd_ungrab_keyboard(s); 1773 } 1774 return TRUE; 1775 } 1776 1777 static gboolean gd_focus_out_event(GtkWidget *widget, 1778 GdkEventCrossing *crossing, gpointer opaque) 1779 { 1780 VirtualConsole *vc = opaque; 1781 GtkDisplayState *s = vc->s; 1782 1783 gtk_release_modifiers(s); 1784 return TRUE; 1785 } 1786 1787 static gboolean gd_configure(GtkWidget *widget, 1788 GdkEventConfigure *cfg, gpointer opaque) 1789 { 1790 VirtualConsole *vc = opaque; 1791 1792 gd_set_ui_info(vc, cfg->width, cfg->height); 1793 return FALSE; 1794 } 1795 1796 /** Virtual Console Callbacks **/ 1797 1798 static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc, 1799 int idx, GSList *group, GtkWidget *view_menu) 1800 { 1801 vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label); 1802 gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx, 1803 HOTKEY_MODIFIERS, 0, 1804 g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL)); 1805 #if GTK_CHECK_VERSION(3, 8, 0) 1806 gtk_accel_label_set_accel( 1807 GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))), 1808 GDK_KEY_1 + idx, HOTKEY_MODIFIERS); 1809 #endif 1810 1811 g_signal_connect(vc->menu_item, "activate", 1812 G_CALLBACK(gd_menu_switch_vc), s); 1813 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item); 1814 1815 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); 1816 return group; 1817 } 1818 1819 #if defined(CONFIG_VTE) 1820 static void gd_menu_copy(GtkMenuItem *item, void *opaque) 1821 { 1822 GtkDisplayState *s = opaque; 1823 VirtualConsole *vc = gd_vc_find_current(s); 1824 1825 #if VTE_CHECK_VERSION(0, 50, 0) 1826 vte_terminal_copy_clipboard_format(VTE_TERMINAL(vc->vte.terminal), 1827 VTE_FORMAT_TEXT); 1828 #else 1829 vte_terminal_copy_clipboard(VTE_TERMINAL(vc->vte.terminal)); 1830 #endif 1831 } 1832 1833 static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque) 1834 { 1835 VirtualConsole *vc = opaque; 1836 1837 if (gtk_adjustment_get_upper(adjustment) > 1838 gtk_adjustment_get_page_size(adjustment)) { 1839 gtk_widget_show(vc->vte.scrollbar); 1840 } else { 1841 gtk_widget_hide(vc->vte.scrollbar); 1842 } 1843 } 1844 1845 static int gd_vc_chr_write(Chardev *chr, const uint8_t *buf, int len) 1846 { 1847 VCChardev *vcd = VC_CHARDEV(chr); 1848 VirtualConsole *vc = vcd->console; 1849 1850 vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len); 1851 return len; 1852 } 1853 1854 static void gd_vc_chr_set_echo(Chardev *chr, bool echo) 1855 { 1856 VCChardev *vcd = VC_CHARDEV(chr); 1857 VirtualConsole *vc = vcd->console; 1858 1859 if (vc) { 1860 vc->vte.echo = echo; 1861 } else { 1862 vcd->echo = echo; 1863 } 1864 } 1865 1866 static int nb_vcs; 1867 static Chardev *vcs[MAX_VCS]; 1868 static void gd_vc_open(Chardev *chr, 1869 ChardevBackend *backend, 1870 bool *be_opened, 1871 Error **errp) 1872 { 1873 if (nb_vcs == MAX_VCS) { 1874 error_setg(errp, "Maximum number of consoles reached"); 1875 return; 1876 } 1877 1878 vcs[nb_vcs++] = chr; 1879 1880 /* console/chardev init sometimes completes elsewhere in a 2nd 1881 * stage, so defer OPENED events until they are fully initialized 1882 */ 1883 *be_opened = false; 1884 } 1885 1886 static void char_gd_vc_class_init(ObjectClass *oc, void *data) 1887 { 1888 ChardevClass *cc = CHARDEV_CLASS(oc); 1889 1890 cc->parse = qemu_chr_parse_vc; 1891 cc->open = gd_vc_open; 1892 cc->chr_write = gd_vc_chr_write; 1893 cc->chr_set_echo = gd_vc_chr_set_echo; 1894 } 1895 1896 static const TypeInfo char_gd_vc_type_info = { 1897 .name = TYPE_CHARDEV_VC, 1898 .parent = TYPE_CHARDEV, 1899 .instance_size = sizeof(VCChardev), 1900 .class_init = char_gd_vc_class_init, 1901 }; 1902 1903 static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size, 1904 gpointer user_data) 1905 { 1906 VirtualConsole *vc = user_data; 1907 1908 if (vc->vte.echo) { 1909 VteTerminal *term = VTE_TERMINAL(vc->vte.terminal); 1910 int i; 1911 for (i = 0; i < size; i++) { 1912 uint8_t c = text[i]; 1913 if (c >= 128 || isprint(c)) { 1914 /* 8-bit characters are considered printable. */ 1915 vte_terminal_feed(term, &text[i], 1); 1916 } else if (c == '\r' || c == '\n') { 1917 vte_terminal_feed(term, "\r\n", 2); 1918 } else { 1919 char ctrl[2] = { '^', 0}; 1920 ctrl[1] = text[i] ^ 64; 1921 vte_terminal_feed(term, ctrl, 2); 1922 } 1923 } 1924 } 1925 1926 qemu_chr_be_write(vc->vte.chr, (uint8_t *)text, (unsigned int)size); 1927 return TRUE; 1928 } 1929 1930 static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, 1931 Chardev *chr, int idx, 1932 GSList *group, GtkWidget *view_menu) 1933 { 1934 char buffer[32]; 1935 GtkWidget *box; 1936 GtkWidget *scrollbar; 1937 GtkAdjustment *vadjustment; 1938 VCChardev *vcd = VC_CHARDEV(chr); 1939 1940 vc->s = s; 1941 vc->vte.echo = vcd->echo; 1942 vc->vte.chr = chr; 1943 vcd->console = vc; 1944 1945 snprintf(buffer, sizeof(buffer), "vc%d", idx); 1946 vc->label = g_strdup_printf("%s", vc->vte.chr->label 1947 ? vc->vte.chr->label : buffer); 1948 group = gd_vc_menu_init(s, vc, idx, group, view_menu); 1949 1950 vc->vte.terminal = vte_terminal_new(); 1951 g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc); 1952 1953 /* The documentation says that the default is UTF-8, but actually it is 1954 * 7-bit ASCII at least in VTE 0.38. 1955 */ 1956 #if VTE_CHECK_VERSION(0, 38, 0) 1957 vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8", NULL); 1958 #else 1959 vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8"); 1960 #endif 1961 1962 vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1); 1963 vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal), 1964 VC_TERM_X_MIN, VC_TERM_Y_MIN); 1965 1966 #if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0) 1967 vadjustment = gtk_scrollable_get_vadjustment 1968 (GTK_SCROLLABLE(vc->vte.terminal)); 1969 #else 1970 vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal)); 1971 #endif 1972 1973 #if GTK_CHECK_VERSION(3, 0, 0) 1974 box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2); 1975 scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment); 1976 #else 1977 box = gtk_hbox_new(false, 2); 1978 scrollbar = gtk_vscrollbar_new(vadjustment); 1979 #endif 1980 1981 gtk_box_pack_end(GTK_BOX(box), scrollbar, FALSE, FALSE, 0); 1982 gtk_box_pack_end(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0); 1983 1984 vc->vte.box = box; 1985 vc->vte.scrollbar = scrollbar; 1986 1987 g_signal_connect(vadjustment, "changed", 1988 G_CALLBACK(gd_vc_adjustment_changed), vc); 1989 1990 vc->type = GD_VC_VTE; 1991 vc->tab_item = box; 1992 vc->focus = vc->vte.terminal; 1993 gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item, 1994 gtk_label_new(vc->label)); 1995 1996 qemu_chr_be_event(vc->vte.chr, CHR_EVENT_OPENED); 1997 1998 return group; 1999 } 2000 2001 static void gd_vcs_init(GtkDisplayState *s, GSList *group, 2002 GtkWidget *view_menu) 2003 { 2004 int i; 2005 2006 for (i = 0; i < nb_vcs; i++) { 2007 VirtualConsole *vc = &s->vc[s->nb_vcs]; 2008 group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu); 2009 s->nb_vcs++; 2010 } 2011 } 2012 #endif /* CONFIG_VTE */ 2013 2014 /** Window Creation **/ 2015 2016 static void gd_connect_vc_gfx_signals(VirtualConsole *vc) 2017 { 2018 #if GTK_CHECK_VERSION(3, 0, 0) 2019 g_signal_connect(vc->gfx.drawing_area, "draw", 2020 G_CALLBACK(gd_draw_event), vc); 2021 #if defined(CONFIG_GTK_GL) 2022 if (gtk_use_gl_area) { 2023 /* wire up GtkGlArea events */ 2024 g_signal_connect(vc->gfx.drawing_area, "render", 2025 G_CALLBACK(gd_render_event), vc); 2026 g_signal_connect(vc->gfx.drawing_area, "resize", 2027 G_CALLBACK(gd_resize_event), vc); 2028 } 2029 #endif 2030 #else 2031 g_signal_connect(vc->gfx.drawing_area, "expose-event", 2032 G_CALLBACK(gd_expose_event), vc); 2033 #endif 2034 if (qemu_console_is_graphic(vc->gfx.dcl.con)) { 2035 g_signal_connect(vc->gfx.drawing_area, "event", 2036 G_CALLBACK(gd_event), vc); 2037 g_signal_connect(vc->gfx.drawing_area, "button-press-event", 2038 G_CALLBACK(gd_button_event), vc); 2039 g_signal_connect(vc->gfx.drawing_area, "button-release-event", 2040 G_CALLBACK(gd_button_event), vc); 2041 g_signal_connect(vc->gfx.drawing_area, "scroll-event", 2042 G_CALLBACK(gd_scroll_event), vc); 2043 g_signal_connect(vc->gfx.drawing_area, "key-press-event", 2044 G_CALLBACK(gd_key_event), vc); 2045 g_signal_connect(vc->gfx.drawing_area, "key-release-event", 2046 G_CALLBACK(gd_key_event), vc); 2047 2048 g_signal_connect(vc->gfx.drawing_area, "enter-notify-event", 2049 G_CALLBACK(gd_enter_event), vc); 2050 g_signal_connect(vc->gfx.drawing_area, "leave-notify-event", 2051 G_CALLBACK(gd_leave_event), vc); 2052 g_signal_connect(vc->gfx.drawing_area, "focus-out-event", 2053 G_CALLBACK(gd_focus_out_event), vc); 2054 g_signal_connect(vc->gfx.drawing_area, "configure-event", 2055 G_CALLBACK(gd_configure), vc); 2056 } else { 2057 g_signal_connect(vc->gfx.drawing_area, "key-press-event", 2058 G_CALLBACK(gd_text_key_down), vc); 2059 } 2060 } 2061 2062 static void gd_connect_signals(GtkDisplayState *s) 2063 { 2064 g_signal_connect(s->show_tabs_item, "activate", 2065 G_CALLBACK(gd_menu_show_tabs), s); 2066 g_signal_connect(s->untabify_item, "activate", 2067 G_CALLBACK(gd_menu_untabify), s); 2068 g_signal_connect(s->show_menubar_item, "activate", 2069 G_CALLBACK(gd_menu_show_menubar), s); 2070 2071 g_signal_connect(s->window, "delete-event", 2072 G_CALLBACK(gd_window_close), s); 2073 2074 g_signal_connect(s->pause_item, "activate", 2075 G_CALLBACK(gd_menu_pause), s); 2076 g_signal_connect(s->reset_item, "activate", 2077 G_CALLBACK(gd_menu_reset), s); 2078 g_signal_connect(s->powerdown_item, "activate", 2079 G_CALLBACK(gd_menu_powerdown), s); 2080 g_signal_connect(s->quit_item, "activate", 2081 G_CALLBACK(gd_menu_quit), s); 2082 #if defined(CONFIG_VTE) 2083 g_signal_connect(s->copy_item, "activate", 2084 G_CALLBACK(gd_menu_copy), s); 2085 #endif 2086 g_signal_connect(s->full_screen_item, "activate", 2087 G_CALLBACK(gd_menu_full_screen), s); 2088 g_signal_connect(s->zoom_in_item, "activate", 2089 G_CALLBACK(gd_menu_zoom_in), s); 2090 g_signal_connect(s->zoom_out_item, "activate", 2091 G_CALLBACK(gd_menu_zoom_out), s); 2092 g_signal_connect(s->zoom_fixed_item, "activate", 2093 G_CALLBACK(gd_menu_zoom_fixed), s); 2094 g_signal_connect(s->zoom_fit_item, "activate", 2095 G_CALLBACK(gd_menu_zoom_fit), s); 2096 g_signal_connect(s->grab_item, "activate", 2097 G_CALLBACK(gd_menu_grab_input), s); 2098 g_signal_connect(s->notebook, "switch-page", 2099 G_CALLBACK(gd_change_page), s); 2100 } 2101 2102 static GtkWidget *gd_create_menu_machine(GtkDisplayState *s) 2103 { 2104 GtkWidget *machine_menu; 2105 GtkWidget *separator; 2106 2107 machine_menu = gtk_menu_new(); 2108 gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group); 2109 2110 s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause")); 2111 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item); 2112 2113 separator = gtk_separator_menu_item_new(); 2114 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator); 2115 2116 s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset")); 2117 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item); 2118 2119 s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down")); 2120 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item); 2121 2122 separator = gtk_separator_menu_item_new(); 2123 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator); 2124 2125 s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit")); 2126 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item), 2127 "<QEMU>/Machine/Quit"); 2128 gtk_accel_map_add_entry("<QEMU>/Machine/Quit", 2129 GDK_KEY_q, HOTKEY_MODIFIERS); 2130 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item); 2131 2132 return machine_menu; 2133 } 2134 2135 static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, 2136 QemuConsole *con, int idx, 2137 GSList *group, GtkWidget *view_menu) 2138 { 2139 vc->label = qemu_console_get_label(con); 2140 vc->s = s; 2141 vc->gfx.scale_x = 1.0; 2142 vc->gfx.scale_y = 1.0; 2143 2144 #if defined(CONFIG_OPENGL) 2145 if (display_opengl) { 2146 #if defined(CONFIG_GTK_GL) 2147 if (gtk_use_gl_area) { 2148 vc->gfx.drawing_area = gtk_gl_area_new(); 2149 vc->gfx.dcl.ops = &dcl_gl_area_ops; 2150 } else 2151 #endif /* CONFIG_GTK_GL */ 2152 { 2153 vc->gfx.drawing_area = gtk_drawing_area_new(); 2154 /* 2155 * gtk_widget_set_double_buffered() was deprecated in 3.14. 2156 * It is required for opengl rendering on X11 though. A 2157 * proper replacement (native opengl support) is only 2158 * available in 3.16+. Silence the warning if possible. 2159 */ 2160 #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE 2161 #pragma GCC diagnostic push 2162 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 2163 #endif 2164 gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE); 2165 #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE 2166 #pragma GCC diagnostic pop 2167 #endif 2168 vc->gfx.dcl.ops = &dcl_egl_ops; 2169 } 2170 } else 2171 #endif 2172 { 2173 vc->gfx.drawing_area = gtk_drawing_area_new(); 2174 vc->gfx.dcl.ops = &dcl_ops; 2175 } 2176 2177 2178 gtk_widget_add_events(vc->gfx.drawing_area, 2179 GDK_POINTER_MOTION_MASK | 2180 GDK_BUTTON_PRESS_MASK | 2181 GDK_BUTTON_RELEASE_MASK | 2182 GDK_BUTTON_MOTION_MASK | 2183 GDK_ENTER_NOTIFY_MASK | 2184 GDK_LEAVE_NOTIFY_MASK | 2185 GDK_SCROLL_MASK | 2186 GDK_KEY_PRESS_MASK); 2187 gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE); 2188 2189 vc->type = GD_VC_GFX; 2190 vc->tab_item = vc->gfx.drawing_area; 2191 vc->focus = vc->gfx.drawing_area; 2192 gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), 2193 vc->tab_item, gtk_label_new(vc->label)); 2194 2195 vc->gfx.dcl.con = con; 2196 register_displaychangelistener(&vc->gfx.dcl); 2197 2198 gd_connect_vc_gfx_signals(vc); 2199 group = gd_vc_menu_init(s, vc, idx, group, view_menu); 2200 2201 if (dpy_ui_info_supported(vc->gfx.dcl.con)) { 2202 gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item)); 2203 s->free_scale = true; 2204 } 2205 2206 return group; 2207 } 2208 2209 static GtkWidget *gd_create_menu_view(GtkDisplayState *s) 2210 { 2211 GSList *group = NULL; 2212 GtkWidget *view_menu; 2213 GtkWidget *separator; 2214 QemuConsole *con; 2215 int vc; 2216 2217 view_menu = gtk_menu_new(); 2218 gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group); 2219 2220 s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen")); 2221 2222 #if defined(CONFIG_VTE) 2223 s->copy_item = gtk_menu_item_new_with_mnemonic(_("_Copy")); 2224 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->copy_item); 2225 #endif 2226 2227 gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0, 2228 g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL)); 2229 #if GTK_CHECK_VERSION(3, 8, 0) 2230 gtk_accel_label_set_accel( 2231 GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))), 2232 GDK_KEY_f, HOTKEY_MODIFIERS); 2233 #endif 2234 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item); 2235 2236 separator = gtk_separator_menu_item_new(); 2237 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 2238 2239 s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In")); 2240 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item), 2241 "<QEMU>/View/Zoom In"); 2242 gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus, 2243 HOTKEY_MODIFIERS); 2244 gtk_accel_group_connect(s->accel_group, GDK_KEY_equal, HOTKEY_MODIFIERS, 0, 2245 g_cclosure_new_swap(G_CALLBACK(gd_accel_zoom_in), s, NULL)); 2246 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item); 2247 2248 s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out")); 2249 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item), 2250 "<QEMU>/View/Zoom Out"); 2251 gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus, 2252 HOTKEY_MODIFIERS); 2253 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item); 2254 2255 s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit")); 2256 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item), 2257 "<QEMU>/View/Zoom Fixed"); 2258 gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0, 2259 HOTKEY_MODIFIERS); 2260 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item); 2261 2262 s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit")); 2263 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item); 2264 2265 separator = gtk_separator_menu_item_new(); 2266 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 2267 2268 s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover")); 2269 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item); 2270 2271 s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input")); 2272 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item), 2273 "<QEMU>/View/Grab Input"); 2274 gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g, 2275 HOTKEY_MODIFIERS); 2276 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item); 2277 2278 separator = gtk_separator_menu_item_new(); 2279 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 2280 2281 /* gfx */ 2282 for (vc = 0;; vc++) { 2283 con = qemu_console_lookup_by_index(vc); 2284 if (!con) { 2285 break; 2286 } 2287 group = gd_vc_gfx_init(s, &s->vc[vc], con, 2288 vc, group, view_menu); 2289 s->nb_vcs++; 2290 } 2291 2292 #if defined(CONFIG_VTE) 2293 /* vte */ 2294 gd_vcs_init(s, group, view_menu); 2295 #endif 2296 2297 separator = gtk_separator_menu_item_new(); 2298 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 2299 2300 s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs")); 2301 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item); 2302 2303 s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab")); 2304 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item); 2305 2306 s->show_menubar_item = gtk_check_menu_item_new_with_mnemonic( 2307 _("Show Menubar")); 2308 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->show_menubar_item), 2309 TRUE); 2310 gtk_accel_group_connect(s->accel_group, GDK_KEY_m, HOTKEY_MODIFIERS, 0, 2311 g_cclosure_new_swap(G_CALLBACK(gd_accel_show_menubar), s, NULL)); 2312 #if GTK_CHECK_VERSION(3, 8, 0) 2313 gtk_accel_label_set_accel( 2314 GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->show_menubar_item))), 2315 GDK_KEY_m, HOTKEY_MODIFIERS); 2316 #endif 2317 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_menubar_item); 2318 2319 return view_menu; 2320 } 2321 2322 static void gd_create_menus(GtkDisplayState *s) 2323 { 2324 GtkSettings *settings; 2325 2326 s->accel_group = gtk_accel_group_new(); 2327 s->machine_menu = gd_create_menu_machine(s); 2328 s->view_menu = gd_create_menu_view(s); 2329 2330 s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine")); 2331 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item), 2332 s->machine_menu); 2333 gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item); 2334 2335 s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View")); 2336 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu); 2337 gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item); 2338 2339 g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group); 2340 gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group); 2341 2342 /* Disable the default "F10" menu shortcut. */ 2343 settings = gtk_widget_get_settings(s->window); 2344 g_object_set(G_OBJECT(settings), "gtk-menu-bar-accel", "", NULL); 2345 } 2346 2347 2348 static gboolean gtkinit; 2349 2350 static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) 2351 { 2352 VirtualConsole *vc; 2353 2354 GtkDisplayState *s = g_malloc0(sizeof(*s)); 2355 char *filename; 2356 GdkDisplay *window_display; 2357 2358 if (!gtkinit) { 2359 fprintf(stderr, "gtk initialization failed\n"); 2360 exit(1); 2361 } 2362 assert(opts->type == DISPLAY_TYPE_GTK); 2363 s->opts = opts; 2364 2365 #if !GTK_CHECK_VERSION(3, 0, 0) 2366 g_printerr("Running QEMU with GTK 2.x is deprecated, and will be removed\n" 2367 "in a future release. Please switch to GTK 3.x instead\n"); 2368 #endif 2369 2370 s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 2371 #if GTK_CHECK_VERSION(3, 2, 0) 2372 s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 2373 #else 2374 s->vbox = gtk_vbox_new(FALSE, 0); 2375 #endif 2376 s->notebook = gtk_notebook_new(); 2377 s->menu_bar = gtk_menu_bar_new(); 2378 2379 s->free_scale = FALSE; 2380 2381 /* Mostly LC_MESSAGES only. See early_gtk_display_init() for details. For 2382 * LC_CTYPE, we need to make sure that non-ASCII characters are considered 2383 * printable, but without changing any of the character classes to make 2384 * sure that we don't accidentally break implicit assumptions. */ 2385 setlocale(LC_MESSAGES, ""); 2386 setlocale(LC_CTYPE, "C.UTF-8"); 2387 bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR); 2388 textdomain("qemu"); 2389 2390 window_display = gtk_widget_get_display(s->window); 2391 s->null_cursor = gdk_cursor_new_for_display(window_display, 2392 GDK_BLANK_CURSOR); 2393 2394 s->mouse_mode_notifier.notify = gd_mouse_mode_change; 2395 qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier); 2396 qemu_add_vm_change_state_handler(gd_change_runstate, s); 2397 2398 filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg"); 2399 if (filename) { 2400 GError *error = NULL; 2401 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error); 2402 if (pixbuf) { 2403 gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf); 2404 } else { 2405 g_error_free(error); 2406 } 2407 g_free(filename); 2408 } 2409 2410 gd_create_menus(s); 2411 2412 gd_connect_signals(s); 2413 2414 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 2415 gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE); 2416 2417 gd_update_caption(s); 2418 2419 gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0); 2420 gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0); 2421 2422 gtk_container_add(GTK_CONTAINER(s->window), s->vbox); 2423 2424 gtk_widget_show_all(s->window); 2425 2426 #ifdef VTE_RESIZE_HACK 2427 { 2428 VirtualConsole *cur = gd_vc_find_current(s); 2429 if (cur) { 2430 int i; 2431 2432 for (i = 0; i < s->nb_vcs; i++) { 2433 VirtualConsole *vc = &s->vc[i]; 2434 if (vc && vc->type == GD_VC_VTE && vc != cur) { 2435 gtk_widget_hide(vc->vte.terminal); 2436 } 2437 } 2438 gd_update_windowsize(cur); 2439 } 2440 } 2441 #endif 2442 2443 vc = gd_vc_find_current(s); 2444 gtk_widget_set_sensitive(s->view_menu, vc != NULL); 2445 #ifdef CONFIG_VTE 2446 gtk_widget_set_sensitive(s->copy_item, 2447 vc && vc->type == GD_VC_VTE); 2448 #endif 2449 2450 if (opts->has_full_screen && 2451 opts->full_screen) { 2452 gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item)); 2453 } 2454 if (opts->u.gtk.has_grab_on_hover && 2455 opts->u.gtk.grab_on_hover) { 2456 gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item)); 2457 } 2458 } 2459 2460 static void early_gtk_display_init(DisplayOptions *opts) 2461 { 2462 /* The QEMU code relies on the assumption that it's always run in 2463 * the C locale. Therefore it is not prepared to deal with 2464 * operations that produce different results depending on the 2465 * locale, such as printf's formatting of decimal numbers, and 2466 * possibly others. 2467 * 2468 * Since GTK+ calls setlocale() by default -importing the locale 2469 * settings from the environment- we must prevent it from doing so 2470 * using gtk_disable_setlocale(). 2471 * 2472 * QEMU's GTK+ UI, however, _does_ have translations for some of 2473 * the menu items. As a trade-off between a functionally correct 2474 * QEMU and a fully internationalized UI we support importing 2475 * LC_MESSAGES from the environment (see the setlocale() call 2476 * earlier in this file). This allows us to display translated 2477 * messages leaving everything else untouched. 2478 */ 2479 gtk_disable_setlocale(); 2480 gtkinit = gtk_init_check(NULL, NULL); 2481 if (!gtkinit) { 2482 /* don't exit yet, that'll break -help */ 2483 return; 2484 } 2485 2486 assert(opts->type == DISPLAY_TYPE_GTK); 2487 if (opts->has_gl && opts->gl != DISPLAYGL_MODE_OFF) { 2488 if (opts->gl == DISPLAYGL_MODE_ES) { 2489 error_report("gtk: opengl es not supported"); 2490 return; 2491 } 2492 #if defined(CONFIG_OPENGL) 2493 #if defined(CONFIG_GTK_GL) && defined(GDK_WINDOWING_WAYLAND) 2494 if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) { 2495 gtk_use_gl_area = true; 2496 gtk_gl_area_init(); 2497 } else 2498 #endif 2499 { 2500 gtk_egl_init(); 2501 } 2502 #endif 2503 } 2504 2505 keycode_map = gd_get_keymap(&keycode_maplen); 2506 2507 #if defined(CONFIG_VTE) 2508 type_register(&char_gd_vc_type_info); 2509 #endif 2510 } 2511 2512 static QemuDisplay qemu_display_gtk = { 2513 .type = DISPLAY_TYPE_GTK, 2514 .early_init = early_gtk_display_init, 2515 .init = gtk_display_init, 2516 }; 2517 2518 static void register_gtk(void) 2519 { 2520 qemu_display_register(&qemu_display_gtk); 2521 } 2522 2523 type_init(register_gtk); 2524