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 #ifdef _WIN32 38 # define _WIN32_WINNT 0x0601 /* needed to get definition of MAPVK_VK_TO_VSC */ 39 #endif 40 41 #include "qemu-common.h" 42 43 #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE 44 /* Work around an -Wstrict-prototypes warning in GTK headers */ 45 #pragma GCC diagnostic push 46 #pragma GCC diagnostic ignored "-Wstrict-prototypes" 47 #endif 48 #include <gtk/gtk.h> 49 #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE 50 #pragma GCC diagnostic pop 51 #endif 52 53 54 #include <gdk/gdkkeysyms.h> 55 #include <glib/gi18n.h> 56 #include <locale.h> 57 #include <vte/vte.h> 58 #include <math.h> 59 60 #include "trace.h" 61 #include "ui/console.h" 62 #include "sysemu/sysemu.h" 63 #include "qmp-commands.h" 64 #include "x_keymap.h" 65 #include "keymaps.h" 66 #include "sysemu/char.h" 67 68 #define MAX_VCS 10 69 70 71 /* Compatibility define to let us build on both Gtk2 and Gtk3 */ 72 #if GTK_CHECK_VERSION(3, 0, 0) 73 static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh) 74 { 75 *ww = gdk_window_get_width(w); 76 *wh = gdk_window_get_height(w); 77 } 78 #endif 79 80 #if !GTK_CHECK_VERSION(2, 20, 0) 81 #define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget) 82 #endif 83 84 #ifndef GDK_KEY_0 85 #define GDK_KEY_0 GDK_0 86 #define GDK_KEY_1 GDK_1 87 #define GDK_KEY_2 GDK_2 88 #define GDK_KEY_f GDK_f 89 #define GDK_KEY_g GDK_g 90 #define GDK_KEY_plus GDK_plus 91 #define GDK_KEY_minus GDK_minus 92 #endif 93 94 #define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK) 95 #define IGNORE_MODIFIER_MASK \ 96 (GDK_MODIFIER_MASK & ~(GDK_LOCK_MASK | GDK_MOD2_MASK)) 97 98 static const int modifier_keycode[] = { 99 /* shift, control, alt keys, meta keys, both left & right */ 100 0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd, 101 }; 102 103 typedef struct VirtualConsole 104 { 105 GtkWidget *menu_item; 106 GtkWidget *terminal; 107 GtkWidget *scrolled_window; 108 CharDriverState *chr; 109 int fd; 110 } VirtualConsole; 111 112 typedef struct GtkDisplayState 113 { 114 GtkWidget *window; 115 116 GtkWidget *menu_bar; 117 118 GtkAccelGroup *accel_group; 119 120 GtkWidget *machine_menu_item; 121 GtkWidget *machine_menu; 122 GtkWidget *pause_item; 123 GtkWidget *reset_item; 124 GtkWidget *powerdown_item; 125 GtkWidget *quit_item; 126 127 GtkWidget *view_menu_item; 128 GtkWidget *view_menu; 129 GtkWidget *full_screen_item; 130 GtkWidget *zoom_in_item; 131 GtkWidget *zoom_out_item; 132 GtkWidget *zoom_fixed_item; 133 GtkWidget *zoom_fit_item; 134 GtkWidget *grab_item; 135 GtkWidget *grab_on_hover_item; 136 GtkWidget *vga_item; 137 138 int nb_vcs; 139 VirtualConsole vc[MAX_VCS]; 140 141 GtkWidget *show_tabs_item; 142 143 GtkWidget *vbox; 144 GtkWidget *notebook; 145 GtkWidget *drawing_area; 146 cairo_surface_t *surface; 147 pixman_image_t *convert; 148 DisplayChangeListener dcl; 149 DisplaySurface *ds; 150 int button_mask; 151 int last_x; 152 int last_y; 153 154 double scale_x; 155 double scale_y; 156 gboolean full_screen; 157 158 GdkCursor *null_cursor; 159 Notifier mouse_mode_notifier; 160 gboolean free_scale; 161 162 bool external_pause_update; 163 164 bool modifier_pressed[ARRAY_SIZE(modifier_keycode)]; 165 } GtkDisplayState; 166 167 static GtkDisplayState *global_state; 168 169 /** Utility Functions **/ 170 171 static bool gd_is_grab_active(GtkDisplayState *s) 172 { 173 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item)); 174 } 175 176 static bool gd_grab_on_hover(GtkDisplayState *s) 177 { 178 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item)); 179 } 180 181 static bool gd_on_vga(GtkDisplayState *s) 182 { 183 return gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)) == 0; 184 } 185 186 static void gd_update_cursor(GtkDisplayState *s, gboolean override) 187 { 188 GdkWindow *window; 189 bool on_vga; 190 191 window = gtk_widget_get_window(GTK_WIDGET(s->drawing_area)); 192 193 on_vga = gd_on_vga(s); 194 195 if ((override || on_vga) && 196 (s->full_screen || kbd_mouse_is_absolute() || gd_is_grab_active(s))) { 197 gdk_window_set_cursor(window, s->null_cursor); 198 } else { 199 gdk_window_set_cursor(window, NULL); 200 } 201 } 202 203 static void gd_update_caption(GtkDisplayState *s) 204 { 205 const char *status = ""; 206 gchar *title; 207 const char *grab = ""; 208 bool is_paused = !runstate_is_running(); 209 210 if (gd_is_grab_active(s)) { 211 grab = _(" - Press Ctrl+Alt+G to release grab"); 212 } 213 214 if (is_paused) { 215 status = _(" [Paused]"); 216 } 217 s->external_pause_update = true; 218 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item), 219 is_paused); 220 s->external_pause_update = false; 221 222 if (qemu_name) { 223 title = g_strdup_printf("QEMU (%s)%s%s", qemu_name, status, grab); 224 } else { 225 title = g_strdup_printf("QEMU%s%s", status, grab); 226 } 227 228 gtk_window_set_title(GTK_WINDOW(s->window), title); 229 230 g_free(title); 231 } 232 233 static void gd_update_windowsize(GtkDisplayState *s) 234 { 235 if (!s->full_screen) { 236 GtkRequisition req; 237 double sx, sy; 238 239 if (s->free_scale) { 240 sx = s->scale_x; 241 sy = s->scale_y; 242 243 s->scale_y = 1.0; 244 s->scale_x = 1.0; 245 } else { 246 sx = 1.0; 247 sy = 1.0; 248 } 249 250 gtk_widget_set_size_request(s->drawing_area, 251 surface_width(s->ds) * s->scale_x, 252 surface_height(s->ds) * s->scale_y); 253 #if GTK_CHECK_VERSION(3, 0, 0) 254 gtk_widget_get_preferred_size(s->vbox, NULL, &req); 255 #else 256 gtk_widget_size_request(s->vbox, &req); 257 #endif 258 259 gtk_window_resize(GTK_WINDOW(s->window), 260 req.width * sx, req.height * sy); 261 } 262 } 263 264 static void gd_update_full_redraw(GtkDisplayState *s) 265 { 266 int ww, wh; 267 gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); 268 gtk_widget_queue_draw_area(s->drawing_area, 0, 0, ww, wh); 269 } 270 271 static void gtk_release_modifiers(GtkDisplayState *s) 272 { 273 int i, keycode; 274 275 if (!gd_on_vga(s)) { 276 return; 277 } 278 for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { 279 keycode = modifier_keycode[i]; 280 if (!s->modifier_pressed[i]) { 281 continue; 282 } 283 if (keycode & SCANCODE_GREY) { 284 kbd_put_keycode(SCANCODE_EMUL0); 285 } 286 kbd_put_keycode(keycode | SCANCODE_UP); 287 s->modifier_pressed[i] = false; 288 } 289 } 290 291 /** DisplayState Callbacks **/ 292 293 static void gd_update(DisplayChangeListener *dcl, 294 int x, int y, int w, int h) 295 { 296 GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); 297 int x1, x2, y1, y2; 298 int mx, my; 299 int fbw, fbh; 300 int ww, wh; 301 302 trace_gd_update(x, y, w, h); 303 304 if (s->convert) { 305 pixman_image_composite(PIXMAN_OP_SRC, s->ds->image, NULL, s->convert, 306 x, y, 0, 0, x, y, w, h); 307 } 308 309 x1 = floor(x * s->scale_x); 310 y1 = floor(y * s->scale_y); 311 312 x2 = ceil(x * s->scale_x + w * s->scale_x); 313 y2 = ceil(y * s->scale_y + h * s->scale_y); 314 315 fbw = surface_width(s->ds) * s->scale_x; 316 fbh = surface_height(s->ds) * s->scale_y; 317 318 gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); 319 320 mx = my = 0; 321 if (ww > fbw) { 322 mx = (ww - fbw) / 2; 323 } 324 if (wh > fbh) { 325 my = (wh - fbh) / 2; 326 } 327 328 gtk_widget_queue_draw_area(s->drawing_area, mx + x1, my + y1, (x2 - x1), (y2 - y1)); 329 } 330 331 static void gd_refresh(DisplayChangeListener *dcl) 332 { 333 graphic_hw_update(dcl->con); 334 } 335 336 #if GTK_CHECK_VERSION(3, 0, 0) 337 static void gd_mouse_set(DisplayChangeListener *dcl, 338 int x, int y, int visible) 339 { 340 GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); 341 GdkDisplay *dpy; 342 GdkDeviceManager *mgr; 343 gint x_root, y_root; 344 345 dpy = gtk_widget_get_display(s->drawing_area); 346 mgr = gdk_display_get_device_manager(dpy); 347 gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area), 348 x, y, &x_root, &y_root); 349 gdk_device_warp(gdk_device_manager_get_client_pointer(mgr), 350 gtk_widget_get_screen(s->drawing_area), 351 x, y); 352 } 353 #else 354 static void gd_mouse_set(DisplayChangeListener *dcl, 355 int x, int y, int visible) 356 { 357 GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); 358 gint x_root, y_root; 359 360 gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area), 361 x, y, &x_root, &y_root); 362 gdk_display_warp_pointer(gtk_widget_get_display(s->drawing_area), 363 gtk_widget_get_screen(s->drawing_area), 364 x_root, y_root); 365 } 366 #endif 367 368 static void gd_cursor_define(DisplayChangeListener *dcl, 369 QEMUCursor *c) 370 { 371 GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); 372 GdkPixbuf *pixbuf; 373 GdkCursor *cursor; 374 375 pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data), 376 GDK_COLORSPACE_RGB, true, 8, 377 c->width, c->height, c->width * 4, 378 NULL, NULL); 379 cursor = gdk_cursor_new_from_pixbuf(gtk_widget_get_display(s->drawing_area), 380 pixbuf, c->hot_x, c->hot_y); 381 gdk_window_set_cursor(gtk_widget_get_window(s->drawing_area), cursor); 382 g_object_unref(pixbuf); 383 #if !GTK_CHECK_VERSION(3, 0, 0) 384 gdk_cursor_unref(cursor); 385 #else 386 g_object_unref(cursor); 387 #endif 388 } 389 390 static void gd_switch(DisplayChangeListener *dcl, 391 DisplaySurface *surface) 392 { 393 GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); 394 bool resized = true; 395 396 trace_gd_switch(surface_width(surface), surface_height(surface)); 397 398 if (s->surface) { 399 cairo_surface_destroy(s->surface); 400 } 401 402 if (s->ds && 403 surface_width(s->ds) == surface_width(surface) && 404 surface_height(s->ds) == surface_height(surface)) { 405 resized = false; 406 } 407 s->ds = surface; 408 409 if (s->convert) { 410 pixman_image_unref(s->convert); 411 s->convert = NULL; 412 } 413 414 if (surface->format == PIXMAN_x8r8g8b8) { 415 /* 416 * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24 417 * 418 * No need to convert, use surface directly. Should be the 419 * common case as this is qemu_default_pixelformat(32) too. 420 */ 421 s->surface = cairo_image_surface_create_for_data 422 (surface_data(surface), 423 CAIRO_FORMAT_RGB24, 424 surface_width(surface), 425 surface_height(surface), 426 surface_stride(surface)); 427 } else { 428 /* Must convert surface, use pixman to do it. */ 429 s->convert = pixman_image_create_bits(PIXMAN_x8r8g8b8, 430 surface_width(surface), 431 surface_height(surface), 432 NULL, 0); 433 s->surface = cairo_image_surface_create_for_data 434 ((void *)pixman_image_get_data(s->convert), 435 CAIRO_FORMAT_RGB24, 436 pixman_image_get_width(s->convert), 437 pixman_image_get_height(s->convert), 438 pixman_image_get_stride(s->convert)); 439 pixman_image_composite(PIXMAN_OP_SRC, s->ds->image, NULL, s->convert, 440 0, 0, 0, 0, 0, 0, 441 pixman_image_get_width(s->convert), 442 pixman_image_get_height(s->convert)); 443 } 444 445 if (resized) { 446 gd_update_windowsize(s); 447 } else { 448 gd_update_full_redraw(s); 449 } 450 } 451 452 /** QEMU Events **/ 453 454 static void gd_change_runstate(void *opaque, int running, RunState state) 455 { 456 GtkDisplayState *s = opaque; 457 458 gd_update_caption(s); 459 } 460 461 static void gd_mouse_mode_change(Notifier *notify, void *data) 462 { 463 gd_update_cursor(container_of(notify, GtkDisplayState, mouse_mode_notifier), 464 FALSE); 465 } 466 467 /** GTK Events **/ 468 469 static gboolean gd_window_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) 470 { 471 GtkDisplayState *s = opaque; 472 gboolean handled = FALSE; 473 474 if (!gd_is_grab_active(s) || 475 (key->state & IGNORE_MODIFIER_MASK) == HOTKEY_MODIFIERS) { 476 handled = gtk_window_activate_key(GTK_WINDOW(widget), key); 477 } 478 if (handled) { 479 gtk_release_modifiers(s); 480 } else { 481 handled = gtk_window_propagate_key_event(GTK_WINDOW(widget), key); 482 } 483 484 return handled; 485 } 486 487 static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event, 488 void *opaque) 489 { 490 GtkDisplayState *s = opaque; 491 492 if (!no_quit) { 493 unregister_displaychangelistener(&s->dcl); 494 qmp_quit(NULL); 495 return FALSE; 496 } 497 498 return TRUE; 499 } 500 501 static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) 502 { 503 GtkDisplayState *s = opaque; 504 int mx, my; 505 int ww, wh; 506 int fbw, fbh; 507 508 if (!gtk_widget_get_realized(widget)) { 509 return FALSE; 510 } 511 512 fbw = surface_width(s->ds); 513 fbh = surface_height(s->ds); 514 515 gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh); 516 517 if (s->full_screen) { 518 s->scale_x = (double)ww / fbw; 519 s->scale_y = (double)wh / fbh; 520 } else if (s->free_scale) { 521 double sx, sy; 522 523 sx = (double)ww / fbw; 524 sy = (double)wh / fbh; 525 526 s->scale_x = s->scale_y = MIN(sx, sy); 527 } 528 529 fbw *= s->scale_x; 530 fbh *= s->scale_y; 531 532 mx = my = 0; 533 if (ww > fbw) { 534 mx = (ww - fbw) / 2; 535 } 536 if (wh > fbh) { 537 my = (wh - fbh) / 2; 538 } 539 540 cairo_rectangle(cr, 0, 0, ww, wh); 541 542 /* Optionally cut out the inner area where the pixmap 543 will be drawn. This avoids 'flashing' since we're 544 not double-buffering. Note we're using the undocumented 545 behaviour of drawing the rectangle from right to left 546 to cut out the whole */ 547 cairo_rectangle(cr, mx + fbw, my, 548 -1 * fbw, fbh); 549 cairo_fill(cr); 550 551 cairo_scale(cr, s->scale_x, s->scale_y); 552 cairo_set_source_surface(cr, s->surface, mx / s->scale_x, my / s->scale_y); 553 cairo_paint(cr); 554 555 return TRUE; 556 } 557 558 #if !GTK_CHECK_VERSION(3, 0, 0) 559 static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose, 560 void *opaque) 561 { 562 cairo_t *cr; 563 gboolean ret; 564 565 cr = gdk_cairo_create(gtk_widget_get_window(widget)); 566 cairo_rectangle(cr, 567 expose->area.x, 568 expose->area.y, 569 expose->area.width, 570 expose->area.height); 571 cairo_clip(cr); 572 573 ret = gd_draw_event(widget, cr, opaque); 574 575 cairo_destroy(cr); 576 577 return ret; 578 } 579 #endif 580 581 static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, 582 void *opaque) 583 { 584 GtkDisplayState *s = opaque; 585 int dx, dy; 586 int x, y; 587 int mx, my; 588 int fbh, fbw; 589 int ww, wh; 590 591 fbw = surface_width(s->ds) * s->scale_x; 592 fbh = surface_height(s->ds) * s->scale_y; 593 594 gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); 595 596 mx = my = 0; 597 if (ww > fbw) { 598 mx = (ww - fbw) / 2; 599 } 600 if (wh > fbh) { 601 my = (wh - fbh) / 2; 602 } 603 604 x = (motion->x - mx) / s->scale_x; 605 y = (motion->y - my) / s->scale_y; 606 607 if (x < 0 || y < 0 || 608 x >= surface_width(s->ds) || 609 y >= surface_height(s->ds)) { 610 return TRUE; 611 } 612 613 if (kbd_mouse_is_absolute()) { 614 dx = x * 0x7FFF / (surface_width(s->ds) - 1); 615 dy = y * 0x7FFF / (surface_height(s->ds) - 1); 616 } else if (s->last_x == -1 || s->last_y == -1) { 617 dx = 0; 618 dy = 0; 619 } else { 620 dx = x - s->last_x; 621 dy = y - s->last_y; 622 } 623 624 s->last_x = x; 625 s->last_y = y; 626 627 if (kbd_mouse_is_absolute() || gd_is_grab_active(s)) { 628 kbd_mouse_event(dx, dy, 0, s->button_mask); 629 } 630 631 if (!kbd_mouse_is_absolute() && gd_is_grab_active(s)) { 632 GdkScreen *screen = gtk_widget_get_screen(s->drawing_area); 633 int x = (int)motion->x_root; 634 int y = (int)motion->y_root; 635 636 /* In relative mode check to see if client pointer hit 637 * one of the screen edges, and if so move it back by 638 * 200 pixels. This is important because the pointer 639 * in the server doesn't correspond 1-for-1, and so 640 * may still be only half way across the screen. Without 641 * this warp, the server pointer would thus appear to hit 642 * an invisible wall */ 643 if (x == 0) { 644 x += 200; 645 } 646 if (y == 0) { 647 y += 200; 648 } 649 if (x == (gdk_screen_get_width(screen) - 1)) { 650 x -= 200; 651 } 652 if (y == (gdk_screen_get_height(screen) - 1)) { 653 y -= 200; 654 } 655 656 if (x != (int)motion->x_root || y != (int)motion->y_root) { 657 #if GTK_CHECK_VERSION(3, 0, 0) 658 GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion); 659 gdk_device_warp(dev, screen, x, y); 660 #else 661 GdkDisplay *display = gtk_widget_get_display(widget); 662 gdk_display_warp_pointer(display, screen, x, y); 663 #endif 664 s->last_x = -1; 665 s->last_y = -1; 666 return FALSE; 667 } 668 } 669 return TRUE; 670 } 671 672 static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, 673 void *opaque) 674 { 675 GtkDisplayState *s = opaque; 676 int dx, dy; 677 int n; 678 679 if (button->button == 1) { 680 n = 0x01; 681 } else if (button->button == 2) { 682 n = 0x04; 683 } else if (button->button == 3) { 684 n = 0x02; 685 } else { 686 n = 0x00; 687 } 688 689 if (button->type == GDK_BUTTON_PRESS) { 690 s->button_mask |= n; 691 } else if (button->type == GDK_BUTTON_RELEASE) { 692 s->button_mask &= ~n; 693 } 694 695 if (kbd_mouse_is_absolute()) { 696 dx = s->last_x * 0x7FFF / (surface_width(s->ds) - 1); 697 dy = s->last_y * 0x7FFF / (surface_height(s->ds) - 1); 698 } else { 699 dx = 0; 700 dy = 0; 701 } 702 703 kbd_mouse_event(dx, dy, 0, s->button_mask); 704 705 return TRUE; 706 } 707 708 static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) 709 { 710 GtkDisplayState *s = opaque; 711 int gdk_keycode = key->hardware_keycode; 712 int i; 713 714 #ifdef _WIN32 715 UINT qemu_keycode = MapVirtualKey(gdk_keycode, MAPVK_VK_TO_VSC); 716 switch (qemu_keycode) { 717 case 103: /* alt gr */ 718 qemu_keycode = 56 | SCANCODE_GREY; 719 break; 720 } 721 #else 722 int qemu_keycode; 723 724 if (gdk_keycode < 9) { 725 qemu_keycode = 0; 726 } else if (gdk_keycode < 97) { 727 qemu_keycode = gdk_keycode - 8; 728 } else if (gdk_keycode < 158) { 729 qemu_keycode = translate_evdev_keycode(gdk_keycode - 97); 730 } else if (gdk_keycode == 208) { /* Hiragana_Katakana */ 731 qemu_keycode = 0x70; 732 } else if (gdk_keycode == 211) { /* backslash */ 733 qemu_keycode = 0x73; 734 } else { 735 qemu_keycode = 0; 736 } 737 #endif 738 739 trace_gd_key_event(gdk_keycode, qemu_keycode, 740 (key->type == GDK_KEY_PRESS) ? "down" : "up"); 741 742 for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { 743 if (qemu_keycode == modifier_keycode[i]) { 744 s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS); 745 } 746 } 747 748 if (qemu_keycode & SCANCODE_GREY) { 749 kbd_put_keycode(SCANCODE_EMUL0); 750 } 751 752 if (key->type == GDK_KEY_PRESS) { 753 kbd_put_keycode(qemu_keycode & SCANCODE_KEYCODEMASK); 754 } else if (key->type == GDK_KEY_RELEASE) { 755 kbd_put_keycode(qemu_keycode | SCANCODE_UP); 756 } else { 757 g_assert_not_reached(); 758 } 759 760 return TRUE; 761 } 762 763 /** Window Menu Actions **/ 764 765 static void gd_menu_pause(GtkMenuItem *item, void *opaque) 766 { 767 GtkDisplayState *s = opaque; 768 769 if (s->external_pause_update) { 770 return; 771 } 772 if (runstate_is_running()) { 773 qmp_stop(NULL); 774 } else { 775 qmp_cont(NULL); 776 } 777 } 778 779 static void gd_menu_reset(GtkMenuItem *item, void *opaque) 780 { 781 qmp_system_reset(NULL); 782 } 783 784 static void gd_menu_powerdown(GtkMenuItem *item, void *opaque) 785 { 786 qmp_system_powerdown(NULL); 787 } 788 789 static void gd_menu_quit(GtkMenuItem *item, void *opaque) 790 { 791 qmp_quit(NULL); 792 } 793 794 static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque) 795 { 796 GtkDisplayState *s = opaque; 797 798 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) { 799 gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), 0); 800 } else { 801 int i; 802 803 gtk_release_modifiers(s); 804 for (i = 0; i < s->nb_vcs; i++) { 805 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) { 806 gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), i + 1); 807 break; 808 } 809 } 810 } 811 } 812 813 static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque) 814 { 815 GtkDisplayState *s = opaque; 816 817 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) { 818 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE); 819 } else { 820 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 821 } 822 } 823 824 static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) 825 { 826 GtkDisplayState *s = opaque; 827 828 if (!s->full_screen) { 829 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 830 gtk_widget_set_size_request(s->menu_bar, 0, 0); 831 gtk_widget_set_size_request(s->drawing_area, -1, -1); 832 gtk_window_fullscreen(GTK_WINDOW(s->window)); 833 if (gd_on_vga(s)) { 834 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE); 835 } 836 s->full_screen = TRUE; 837 } else { 838 gtk_window_unfullscreen(GTK_WINDOW(s->window)); 839 gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s); 840 gtk_widget_set_size_request(s->menu_bar, -1, -1); 841 gtk_widget_set_size_request(s->drawing_area, 842 surface_width(s->ds), 843 surface_height(s->ds)); 844 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE); 845 s->full_screen = FALSE; 846 s->scale_x = 1.0; 847 s->scale_y = 1.0; 848 } 849 850 gd_update_cursor(s, FALSE); 851 } 852 853 static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque) 854 { 855 GtkDisplayState *s = opaque; 856 857 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), 858 FALSE); 859 860 s->scale_x += .25; 861 s->scale_y += .25; 862 863 gd_update_windowsize(s); 864 } 865 866 static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque) 867 { 868 GtkDisplayState *s = opaque; 869 870 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), 871 FALSE); 872 873 s->scale_x -= .25; 874 s->scale_y -= .25; 875 876 s->scale_x = MAX(s->scale_x, .25); 877 s->scale_y = MAX(s->scale_y, .25); 878 879 gd_update_windowsize(s); 880 } 881 882 static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque) 883 { 884 GtkDisplayState *s = opaque; 885 886 s->scale_x = 1.0; 887 s->scale_y = 1.0; 888 889 gd_update_windowsize(s); 890 } 891 892 static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque) 893 { 894 GtkDisplayState *s = opaque; 895 896 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) { 897 s->free_scale = TRUE; 898 } else { 899 s->free_scale = FALSE; 900 s->scale_x = 1.0; 901 s->scale_y = 1.0; 902 gd_update_windowsize(s); 903 } 904 905 gd_update_full_redraw(s); 906 } 907 908 static void gd_grab_keyboard(GtkDisplayState *s) 909 { 910 #if GTK_CHECK_VERSION(3, 0, 0) 911 GdkDisplay *display = gtk_widget_get_display(s->drawing_area); 912 GdkDeviceManager *mgr = gdk_display_get_device_manager(display); 913 GList *devices = gdk_device_manager_list_devices(mgr, 914 GDK_DEVICE_TYPE_MASTER); 915 GList *tmp = devices; 916 while (tmp) { 917 GdkDevice *dev = tmp->data; 918 if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) { 919 gdk_device_grab(dev, 920 gtk_widget_get_window(s->drawing_area), 921 GDK_OWNERSHIP_NONE, 922 FALSE, 923 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, 924 NULL, 925 GDK_CURRENT_TIME); 926 } 927 tmp = tmp->next; 928 } 929 g_list_free(devices); 930 #else 931 gdk_keyboard_grab(gtk_widget_get_window(s->drawing_area), 932 FALSE, 933 GDK_CURRENT_TIME); 934 #endif 935 } 936 937 static void gd_ungrab_keyboard(GtkDisplayState *s) 938 { 939 #if GTK_CHECK_VERSION(3, 0, 0) 940 GdkDisplay *display = gtk_widget_get_display(s->drawing_area); 941 GdkDeviceManager *mgr = gdk_display_get_device_manager(display); 942 GList *devices = gdk_device_manager_list_devices(mgr, 943 GDK_DEVICE_TYPE_MASTER); 944 GList *tmp = devices; 945 while (tmp) { 946 GdkDevice *dev = tmp->data; 947 if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) { 948 gdk_device_ungrab(dev, 949 GDK_CURRENT_TIME); 950 } 951 tmp = tmp->next; 952 } 953 g_list_free(devices); 954 #else 955 gdk_keyboard_ungrab(GDK_CURRENT_TIME); 956 #endif 957 } 958 959 static void gd_grab_pointer(GtkDisplayState *s) 960 { 961 #if GTK_CHECK_VERSION(3, 0, 0) 962 GdkDisplay *display = gtk_widget_get_display(s->drawing_area); 963 GdkDeviceManager *mgr = gdk_display_get_device_manager(display); 964 GList *devices = gdk_device_manager_list_devices(mgr, 965 GDK_DEVICE_TYPE_MASTER); 966 GList *tmp = devices; 967 while (tmp) { 968 GdkDevice *dev = tmp->data; 969 if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) { 970 gdk_device_grab(dev, 971 gtk_widget_get_window(s->drawing_area), 972 GDK_OWNERSHIP_NONE, 973 FALSE, /* All events to come to our 974 window directly */ 975 GDK_POINTER_MOTION_MASK | 976 GDK_BUTTON_PRESS_MASK | 977 GDK_BUTTON_RELEASE_MASK | 978 GDK_BUTTON_MOTION_MASK | 979 GDK_SCROLL_MASK, 980 s->null_cursor, 981 GDK_CURRENT_TIME); 982 } 983 tmp = tmp->next; 984 } 985 g_list_free(devices); 986 #else 987 gdk_pointer_grab(gtk_widget_get_window(s->drawing_area), 988 FALSE, /* All events to come to our window directly */ 989 GDK_POINTER_MOTION_MASK | 990 GDK_BUTTON_PRESS_MASK | 991 GDK_BUTTON_RELEASE_MASK | 992 GDK_BUTTON_MOTION_MASK | 993 GDK_SCROLL_MASK, 994 NULL, /* Allow cursor to move over entire desktop */ 995 s->null_cursor, 996 GDK_CURRENT_TIME); 997 #endif 998 } 999 1000 static void gd_ungrab_pointer(GtkDisplayState *s) 1001 { 1002 #if GTK_CHECK_VERSION(3, 0, 0) 1003 GdkDisplay *display = gtk_widget_get_display(s->drawing_area); 1004 GdkDeviceManager *mgr = gdk_display_get_device_manager(display); 1005 GList *devices = gdk_device_manager_list_devices(mgr, 1006 GDK_DEVICE_TYPE_MASTER); 1007 GList *tmp = devices; 1008 while (tmp) { 1009 GdkDevice *dev = tmp->data; 1010 if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) { 1011 gdk_device_ungrab(dev, 1012 GDK_CURRENT_TIME); 1013 } 1014 tmp = tmp->next; 1015 } 1016 g_list_free(devices); 1017 #else 1018 gdk_pointer_ungrab(GDK_CURRENT_TIME); 1019 #endif 1020 } 1021 1022 static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) 1023 { 1024 GtkDisplayState *s = opaque; 1025 1026 if (gd_is_grab_active(s)) { 1027 gd_grab_keyboard(s); 1028 gd_grab_pointer(s); 1029 } else { 1030 gd_ungrab_keyboard(s); 1031 gd_ungrab_pointer(s); 1032 } 1033 1034 gd_update_caption(s); 1035 gd_update_cursor(s, FALSE); 1036 } 1037 1038 static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, 1039 gpointer data) 1040 { 1041 GtkDisplayState *s = data; 1042 guint last_page; 1043 gboolean on_vga; 1044 1045 if (!gtk_widget_get_realized(s->notebook)) { 1046 return; 1047 } 1048 1049 last_page = gtk_notebook_get_current_page(nb); 1050 1051 if (last_page) { 1052 gtk_widget_set_size_request(s->vc[last_page - 1].terminal, -1, -1); 1053 } 1054 1055 on_vga = arg2 == 0; 1056 1057 if (!on_vga) { 1058 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 1059 FALSE); 1060 } else if (s->full_screen) { 1061 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), 1062 TRUE); 1063 } 1064 1065 if (arg2 == 0) { 1066 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE); 1067 } else { 1068 VirtualConsole *vc = &s->vc[arg2 - 1]; 1069 VteTerminal *term = VTE_TERMINAL(vc->terminal); 1070 int width, height; 1071 1072 width = 80 * vte_terminal_get_char_width(term); 1073 height = 25 * vte_terminal_get_char_height(term); 1074 1075 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); 1076 gtk_widget_set_size_request(vc->terminal, width, height); 1077 } 1078 1079 gtk_widget_set_sensitive(s->grab_item, on_vga); 1080 1081 gd_update_cursor(s, TRUE); 1082 } 1083 1084 static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data) 1085 { 1086 GtkDisplayState *s = data; 1087 1088 if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) { 1089 gd_grab_keyboard(s); 1090 } 1091 1092 return TRUE; 1093 } 1094 1095 static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data) 1096 { 1097 GtkDisplayState *s = data; 1098 1099 if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) { 1100 gd_ungrab_keyboard(s); 1101 } 1102 1103 return TRUE; 1104 } 1105 1106 static gboolean gd_focus_out_event(GtkWidget *widget, 1107 GdkEventCrossing *crossing, gpointer data) 1108 { 1109 GtkDisplayState *s = data; 1110 1111 gtk_release_modifiers(s); 1112 1113 return TRUE; 1114 } 1115 1116 /** Virtual Console Callbacks **/ 1117 1118 static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len) 1119 { 1120 VirtualConsole *vc = chr->opaque; 1121 1122 return write(vc->fd, buf, len); 1123 } 1124 1125 static int nb_vcs; 1126 static CharDriverState *vcs[MAX_VCS]; 1127 1128 static CharDriverState *gd_vc_handler(ChardevVC *unused) 1129 { 1130 CharDriverState *chr; 1131 1132 chr = g_malloc0(sizeof(*chr)); 1133 chr->chr_write = gd_vc_chr_write; 1134 /* defer OPENED events until our vc is fully initialized */ 1135 chr->explicit_be_open = true; 1136 1137 vcs[nb_vcs++] = chr; 1138 1139 return chr; 1140 } 1141 1142 void early_gtk_display_init(void) 1143 { 1144 register_vc_handler(gd_vc_handler); 1145 } 1146 1147 static gboolean gd_vc_in(GIOChannel *chan, GIOCondition cond, void *opaque) 1148 { 1149 VirtualConsole *vc = opaque; 1150 uint8_t buffer[1024]; 1151 ssize_t len; 1152 1153 len = read(vc->fd, buffer, sizeof(buffer)); 1154 if (len <= 0) { 1155 return FALSE; 1156 } 1157 1158 qemu_chr_be_write(vc->chr, buffer, len); 1159 1160 return TRUE; 1161 } 1162 1163 static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group, 1164 GtkWidget *view_menu) 1165 { 1166 const char *label; 1167 char buffer[32]; 1168 char path[32]; 1169 #if VTE_CHECK_VERSION(0, 26, 0) 1170 VtePty *pty; 1171 #endif 1172 GIOChannel *chan; 1173 GtkWidget *scrolled_window; 1174 GtkAdjustment *vadjustment; 1175 int master_fd, slave_fd; 1176 1177 snprintf(buffer, sizeof(buffer), "vc%d", index); 1178 snprintf(path, sizeof(path), "<QEMU>/View/VC%d", index); 1179 1180 vc->chr = vcs[index]; 1181 1182 if (vc->chr->label) { 1183 label = vc->chr->label; 1184 } else { 1185 label = buffer; 1186 } 1187 1188 vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label); 1189 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); 1190 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path); 1191 gtk_accel_map_add_entry(path, GDK_KEY_2 + index, HOTKEY_MODIFIERS); 1192 1193 vc->terminal = vte_terminal_new(); 1194 1195 master_fd = qemu_openpty_raw(&slave_fd, NULL); 1196 g_assert(master_fd != -1); 1197 1198 #if VTE_CHECK_VERSION(0, 26, 0) 1199 pty = vte_pty_new_foreign(master_fd, NULL); 1200 vte_terminal_set_pty_object(VTE_TERMINAL(vc->terminal), pty); 1201 #else 1202 vte_terminal_set_pty(VTE_TERMINAL(vc->terminal), master_fd); 1203 #endif 1204 1205 vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1); 1206 1207 vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal)); 1208 1209 scrolled_window = gtk_scrolled_window_new(NULL, vadjustment); 1210 gtk_container_add(GTK_CONTAINER(scrolled_window), vc->terminal); 1211 1212 vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25); 1213 1214 vc->fd = slave_fd; 1215 vc->chr->opaque = vc; 1216 vc->scrolled_window = scrolled_window; 1217 1218 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vc->scrolled_window), 1219 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); 1220 1221 gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), scrolled_window, gtk_label_new(label)); 1222 g_signal_connect(vc->menu_item, "activate", 1223 G_CALLBACK(gd_menu_switch_vc), s); 1224 1225 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item); 1226 1227 qemu_chr_be_generic_open(vc->chr); 1228 if (vc->chr->init) { 1229 vc->chr->init(vc->chr); 1230 } 1231 1232 chan = g_io_channel_unix_new(vc->fd); 1233 g_io_add_watch(chan, G_IO_IN, gd_vc_in, vc); 1234 1235 return group; 1236 } 1237 1238 /** Window Creation **/ 1239 1240 static void gd_connect_signals(GtkDisplayState *s) 1241 { 1242 g_signal_connect(s->show_tabs_item, "activate", 1243 G_CALLBACK(gd_menu_show_tabs), s); 1244 1245 g_signal_connect(s->window, "key-press-event", 1246 G_CALLBACK(gd_window_key_event), s); 1247 g_signal_connect(s->window, "delete-event", 1248 G_CALLBACK(gd_window_close), s); 1249 1250 #if GTK_CHECK_VERSION(3, 0, 0) 1251 g_signal_connect(s->drawing_area, "draw", 1252 G_CALLBACK(gd_draw_event), s); 1253 #else 1254 g_signal_connect(s->drawing_area, "expose-event", 1255 G_CALLBACK(gd_expose_event), s); 1256 #endif 1257 g_signal_connect(s->drawing_area, "motion-notify-event", 1258 G_CALLBACK(gd_motion_event), s); 1259 g_signal_connect(s->drawing_area, "button-press-event", 1260 G_CALLBACK(gd_button_event), s); 1261 g_signal_connect(s->drawing_area, "button-release-event", 1262 G_CALLBACK(gd_button_event), s); 1263 g_signal_connect(s->drawing_area, "key-press-event", 1264 G_CALLBACK(gd_key_event), s); 1265 g_signal_connect(s->drawing_area, "key-release-event", 1266 G_CALLBACK(gd_key_event), s); 1267 1268 g_signal_connect(s->pause_item, "activate", 1269 G_CALLBACK(gd_menu_pause), s); 1270 g_signal_connect(s->reset_item, "activate", 1271 G_CALLBACK(gd_menu_reset), s); 1272 g_signal_connect(s->powerdown_item, "activate", 1273 G_CALLBACK(gd_menu_powerdown), s); 1274 g_signal_connect(s->quit_item, "activate", 1275 G_CALLBACK(gd_menu_quit), s); 1276 g_signal_connect(s->full_screen_item, "activate", 1277 G_CALLBACK(gd_menu_full_screen), s); 1278 g_signal_connect(s->zoom_in_item, "activate", 1279 G_CALLBACK(gd_menu_zoom_in), s); 1280 g_signal_connect(s->zoom_out_item, "activate", 1281 G_CALLBACK(gd_menu_zoom_out), s); 1282 g_signal_connect(s->zoom_fixed_item, "activate", 1283 G_CALLBACK(gd_menu_zoom_fixed), s); 1284 g_signal_connect(s->zoom_fit_item, "activate", 1285 G_CALLBACK(gd_menu_zoom_fit), s); 1286 g_signal_connect(s->vga_item, "activate", 1287 G_CALLBACK(gd_menu_switch_vc), s); 1288 g_signal_connect(s->grab_item, "activate", 1289 G_CALLBACK(gd_menu_grab_input), s); 1290 g_signal_connect(s->notebook, "switch-page", 1291 G_CALLBACK(gd_change_page), s); 1292 g_signal_connect(s->drawing_area, "enter-notify-event", 1293 G_CALLBACK(gd_enter_event), s); 1294 g_signal_connect(s->drawing_area, "leave-notify-event", 1295 G_CALLBACK(gd_leave_event), s); 1296 g_signal_connect(s->drawing_area, "focus-out-event", 1297 G_CALLBACK(gd_focus_out_event), s); 1298 } 1299 1300 static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *accel_group) 1301 { 1302 GtkWidget *machine_menu; 1303 GtkWidget *separator; 1304 GtkStockItem item; 1305 1306 machine_menu = gtk_menu_new(); 1307 gtk_menu_set_accel_group(GTK_MENU(machine_menu), accel_group); 1308 1309 s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause")); 1310 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item); 1311 1312 separator = gtk_separator_menu_item_new(); 1313 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator); 1314 1315 s->reset_item = gtk_image_menu_item_new_with_mnemonic(_("_Reset")); 1316 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item); 1317 1318 s->powerdown_item = gtk_image_menu_item_new_with_mnemonic(_("Power _Down")); 1319 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item); 1320 1321 separator = gtk_separator_menu_item_new(); 1322 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator); 1323 1324 s->quit_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL); 1325 gtk_stock_lookup(GTK_STOCK_QUIT, &item); 1326 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item), 1327 "<QEMU>/Machine/Quit"); 1328 gtk_accel_map_add_entry("<QEMU>/Machine/Quit", item.keyval, item.modifier); 1329 gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item); 1330 1331 return machine_menu; 1332 } 1333 1334 static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_group) 1335 { 1336 GSList *group = NULL; 1337 GtkWidget *view_menu; 1338 GtkWidget *separator; 1339 int i; 1340 1341 view_menu = gtk_menu_new(); 1342 gtk_menu_set_accel_group(GTK_MENU(view_menu), accel_group); 1343 1344 s->full_screen_item = 1345 gtk_image_menu_item_new_from_stock(GTK_STOCK_FULLSCREEN, NULL); 1346 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->full_screen_item), 1347 "<QEMU>/View/Full Screen"); 1348 gtk_accel_map_add_entry("<QEMU>/View/Full Screen", GDK_KEY_f, 1349 HOTKEY_MODIFIERS); 1350 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item); 1351 1352 separator = gtk_separator_menu_item_new(); 1353 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 1354 1355 s->zoom_in_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_IN, NULL); 1356 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item), 1357 "<QEMU>/View/Zoom In"); 1358 gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus, 1359 HOTKEY_MODIFIERS); 1360 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item); 1361 1362 s->zoom_out_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_OUT, NULL); 1363 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item), 1364 "<QEMU>/View/Zoom Out"); 1365 gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus, 1366 HOTKEY_MODIFIERS); 1367 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item); 1368 1369 s->zoom_fixed_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_100, NULL); 1370 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item), 1371 "<QEMU>/View/Zoom Fixed"); 1372 gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0, 1373 HOTKEY_MODIFIERS); 1374 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item); 1375 1376 s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit")); 1377 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item); 1378 1379 separator = gtk_separator_menu_item_new(); 1380 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 1381 1382 s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover")); 1383 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item); 1384 1385 s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input")); 1386 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item), 1387 "<QEMU>/View/Grab Input"); 1388 gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g, 1389 HOTKEY_MODIFIERS); 1390 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item); 1391 1392 separator = gtk_separator_menu_item_new(); 1393 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 1394 1395 s->vga_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA"); 1396 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s->vga_item)); 1397 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->vga_item), 1398 "<QEMU>/View/VGA"); 1399 gtk_accel_map_add_entry("<QEMU>/View/VGA", GDK_KEY_1, HOTKEY_MODIFIERS); 1400 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->vga_item); 1401 1402 for (i = 0; i < nb_vcs; i++) { 1403 VirtualConsole *vc = &s->vc[i]; 1404 1405 group = gd_vc_init(s, vc, i, group, view_menu); 1406 s->nb_vcs++; 1407 } 1408 1409 separator = gtk_separator_menu_item_new(); 1410 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); 1411 1412 s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs")); 1413 gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item); 1414 1415 return view_menu; 1416 } 1417 1418 static void gd_create_menus(GtkDisplayState *s) 1419 { 1420 GtkAccelGroup *accel_group; 1421 1422 accel_group = gtk_accel_group_new(); 1423 s->machine_menu = gd_create_menu_machine(s, accel_group); 1424 s->view_menu = gd_create_menu_view(s, accel_group); 1425 1426 s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine")); 1427 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item), 1428 s->machine_menu); 1429 gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item); 1430 1431 s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View")); 1432 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu); 1433 gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item); 1434 1435 g_object_set_data(G_OBJECT(s->window), "accel_group", accel_group); 1436 gtk_window_add_accel_group(GTK_WINDOW(s->window), accel_group); 1437 s->accel_group = accel_group; 1438 } 1439 1440 static const DisplayChangeListenerOps dcl_ops = { 1441 .dpy_name = "gtk", 1442 .dpy_gfx_update = gd_update, 1443 .dpy_gfx_switch = gd_switch, 1444 .dpy_refresh = gd_refresh, 1445 .dpy_mouse_set = gd_mouse_set, 1446 .dpy_cursor_define = gd_cursor_define, 1447 }; 1448 1449 void gtk_display_init(DisplayState *ds, bool full_screen) 1450 { 1451 GtkDisplayState *s = g_malloc0(sizeof(*s)); 1452 char *filename; 1453 1454 gtk_init(NULL, NULL); 1455 1456 s->dcl.ops = &dcl_ops; 1457 s->dcl.con = qemu_console_lookup_by_index(0); 1458 1459 s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1460 #if GTK_CHECK_VERSION(3, 2, 0) 1461 s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 1462 #else 1463 s->vbox = gtk_vbox_new(FALSE, 0); 1464 #endif 1465 s->notebook = gtk_notebook_new(); 1466 s->drawing_area = gtk_drawing_area_new(); 1467 s->menu_bar = gtk_menu_bar_new(); 1468 1469 s->scale_x = 1.0; 1470 s->scale_y = 1.0; 1471 s->free_scale = FALSE; 1472 1473 setlocale(LC_ALL, ""); 1474 bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR); 1475 textdomain("qemu"); 1476 1477 s->null_cursor = gdk_cursor_new(GDK_BLANK_CURSOR); 1478 1479 s->mouse_mode_notifier.notify = gd_mouse_mode_change; 1480 qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier); 1481 qemu_add_vm_change_state_handler(gd_change_runstate, s); 1482 1483 gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), s->drawing_area, gtk_label_new("VGA")); 1484 1485 filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg"); 1486 if (filename) { 1487 GError *error = NULL; 1488 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error); 1489 if (pixbuf) { 1490 gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf); 1491 } else { 1492 g_error_free(error); 1493 } 1494 g_free(filename); 1495 } 1496 1497 gd_create_menus(s); 1498 1499 gd_connect_signals(s); 1500 1501 gtk_widget_add_events(s->drawing_area, 1502 GDK_POINTER_MOTION_MASK | 1503 GDK_BUTTON_PRESS_MASK | 1504 GDK_BUTTON_RELEASE_MASK | 1505 GDK_BUTTON_MOTION_MASK | 1506 GDK_ENTER_NOTIFY_MASK | 1507 GDK_LEAVE_NOTIFY_MASK | 1508 GDK_SCROLL_MASK | 1509 GDK_KEY_PRESS_MASK); 1510 gtk_widget_set_double_buffered(s->drawing_area, FALSE); 1511 gtk_widget_set_can_focus(s->drawing_area, TRUE); 1512 1513 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); 1514 gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE); 1515 1516 gd_update_caption(s); 1517 1518 gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0); 1519 gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0); 1520 1521 gtk_container_add(GTK_CONTAINER(s->window), s->vbox); 1522 1523 gtk_widget_show_all(s->window); 1524 1525 if (full_screen) { 1526 gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item)); 1527 } 1528 1529 register_displaychangelistener(&s->dcl); 1530 1531 global_state = s; 1532 } 1533