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