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