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