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