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