4 * Copyright IBM, Corp. 2012
7 * Anthony Liguori <aliguori@us.ibm.com>
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.
12 * Portions from gtk-vnc:
16 * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
17 * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
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.
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.
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
35 #include <gdk/gdkkeysyms.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
44 #include "qemu-common.h"
45 #include "ui/console.h"
46 #include "sysemu/sysemu.h"
47 #include "qmp-commands.h"
50 #include "char/char.h"
55 #define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
57 #define DPRINTF(fmt, ...) do { } while (0)
62 typedef struct VirtualConsole
66 GtkWidget
*scrolled_window
;
71 typedef struct GtkDisplayState
77 GtkWidget
*file_menu_item
;
81 GtkWidget
*view_menu_item
;
83 GtkWidget
*full_screen_item
;
84 GtkWidget
*zoom_in_item
;
85 GtkWidget
*zoom_out_item
;
86 GtkWidget
*zoom_fixed_item
;
87 GtkWidget
*zoom_fit_item
;
89 GtkWidget
*grab_on_hover_item
;
93 VirtualConsole vc
[MAX_VCS
];
95 GtkWidget
*show_tabs_item
;
99 GtkWidget
*drawing_area
;
100 cairo_surface_t
*surface
;
101 DisplayChangeListener dcl
;
109 gboolean full_screen
;
111 GdkCursor
*null_cursor
;
112 Notifier mouse_mode_notifier
;
116 static GtkDisplayState
*global_state
;
118 /** Utility Functions **/
120 static bool gd_is_grab_active(GtkDisplayState
*s
)
122 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s
->grab_item
));
125 static bool gd_grab_on_hover(GtkDisplayState
*s
)
127 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s
->grab_on_hover_item
));
130 static bool gd_on_vga(GtkDisplayState
*s
)
132 return gtk_notebook_get_current_page(GTK_NOTEBOOK(s
->notebook
)) == 0;
135 static void gd_update_cursor(GtkDisplayState
*s
, gboolean override
)
140 window
= gtk_widget_get_window(GTK_WIDGET(s
->drawing_area
));
142 on_vga
= gd_on_vga(s
);
144 if ((override
|| on_vga
) &&
145 (s
->full_screen
|| kbd_mouse_is_absolute() || gd_is_grab_active(s
))) {
146 gdk_window_set_cursor(window
, s
->null_cursor
);
148 gdk_window_set_cursor(window
, NULL
);
152 static void gd_update_caption(GtkDisplayState
*s
)
154 const char *status
= "";
156 const char *grab
= "";
158 if (gd_is_grab_active(s
)) {
159 grab
= " - Press Ctrl+Alt+G to release grab";
162 if (!runstate_is_running()) {
163 status
= " [Stopped]";
167 title
= g_strdup_printf("QEMU (%s)%s%s", qemu_name
, status
, grab
);
169 title
= g_strdup_printf("QEMU%s%s", status
, grab
);
172 gtk_window_set_title(GTK_WINDOW(s
->window
), title
);
177 /** DisplayState Callbacks **/
179 static void gd_update(DisplayState
*ds
, int x
, int y
, int w
, int h
)
181 GtkDisplayState
*s
= ds
->opaque
;
187 DPRINTF("update(x=%d, y=%d, w=%d, h=%d)\n", x
, y
, w
, h
);
189 x1
= floor(x
* s
->scale_x
);
190 y1
= floor(y
* s
->scale_y
);
192 x2
= ceil(x
* s
->scale_x
+ w
* s
->scale_x
);
193 y2
= ceil(y
* s
->scale_y
+ h
* s
->scale_y
);
195 fbw
= ds_get_width(s
->ds
) * s
->scale_x
;
196 fbh
= ds_get_height(s
->ds
) * s
->scale_y
;
198 gdk_drawable_get_size(gtk_widget_get_window(s
->drawing_area
), &ww
, &wh
);
208 gtk_widget_queue_draw_area(s
->drawing_area
, mx
+ x1
, my
+ y1
, (x2
- x1
), (y2
- y1
));
211 static void gd_refresh(DisplayState
*ds
)
216 static void gd_resize(DisplayState
*ds
)
218 GtkDisplayState
*s
= ds
->opaque
;
222 DPRINTF("resize(width=%d, height=%d)\n",
223 ds_get_width(ds
), ds_get_height(ds
));
226 cairo_surface_destroy(s
->surface
);
229 switch (ds
->surface
->pf
.bits_per_pixel
) {
231 kind
= CAIRO_FORMAT_A8
;
234 kind
= CAIRO_FORMAT_RGB16_565
;
237 kind
= CAIRO_FORMAT_RGB24
;
240 g_assert_not_reached();
244 stride
= cairo_format_stride_for_width(kind
, ds_get_width(ds
));
245 g_assert(ds_get_linesize(ds
) == stride
);
247 s
->surface
= cairo_image_surface_create_for_data(ds_get_data(ds
),
251 ds_get_linesize(ds
));
253 if (!s
->full_screen
) {
268 gtk_widget_set_size_request(s
->drawing_area
,
269 ds_get_width(ds
) * s
->scale_x
,
270 ds_get_height(ds
) * s
->scale_y
);
271 gtk_widget_size_request(s
->vbox
, &req
);
273 gtk_window_resize(GTK_WINDOW(s
->window
),
274 req
.width
* sx
, req
.height
* sy
);
280 static void gd_change_runstate(void *opaque
, int running
, RunState state
)
282 GtkDisplayState
*s
= opaque
;
284 gd_update_caption(s
);
287 static void gd_mouse_mode_change(Notifier
*notify
, void *data
)
289 gd_update_cursor(container_of(notify
, GtkDisplayState
, mouse_mode_notifier
),
295 static gboolean
gd_window_close(GtkWidget
*widget
, GdkEvent
*event
,
298 GtkDisplayState
*s
= opaque
;
301 unregister_displaychangelistener(s
->ds
, &s
->dcl
);
309 static gboolean
gd_draw_event(GtkWidget
*widget
, cairo_t
*cr
, void *opaque
)
311 GtkDisplayState
*s
= opaque
;
316 if (!gtk_widget_get_realized(widget
)) {
320 fbw
= ds_get_width(s
->ds
);
321 fbh
= ds_get_height(s
->ds
);
323 gdk_drawable_get_size(gtk_widget_get_window(widget
), &ww
, &wh
);
325 if (s
->full_screen
) {
326 s
->scale_x
= (double)ww
/ fbw
;
327 s
->scale_y
= (double)wh
/ fbh
;
328 } else if (s
->free_scale
) {
331 sx
= (double)ww
/ fbw
;
332 sy
= (double)wh
/ fbh
;
334 s
->scale_x
= s
->scale_y
= MIN(sx
, sy
);
348 cairo_rectangle(cr
, 0, 0, ww
, wh
);
350 /* Optionally cut out the inner area where the pixmap
351 will be drawn. This avoids 'flashing' since we're
352 not double-buffering. Note we're using the undocumented
353 behaviour of drawing the rectangle from right to left
354 to cut out the whole */
355 cairo_rectangle(cr
, mx
+ fbw
, my
,
359 cairo_scale(cr
, s
->scale_x
, s
->scale_y
);
360 cairo_set_source_surface(cr
, s
->surface
, mx
/ s
->scale_x
, my
/ s
->scale_y
);
366 static gboolean
gd_expose_event(GtkWidget
*widget
, GdkEventExpose
*expose
,
372 cr
= gdk_cairo_create(gtk_widget_get_window(widget
));
377 expose
->area
.height
);
380 ret
= gd_draw_event(widget
, cr
, opaque
);
387 static gboolean
gd_motion_event(GtkWidget
*widget
, GdkEventMotion
*motion
,
390 GtkDisplayState
*s
= opaque
;
397 fbw
= ds_get_width(s
->ds
) * s
->scale_x
;
398 fbh
= ds_get_height(s
->ds
) * s
->scale_y
;
400 gdk_drawable_get_size(gtk_widget_get_window(s
->drawing_area
), &ww
, &wh
);
410 x
= (motion
->x
- mx
) / s
->scale_x
;
411 y
= (motion
->y
- my
) / s
->scale_y
;
413 if (x
< 0 || y
< 0 ||
414 x
>= ds_get_width(s
->ds
) ||
415 y
>= ds_get_height(s
->ds
)) {
419 if (kbd_mouse_is_absolute()) {
420 dx
= x
* 0x7FFF / (ds_get_width(s
->ds
) - 1);
421 dy
= y
* 0x7FFF / (ds_get_height(s
->ds
) - 1);
422 } else if (s
->last_x
== -1 || s
->last_y
== -1) {
433 if (kbd_mouse_is_absolute() || gd_is_grab_active(s
)) {
434 kbd_mouse_event(dx
, dy
, 0, s
->button_mask
);
437 if (!kbd_mouse_is_absolute() && gd_is_grab_active(s
)) {
438 GdkDrawable
*drawable
= GDK_DRAWABLE(gtk_widget_get_window(s
->drawing_area
));
439 GdkDisplay
*display
= gdk_drawable_get_display(drawable
);
440 GdkScreen
*screen
= gdk_drawable_get_screen(drawable
);
441 int x
= (int)motion
->x_root
;
442 int y
= (int)motion
->y_root
;
444 /* In relative mode check to see if client pointer hit
445 * one of the screen edges, and if so move it back by
446 * 200 pixels. This is important because the pointer
447 * in the server doesn't correspond 1-for-1, and so
448 * may still be only half way across the screen. Without
449 * this warp, the server pointer would thus appear to hit
450 * an invisible wall */
457 if (x
== (gdk_screen_get_width(screen
) - 1)) {
460 if (y
== (gdk_screen_get_height(screen
) - 1)) {
464 if (x
!= (int)motion
->x_root
|| y
!= (int)motion
->y_root
) {
465 gdk_display_warp_pointer(display
, screen
, x
, y
);
474 static gboolean
gd_button_event(GtkWidget
*widget
, GdkEventButton
*button
,
477 GtkDisplayState
*s
= opaque
;
481 if (button
->button
== 1) {
483 } else if (button
->button
== 2) {
485 } else if (button
->button
== 3) {
491 if (button
->type
== GDK_BUTTON_PRESS
) {
493 } else if (button
->type
== GDK_BUTTON_RELEASE
) {
494 s
->button_mask
&= ~n
;
497 if (kbd_mouse_is_absolute()) {
498 dx
= s
->last_x
* 0x7FFF / (ds_get_width(s
->ds
) - 1);
499 dy
= s
->last_y
* 0x7FFF / (ds_get_height(s
->ds
) - 1);
505 kbd_mouse_event(dx
, dy
, 0, s
->button_mask
);
510 static gboolean
gd_key_event(GtkWidget
*widget
, GdkEventKey
*key
, void *opaque
)
515 gdk_keycode
= key
->hardware_keycode
;
517 if (gdk_keycode
< 9) {
519 } else if (gdk_keycode
< 97) {
520 qemu_keycode
= gdk_keycode
- 8;
521 } else if (gdk_keycode
< 158) {
522 qemu_keycode
= translate_evdev_keycode(gdk_keycode
- 97);
523 } else if (gdk_keycode
== 208) { /* Hiragana_Katakana */
525 } else if (gdk_keycode
== 211) { /* backslash */
531 DPRINTF("translated GDK keycode %d to QEMU keycode %d (%s)\n",
532 gdk_keycode
, qemu_keycode
,
533 (key
->type
== GDK_KEY_PRESS
) ? "down" : "up");
535 if (qemu_keycode
& SCANCODE_GREY
) {
536 kbd_put_keycode(SCANCODE_EMUL0
);
539 if (key
->type
== GDK_KEY_PRESS
) {
540 kbd_put_keycode(qemu_keycode
& SCANCODE_KEYCODEMASK
);
541 } else if (key
->type
== GDK_KEY_RELEASE
) {
542 kbd_put_keycode(qemu_keycode
| SCANCODE_UP
);
544 g_assert_not_reached();
550 /** Window Menu Actions **/
552 static void gd_menu_quit(GtkMenuItem
*item
, void *opaque
)
557 static void gd_menu_switch_vc(GtkMenuItem
*item
, void *opaque
)
559 GtkDisplayState
*s
= opaque
;
561 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s
->vga_item
))) {
562 gtk_notebook_set_current_page(GTK_NOTEBOOK(s
->notebook
), 0);
566 for (i
= 0; i
< s
->nb_vcs
; i
++) {
567 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s
->vc
[i
].menu_item
))) {
568 gtk_notebook_set_current_page(GTK_NOTEBOOK(s
->notebook
), i
+ 1);
575 static void gd_menu_show_tabs(GtkMenuItem
*item
, void *opaque
)
577 GtkDisplayState
*s
= opaque
;
579 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s
->show_tabs_item
))) {
580 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s
->notebook
), TRUE
);
582 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s
->notebook
), FALSE
);
586 static void gd_menu_full_screen(GtkMenuItem
*item
, void *opaque
)
588 GtkDisplayState
*s
= opaque
;
590 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s
->full_screen_item
))) {
591 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s
->notebook
), FALSE
);
592 gtk_widget_set_size_request(s
->menu_bar
, 0, 0);
593 gtk_widget_set_size_request(s
->drawing_area
, -1, -1);
594 gtk_window_fullscreen(GTK_WINDOW(s
->window
));
596 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s
->grab_item
), TRUE
);
598 s
->full_screen
= TRUE
;
600 gtk_window_unfullscreen(GTK_WINDOW(s
->window
));
601 gd_menu_show_tabs(GTK_MENU_ITEM(s
->show_tabs_item
), s
);
602 gtk_widget_set_size_request(s
->menu_bar
, -1, -1);
603 gtk_widget_set_size_request(s
->drawing_area
,
604 ds_get_width(s
->ds
), ds_get_height(s
->ds
));
605 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s
->grab_item
), FALSE
);
606 s
->full_screen
= FALSE
;
611 gd_update_cursor(s
, FALSE
);
614 static void gd_menu_zoom_in(GtkMenuItem
*item
, void *opaque
)
616 GtkDisplayState
*s
= opaque
;
618 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s
->zoom_fit_item
),
627 static void gd_menu_zoom_out(GtkMenuItem
*item
, void *opaque
)
629 GtkDisplayState
*s
= opaque
;
631 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s
->zoom_fit_item
),
637 s
->scale_x
= MAX(s
->scale_x
, .25);
638 s
->scale_y
= MAX(s
->scale_y
, .25);
643 static void gd_menu_zoom_fixed(GtkMenuItem
*item
, void *opaque
)
645 GtkDisplayState
*s
= opaque
;
653 static void gd_menu_zoom_fit(GtkMenuItem
*item
, void *opaque
)
655 GtkDisplayState
*s
= opaque
;
658 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s
->zoom_fit_item
))) {
659 s
->free_scale
= TRUE
;
661 s
->free_scale
= FALSE
;
666 gdk_drawable_get_size(gtk_widget_get_window(s
->drawing_area
), &ww
, &wh
);
667 gtk_widget_queue_draw_area(s
->drawing_area
, 0, 0, ww
, wh
);
670 static void gd_grab_keyboard(GtkDisplayState
*s
)
672 gdk_keyboard_grab(gtk_widget_get_window(GTK_WIDGET(s
->drawing_area
)),
677 static void gd_ungrab_keyboard(GtkDisplayState
*s
)
679 gdk_keyboard_ungrab(GDK_CURRENT_TIME
);
682 static void gd_menu_grab_input(GtkMenuItem
*item
, void *opaque
)
684 GtkDisplayState
*s
= opaque
;
686 if (gd_is_grab_active(s
)) {
688 gdk_pointer_grab(gtk_widget_get_window(GTK_WIDGET(s
->drawing_area
)),
689 FALSE
, /* All events to come to our window directly */
690 GDK_POINTER_MOTION_MASK
|
691 GDK_BUTTON_PRESS_MASK
|
692 GDK_BUTTON_RELEASE_MASK
|
693 GDK_BUTTON_MOTION_MASK
|
695 NULL
, /* Allow cursor to move over entire desktop */
699 gd_ungrab_keyboard(s
);
700 gdk_pointer_ungrab(GDK_CURRENT_TIME
);
703 gd_update_caption(s
);
704 gd_update_cursor(s
, FALSE
);
707 static void gd_change_page(GtkNotebook
*nb
, gpointer arg1
, guint arg2
,
710 GtkDisplayState
*s
= data
;
714 if (!gtk_widget_get_realized(s
->notebook
)) {
718 last_page
= gtk_notebook_get_current_page(nb
);
721 gtk_widget_set_size_request(s
->vc
[last_page
- 1].terminal
, -1, -1);
727 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s
->grab_item
),
729 } else if (s
->full_screen
) {
730 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s
->grab_item
),
735 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s
->vga_item
), TRUE
);
737 VirtualConsole
*vc
= &s
->vc
[arg2
- 1];
738 VteTerminal
*term
= VTE_TERMINAL(vc
->terminal
);
741 width
= 80 * vte_terminal_get_char_width(term
);
742 height
= 25 * vte_terminal_get_char_height(term
);
744 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc
->menu_item
), TRUE
);
745 gtk_widget_set_size_request(vc
->terminal
, width
, height
);
748 gtk_widget_set_sensitive(s
->grab_item
, on_vga
);
750 gd_update_cursor(s
, TRUE
);
753 static gboolean
gd_enter_event(GtkWidget
*widget
, GdkEventCrossing
*crossing
, gpointer data
)
755 GtkDisplayState
*s
= data
;
757 if (!gd_is_grab_active(s
) && gd_grab_on_hover(s
)) {
764 static gboolean
gd_leave_event(GtkWidget
*widget
, GdkEventCrossing
*crossing
, gpointer data
)
766 GtkDisplayState
*s
= data
;
768 if (!gd_is_grab_active(s
) && gd_grab_on_hover(s
)) {
769 gd_ungrab_keyboard(s
);
775 /** Virtual Console Callbacks **/
777 static int gd_vc_chr_write(CharDriverState
*chr
, const uint8_t *buf
, int len
)
779 VirtualConsole
*vc
= chr
->opaque
;
781 return write(vc
->fd
, buf
, len
);
785 static CharDriverState
*vcs
[MAX_VCS
];
787 static CharDriverState
*gd_vc_handler(QemuOpts
*opts
)
789 CharDriverState
*chr
;
791 chr
= g_malloc0(sizeof(*chr
));
792 chr
->chr_write
= gd_vc_chr_write
;
799 void early_gtk_display_init(void)
801 register_vc_handler(gd_vc_handler
);
804 static gboolean
gd_vc_in(GIOChannel
*chan
, GIOCondition cond
, void *opaque
)
806 VirtualConsole
*vc
= opaque
;
807 uint8_t buffer
[1024];
810 len
= read(vc
->fd
, buffer
, sizeof(buffer
));
815 qemu_chr_be_write(vc
->chr
, buffer
, len
);
820 static GSList
*gd_vc_init(GtkDisplayState
*s
, VirtualConsole
*vc
, int index
, GSList
*group
)
827 GtkWidget
*scrolled_window
;
828 GtkAdjustment
*vadjustment
;
829 int master_fd
, slave_fd
, ret
;
832 snprintf(buffer
, sizeof(buffer
), "vc%d", index
);
833 snprintf(path
, sizeof(path
), "<QEMU>/View/VC%d", index
);
835 vc
->chr
= vcs
[index
];
837 if (vc
->chr
->label
) {
838 label
= vc
->chr
->label
;
843 vc
->menu_item
= gtk_radio_menu_item_new_with_mnemonic(group
, label
);
844 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc
->menu_item
));
845 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc
->menu_item
), path
);
846 gtk_accel_map_add_entry(path
, GDK_KEY_2
+ index
, GDK_CONTROL_MASK
| GDK_MOD1_MASK
);
848 vc
->terminal
= vte_terminal_new();
850 ret
= openpty(&master_fd
, &slave_fd
, NULL
, NULL
, NULL
);
853 /* Set raw attributes on the pty. */
854 tcgetattr(slave_fd
, &tty
);
856 tcsetattr(slave_fd
, TCSAFLUSH
, &tty
);
858 pty
= vte_pty_new_foreign(master_fd
, NULL
);
860 vte_terminal_set_pty_object(VTE_TERMINAL(vc
->terminal
), pty
);
862 vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc
->terminal
), -1);
864 vadjustment
= vte_terminal_get_adjustment(VTE_TERMINAL(vc
->terminal
));
866 scrolled_window
= gtk_scrolled_window_new(NULL
, vadjustment
);
867 gtk_container_add(GTK_CONTAINER(scrolled_window
), vc
->terminal
);
869 vte_terminal_set_size(VTE_TERMINAL(vc
->terminal
), 80, 25);
872 vc
->chr
->opaque
= vc
;
873 vc
->scrolled_window
= scrolled_window
;
875 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vc
->scrolled_window
),
876 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
878 gtk_notebook_append_page(GTK_NOTEBOOK(s
->notebook
), scrolled_window
, gtk_label_new(label
));
879 g_signal_connect(vc
->menu_item
, "activate",
880 G_CALLBACK(gd_menu_switch_vc
), s
);
882 gtk_menu_append(GTK_MENU(s
->view_menu
), vc
->menu_item
);
884 qemu_chr_generic_open(vc
->chr
);
886 vc
->chr
->init(vc
->chr
);
889 chan
= g_io_channel_unix_new(vc
->fd
);
890 g_io_add_watch(chan
, G_IO_IN
, gd_vc_in
, vc
);
895 /** Window Creation **/
897 static void gd_connect_signals(GtkDisplayState
*s
)
899 g_signal_connect(s
->show_tabs_item
, "activate",
900 G_CALLBACK(gd_menu_show_tabs
), s
);
902 g_signal_connect(s
->window
, "delete-event",
903 G_CALLBACK(gd_window_close
), s
);
905 g_signal_connect(s
->drawing_area
, "expose-event",
906 G_CALLBACK(gd_expose_event
), s
);
907 g_signal_connect(s
->drawing_area
, "motion-notify-event",
908 G_CALLBACK(gd_motion_event
), s
);
909 g_signal_connect(s
->drawing_area
, "button-press-event",
910 G_CALLBACK(gd_button_event
), s
);
911 g_signal_connect(s
->drawing_area
, "button-release-event",
912 G_CALLBACK(gd_button_event
), s
);
913 g_signal_connect(s
->drawing_area
, "key-press-event",
914 G_CALLBACK(gd_key_event
), s
);
915 g_signal_connect(s
->drawing_area
, "key-release-event",
916 G_CALLBACK(gd_key_event
), s
);
918 g_signal_connect(s
->quit_item
, "activate",
919 G_CALLBACK(gd_menu_quit
), s
);
920 g_signal_connect(s
->full_screen_item
, "activate",
921 G_CALLBACK(gd_menu_full_screen
), s
);
922 g_signal_connect(s
->zoom_in_item
, "activate",
923 G_CALLBACK(gd_menu_zoom_in
), s
);
924 g_signal_connect(s
->zoom_out_item
, "activate",
925 G_CALLBACK(gd_menu_zoom_out
), s
);
926 g_signal_connect(s
->zoom_fixed_item
, "activate",
927 G_CALLBACK(gd_menu_zoom_fixed
), s
);
928 g_signal_connect(s
->zoom_fit_item
, "activate",
929 G_CALLBACK(gd_menu_zoom_fit
), s
);
930 g_signal_connect(s
->vga_item
, "activate",
931 G_CALLBACK(gd_menu_switch_vc
), s
);
932 g_signal_connect(s
->grab_item
, "activate",
933 G_CALLBACK(gd_menu_grab_input
), s
);
934 g_signal_connect(s
->notebook
, "switch-page",
935 G_CALLBACK(gd_change_page
), s
);
936 g_signal_connect(s
->drawing_area
, "enter-notify-event",
937 G_CALLBACK(gd_enter_event
), s
);
938 g_signal_connect(s
->drawing_area
, "leave-notify-event",
939 G_CALLBACK(gd_leave_event
), s
);
942 static void gd_create_menus(GtkDisplayState
*s
)
945 GtkAccelGroup
*accel_group
;
946 GSList
*group
= NULL
;
947 GtkWidget
*separator
;
950 accel_group
= gtk_accel_group_new();
951 s
->file_menu
= gtk_menu_new();
952 gtk_menu_set_accel_group(GTK_MENU(s
->file_menu
), accel_group
);
953 s
->file_menu_item
= gtk_menu_item_new_with_mnemonic("_File");
955 s
->quit_item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT
, NULL
);
956 gtk_stock_lookup(GTK_STOCK_QUIT
, &item
);
957 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s
->quit_item
),
959 gtk_accel_map_add_entry("<QEMU>/File/Quit", item
.keyval
, item
.modifier
);
961 s
->view_menu
= gtk_menu_new();
962 gtk_menu_set_accel_group(GTK_MENU(s
->view_menu
), accel_group
);
963 s
->view_menu_item
= gtk_menu_item_new_with_mnemonic("_View");
965 s
->full_screen_item
= gtk_check_menu_item_new_with_mnemonic("_Full Screen");
966 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s
->full_screen_item
),
967 "<QEMU>/View/Full Screen");
968 gtk_accel_map_add_entry("<QEMU>/View/Full Screen", GDK_KEY_f
, GDK_CONTROL_MASK
| GDK_MOD1_MASK
);
969 gtk_menu_append(GTK_MENU(s
->view_menu
), s
->full_screen_item
);
971 separator
= gtk_separator_menu_item_new();
972 gtk_menu_append(GTK_MENU(s
->view_menu
), separator
);
974 s
->zoom_in_item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_IN
, NULL
);
975 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s
->zoom_in_item
),
976 "<QEMU>/View/Zoom In");
977 gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus
, GDK_CONTROL_MASK
| GDK_MOD1_MASK
);
978 gtk_menu_append(GTK_MENU(s
->view_menu
), s
->zoom_in_item
);
980 s
->zoom_out_item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_OUT
, NULL
);
981 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s
->zoom_out_item
),
982 "<QEMU>/View/Zoom Out");
983 gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus
, GDK_CONTROL_MASK
| GDK_MOD1_MASK
);
984 gtk_menu_append(GTK_MENU(s
->view_menu
), s
->zoom_out_item
);
986 s
->zoom_fixed_item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_100
, NULL
);
987 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s
->zoom_fixed_item
),
988 "<QEMU>/View/Zoom Fixed");
989 gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0
, GDK_CONTROL_MASK
| GDK_MOD1_MASK
);
990 gtk_menu_append(GTK_MENU(s
->view_menu
), s
->zoom_fixed_item
);
992 s
->zoom_fit_item
= gtk_check_menu_item_new_with_mnemonic("Zoom To _Fit");
993 gtk_menu_append(GTK_MENU(s
->view_menu
), s
->zoom_fit_item
);
995 separator
= gtk_separator_menu_item_new();
996 gtk_menu_append(GTK_MENU(s
->view_menu
), separator
);
998 s
->grab_on_hover_item
= gtk_check_menu_item_new_with_mnemonic("Grab On _Hover");
999 gtk_menu_append(GTK_MENU(s
->view_menu
), s
->grab_on_hover_item
);
1001 s
->grab_item
= gtk_check_menu_item_new_with_mnemonic("_Grab Input");
1002 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s
->grab_item
),
1003 "<QEMU>/View/Grab Input");
1004 gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g
, GDK_CONTROL_MASK
| GDK_MOD1_MASK
);
1005 gtk_menu_append(GTK_MENU(s
->view_menu
), s
->grab_item
);
1007 separator
= gtk_separator_menu_item_new();
1008 gtk_menu_append(GTK_MENU(s
->view_menu
), separator
);
1010 s
->vga_item
= gtk_radio_menu_item_new_with_mnemonic(group
, "_VGA");
1011 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s
->vga_item
));
1012 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s
->vga_item
),
1014 gtk_accel_map_add_entry("<QEMU>/View/VGA", GDK_KEY_1
, GDK_CONTROL_MASK
| GDK_MOD1_MASK
);
1015 gtk_menu_append(GTK_MENU(s
->view_menu
), s
->vga_item
);
1017 for (i
= 0; i
< nb_vcs
; i
++) {
1018 VirtualConsole
*vc
= &s
->vc
[i
];
1020 group
= gd_vc_init(s
, vc
, i
, group
);
1024 separator
= gtk_separator_menu_item_new();
1025 gtk_menu_append(GTK_MENU(s
->view_menu
), separator
);
1027 s
->show_tabs_item
= gtk_check_menu_item_new_with_mnemonic("Show _Tabs");
1028 gtk_menu_append(GTK_MENU(s
->view_menu
), s
->show_tabs_item
);
1030 g_object_set_data(G_OBJECT(s
->window
), "accel_group", accel_group
);
1031 gtk_window_add_accel_group(GTK_WINDOW(s
->window
), accel_group
);
1033 gtk_menu_append(GTK_MENU(s
->file_menu
), s
->quit_item
);
1034 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s
->file_menu_item
), s
->file_menu
);
1035 gtk_menu_shell_append(GTK_MENU_SHELL(s
->menu_bar
), s
->file_menu_item
);
1037 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s
->view_menu_item
), s
->view_menu
);
1038 gtk_menu_shell_append(GTK_MENU_SHELL(s
->menu_bar
), s
->view_menu_item
);
1041 void gtk_display_init(DisplayState
*ds
)
1043 GtkDisplayState
*s
= g_malloc0(sizeof(*s
));
1045 gtk_init(NULL
, NULL
);
1049 s
->dcl
.dpy_gfx_update
= gd_update
;
1050 s
->dcl
.dpy_gfx_resize
= gd_resize
;
1051 s
->dcl
.dpy_refresh
= gd_refresh
;
1053 s
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1054 s
->vbox
= gtk_vbox_new(FALSE
, 0);
1055 s
->notebook
= gtk_notebook_new();
1056 s
->drawing_area
= gtk_drawing_area_new();
1057 s
->menu_bar
= gtk_menu_bar_new();
1061 s
->free_scale
= FALSE
;
1063 s
->null_cursor
= gdk_cursor_new(GDK_BLANK_CURSOR
);
1065 s
->mouse_mode_notifier
.notify
= gd_mouse_mode_change
;
1066 qemu_add_mouse_mode_change_notifier(&s
->mouse_mode_notifier
);
1067 qemu_add_vm_change_state_handler(gd_change_runstate
, s
);
1069 gtk_notebook_append_page(GTK_NOTEBOOK(s
->notebook
), s
->drawing_area
, gtk_label_new("VGA"));
1073 gd_connect_signals(s
);
1075 gtk_widget_add_events(s
->drawing_area
,
1076 GDK_POINTER_MOTION_MASK
|
1077 GDK_BUTTON_PRESS_MASK
|
1078 GDK_BUTTON_RELEASE_MASK
|
1079 GDK_BUTTON_MOTION_MASK
|
1080 GDK_ENTER_NOTIFY_MASK
|
1081 GDK_LEAVE_NOTIFY_MASK
|
1083 GDK_KEY_PRESS_MASK
);
1084 gtk_widget_set_double_buffered(s
->drawing_area
, FALSE
);
1085 gtk_widget_set_can_focus(s
->drawing_area
, TRUE
);
1087 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s
->notebook
), FALSE
);
1088 gtk_notebook_set_show_border(GTK_NOTEBOOK(s
->notebook
), FALSE
);
1090 gd_update_caption(s
);
1092 gtk_box_pack_start(GTK_BOX(s
->vbox
), s
->menu_bar
, FALSE
, TRUE
, 0);
1093 gtk_box_pack_start(GTK_BOX(s
->vbox
), s
->notebook
, TRUE
, TRUE
, 0);
1095 gtk_container_add(GTK_CONTAINER(s
->window
), s
->vbox
);
1097 gtk_widget_show_all(s
->window
);
1099 register_displaychangelistener(ds
, &s
->dcl
);