]> git.proxmox.com Git - mirror_qemu.git/blob - ui/gtk.c
Remove use of gdk_drawable_get_{screen, display}
[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 #include "qemu-common.h"
38
39 #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
40 /* Work around an -Wstrict-prototypes warning in GTK headers */
41 #pragma GCC diagnostic ignored "-Wstrict-prototypes"
42 #endif
43 #include <gtk/gtk.h>
44 #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
45 #pragma GCC diagnostic error "-Wstrict-prototypes"
46 #endif
47
48
49 #include <gdk/gdkkeysyms.h>
50 #include <glib/gi18n.h>
51 #include <locale.h>
52 #include <vte/vte.h>
53 #include <sys/types.h>
54 #include <sys/socket.h>
55 #include <sys/un.h>
56 #include <sys/wait.h>
57 #include <pty.h>
58 #include <math.h>
59
60 #include "ui/console.h"
61 #include "sysemu/sysemu.h"
62 #include "qmp-commands.h"
63 #include "x_keymap.h"
64 #include "keymaps.h"
65 #include "char/char.h"
66
67 //#define DEBUG_GTK
68
69 #ifdef DEBUG_GTK
70 #define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
71 #else
72 #define DPRINTF(fmt, ...) do { } while (0)
73 #endif
74
75 #define MAX_VCS 10
76
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 typedef struct VirtualConsole
88 {
89 GtkWidget *menu_item;
90 GtkWidget *terminal;
91 GtkWidget *scrolled_window;
92 CharDriverState *chr;
93 int fd;
94 } VirtualConsole;
95
96 typedef struct GtkDisplayState
97 {
98 GtkWidget *window;
99
100 GtkWidget *menu_bar;
101
102 GtkAccelGroup *accel_group;
103
104 GtkWidget *machine_menu_item;
105 GtkWidget *machine_menu;
106 GtkWidget *pause_item;
107 GtkWidget *reset_item;
108 GtkWidget *powerdown_item;
109 GtkWidget *quit_item;
110
111 GtkWidget *view_menu_item;
112 GtkWidget *view_menu;
113 GtkWidget *full_screen_item;
114 GtkWidget *zoom_in_item;
115 GtkWidget *zoom_out_item;
116 GtkWidget *zoom_fixed_item;
117 GtkWidget *zoom_fit_item;
118 GtkWidget *grab_item;
119 GtkWidget *grab_on_hover_item;
120 GtkWidget *vga_item;
121
122 int nb_vcs;
123 VirtualConsole vc[MAX_VCS];
124
125 GtkWidget *show_tabs_item;
126
127 GtkWidget *vbox;
128 GtkWidget *notebook;
129 GtkWidget *drawing_area;
130 cairo_surface_t *surface;
131 DisplayChangeListener dcl;
132 DisplayState *ds;
133 int button_mask;
134 int last_x;
135 int last_y;
136
137 double scale_x;
138 double scale_y;
139 gboolean full_screen;
140
141 GdkCursor *null_cursor;
142 Notifier mouse_mode_notifier;
143 gboolean free_scale;
144
145 bool external_pause_update;
146 } GtkDisplayState;
147
148 static GtkDisplayState *global_state;
149
150 /** Utility Functions **/
151
152 static bool gd_is_grab_active(GtkDisplayState *s)
153 {
154 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
155 }
156
157 static bool gd_grab_on_hover(GtkDisplayState *s)
158 {
159 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
160 }
161
162 static bool gd_on_vga(GtkDisplayState *s)
163 {
164 return gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)) == 0;
165 }
166
167 static void gd_update_cursor(GtkDisplayState *s, gboolean override)
168 {
169 GdkWindow *window;
170 bool on_vga;
171
172 window = gtk_widget_get_window(GTK_WIDGET(s->drawing_area));
173
174 on_vga = gd_on_vga(s);
175
176 if ((override || on_vga) &&
177 (s->full_screen || kbd_mouse_is_absolute() || gd_is_grab_active(s))) {
178 gdk_window_set_cursor(window, s->null_cursor);
179 } else {
180 gdk_window_set_cursor(window, NULL);
181 }
182 }
183
184 static void gd_update_caption(GtkDisplayState *s)
185 {
186 const char *status = "";
187 gchar *title;
188 const char *grab = "";
189 bool is_paused = !runstate_is_running();
190
191 if (gd_is_grab_active(s)) {
192 grab = " - Press Ctrl+Alt+G to release grab";
193 }
194
195 if (is_paused) {
196 status = " [Paused]";
197 }
198 s->external_pause_update = true;
199 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
200 is_paused);
201 s->external_pause_update = false;
202
203 if (qemu_name) {
204 title = g_strdup_printf("QEMU (%s)%s%s", qemu_name, status, grab);
205 } else {
206 title = g_strdup_printf("QEMU%s%s", status, grab);
207 }
208
209 gtk_window_set_title(GTK_WINDOW(s->window), title);
210
211 g_free(title);
212 }
213
214 /** DisplayState Callbacks **/
215
216 static void gd_update(DisplayState *ds, int x, int y, int w, int h)
217 {
218 GtkDisplayState *s = ds->opaque;
219 int x1, x2, y1, y2;
220 int mx, my;
221 int fbw, fbh;
222 int ww, wh;
223
224 DPRINTF("update(x=%d, y=%d, w=%d, h=%d)\n", x, y, w, h);
225
226 x1 = floor(x * s->scale_x);
227 y1 = floor(y * s->scale_y);
228
229 x2 = ceil(x * s->scale_x + w * s->scale_x);
230 y2 = ceil(y * s->scale_y + h * s->scale_y);
231
232 fbw = ds_get_width(s->ds) * s->scale_x;
233 fbh = ds_get_height(s->ds) * s->scale_y;
234
235 gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
236
237 mx = my = 0;
238 if (ww > fbw) {
239 mx = (ww - fbw) / 2;
240 }
241 if (wh > fbh) {
242 my = (wh - fbh) / 2;
243 }
244
245 gtk_widget_queue_draw_area(s->drawing_area, mx + x1, my + y1, (x2 - x1), (y2 - y1));
246 }
247
248 static void gd_refresh(DisplayState *ds)
249 {
250 vga_hw_update();
251 }
252
253 static void gd_resize(DisplayState *ds)
254 {
255 GtkDisplayState *s = ds->opaque;
256 cairo_format_t kind;
257 int stride;
258
259 DPRINTF("resize(width=%d, height=%d)\n",
260 ds_get_width(ds), ds_get_height(ds));
261
262 if (s->surface) {
263 cairo_surface_destroy(s->surface);
264 }
265
266 switch (ds->surface->pf.bits_per_pixel) {
267 case 8:
268 kind = CAIRO_FORMAT_A8;
269 break;
270 case 16:
271 kind = CAIRO_FORMAT_RGB16_565;
272 break;
273 case 32:
274 kind = CAIRO_FORMAT_RGB24;
275 break;
276 default:
277 g_assert_not_reached();
278 break;
279 }
280
281 stride = cairo_format_stride_for_width(kind, ds_get_width(ds));
282 g_assert(ds_get_linesize(ds) == stride);
283
284 s->surface = cairo_image_surface_create_for_data(ds_get_data(ds),
285 kind,
286 ds_get_width(ds),
287 ds_get_height(ds),
288 ds_get_linesize(ds));
289
290 if (!s->full_screen) {
291 GtkRequisition req;
292 double sx, sy;
293
294 if (s->free_scale) {
295 sx = s->scale_x;
296 sy = s->scale_y;
297
298 s->scale_y = 1.0;
299 s->scale_x = 1.0;
300 } else {
301 sx = 1.0;
302 sy = 1.0;
303 }
304
305 gtk_widget_set_size_request(s->drawing_area,
306 ds_get_width(ds) * s->scale_x,
307 ds_get_height(ds) * s->scale_y);
308 gtk_widget_size_request(s->vbox, &req);
309
310 gtk_window_resize(GTK_WINDOW(s->window),
311 req.width * sx, req.height * sy);
312 }
313 }
314
315 /** QEMU Events **/
316
317 static void gd_change_runstate(void *opaque, int running, RunState state)
318 {
319 GtkDisplayState *s = opaque;
320
321 gd_update_caption(s);
322 }
323
324 static void gd_mouse_mode_change(Notifier *notify, void *data)
325 {
326 gd_update_cursor(container_of(notify, GtkDisplayState, mouse_mode_notifier),
327 FALSE);
328 }
329
330 /** GTK Events **/
331
332 static gboolean gd_window_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
333 {
334 GtkDisplayState *s = opaque;
335 GtkAccelGroupEntry *entries;
336 guint n_entries = 0;
337 gboolean propagate_accel = TRUE;
338 gboolean handled = FALSE;
339
340 entries = gtk_accel_group_query(s->accel_group, key->keyval,
341 key->state, &n_entries);
342 if (n_entries) {
343 const char *quark = g_quark_to_string(entries[0].accel_path_quark);
344
345 if (gd_is_grab_active(s) && strstart(quark, "<QEMU>/File/", NULL)) {
346 propagate_accel = FALSE;
347 }
348 }
349
350 if (!handled && propagate_accel) {
351 handled = gtk_window_activate_key(GTK_WINDOW(widget), key);
352 }
353
354 if (!handled) {
355 handled = gtk_window_propagate_key_event(GTK_WINDOW(widget), key);
356 }
357
358 return handled;
359 }
360
361 static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
362 void *opaque)
363 {
364 GtkDisplayState *s = opaque;
365
366 if (!no_quit) {
367 unregister_displaychangelistener(s->ds, &s->dcl);
368 qmp_quit(NULL);
369 return FALSE;
370 }
371
372 return TRUE;
373 }
374
375 static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
376 {
377 GtkDisplayState *s = opaque;
378 int mx, my;
379 int ww, wh;
380 int fbw, fbh;
381
382 if (!gtk_widget_get_realized(widget)) {
383 return FALSE;
384 }
385
386 fbw = ds_get_width(s->ds);
387 fbh = ds_get_height(s->ds);
388
389 gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
390
391 if (s->full_screen) {
392 s->scale_x = (double)ww / fbw;
393 s->scale_y = (double)wh / fbh;
394 } else if (s->free_scale) {
395 double sx, sy;
396
397 sx = (double)ww / fbw;
398 sy = (double)wh / fbh;
399
400 s->scale_x = s->scale_y = MIN(sx, sy);
401 }
402
403 fbw *= s->scale_x;
404 fbh *= s->scale_y;
405
406 mx = my = 0;
407 if (ww > fbw) {
408 mx = (ww - fbw) / 2;
409 }
410 if (wh > fbh) {
411 my = (wh - fbh) / 2;
412 }
413
414 cairo_rectangle(cr, 0, 0, ww, wh);
415
416 /* Optionally cut out the inner area where the pixmap
417 will be drawn. This avoids 'flashing' since we're
418 not double-buffering. Note we're using the undocumented
419 behaviour of drawing the rectangle from right to left
420 to cut out the whole */
421 cairo_rectangle(cr, mx + fbw, my,
422 -1 * fbw, fbh);
423 cairo_fill(cr);
424
425 cairo_scale(cr, s->scale_x, s->scale_y);
426 cairo_set_source_surface(cr, s->surface, mx / s->scale_x, my / s->scale_y);
427 cairo_paint(cr);
428
429 return TRUE;
430 }
431
432 static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
433 void *opaque)
434 {
435 cairo_t *cr;
436 gboolean ret;
437
438 cr = gdk_cairo_create(gtk_widget_get_window(widget));
439 cairo_rectangle(cr,
440 expose->area.x,
441 expose->area.y,
442 expose->area.width,
443 expose->area.height);
444 cairo_clip(cr);
445
446 ret = gd_draw_event(widget, cr, opaque);
447
448 cairo_destroy(cr);
449
450 return ret;
451 }
452
453 static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
454 void *opaque)
455 {
456 GtkDisplayState *s = opaque;
457 int dx, dy;
458 int x, y;
459 int mx, my;
460 int fbh, fbw;
461 int ww, wh;
462
463 fbw = ds_get_width(s->ds) * s->scale_x;
464 fbh = ds_get_height(s->ds) * s->scale_y;
465
466 gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
467
468 mx = my = 0;
469 if (ww > fbw) {
470 mx = (ww - fbw) / 2;
471 }
472 if (wh > fbh) {
473 my = (wh - fbh) / 2;
474 }
475
476 x = (motion->x - mx) / s->scale_x;
477 y = (motion->y - my) / s->scale_y;
478
479 if (x < 0 || y < 0 ||
480 x >= ds_get_width(s->ds) ||
481 y >= ds_get_height(s->ds)) {
482 return TRUE;
483 }
484
485 if (kbd_mouse_is_absolute()) {
486 dx = x * 0x7FFF / (ds_get_width(s->ds) - 1);
487 dy = y * 0x7FFF / (ds_get_height(s->ds) - 1);
488 } else if (s->last_x == -1 || s->last_y == -1) {
489 dx = 0;
490 dy = 0;
491 } else {
492 dx = x - s->last_x;
493 dy = y - s->last_y;
494 }
495
496 s->last_x = x;
497 s->last_y = y;
498
499 if (kbd_mouse_is_absolute() || gd_is_grab_active(s)) {
500 kbd_mouse_event(dx, dy, 0, s->button_mask);
501 }
502
503 if (!kbd_mouse_is_absolute() && gd_is_grab_active(s)) {
504 GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
505 GdkScreen *screen = gtk_widget_get_screen(s->drawing_area);
506 int x = (int)motion->x_root;
507 int y = (int)motion->y_root;
508
509 /* In relative mode check to see if client pointer hit
510 * one of the screen edges, and if so move it back by
511 * 200 pixels. This is important because the pointer
512 * in the server doesn't correspond 1-for-1, and so
513 * may still be only half way across the screen. Without
514 * this warp, the server pointer would thus appear to hit
515 * an invisible wall */
516 if (x == 0) {
517 x += 200;
518 }
519 if (y == 0) {
520 y += 200;
521 }
522 if (x == (gdk_screen_get_width(screen) - 1)) {
523 x -= 200;
524 }
525 if (y == (gdk_screen_get_height(screen) - 1)) {
526 y -= 200;
527 }
528
529 if (x != (int)motion->x_root || y != (int)motion->y_root) {
530 gdk_display_warp_pointer(display, screen, x, y);
531 s->last_x = -1;
532 s->last_y = -1;
533 return FALSE;
534 }
535 }
536 return TRUE;
537 }
538
539 static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
540 void *opaque)
541 {
542 GtkDisplayState *s = opaque;
543 int dx, dy;
544 int n;
545
546 if (button->button == 1) {
547 n = 0x01;
548 } else if (button->button == 2) {
549 n = 0x04;
550 } else if (button->button == 3) {
551 n = 0x02;
552 } else {
553 n = 0x00;
554 }
555
556 if (button->type == GDK_BUTTON_PRESS) {
557 s->button_mask |= n;
558 } else if (button->type == GDK_BUTTON_RELEASE) {
559 s->button_mask &= ~n;
560 }
561
562 if (kbd_mouse_is_absolute()) {
563 dx = s->last_x * 0x7FFF / (ds_get_width(s->ds) - 1);
564 dy = s->last_y * 0x7FFF / (ds_get_height(s->ds) - 1);
565 } else {
566 dx = 0;
567 dy = 0;
568 }
569
570 kbd_mouse_event(dx, dy, 0, s->button_mask);
571
572 return TRUE;
573 }
574
575 static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
576 {
577 int gdk_keycode;
578 int qemu_keycode;
579
580 gdk_keycode = key->hardware_keycode;
581
582 if (gdk_keycode < 9) {
583 qemu_keycode = 0;
584 } else if (gdk_keycode < 97) {
585 qemu_keycode = gdk_keycode - 8;
586 } else if (gdk_keycode < 158) {
587 qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
588 } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
589 qemu_keycode = 0x70;
590 } else if (gdk_keycode == 211) { /* backslash */
591 qemu_keycode = 0x73;
592 } else {
593 qemu_keycode = 0;
594 }
595
596 DPRINTF("translated GDK keycode %d to QEMU keycode %d (%s)\n",
597 gdk_keycode, qemu_keycode,
598 (key->type == GDK_KEY_PRESS) ? "down" : "up");
599
600 if (qemu_keycode & SCANCODE_GREY) {
601 kbd_put_keycode(SCANCODE_EMUL0);
602 }
603
604 if (key->type == GDK_KEY_PRESS) {
605 kbd_put_keycode(qemu_keycode & SCANCODE_KEYCODEMASK);
606 } else if (key->type == GDK_KEY_RELEASE) {
607 kbd_put_keycode(qemu_keycode | SCANCODE_UP);
608 } else {
609 g_assert_not_reached();
610 }
611
612 return TRUE;
613 }
614
615 /** Window Menu Actions **/
616
617 static void gd_menu_pause(GtkMenuItem *item, void *opaque)
618 {
619 GtkDisplayState *s = opaque;
620
621 if (s->external_pause_update) {
622 return;
623 }
624 if (runstate_is_running()) {
625 qmp_stop(NULL);
626 } else {
627 qmp_cont(NULL);
628 }
629 }
630
631 static void gd_menu_reset(GtkMenuItem *item, void *opaque)
632 {
633 qmp_system_reset(NULL);
634 }
635
636 static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
637 {
638 qmp_system_powerdown(NULL);
639 }
640
641 static void gd_menu_quit(GtkMenuItem *item, void *opaque)
642 {
643 qmp_quit(NULL);
644 }
645
646 static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
647 {
648 GtkDisplayState *s = opaque;
649
650 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) {
651 gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), 0);
652 } else {
653 int i;
654
655 for (i = 0; i < s->nb_vcs; i++) {
656 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) {
657 gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), i + 1);
658 break;
659 }
660 }
661 }
662 }
663
664 static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
665 {
666 GtkDisplayState *s = opaque;
667
668 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
669 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
670 } else {
671 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
672 }
673 }
674
675 static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
676 {
677 GtkDisplayState *s = opaque;
678
679 if (!s->full_screen) {
680 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
681 gtk_widget_set_size_request(s->menu_bar, 0, 0);
682 gtk_widget_set_size_request(s->drawing_area, -1, -1);
683 gtk_window_fullscreen(GTK_WINDOW(s->window));
684 if (gd_on_vga(s)) {
685 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE);
686 }
687 s->full_screen = TRUE;
688 } else {
689 gtk_window_unfullscreen(GTK_WINDOW(s->window));
690 gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
691 gtk_widget_set_size_request(s->menu_bar, -1, -1);
692 gtk_widget_set_size_request(s->drawing_area,
693 ds_get_width(s->ds), ds_get_height(s->ds));
694 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE);
695 s->full_screen = FALSE;
696 s->scale_x = 1.0;
697 s->scale_y = 1.0;
698 }
699
700 gd_update_cursor(s, FALSE);
701 }
702
703 static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
704 {
705 GtkDisplayState *s = opaque;
706
707 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
708 FALSE);
709
710 s->scale_x += .25;
711 s->scale_y += .25;
712
713 gd_resize(s->ds);
714 }
715
716 static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
717 {
718 GtkDisplayState *s = opaque;
719
720 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
721 FALSE);
722
723 s->scale_x -= .25;
724 s->scale_y -= .25;
725
726 s->scale_x = MAX(s->scale_x, .25);
727 s->scale_y = MAX(s->scale_y, .25);
728
729 gd_resize(s->ds);
730 }
731
732 static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
733 {
734 GtkDisplayState *s = opaque;
735
736 s->scale_x = 1.0;
737 s->scale_y = 1.0;
738
739 gd_resize(s->ds);
740 }
741
742 static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
743 {
744 GtkDisplayState *s = opaque;
745 int ww, wh;
746
747 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
748 s->free_scale = TRUE;
749 } else {
750 s->free_scale = FALSE;
751 }
752
753 gd_resize(s->ds);
754
755 gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
756 gtk_widget_queue_draw_area(s->drawing_area, 0, 0, ww, wh);
757 }
758
759 static void gd_grab_keyboard(GtkDisplayState *s)
760 {
761 gdk_keyboard_grab(gtk_widget_get_window(GTK_WIDGET(s->drawing_area)),
762 FALSE,
763 GDK_CURRENT_TIME);
764 }
765
766 static void gd_ungrab_keyboard(GtkDisplayState *s)
767 {
768 gdk_keyboard_ungrab(GDK_CURRENT_TIME);
769 }
770
771 static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
772 {
773 GtkDisplayState *s = opaque;
774
775 if (gd_is_grab_active(s)) {
776 gd_grab_keyboard(s);
777 gdk_pointer_grab(gtk_widget_get_window(GTK_WIDGET(s->drawing_area)),
778 FALSE, /* All events to come to our window directly */
779 GDK_POINTER_MOTION_MASK |
780 GDK_BUTTON_PRESS_MASK |
781 GDK_BUTTON_RELEASE_MASK |
782 GDK_BUTTON_MOTION_MASK |
783 GDK_SCROLL_MASK,
784 NULL, /* Allow cursor to move over entire desktop */
785 s->null_cursor,
786 GDK_CURRENT_TIME);
787 } else {
788 gd_ungrab_keyboard(s);
789 gdk_pointer_ungrab(GDK_CURRENT_TIME);
790 }
791
792 gd_update_caption(s);
793 gd_update_cursor(s, FALSE);
794 }
795
796 static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
797 gpointer data)
798 {
799 GtkDisplayState *s = data;
800 guint last_page;
801 gboolean on_vga;
802
803 if (!gtk_widget_get_realized(s->notebook)) {
804 return;
805 }
806
807 last_page = gtk_notebook_get_current_page(nb);
808
809 if (last_page) {
810 gtk_widget_set_size_request(s->vc[last_page - 1].terminal, -1, -1);
811 }
812
813 on_vga = arg2 == 0;
814
815 if (!on_vga) {
816 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
817 FALSE);
818 } else if (s->full_screen) {
819 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
820 TRUE);
821 }
822
823 if (arg2 == 0) {
824 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE);
825 } else {
826 VirtualConsole *vc = &s->vc[arg2 - 1];
827 VteTerminal *term = VTE_TERMINAL(vc->terminal);
828 int width, height;
829
830 width = 80 * vte_terminal_get_char_width(term);
831 height = 25 * vte_terminal_get_char_height(term);
832
833 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
834 gtk_widget_set_size_request(vc->terminal, width, height);
835 }
836
837 gtk_widget_set_sensitive(s->grab_item, on_vga);
838
839 gd_update_cursor(s, TRUE);
840 }
841
842 static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data)
843 {
844 GtkDisplayState *s = data;
845
846 if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) {
847 gd_grab_keyboard(s);
848 }
849
850 return TRUE;
851 }
852
853 static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data)
854 {
855 GtkDisplayState *s = data;
856
857 if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) {
858 gd_ungrab_keyboard(s);
859 }
860
861 return TRUE;
862 }
863
864 /** Virtual Console Callbacks **/
865
866 static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
867 {
868 VirtualConsole *vc = chr->opaque;
869
870 return write(vc->fd, buf, len);
871 }
872
873 static int nb_vcs;
874 static CharDriverState *vcs[MAX_VCS];
875
876 static CharDriverState *gd_vc_handler(QemuOpts *opts)
877 {
878 CharDriverState *chr;
879
880 chr = g_malloc0(sizeof(*chr));
881 chr->chr_write = gd_vc_chr_write;
882
883 vcs[nb_vcs++] = chr;
884
885 return chr;
886 }
887
888 void early_gtk_display_init(void)
889 {
890 register_vc_handler(gd_vc_handler);
891 }
892
893 static gboolean gd_vc_in(GIOChannel *chan, GIOCondition cond, void *opaque)
894 {
895 VirtualConsole *vc = opaque;
896 uint8_t buffer[1024];
897 ssize_t len;
898
899 len = read(vc->fd, buffer, sizeof(buffer));
900 if (len <= 0) {
901 return FALSE;
902 }
903
904 qemu_chr_be_write(vc->chr, buffer, len);
905
906 return TRUE;
907 }
908
909 static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group)
910 {
911 const char *label;
912 char buffer[32];
913 char path[32];
914 #if VTE_CHECK_VERSION(0, 26, 0)
915 VtePty *pty;
916 #endif
917 GIOChannel *chan;
918 GtkWidget *scrolled_window;
919 GtkAdjustment *vadjustment;
920 int master_fd, slave_fd, ret;
921 struct termios tty;
922
923 snprintf(buffer, sizeof(buffer), "vc%d", index);
924 snprintf(path, sizeof(path), "<QEMU>/View/VC%d", index);
925
926 vc->chr = vcs[index];
927
928 if (vc->chr->label) {
929 label = vc->chr->label;
930 } else {
931 label = buffer;
932 }
933
934 vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label);
935 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
936 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path);
937 gtk_accel_map_add_entry(path, GDK_KEY_2 + index, GDK_CONTROL_MASK | GDK_MOD1_MASK);
938
939 vc->terminal = vte_terminal_new();
940
941 ret = openpty(&master_fd, &slave_fd, NULL, NULL, NULL);
942 g_assert(ret != -1);
943
944 /* Set raw attributes on the pty. */
945 tcgetattr(slave_fd, &tty);
946 cfmakeraw(&tty);
947 tcsetattr(slave_fd, TCSAFLUSH, &tty);
948
949 #if VTE_CHECK_VERSION(0, 26, 0)
950 pty = vte_pty_new_foreign(master_fd, NULL);
951 vte_terminal_set_pty_object(VTE_TERMINAL(vc->terminal), pty);
952 #else
953 vte_terminal_set_pty(VTE_TERMINAL(vc->terminal), master_fd);
954 #endif
955
956 vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1);
957
958 vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal));
959
960 scrolled_window = gtk_scrolled_window_new(NULL, vadjustment);
961 gtk_container_add(GTK_CONTAINER(scrolled_window), vc->terminal);
962
963 vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25);
964
965 vc->fd = slave_fd;
966 vc->chr->opaque = vc;
967 vc->scrolled_window = scrolled_window;
968
969 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vc->scrolled_window),
970 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
971
972 gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), scrolled_window, gtk_label_new(label));
973 g_signal_connect(vc->menu_item, "activate",
974 G_CALLBACK(gd_menu_switch_vc), s);
975
976 gtk_menu_append(GTK_MENU(s->view_menu), vc->menu_item);
977
978 qemu_chr_generic_open(vc->chr);
979 if (vc->chr->init) {
980 vc->chr->init(vc->chr);
981 }
982
983 chan = g_io_channel_unix_new(vc->fd);
984 g_io_add_watch(chan, G_IO_IN, gd_vc_in, vc);
985
986 return group;
987 }
988
989 /** Window Creation **/
990
991 static void gd_connect_signals(GtkDisplayState *s)
992 {
993 g_signal_connect(s->show_tabs_item, "activate",
994 G_CALLBACK(gd_menu_show_tabs), s);
995
996 g_signal_connect(s->window, "key-press-event",
997 G_CALLBACK(gd_window_key_event), s);
998 g_signal_connect(s->window, "delete-event",
999 G_CALLBACK(gd_window_close), s);
1000
1001 g_signal_connect(s->drawing_area, "expose-event",
1002 G_CALLBACK(gd_expose_event), s);
1003 g_signal_connect(s->drawing_area, "motion-notify-event",
1004 G_CALLBACK(gd_motion_event), s);
1005 g_signal_connect(s->drawing_area, "button-press-event",
1006 G_CALLBACK(gd_button_event), s);
1007 g_signal_connect(s->drawing_area, "button-release-event",
1008 G_CALLBACK(gd_button_event), s);
1009 g_signal_connect(s->drawing_area, "key-press-event",
1010 G_CALLBACK(gd_key_event), s);
1011 g_signal_connect(s->drawing_area, "key-release-event",
1012 G_CALLBACK(gd_key_event), s);
1013
1014 g_signal_connect(s->pause_item, "activate",
1015 G_CALLBACK(gd_menu_pause), s);
1016 g_signal_connect(s->reset_item, "activate",
1017 G_CALLBACK(gd_menu_reset), s);
1018 g_signal_connect(s->powerdown_item, "activate",
1019 G_CALLBACK(gd_menu_powerdown), s);
1020 g_signal_connect(s->quit_item, "activate",
1021 G_CALLBACK(gd_menu_quit), s);
1022 g_signal_connect(s->full_screen_item, "activate",
1023 G_CALLBACK(gd_menu_full_screen), s);
1024 g_signal_connect(s->zoom_in_item, "activate",
1025 G_CALLBACK(gd_menu_zoom_in), s);
1026 g_signal_connect(s->zoom_out_item, "activate",
1027 G_CALLBACK(gd_menu_zoom_out), s);
1028 g_signal_connect(s->zoom_fixed_item, "activate",
1029 G_CALLBACK(gd_menu_zoom_fixed), s);
1030 g_signal_connect(s->zoom_fit_item, "activate",
1031 G_CALLBACK(gd_menu_zoom_fit), s);
1032 g_signal_connect(s->vga_item, "activate",
1033 G_CALLBACK(gd_menu_switch_vc), s);
1034 g_signal_connect(s->grab_item, "activate",
1035 G_CALLBACK(gd_menu_grab_input), s);
1036 g_signal_connect(s->notebook, "switch-page",
1037 G_CALLBACK(gd_change_page), s);
1038 g_signal_connect(s->drawing_area, "enter-notify-event",
1039 G_CALLBACK(gd_enter_event), s);
1040 g_signal_connect(s->drawing_area, "leave-notify-event",
1041 G_CALLBACK(gd_leave_event), s);
1042 }
1043
1044 static void gd_create_menus(GtkDisplayState *s)
1045 {
1046 GtkStockItem item;
1047 GtkAccelGroup *accel_group;
1048 GSList *group = NULL;
1049 GtkWidget *separator;
1050 int i;
1051
1052 accel_group = gtk_accel_group_new();
1053 s->machine_menu = gtk_menu_new();
1054 gtk_menu_set_accel_group(GTK_MENU(s->machine_menu), accel_group);
1055 s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
1056
1057 s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
1058 gtk_menu_append(GTK_MENU(s->machine_menu), s->pause_item);
1059
1060 separator = gtk_separator_menu_item_new();
1061 gtk_menu_append(GTK_MENU(s->machine_menu), separator);
1062
1063 s->reset_item = gtk_image_menu_item_new_with_mnemonic(_("_Reset"));
1064 gtk_menu_append(GTK_MENU(s->machine_menu), s->reset_item);
1065
1066 s->powerdown_item = gtk_image_menu_item_new_with_mnemonic(_("Power _Down"));
1067 gtk_menu_append(GTK_MENU(s->machine_menu), s->powerdown_item);
1068
1069 separator = gtk_separator_menu_item_new();
1070 gtk_menu_append(GTK_MENU(s->machine_menu), separator);
1071
1072 s->quit_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
1073 gtk_stock_lookup(GTK_STOCK_QUIT, &item);
1074 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
1075 "<QEMU>/Machine/Quit");
1076 gtk_accel_map_add_entry("<QEMU>/Machine/Quit", item.keyval, item.modifier);
1077 gtk_menu_append(GTK_MENU(s->machine_menu), s->quit_item);
1078
1079 s->view_menu = gtk_menu_new();
1080 gtk_menu_set_accel_group(GTK_MENU(s->view_menu), accel_group);
1081 s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
1082
1083 s->full_screen_item =
1084 gtk_image_menu_item_new_from_stock(GTK_STOCK_FULLSCREEN, NULL);
1085 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->full_screen_item),
1086 "<QEMU>/View/Full Screen");
1087 gtk_accel_map_add_entry("<QEMU>/View/Full Screen", GDK_KEY_f, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1088 gtk_menu_append(GTK_MENU(s->view_menu), s->full_screen_item);
1089
1090 separator = gtk_separator_menu_item_new();
1091 gtk_menu_append(GTK_MENU(s->view_menu), separator);
1092
1093 s->zoom_in_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_IN, NULL);
1094 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
1095 "<QEMU>/View/Zoom In");
1096 gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1097 gtk_menu_append(GTK_MENU(s->view_menu), s->zoom_in_item);
1098
1099 s->zoom_out_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_OUT, NULL);
1100 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
1101 "<QEMU>/View/Zoom Out");
1102 gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1103 gtk_menu_append(GTK_MENU(s->view_menu), s->zoom_out_item);
1104
1105 s->zoom_fixed_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_100, NULL);
1106 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
1107 "<QEMU>/View/Zoom Fixed");
1108 gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1109 gtk_menu_append(GTK_MENU(s->view_menu), s->zoom_fixed_item);
1110
1111 s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
1112 gtk_menu_append(GTK_MENU(s->view_menu), s->zoom_fit_item);
1113
1114 separator = gtk_separator_menu_item_new();
1115 gtk_menu_append(GTK_MENU(s->view_menu), separator);
1116
1117 s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
1118 gtk_menu_append(GTK_MENU(s->view_menu), s->grab_on_hover_item);
1119
1120 s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
1121 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
1122 "<QEMU>/View/Grab Input");
1123 gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1124 gtk_menu_append(GTK_MENU(s->view_menu), s->grab_item);
1125
1126 separator = gtk_separator_menu_item_new();
1127 gtk_menu_append(GTK_MENU(s->view_menu), separator);
1128
1129 s->vga_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA");
1130 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s->vga_item));
1131 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->vga_item),
1132 "<QEMU>/View/VGA");
1133 gtk_accel_map_add_entry("<QEMU>/View/VGA", GDK_KEY_1, GDK_CONTROL_MASK | GDK_MOD1_MASK);
1134 gtk_menu_append(GTK_MENU(s->view_menu), s->vga_item);
1135
1136 for (i = 0; i < nb_vcs; i++) {
1137 VirtualConsole *vc = &s->vc[i];
1138
1139 group = gd_vc_init(s, vc, i, group);
1140 s->nb_vcs++;
1141 }
1142
1143 separator = gtk_separator_menu_item_new();
1144 gtk_menu_append(GTK_MENU(s->view_menu), separator);
1145
1146 s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
1147 gtk_menu_append(GTK_MENU(s->view_menu), s->show_tabs_item);
1148
1149 g_object_set_data(G_OBJECT(s->window), "accel_group", accel_group);
1150 gtk_window_add_accel_group(GTK_WINDOW(s->window), accel_group);
1151 s->accel_group = accel_group;
1152
1153 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
1154 s->machine_menu);
1155 gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
1156
1157 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
1158 gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
1159 }
1160
1161 void gtk_display_init(DisplayState *ds)
1162 {
1163 GtkDisplayState *s = g_malloc0(sizeof(*s));
1164
1165 gtk_init(NULL, NULL);
1166
1167 ds->opaque = s;
1168 s->ds = ds;
1169 s->dcl.dpy_gfx_update = gd_update;
1170 s->dcl.dpy_gfx_resize = gd_resize;
1171 s->dcl.dpy_refresh = gd_refresh;
1172
1173 s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1174 s->vbox = gtk_vbox_new(FALSE, 0);
1175 s->notebook = gtk_notebook_new();
1176 s->drawing_area = gtk_drawing_area_new();
1177 s->menu_bar = gtk_menu_bar_new();
1178
1179 s->scale_x = 1.0;
1180 s->scale_y = 1.0;
1181 s->free_scale = FALSE;
1182
1183 setlocale(LC_ALL, "");
1184 bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
1185 textdomain("qemu");
1186
1187 s->null_cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
1188
1189 s->mouse_mode_notifier.notify = gd_mouse_mode_change;
1190 qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
1191 qemu_add_vm_change_state_handler(gd_change_runstate, s);
1192
1193 gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), s->drawing_area, gtk_label_new("VGA"));
1194
1195 gd_create_menus(s);
1196
1197 gd_connect_signals(s);
1198
1199 gtk_widget_add_events(s->drawing_area,
1200 GDK_POINTER_MOTION_MASK |
1201 GDK_BUTTON_PRESS_MASK |
1202 GDK_BUTTON_RELEASE_MASK |
1203 GDK_BUTTON_MOTION_MASK |
1204 GDK_ENTER_NOTIFY_MASK |
1205 GDK_LEAVE_NOTIFY_MASK |
1206 GDK_SCROLL_MASK |
1207 GDK_KEY_PRESS_MASK);
1208 gtk_widget_set_double_buffered(s->drawing_area, FALSE);
1209 gtk_widget_set_can_focus(s->drawing_area, TRUE);
1210
1211 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
1212 gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
1213
1214 gd_update_caption(s);
1215
1216 gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
1217 gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
1218
1219 gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
1220
1221 gtk_widget_show_all(s->window);
1222
1223 register_displaychangelistener(ds, &s->dcl);
1224
1225 global_state = s;
1226 }