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"
54 #define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
56 #define DPRINTF(fmt, ...) do { } while (0)
59 typedef struct VirtualConsole
63 GtkWidget
*scrolled_window
;
68 typedef struct GtkDisplayState
74 GtkWidget
*file_menu_item
;
78 GtkWidget
*view_menu_item
;
82 GtkWidget
*show_tabs_item
;
86 GtkWidget
*drawing_area
;
87 cairo_surface_t
*surface
;
88 DisplayChangeListener dcl
;
97 GdkCursor
*null_cursor
;
98 Notifier mouse_mode_notifier
;
101 static GtkDisplayState
*global_state
;
103 /** Utility Functions **/
105 static void gd_update_cursor(GtkDisplayState
*s
, gboolean override
)
110 window
= gtk_widget_get_window(GTK_WIDGET(s
->drawing_area
));
112 on_vga
= (gtk_notebook_get_current_page(GTK_NOTEBOOK(s
->notebook
)) == 0);
114 if ((override
|| on_vga
) && kbd_mouse_is_absolute()) {
115 gdk_window_set_cursor(window
, s
->null_cursor
);
117 gdk_window_set_cursor(window
, NULL
);
121 static void gd_update_caption(GtkDisplayState
*s
)
123 const char *status
= "";
126 if (!runstate_is_running()) {
127 status
= " [Stopped]";
131 title
= g_strdup_printf("QEMU (%s)%s", qemu_name
, status
);
133 title
= g_strdup_printf("QEMU%s", status
);
136 gtk_window_set_title(GTK_WINDOW(s
->window
), title
);
141 /** DisplayState Callbacks **/
143 static void gd_update(DisplayState
*ds
, int x
, int y
, int w
, int h
)
145 GtkDisplayState
*s
= ds
->opaque
;
148 DPRINTF("update(x=%d, y=%d, w=%d, h=%d)\n", x
, y
, w
, h
);
150 x1
= floor(x
* s
->scale_x
);
151 y1
= floor(y
* s
->scale_y
);
153 x2
= ceil(x
* s
->scale_x
+ w
* s
->scale_x
);
154 y2
= ceil(y
* s
->scale_y
+ h
* s
->scale_y
);
156 gtk_widget_queue_draw_area(s
->drawing_area
, x1
, y1
, (x2
- x1
), (y2
- y1
));
159 static void gd_refresh(DisplayState
*ds
)
164 static void gd_resize(DisplayState
*ds
)
166 GtkDisplayState
*s
= ds
->opaque
;
170 DPRINTF("resize(width=%d, height=%d)\n",
171 ds_get_width(ds
), ds_get_height(ds
));
174 cairo_surface_destroy(s
->surface
);
177 switch (ds
->surface
->pf
.bits_per_pixel
) {
179 kind
= CAIRO_FORMAT_A8
;
182 kind
= CAIRO_FORMAT_RGB16_565
;
185 kind
= CAIRO_FORMAT_RGB24
;
188 g_assert_not_reached();
192 stride
= cairo_format_stride_for_width(kind
, ds_get_width(ds
));
193 g_assert(ds_get_linesize(ds
) == stride
);
195 s
->surface
= cairo_image_surface_create_for_data(ds_get_data(ds
),
199 ds_get_linesize(ds
));
201 gtk_widget_set_size_request(s
->drawing_area
,
202 ds_get_width(ds
) * s
->scale_x
,
203 ds_get_height(ds
) * s
->scale_y
);
208 static void gd_change_runstate(void *opaque
, int running
, RunState state
)
210 GtkDisplayState
*s
= opaque
;
212 gd_update_caption(s
);
215 static void gd_mouse_mode_change(Notifier
*notify
, void *data
)
217 gd_update_cursor(container_of(notify
, GtkDisplayState
, mouse_mode_notifier
),
223 static gboolean
gd_window_close(GtkWidget
*widget
, GdkEvent
*event
,
226 GtkDisplayState
*s
= opaque
;
229 unregister_displaychangelistener(s
->ds
, &s
->dcl
);
237 static gboolean
gd_draw_event(GtkWidget
*widget
, cairo_t
*cr
, void *opaque
)
239 GtkDisplayState
*s
= opaque
;
243 fbw
= ds_get_width(s
->ds
);
244 fbh
= ds_get_height(s
->ds
);
246 gdk_drawable_get_size(gtk_widget_get_window(widget
), &ww
, &wh
);
248 cairo_rectangle(cr
, 0, 0, ww
, wh
);
250 if (ww
!= fbw
|| wh
!= fbh
) {
251 s
->scale_x
= (double)ww
/ fbw
;
252 s
->scale_y
= (double)wh
/ fbh
;
253 cairo_scale(cr
, s
->scale_x
, s
->scale_y
);
259 cairo_set_source_surface(cr
, s
->surface
, 0, 0);
265 static gboolean
gd_expose_event(GtkWidget
*widget
, GdkEventExpose
*expose
,
271 cr
= gdk_cairo_create(gtk_widget_get_window(widget
));
276 expose
->area
.height
);
279 ret
= gd_draw_event(widget
, cr
, opaque
);
286 static gboolean
gd_motion_event(GtkWidget
*widget
, GdkEventMotion
*motion
,
289 GtkDisplayState
*s
= opaque
;
293 x
= motion
->x
/ s
->scale_x
;
294 y
= motion
->y
/ s
->scale_y
;
296 if (kbd_mouse_is_absolute()) {
297 dx
= x
* 0x7FFF / (ds_get_width(s
->ds
) - 1);
298 dy
= y
* 0x7FFF / (ds_get_height(s
->ds
) - 1);
299 } else if (s
->last_x
== -1 || s
->last_y
== -1) {
310 if (kbd_mouse_is_absolute()) {
311 kbd_mouse_event(dx
, dy
, 0, s
->button_mask
);
317 static gboolean
gd_button_event(GtkWidget
*widget
, GdkEventButton
*button
,
320 GtkDisplayState
*s
= opaque
;
324 if (button
->button
== 1) {
326 } else if (button
->button
== 2) {
328 } else if (button
->button
== 3) {
334 if (button
->type
== GDK_BUTTON_PRESS
) {
336 } else if (button
->type
== GDK_BUTTON_RELEASE
) {
337 s
->button_mask
&= ~n
;
340 if (kbd_mouse_is_absolute()) {
341 dx
= s
->last_x
* 0x7FFF / (ds_get_width(s
->ds
) - 1);
342 dy
= s
->last_y
* 0x7FFF / (ds_get_height(s
->ds
) - 1);
348 kbd_mouse_event(dx
, dy
, 0, s
->button_mask
);
353 static gboolean
gd_key_event(GtkWidget
*widget
, GdkEventKey
*key
, void *opaque
)
358 gdk_keycode
= key
->hardware_keycode
;
360 if (gdk_keycode
< 9) {
362 } else if (gdk_keycode
< 97) {
363 qemu_keycode
= gdk_keycode
- 8;
364 } else if (gdk_keycode
< 158) {
365 qemu_keycode
= translate_evdev_keycode(gdk_keycode
- 97);
366 } else if (gdk_keycode
== 208) { /* Hiragana_Katakana */
368 } else if (gdk_keycode
== 211) { /* backslash */
374 DPRINTF("translated GDK keycode %d to QEMU keycode %d (%s)\n",
375 gdk_keycode
, qemu_keycode
,
376 (key
->type
== GDK_KEY_PRESS
) ? "down" : "up");
378 if (qemu_keycode
& SCANCODE_GREY
) {
379 kbd_put_keycode(SCANCODE_EMUL0
);
382 if (key
->type
== GDK_KEY_PRESS
) {
383 kbd_put_keycode(qemu_keycode
& SCANCODE_KEYCODEMASK
);
384 } else if (key
->type
== GDK_KEY_RELEASE
) {
385 kbd_put_keycode(qemu_keycode
| SCANCODE_UP
);
387 g_assert_not_reached();
393 /** Window Menu Actions **/
395 static void gd_menu_quit(GtkMenuItem
*item
, void *opaque
)
400 static void gd_menu_switch_vc(GtkMenuItem
*item
, void *opaque
)
402 GtkDisplayState
*s
= opaque
;
404 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s
->vga_item
))) {
405 gtk_notebook_set_current_page(GTK_NOTEBOOK(s
->notebook
), 0);
409 static void gd_menu_show_tabs(GtkMenuItem
*item
, void *opaque
)
411 GtkDisplayState
*s
= opaque
;
413 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s
->show_tabs_item
))) {
414 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s
->notebook
), TRUE
);
416 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s
->notebook
), FALSE
);
420 static void gd_change_page(GtkNotebook
*nb
, gpointer arg1
, guint arg2
,
423 GtkDisplayState
*s
= data
;
425 if (!gtk_widget_get_realized(s
->notebook
)) {
429 gd_update_cursor(s
, TRUE
);
432 void early_gtk_display_init(void)
436 /** Window Creation **/
438 static void gd_connect_signals(GtkDisplayState
*s
)
440 g_signal_connect(s
->show_tabs_item
, "activate",
441 G_CALLBACK(gd_menu_show_tabs
), s
);
443 g_signal_connect(s
->window
, "delete-event",
444 G_CALLBACK(gd_window_close
), s
);
446 g_signal_connect(s
->drawing_area
, "expose-event",
447 G_CALLBACK(gd_expose_event
), s
);
448 g_signal_connect(s
->drawing_area
, "motion-notify-event",
449 G_CALLBACK(gd_motion_event
), s
);
450 g_signal_connect(s
->drawing_area
, "button-press-event",
451 G_CALLBACK(gd_button_event
), s
);
452 g_signal_connect(s
->drawing_area
, "button-release-event",
453 G_CALLBACK(gd_button_event
), s
);
454 g_signal_connect(s
->drawing_area
, "key-press-event",
455 G_CALLBACK(gd_key_event
), s
);
456 g_signal_connect(s
->drawing_area
, "key-release-event",
457 G_CALLBACK(gd_key_event
), s
);
459 g_signal_connect(s
->quit_item
, "activate",
460 G_CALLBACK(gd_menu_quit
), s
);
461 g_signal_connect(s
->vga_item
, "activate",
462 G_CALLBACK(gd_menu_switch_vc
), s
);
463 g_signal_connect(s
->notebook
, "switch-page",
464 G_CALLBACK(gd_change_page
), s
);
467 static void gd_create_menus(GtkDisplayState
*s
)
470 GtkAccelGroup
*accel_group
;
471 GSList
*group
= NULL
;
472 GtkWidget
*separator
;
474 accel_group
= gtk_accel_group_new();
475 s
->file_menu
= gtk_menu_new();
476 gtk_menu_set_accel_group(GTK_MENU(s
->file_menu
), accel_group
);
477 s
->file_menu_item
= gtk_menu_item_new_with_mnemonic("_File");
479 s
->quit_item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT
, NULL
);
480 gtk_stock_lookup(GTK_STOCK_QUIT
, &item
);
481 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s
->quit_item
),
483 gtk_accel_map_add_entry("<QEMU>/File/Quit", item
.keyval
, item
.modifier
);
485 s
->view_menu
= gtk_menu_new();
486 gtk_menu_set_accel_group(GTK_MENU(s
->view_menu
), accel_group
);
487 s
->view_menu_item
= gtk_menu_item_new_with_mnemonic("_View");
489 separator
= gtk_separator_menu_item_new();
490 gtk_menu_append(GTK_MENU(s
->view_menu
), separator
);
492 s
->vga_item
= gtk_radio_menu_item_new_with_mnemonic(group
, "_VGA");
493 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s
->vga_item
));
494 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s
->vga_item
),
496 gtk_accel_map_add_entry("<QEMU>/View/VGA", GDK_KEY_1
, GDK_CONTROL_MASK
| GDK_MOD1_MASK
);
497 gtk_menu_append(GTK_MENU(s
->view_menu
), s
->vga_item
);
499 separator
= gtk_separator_menu_item_new();
500 gtk_menu_append(GTK_MENU(s
->view_menu
), separator
);
502 s
->show_tabs_item
= gtk_check_menu_item_new_with_mnemonic("Show _Tabs");
503 gtk_menu_append(GTK_MENU(s
->view_menu
), s
->show_tabs_item
);
505 g_object_set_data(G_OBJECT(s
->window
), "accel_group", accel_group
);
506 gtk_window_add_accel_group(GTK_WINDOW(s
->window
), accel_group
);
508 gtk_menu_append(GTK_MENU(s
->file_menu
), s
->quit_item
);
509 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s
->file_menu_item
), s
->file_menu
);
510 gtk_menu_shell_append(GTK_MENU_SHELL(s
->menu_bar
), s
->file_menu_item
);
512 gtk_menu_item_set_submenu(GTK_MENU_ITEM(s
->view_menu_item
), s
->view_menu
);
513 gtk_menu_shell_append(GTK_MENU_SHELL(s
->menu_bar
), s
->view_menu_item
);
516 void gtk_display_init(DisplayState
*ds
)
518 GtkDisplayState
*s
= g_malloc0(sizeof(*s
));
520 gtk_init(NULL
, NULL
);
524 s
->dcl
.dpy_gfx_update
= gd_update
;
525 s
->dcl
.dpy_gfx_resize
= gd_resize
;
526 s
->dcl
.dpy_refresh
= gd_refresh
;
528 s
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
529 s
->vbox
= gtk_vbox_new(FALSE
, 0);
530 s
->notebook
= gtk_notebook_new();
531 s
->drawing_area
= gtk_drawing_area_new();
532 s
->menu_bar
= gtk_menu_bar_new();
537 s
->null_cursor
= gdk_cursor_new(GDK_BLANK_CURSOR
);
539 s
->mouse_mode_notifier
.notify
= gd_mouse_mode_change
;
540 qemu_add_mouse_mode_change_notifier(&s
->mouse_mode_notifier
);
541 qemu_add_vm_change_state_handler(gd_change_runstate
, s
);
543 gtk_notebook_append_page(GTK_NOTEBOOK(s
->notebook
), s
->drawing_area
, gtk_label_new("VGA"));
547 gd_connect_signals(s
);
549 gtk_widget_add_events(s
->drawing_area
,
550 GDK_POINTER_MOTION_MASK
|
551 GDK_BUTTON_PRESS_MASK
|
552 GDK_BUTTON_RELEASE_MASK
|
553 GDK_BUTTON_MOTION_MASK
|
556 gtk_widget_set_double_buffered(s
->drawing_area
, FALSE
);
557 gtk_widget_set_can_focus(s
->drawing_area
, TRUE
);
559 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s
->notebook
), FALSE
);
560 gtk_notebook_set_show_border(GTK_NOTEBOOK(s
->notebook
), FALSE
);
562 gtk_window_set_resizable(GTK_WINDOW(s
->window
), FALSE
);
564 gd_update_caption(s
);
566 gtk_box_pack_start(GTK_BOX(s
->vbox
), s
->menu_bar
, FALSE
, TRUE
, 0);
567 gtk_box_pack_start(GTK_BOX(s
->vbox
), s
->notebook
, TRUE
, TRUE
, 0);
569 gtk_container_add(GTK_CONTAINER(s
->window
), s
->vbox
);
571 gtk_widget_show_all(s
->window
);
573 register_displaychangelistener(ds
, &s
->dcl
);