diff options
Diffstat (limited to 'ui')
| -rw-r--r-- | ui/console.c | 266 | ||||
| -rw-r--r-- | ui/curses.c | 14 | ||||
| -rw-r--r-- | ui/gtk.c | 1108 | ||||
| -rw-r--r-- | ui/input-keymap.c | 13 | ||||
| -rw-r--r-- | ui/input-legacy.c | 45 | ||||
| -rw-r--r-- | ui/input.c | 157 | ||||
| -rw-r--r-- | ui/sdl2.c | 89 | ||||
| -rw-r--r-- | ui/vnc-enc-tight.c | 8 | ||||
| -rw-r--r-- | ui/vnc.c | 63 |
9 files changed, 1177 insertions, 586 deletions
diff --git a/ui/console.c b/ui/console.c index 34d1eaa955..2ce55a69d0 100644 --- a/ui/console.c +++ b/ui/console.c @@ -143,8 +143,6 @@ struct QemuConsole { TextCell *cells; int text_x[2], text_y[2], cursor_invalidate; int echo; - bool cursor_visible_phase; - QEMUTimer *cursor_timer; int update_x0; int update_y0; @@ -177,10 +175,14 @@ static DisplayState *display_state; static QemuConsole *active_console; static QemuConsole *consoles[MAX_CONSOLES]; static int nb_consoles = 0; +static bool cursor_visible_phase; +static QEMUTimer *cursor_timer; static void text_console_do_init(CharDriverState *chr, DisplayState *ds); static void dpy_refresh(DisplayState *s); static DisplayState *get_alloc_displaystate(void); +static void text_console_update_cursor_timer(void); +static void text_console_update_cursor(void *opaque); static void gui_update(void *opaque) { @@ -475,6 +477,9 @@ static inline void text_update_xy(QemuConsole *s, int x, int y) static void invalidate_xy(QemuConsole *s, int x, int y) { + if (!qemu_console_is_visible(s)) { + return; + } if (s->update_x0 > x * FONT_WIDTH) s->update_x0 = x * FONT_WIDTH; if (s->update_y0 > y * FONT_HEIGHT) @@ -490,25 +495,20 @@ static void update_xy(QemuConsole *s, int x, int y) TextCell *c; int y1, y2; - if (!qemu_console_is_visible(s)) { - return; - } - if (s->ds->have_text) { text_update_xy(s, x, y); } - if (s->ds->have_gfx) { - y1 = (s->y_base + y) % s->total_height; - y2 = y1 - s->y_displayed; - if (y2 < 0) - y2 += s->total_height; - if (y2 < s->height) { - c = &s->cells[y1 * s->width + x]; - vga_putcharxy(s, x, y2, c->ch, - &(c->t_attrib)); - invalidate_xy(s, x, y2); - } + y1 = (s->y_base + y) % s->total_height; + y2 = y1 - s->y_displayed; + if (y2 < 0) { + y2 += s->total_height; + } + if (y2 < s->height) { + c = &s->cells[y1 * s->width + x]; + vga_putcharxy(s, x, y2, c->ch, + &(c->t_attrib)); + invalidate_xy(s, x, y2); } } @@ -518,33 +518,28 @@ static void console_show_cursor(QemuConsole *s, int show) int y, y1; int x = s->x; - if (!qemu_console_is_visible(s)) { - return; - } - if (s->ds->have_text) { s->cursor_invalidate = 1; } - if (s->ds->have_gfx) { - if (x >= s->width) { - x = s->width - 1; - } - y1 = (s->y_base + s->y) % s->total_height; - y = y1 - s->y_displayed; - if (y < 0) - y += s->total_height; - if (y < s->height) { - c = &s->cells[y1 * s->width + x]; - if (show && s->cursor_visible_phase) { - TextAttributes t_attrib = s->t_attrib_default; - t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */ - vga_putcharxy(s, x, y, c->ch, &t_attrib); - } else { - vga_putcharxy(s, x, y, c->ch, &(c->t_attrib)); - } - invalidate_xy(s, x, y); + if (x >= s->width) { + x = s->width - 1; + } + y1 = (s->y_base + s->y) % s->total_height; + y = y1 - s->y_displayed; + if (y < 0) { + y += s->total_height; + } + if (y < s->height) { + c = &s->cells[y1 * s->width + x]; + if (show && cursor_visible_phase) { + TextAttributes t_attrib = s->t_attrib_default; + t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */ + vga_putcharxy(s, x, y, c->ch, &t_attrib); + } else { + vga_putcharxy(s, x, y, c->ch, &(c->t_attrib)); } + invalidate_xy(s, x, y); } } @@ -554,10 +549,6 @@ static void console_refresh(QemuConsole *s) TextCell *c; int x, y, y1; - if (!qemu_console_is_visible(s)) { - return; - } - if (s->ds->have_text) { s->text_x[0] = 0; s->text_y[0] = 0; @@ -566,25 +557,23 @@ static void console_refresh(QemuConsole *s) s->cursor_invalidate = 1; } - if (s->ds->have_gfx) { - vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface), - color_table_rgb[0][COLOR_BLACK]); - y1 = s->y_displayed; - for (y = 0; y < s->height; y++) { - c = s->cells + y1 * s->width; - for (x = 0; x < s->width; x++) { - vga_putcharxy(s, x, y, c->ch, - &(c->t_attrib)); - c++; - } - if (++y1 == s->total_height) { - y1 = 0; - } + vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface), + color_table_rgb[0][COLOR_BLACK]); + y1 = s->y_displayed; + for (y = 0; y < s->height; y++) { + c = s->cells + y1 * s->width; + for (x = 0; x < s->width; x++) { + vga_putcharxy(s, x, y, c->ch, + &(c->t_attrib)); + c++; + } + if (++y1 == s->total_height) { + y1 = 0; } - console_show_cursor(s, 1); - dpy_gfx_update(s, 0, 0, - surface_width(surface), surface_height(surface)); } + console_show_cursor(s, 1); + dpy_gfx_update(s, 0, 0, + surface_width(surface), surface_height(surface)); } static void console_scroll(QemuConsole *s, int ydelta) @@ -640,7 +629,7 @@ static void console_put_lf(QemuConsole *s) c->t_attrib = s->t_attrib_default; c++; } - if (qemu_console_is_visible(s) && s->y_displayed == s->y_base) { + if (s->y_displayed == s->y_base) { if (s->ds->have_text) { s->text_x[0] = 0; s->text_y[0] = 0; @@ -648,18 +637,16 @@ static void console_put_lf(QemuConsole *s) s->text_y[1] = s->height - 1; } - if (s->ds->have_gfx) { - vga_bitblt(s, 0, FONT_HEIGHT, 0, 0, - s->width * FONT_WIDTH, - (s->height - 1) * FONT_HEIGHT); - vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT, - s->width * FONT_WIDTH, FONT_HEIGHT, - color_table_rgb[0][s->t_attrib_default.bgcol]); - s->update_x0 = 0; - s->update_y0 = 0; - s->update_x1 = s->width * FONT_WIDTH; - s->update_y1 = s->height * FONT_HEIGHT; - } + vga_bitblt(s, 0, FONT_HEIGHT, 0, 0, + s->width * FONT_WIDTH, + (s->height - 1) * FONT_HEIGHT); + vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT, + s->width * FONT_WIDTH, FONT_HEIGHT, + color_table_rgb[0][s->t_attrib_default.bgcol]); + s->update_x0 = 0; + s->update_y0 = 0; + s->update_x1 = s->width * FONT_WIDTH; + s->update_y1 = s->height * FONT_HEIGHT; } } } @@ -1004,9 +991,6 @@ void console_select(unsigned int index) if (s) { DisplayState *ds = s->ds; - if (active_console && active_console->cursor_timer) { - timer_del(active_console->cursor_timer); - } active_console = s; if (ds->have_gfx) { QLIST_FOREACH(dcl, &ds->listeners, next) { @@ -1023,10 +1007,7 @@ void console_select(unsigned int index) if (ds->have_text) { dpy_text_resize(s, s->width, s->height); } - if (s->cursor_timer) { - timer_mod(s->cursor_timer, - qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2); - } + text_console_update_cursor(NULL); } } @@ -1075,13 +1056,11 @@ static void kbd_send_chars(void *opaque) } /* called when an ascii key is pressed */ -void kbd_put_keysym(int keysym) +void kbd_put_keysym_console(QemuConsole *s, int keysym) { - QemuConsole *s; uint8_t buf[16], *q; int c; - s = active_console; if (!s || (s->console_type == GRAPHIC_CONSOLE)) return; @@ -1130,6 +1109,44 @@ void kbd_put_keysym(int keysym) } } +static const int qcode_to_keysym[Q_KEY_CODE_MAX] = { + [Q_KEY_CODE_UP] = QEMU_KEY_UP, + [Q_KEY_CODE_DOWN] = QEMU_KEY_DOWN, + [Q_KEY_CODE_RIGHT] = QEMU_KEY_RIGHT, + [Q_KEY_CODE_LEFT] = QEMU_KEY_LEFT, + [Q_KEY_CODE_HOME] = QEMU_KEY_HOME, + [Q_KEY_CODE_END] = QEMU_KEY_END, + [Q_KEY_CODE_PGUP] = QEMU_KEY_PAGEUP, + [Q_KEY_CODE_PGDN] = QEMU_KEY_PAGEDOWN, + [Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE, +}; + +bool kbd_put_qcode_console(QemuConsole *s, int qcode) +{ + int keysym; + + keysym = qcode_to_keysym[qcode]; + if (keysym == 0) { + return false; + } + kbd_put_keysym_console(s, keysym); + return true; +} + +void kbd_put_string_console(QemuConsole *s, const char *str, int len) +{ + int i; + + for (i = 0; i < len && str[i]; i++) { + kbd_put_keysym_console(s, str[i]); + } +} + +void kbd_put_keysym(int keysym) +{ + kbd_put_keysym_console(active_console, keysym); +} + static void text_console_invalidate(void *opaque) { QemuConsole *s = (QemuConsole *) opaque; @@ -1167,9 +1184,9 @@ static void text_console_update(void *opaque, console_ch_t *chardata) } } -static QemuConsole *new_console(DisplayState *ds, console_type_t console_type) +static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, + uint32_t head) { - Error *local_err = NULL; Object *obj; QemuConsole *s; int i; @@ -1179,13 +1196,14 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type) obj = object_new(TYPE_QEMU_CONSOLE); s = QEMU_CONSOLE(obj); + s->head = head; object_property_add_link(obj, "device", TYPE_DEVICE, (Object **)&s->device, object_property_allow_set_link, OBJ_PROP_LINK_UNREF_ON_RELEASE, - &local_err); + &error_abort); object_property_add_uint32_ptr(obj, "head", - &s->head, &local_err); + &s->head, &error_abort); if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && (console_type == GRAPHIC_CONSOLE))) { @@ -1270,19 +1288,18 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp, return surface; } -static DisplaySurface *qemu_create_dummy_surface(void) +static DisplaySurface *qemu_create_message_surface(int w, int h, + const char *msg) { - static const char msg[] = - "This VM has no graphic display device."; - DisplaySurface *surface = qemu_create_displaysurface(640, 480); + DisplaySurface *surface = qemu_create_displaysurface(w, h); pixman_color_t bg = color_table_rgb[0][COLOR_BLACK]; pixman_color_t fg = color_table_rgb[0][COLOR_WHITE]; pixman_image_t *glyph; int len, x, y, i; len = strlen(msg); - x = (640/FONT_WIDTH - len) / 2; - y = (480/FONT_HEIGHT - 1) / 2; + x = (w / FONT_WIDTH - len) / 2; + y = (h / FONT_HEIGHT - 1) / 2; for (i = 0; i < len; i++) { glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]); qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg, @@ -1304,6 +1321,8 @@ void qemu_free_displaysurface(DisplaySurface *surface) void register_displaychangelistener(DisplayChangeListener *dcl) { + static const char nodev[] = + "This VM has no graphic display device."; static DisplaySurface *dummy; QemuConsole *con; @@ -1322,11 +1341,12 @@ void register_displaychangelistener(DisplayChangeListener *dcl) dcl->ops->dpy_gfx_switch(dcl, con->surface); } else { if (!dummy) { - dummy = qemu_create_dummy_surface(); + dummy = qemu_create_message_surface(640, 480, nodev); } dcl->ops->dpy_gfx_switch(dcl, dummy); } } + text_console_update_cursor(NULL); } void update_displaychangelistener(DisplayChangeListener *dcl, @@ -1550,6 +1570,8 @@ static DisplayState *get_alloc_displaystate(void) { if (!display_state) { display_state = g_new0(DisplayState, 1); + cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, + text_console_update_cursor, NULL); } return display_state; } @@ -1560,7 +1582,6 @@ static DisplayState *get_alloc_displaystate(void) */ DisplayState *init_displaystate(void) { - Error *local_err = NULL; gchar *name; int i; @@ -1579,7 +1600,7 @@ DisplayState *init_displaystate(void) * doesn't change any more */ name = g_strdup_printf("console[%d]", i); object_property_add_child(container_get(object_get_root(), "/backend"), - name, OBJECT(consoles[i]), &local_err); + name, OBJECT(consoles[i]), &error_abort); g_free(name); } @@ -1590,7 +1611,8 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, const GraphicHwOps *hw_ops, void *opaque) { - Error *local_err = NULL; + static const char noinit[] = + "Guest has not initialized the display (yet)."; int width = 640; int height = 480; QemuConsole *s; @@ -1598,17 +1620,15 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, ds = get_alloc_displaystate(); trace_console_gfx_new(); - s = new_console(ds, GRAPHIC_CONSOLE); + s = new_console(ds, GRAPHIC_CONSOLE, head); s->hw_ops = hw_ops; s->hw = opaque; if (dev) { - object_property_set_link(OBJECT(s), OBJECT(dev), - "device", &local_err); - object_property_set_int(OBJECT(s), head, - "head", &local_err); + object_property_set_link(OBJECT(s), OBJECT(dev), "device", + &error_abort); } - s->surface = qemu_create_displaysurface(width, height); + s->surface = qemu_create_message_surface(width, height, noinit); return s; } @@ -1622,7 +1642,6 @@ QemuConsole *qemu_console_lookup_by_index(unsigned int index) QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) { - Error *local_err = NULL; Object *obj; uint32_t h; int i; @@ -1632,12 +1651,12 @@ QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) continue; } obj = object_property_get_link(OBJECT(consoles[i]), - "device", &local_err); + "device", &error_abort); if (DEVICE(obj) != dev) { continue; } h = object_property_get_int(OBJECT(consoles[i]), - "head", &local_err); + "head", &error_abort); if (h != head) { continue; } @@ -1712,14 +1731,32 @@ static void text_console_set_echo(CharDriverState *chr, bool echo) s->echo = echo; } +static void text_console_update_cursor_timer(void) +{ + timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + + CONSOLE_CURSOR_PERIOD / 2); +} + static void text_console_update_cursor(void *opaque) { - QemuConsole *s = opaque; + QemuConsole *s; + int i, count = 0; - s->cursor_visible_phase = !s->cursor_visible_phase; - graphic_hw_invalidate(s); - timer_mod(s->cursor_timer, - qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2); + cursor_visible_phase = !cursor_visible_phase; + + for (i = 0; i < nb_consoles; i++) { + s = consoles[i]; + if (qemu_console_is_graphic(s) || + !qemu_console_is_visible(s)) { + continue; + } + count++; + graphic_hw_invalidate(s); + } + + if (count) { + text_console_update_cursor_timer(); + } } static const GraphicHwOps text_console_ops = { @@ -1755,9 +1792,6 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds) s->surface = qemu_create_displaysurface(g_width, g_height); } - s->cursor_timer = - timer_new_ms(QEMU_CLOCK_REALTIME, text_console_update_cursor, s); - s->hw_ops = &text_console_ops; s->hw = s; @@ -1811,9 +1845,9 @@ static CharDriverState *text_console_init(ChardevVC *vc) trace_console_txt_new(width, height); if (width == 0 || height == 0) { - s = new_console(NULL, TEXT_CONSOLE); + s = new_console(NULL, TEXT_CONSOLE, 0); } else { - s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE); + s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0); s->surface = qemu_create_displaysurface(width, height); } diff --git a/ui/curses.c b/ui/curses.c index b044790e43..8edb038bb3 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -277,31 +277,41 @@ static void curses_refresh(DisplayChangeListener *dcl) * events, we need to emit both for each key received */ if (keycode & SHIFT) { qemu_input_event_send_key_number(NULL, SHIFT_CODE, true); + qemu_input_event_send_key_delay(0); } if (keycode & CNTRL) { qemu_input_event_send_key_number(NULL, CNTRL_CODE, true); + qemu_input_event_send_key_delay(0); } if (keycode & ALT) { qemu_input_event_send_key_number(NULL, ALT_CODE, true); + qemu_input_event_send_key_delay(0); } if (keycode & ALTGR) { qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true); + qemu_input_event_send_key_delay(0); } - qemu_input_event_send_key_number(NULL, keycode, true); - qemu_input_event_send_key_number(NULL, keycode, false); + qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true); + qemu_input_event_send_key_delay(0); + qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false); + qemu_input_event_send_key_delay(0); if (keycode & ALTGR) { qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false); + qemu_input_event_send_key_delay(0); } if (keycode & ALT) { qemu_input_event_send_key_number(NULL, ALT_CODE, false); + qemu_input_event_send_key_delay(0); } if (keycode & CNTRL) { qemu_input_event_send_key_number(NULL, CNTRL_CODE, false); + qemu_input_event_send_key_delay(0); } if (keycode & SHIFT) { qemu_input_event_send_key_number(NULL, SHIFT_CODE, false); + qemu_input_event_send_key_delay(0); } } else { keysym = curses2qemu[chr]; diff --git a/ui/gtk.c b/ui/gtk.c index 9f5061a1be..b9089360f0 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -67,13 +67,38 @@ #include "x_keymap.h" #include "keymaps.h" #include "sysemu/char.h" +#include "qom/object.h" +#ifndef _WIN32 +#include <gdk/gdkx.h> +#include <X11/XKBlib.h> +#endif #define MAX_VCS 10 +#define VC_WINDOW_X_MIN 320 +#define VC_WINDOW_Y_MIN 240 +#define VC_TERM_X_MIN 80 +#define VC_TERM_Y_MIN 25 +#define VC_SCALE_MIN 0.25 +#define VC_SCALE_STEP 0.25 #if !defined(CONFIG_VTE) # define VTE_CHECK_VERSION(a, b, c) 0 #endif +#if defined(CONFIG_VTE) && !GTK_CHECK_VERSION(3, 0, 0) +/* + * The gtk2 vte terminal widget seriously messes up the window resize + * for some reason. You basically can't make the qemu window smaller + * any more because the toplevel window geoemtry hints are overridden. + * + * Workaround that by hiding all vte widgets, except the one in the + * current tab. + * + * Luckily everything works smooth in gtk3. + */ +# define VTE_RESIZE_HACK 1 +#endif + /* Compatibility define to let us build on both Gtk2 and Gtk3 */ #if GTK_CHECK_VERSION(3, 0, 0) static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh) @@ -105,18 +130,48 @@ static const int modifier_keycode[] = { 0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, 0xdb, 0xdd, }; -typedef struct VirtualConsole -{ - GtkWidget *menu_item; - GtkWidget *terminal; +typedef struct GtkDisplayState GtkDisplayState; + +typedef struct VirtualGfxConsole { + GtkWidget *drawing_area; + DisplayChangeListener dcl; + DisplaySurface *ds; + pixman_image_t *convert; + cairo_surface_t *surface; + double scale_x; + double scale_y; +} VirtualGfxConsole; + #if defined(CONFIG_VTE) - GtkWidget *scrolled_window; +typedef struct VirtualVteConsole { + GtkWidget *box; + GtkWidget *scrollbar; + GtkWidget *terminal; CharDriverState *chr; +} VirtualVteConsole; #endif + +typedef enum VirtualConsoleType { + GD_VC_GFX, + GD_VC_VTE, +} VirtualConsoleType; + +typedef struct VirtualConsole { + GtkDisplayState *s; + char *label; + GtkWidget *window; + GtkWidget *menu_item; + GtkWidget *tab_item; + VirtualConsoleType type; + union { + VirtualGfxConsole gfx; +#if defined(CONFIG_VTE) + VirtualVteConsole vte; +#endif + }; } VirtualConsole; -typedef struct GtkDisplayState -{ +struct GtkDisplayState { GtkWidget *window; GtkWidget *menu_bar; @@ -139,29 +194,24 @@ typedef struct GtkDisplayState GtkWidget *zoom_fit_item; GtkWidget *grab_item; GtkWidget *grab_on_hover_item; - GtkWidget *vga_item; int nb_vcs; VirtualConsole vc[MAX_VCS]; GtkWidget *show_tabs_item; + GtkWidget *untabify_item; GtkWidget *vbox; GtkWidget *notebook; - GtkWidget *drawing_area; - cairo_surface_t *surface; - pixman_image_t *convert; - DisplayChangeListener dcl; - DisplaySurface *ds; int button_mask; gboolean last_set; int last_x; int last_y; int grab_x_root; int grab_y_root; + VirtualConsole *kbd_owner; + VirtualConsole *ptr_owner; - double scale_x; - double scale_y; gboolean full_screen; GdkCursor *null_cursor; @@ -171,12 +221,52 @@ typedef struct GtkDisplayState bool external_pause_update; bool modifier_pressed[ARRAY_SIZE(modifier_keycode)]; -} GtkDisplayState; + bool has_evdev; +}; -static GtkDisplayState *global_state; +static void gd_grab_pointer(VirtualConsole *vc); +static void gd_ungrab_pointer(GtkDisplayState *s); /** Utility Functions **/ +static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s) +{ + VirtualConsole *vc; + gint i; + + for (i = 0; i < s->nb_vcs; i++) { + vc = &s->vc[i]; + if (gtk_check_menu_item_get_active + (GTK_CHECK_MENU_ITEM(vc->menu_item))) { + return vc; + } + } + return NULL; +} + +static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page) +{ + VirtualConsole *vc; + gint i, p; + + for (i = 0; i < s->nb_vcs; i++) { + vc = &s->vc[i]; + p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item); + if (p == page) { + return vc; + } + } + return NULL; +} + +static VirtualConsole *gd_vc_find_current(GtkDisplayState *s) +{ + gint page; + + page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)); + return gd_vc_find_by_page(s, page); +} + static bool gd_is_grab_active(GtkDisplayState *s) { return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item)); @@ -187,22 +277,17 @@ static bool gd_grab_on_hover(GtkDisplayState *s) return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item)); } -static bool gd_on_vga(GtkDisplayState *s) -{ - return gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)) == 0; -} - -static void gd_update_cursor(GtkDisplayState *s, gboolean override) +static void gd_update_cursor(VirtualConsole *vc) { + GtkDisplayState *s = vc->s; GdkWindow *window; - bool on_vga; - window = gtk_widget_get_window(GTK_WIDGET(s->drawing_area)); - - on_vga = gd_on_vga(s); + if (vc->type != GD_VC_GFX) { + return; + } - if ((override || on_vga) && - (s->full_screen || qemu_input_is_absolute() || gd_is_grab_active(s))) { + window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area)); + if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) { gdk_window_set_cursor(window, s->null_cursor); } else { gdk_window_set_cursor(window, NULL); @@ -212,11 +297,20 @@ static void gd_update_cursor(GtkDisplayState *s, gboolean override) static void gd_update_caption(GtkDisplayState *s) { const char *status = ""; + gchar *prefix; gchar *title; const char *grab = ""; bool is_paused = !runstate_is_running(); + int i; - if (gd_is_grab_active(s)) { + if (qemu_name) { + prefix = g_strdup_printf("QEMU (%s)", qemu_name); + } else { + prefix = g_strdup_printf("QEMU"); + } + + if (s->ptr_owner != NULL && + s->ptr_owner->window == NULL) { grab = _(" - Press Ctrl+Alt+G to release grab"); } @@ -228,60 +322,100 @@ static void gd_update_caption(GtkDisplayState *s) is_paused); s->external_pause_update = false; - if (qemu_name) { - title = g_strdup_printf("QEMU (%s)%s%s", qemu_name, status, grab); - } else { - title = g_strdup_printf("QEMU%s%s", status, grab); - } - + title = g_strdup_printf("%s%s%s", prefix, status, grab); gtk_window_set_title(GTK_WINDOW(s->window), title); - g_free(title); + + for (i = 0; i < s->nb_vcs; i++) { + VirtualConsole *vc = &s->vc[i]; + + if (!vc->window) { + continue; + } + title = g_strdup_printf("%s: %s%s%s", prefix, vc->label, + vc == s->kbd_owner ? " +kbd" : "", + vc == s->ptr_owner ? " +ptr" : ""); + gtk_window_set_title(GTK_WINDOW(vc->window), title); + g_free(title); + } + + g_free(prefix); } -static void gd_update_windowsize(GtkDisplayState *s) +static void gd_update_geometry_hints(VirtualConsole *vc) { - if (!s->full_screen) { - GtkRequisition req; - double sx, sy; + GtkDisplayState *s = vc->s; + GdkWindowHints mask = 0; + GdkGeometry geo = {}; + GtkWidget *geo_widget = NULL; + GtkWindow *geo_window; + if (vc->type == GD_VC_GFX) { if (s->free_scale) { - sx = s->scale_x; - sy = s->scale_y; - - s->scale_y = 1.0; - s->scale_x = 1.0; + geo.min_width = surface_width(vc->gfx.ds) * VC_SCALE_MIN; + geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN; + mask |= GDK_HINT_MIN_SIZE; } else { - sx = 1.0; - sy = 1.0; + geo.min_width = surface_width(vc->gfx.ds) * vc->gfx.scale_x; + geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y; + mask |= GDK_HINT_MIN_SIZE; } + geo_widget = vc->gfx.drawing_area; + gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height); - gtk_widget_set_size_request(s->drawing_area, - surface_width(s->ds) * s->scale_x, - surface_height(s->ds) * s->scale_y); -#if GTK_CHECK_VERSION(3, 0, 0) - gtk_widget_get_preferred_size(s->vbox, NULL, &req); -#else - gtk_widget_size_request(s->vbox, &req); +#if defined(CONFIG_VTE) + } else if (vc->type == GD_VC_VTE) { + VteTerminal *term = VTE_TERMINAL(vc->vte.terminal); + GtkBorder *ib; + + geo.width_inc = vte_terminal_get_char_width(term); + geo.height_inc = vte_terminal_get_char_height(term); + mask |= GDK_HINT_RESIZE_INC; + geo.base_width = geo.width_inc; + geo.base_height = geo.height_inc; + mask |= GDK_HINT_BASE_SIZE; + geo.min_width = geo.width_inc * VC_TERM_X_MIN; + geo.min_height = geo.height_inc * VC_TERM_Y_MIN; + mask |= GDK_HINT_MIN_SIZE; + gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL); + geo.base_width += ib->left + ib->right; + geo.base_height += ib->top + ib->bottom; + geo.min_width += ib->left + ib->right; + geo.min_height += ib->top + ib->bottom; + geo_widget = vc->vte.terminal; #endif + } + + geo_window = GTK_WINDOW(vc->window ? vc->window : s->window); + gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask); +} + +static void gd_update_windowsize(VirtualConsole *vc) +{ + GtkDisplayState *s = vc->s; + + gd_update_geometry_hints(vc); - gtk_window_resize(GTK_WINDOW(s->window), - req.width * sx, req.height * sy); + if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) { + gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window), + VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN); } } -static void gd_update_full_redraw(GtkDisplayState *s) +static void gd_update_full_redraw(VirtualConsole *vc) { + GtkWidget *area = vc->gfx.drawing_area; int ww, wh; - gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); - gtk_widget_queue_draw_area(s->drawing_area, 0, 0, ww, wh); + gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh); + gtk_widget_queue_draw_area(area, 0, 0, ww, wh); } static void gtk_release_modifiers(GtkDisplayState *s) { + VirtualConsole *vc = gd_vc_find_current(s); int i, keycode; - if (!gd_on_vga(s)) { + if (vc->type != GD_VC_GFX) { return; } for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { @@ -289,7 +423,7 @@ static void gtk_release_modifiers(GtkDisplayState *s) if (!s->modifier_pressed[i]) { continue; } - qemu_input_event_send_key_number(s->dcl.con, keycode, false); + qemu_input_event_send_key_number(vc->gfx.dcl.con, keycode, false); s->modifier_pressed[i] = false; } } @@ -299,29 +433,31 @@ static void gtk_release_modifiers(GtkDisplayState *s) static void gd_update(DisplayChangeListener *dcl, int x, int y, int w, int h) { - GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); int x1, x2, y1, y2; int mx, my; int fbw, fbh; int ww, wh; - trace_gd_update(x, y, w, h); + trace_gd_update(vc->label, x, y, w, h); - if (s->convert) { - pixman_image_composite(PIXMAN_OP_SRC, s->ds->image, NULL, s->convert, + if (vc->gfx.convert) { + pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, + NULL, vc->gfx.convert, x, y, 0, 0, x, y, w, h); } - x1 = floor(x * s->scale_x); - y1 = floor(y * s->scale_y); + x1 = floor(x * vc->gfx.scale_x); + y1 = floor(y * vc->gfx.scale_y); - x2 = ceil(x * s->scale_x + w * s->scale_x); - y2 = ceil(y * s->scale_y + h * s->scale_y); + x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x); + y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y); - fbw = surface_width(s->ds) * s->scale_x; - fbh = surface_height(s->ds) * s->scale_y; + fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; + fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; - gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); + gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area), + &ww, &wh); mx = my = 0; if (ww > fbw) { @@ -331,7 +467,8 @@ static void gd_update(DisplayChangeListener *dcl, my = (wh - fbh) / 2; } - gtk_widget_queue_draw_area(s->drawing_area, mx + x1, my + y1, (x2 - x1), (y2 - y1)); + gtk_widget_queue_draw_area(vc->gfx.drawing_area, + mx + x1, my + y1, (x2 - x1), (y2 - y1)); } static void gd_refresh(DisplayChangeListener *dcl) @@ -343,7 +480,7 @@ static void gd_refresh(DisplayChangeListener *dcl) static void gd_mouse_set(DisplayChangeListener *dcl, int x, int y, int visible) { - GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GdkDisplay *dpy; GdkDeviceManager *mgr; gint x_root, y_root; @@ -352,29 +489,29 @@ static void gd_mouse_set(DisplayChangeListener *dcl, return; } - dpy = gtk_widget_get_display(s->drawing_area); + dpy = gtk_widget_get_display(vc->gfx.drawing_area); mgr = gdk_display_get_device_manager(dpy); - gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area), + gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area), x, y, &x_root, &y_root); gdk_device_warp(gdk_device_manager_get_client_pointer(mgr), - gtk_widget_get_screen(s->drawing_area), + gtk_widget_get_screen(vc->gfx.drawing_area), x_root, y_root); } #else static void gd_mouse_set(DisplayChangeListener *dcl, int x, int y, int visible) { - GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); gint x_root, y_root; if (qemu_input_is_absolute()) { return; } - gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area), + gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area), x, y, &x_root, &y_root); - gdk_display_warp_pointer(gtk_widget_get_display(s->drawing_area), - gtk_widget_get_screen(s->drawing_area), + gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area), + gtk_widget_get_screen(vc->gfx.drawing_area), x_root, y_root); } #endif @@ -382,7 +519,7 @@ static void gd_mouse_set(DisplayChangeListener *dcl, static void gd_cursor_define(DisplayChangeListener *dcl, QEMUCursor *c) { - GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GdkPixbuf *pixbuf; GdkCursor *cursor; @@ -390,9 +527,10 @@ static void gd_cursor_define(DisplayChangeListener *dcl, GDK_COLORSPACE_RGB, true, 8, c->width, c->height, c->width * 4, NULL, NULL); - cursor = gdk_cursor_new_from_pixbuf(gtk_widget_get_display(s->drawing_area), - pixbuf, c->hot_x, c->hot_y); - gdk_window_set_cursor(gtk_widget_get_window(s->drawing_area), cursor); + cursor = gdk_cursor_new_from_pixbuf + (gtk_widget_get_display(vc->gfx.drawing_area), + pixbuf, c->hot_x, c->hot_y); + gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor); g_object_unref(pixbuf); #if !GTK_CHECK_VERSION(3, 0, 0) gdk_cursor_unref(cursor); @@ -404,25 +542,25 @@ static void gd_cursor_define(DisplayChangeListener *dcl, static void gd_switch(DisplayChangeListener *dcl, DisplaySurface *surface) { - GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl); + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); bool resized = true; - trace_gd_switch(surface_width(surface), surface_height(surface)); + trace_gd_switch(vc->label, surface_width(surface), surface_height(surface)); - if (s->surface) { - cairo_surface_destroy(s->surface); + if (vc->gfx.surface) { + cairo_surface_destroy(vc->gfx.surface); } - if (s->ds && - surface_width(s->ds) == surface_width(surface) && - surface_height(s->ds) == surface_height(surface)) { + if (vc->gfx.ds && + surface_width(vc->gfx.ds) == surface_width(surface) && + surface_height(vc->gfx.ds) == surface_height(surface)) { resized = false; } - s->ds = surface; + vc->gfx.ds = surface; - if (s->convert) { - pixman_image_unref(s->convert); - s->convert = NULL; + if (vc->gfx.convert) { + pixman_image_unref(vc->gfx.convert); + vc->gfx.convert = NULL; } if (surface->format == PIXMAN_x8r8g8b8) { @@ -432,7 +570,7 @@ static void gd_switch(DisplayChangeListener *dcl, * No need to convert, use surface directly. Should be the * common case as this is qemu_default_pixelformat(32) too. */ - s->surface = cairo_image_surface_create_for_data + vc->gfx.surface = cairo_image_surface_create_for_data (surface_data(surface), CAIRO_FORMAT_RGB24, surface_width(surface), @@ -440,26 +578,27 @@ static void gd_switch(DisplayChangeListener *dcl, surface_stride(surface)); } else { /* Must convert surface, use pixman to do it. */ - s->convert = pixman_image_create_bits(PIXMAN_x8r8g8b8, - surface_width(surface), - surface_height(surface), - NULL, 0); - s->surface = cairo_image_surface_create_for_data - ((void *)pixman_image_get_data(s->convert), + vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8, + surface_width(surface), + surface_height(surface), + NULL, 0); + vc->gfx.surface = cairo_image_surface_create_for_data + ((void *)pixman_image_get_data(vc->gfx.convert), CAIRO_FORMAT_RGB24, - pixman_image_get_width(s->convert), - pixman_image_get_height(s->convert), - pixman_image_get_stride(s->convert)); - pixman_image_composite(PIXMAN_OP_SRC, s->ds->image, NULL, s->convert, + pixman_image_get_width(vc->gfx.convert), + pixman_image_get_height(vc->gfx.convert), + pixman_image_get_stride(vc->gfx.convert)); + pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, + NULL, vc->gfx.convert, 0, 0, 0, 0, 0, 0, - pixman_image_get_width(s->convert), - pixman_image_get_height(s->convert)); + pixman_image_get_width(vc->gfx.convert), + pixman_image_get_height(vc->gfx.convert)); } if (resized) { - gd_update_windowsize(s); + gd_update_windowsize(vc); } else { - gd_update_full_redraw(s); + gd_update_full_redraw(vc); } } @@ -475,6 +614,7 @@ static void gd_change_runstate(void *opaque, int running, RunState state) static void gd_mouse_mode_change(Notifier *notify, void *data) { GtkDisplayState *s; + int i; s = container_of(notify, GtkDisplayState, mouse_mode_notifier); /* release the grab at switching to absolute mode */ @@ -482,7 +622,10 @@ static void gd_mouse_mode_change(Notifier *notify, void *data) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE); } - gd_update_cursor(s, FALSE); + for (i = 0; i < s->nb_vcs; i++) { + VirtualConsole *vc = &s->vc[i]; + gd_update_cursor(vc); + } } /** GTK Events **/ @@ -491,9 +634,15 @@ static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event, void *opaque) { GtkDisplayState *s = opaque; + int i; if (!no_quit) { - unregister_displaychangelistener(&s->dcl); + for (i = 0; i < s->nb_vcs; i++) { + if (s->vc[i].type != GD_VC_GFX) { + continue; + } + unregister_displaychangelistener(&s->vc[i].gfx.dcl); + } qmp_quit(NULL); return FALSE; } @@ -503,7 +652,8 @@ static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event, static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) { - GtkDisplayState *s = opaque; + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; int mx, my; int ww, wh; int fbw, fbh; @@ -512,25 +662,25 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) return FALSE; } - fbw = surface_width(s->ds); - fbh = surface_height(s->ds); + fbw = surface_width(vc->gfx.ds); + fbh = surface_height(vc->gfx.ds); gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh); if (s->full_screen) { - s->scale_x = (double)ww / fbw; - s->scale_y = (double)wh / fbh; + vc->gfx.scale_x = (double)ww / fbw; + vc->gfx.scale_y = (double)wh / fbh; } else if (s->free_scale) { double sx, sy; sx = (double)ww / fbw; sy = (double)wh / fbh; - s->scale_x = s->scale_y = MIN(sx, sy); + vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); } - fbw *= s->scale_x; - fbh *= s->scale_y; + fbw *= vc->gfx.scale_x; + fbh *= vc->gfx.scale_y; mx = my = 0; if (ww > fbw) { @@ -551,8 +701,9 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) -1 * fbw, fbh); cairo_fill(cr); - cairo_scale(cr, s->scale_x, s->scale_y); - cairo_set_source_surface(cr, s->surface, mx / s->scale_x, my / s->scale_y); + cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y); + cairo_set_source_surface(cr, vc->gfx.surface, + mx / vc->gfx.scale_x, my / vc->gfx.scale_y); cairo_paint(cr); return TRUE; @@ -584,16 +735,18 @@ static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose, static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, void *opaque) { - GtkDisplayState *s = opaque; + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; int x, y; int mx, my; int fbh, fbw; int ww, wh; - fbw = surface_width(s->ds) * s->scale_x; - fbh = surface_height(s->ds) * s->scale_y; + fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; + fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; - gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); + gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area), + &ww, &wh); mx = my = 0; if (ww > fbw) { @@ -603,31 +756,31 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, my = (wh - fbh) / 2; } - x = (motion->x - mx) / s->scale_x; - y = (motion->y - my) / s->scale_y; + x = (motion->x - mx) / vc->gfx.scale_x; + y = (motion->y - my) / vc->gfx.scale_y; if (qemu_input_is_absolute()) { if (x < 0 || y < 0 || - x >= surface_width(s->ds) || - y >= surface_height(s->ds)) { + x >= surface_width(vc->gfx.ds) || + y >= surface_height(vc->gfx.ds)) { return TRUE; } - qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_X, x, - surface_width(s->ds)); - qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_Y, y, - surface_height(s->ds)); + qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x, + surface_width(vc->gfx.ds)); + qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y, + surface_height(vc->gfx.ds)); qemu_input_event_sync(); - } else if (s->last_set && gd_is_grab_active(s)) { - qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_X, x - s->last_x); - qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_Y, y - s->last_y); + } else if (s->last_set && s->ptr_owner == vc) { + qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x); + qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y); qemu_input_event_sync(); } s->last_x = x; s->last_y = y; s->last_set = TRUE; - if (!qemu_input_is_absolute() && gd_is_grab_active(s)) { - GdkScreen *screen = gtk_widget_get_screen(s->drawing_area); + if (!qemu_input_is_absolute() && s->ptr_owner == vc) { + GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area); int x = (int)motion->x_root; int y = (int)motion->y_root; @@ -669,14 +822,21 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, void *opaque) { - GtkDisplayState *s = opaque; + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; InputButton btn; /* implicitly grab the input at the first click in the relative mode */ if (button->button == 1 && button->type == GDK_BUTTON_PRESS && - !qemu_input_is_absolute() && !gd_is_grab_active(s)) { - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), - TRUE); + !qemu_input_is_absolute() && s->ptr_owner != vc) { + gd_ungrab_pointer(s); + if (!vc->window) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), + TRUE); + } else { + gd_grab_pointer(vc); + gd_update_caption(s); + } return TRUE; } @@ -690,7 +850,8 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, return TRUE; } - qemu_input_queue_btn(s->dcl.con, btn, button->type == GDK_BUTTON_PRESS); + qemu_input_queue_btn(vc->gfx.dcl.con, btn, + button->type == GDK_BUTTON_PRESS); qemu_input_event_sync(); return TRUE; } @@ -698,7 +859,7 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll, void *opaque) { - GtkDisplayState *s = opaque; + VirtualConsole *vc = opaque; InputButton btn; if (scroll->direction == GDK_SCROLL_UP) { @@ -709,16 +870,17 @@ static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll, return TRUE; } - qemu_input_queue_btn(s->dcl.con, btn, true); + qemu_input_queue_btn(vc->gfx.dcl.con, btn, true); qemu_input_event_sync(); - qemu_input_queue_btn(s->dcl.con, btn, false); + qemu_input_queue_btn(vc->gfx.dcl.con, btn, false); qemu_input_event_sync(); return TRUE; } static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) { - GtkDisplayState *s = opaque; + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; int gdk_keycode = key->hardware_keycode; int i; @@ -737,7 +899,11 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) } else if (gdk_keycode < 97) { qemu_keycode = gdk_keycode - 8; } else if (gdk_keycode < 158) { - qemu_keycode = translate_evdev_keycode(gdk_keycode - 97); + if (s->has_evdev) { + qemu_keycode = translate_evdev_keycode(gdk_keycode - 97); + } else { + qemu_keycode = translate_xfree86_keycode(gdk_keycode - 97); + } } else if (gdk_keycode == 208) { /* Hiragana_Katakana */ qemu_keycode = 0x70; } else if (gdk_keycode == 211) { /* backslash */ @@ -747,7 +913,7 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) } #endif - trace_gd_key_event(gdk_keycode, qemu_keycode, + trace_gd_key_event(vc->label, gdk_keycode, qemu_keycode, (key->type == GDK_KEY_PRESS) ? "down" : "up"); for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { @@ -756,7 +922,7 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) } } - qemu_input_event_send_key_number(s->dcl.con, qemu_keycode, + qemu_input_event_send_key_number(vc->gfx.dcl.con, qemu_keycode, key->type == GDK_KEY_PRESS); return TRUE; @@ -804,19 +970,14 @@ static void gd_menu_quit(GtkMenuItem *item, void *opaque) static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_by_menu(s); + gint page; - if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) { - gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), 0); - } else { - int i; - - gtk_release_modifiers(s); - for (i = 0; i < s->nb_vcs; i++) { - if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) { - gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), i + 1); - break; - } - } + gtk_release_modifiers(s); + if (vc) { + page = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), + vc->tab_item); + gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), page); } } @@ -831,94 +992,159 @@ static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque) } } +static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event, + void *opaque) +{ + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; + + gtk_widget_set_sensitive(vc->menu_item, true); + gtk_widget_reparent(vc->tab_item, s->notebook); + gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook), + vc->tab_item, vc->label); + gtk_widget_destroy(vc->window); + vc->window = NULL; + return TRUE; +} + +static gboolean gd_win_grab(void *opaque) +{ + VirtualConsole *vc = opaque; + + fprintf(stderr, "%s: %s\n", __func__, vc->label); + if (vc->s->ptr_owner) { + gd_ungrab_pointer(vc->s); + } else { + gd_grab_pointer(vc); + } + gd_update_caption(vc->s); + return TRUE; +} + +static void gd_menu_untabify(GtkMenuItem *item, void *opaque) +{ + GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); + + if (vc->type == GD_VC_GFX) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), + FALSE); + } + if (!vc->window) { + gtk_widget_set_sensitive(vc->menu_item, false); + vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_reparent(vc->tab_item, vc->window); + + g_signal_connect(vc->window, "delete-event", + G_CALLBACK(gd_tab_window_close), vc); + gtk_widget_show_all(vc->window); + + GtkAccelGroup *ag = gtk_accel_group_new(); + gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag); + + GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab), vc, NULL); + gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb); + + gd_update_geometry_hints(vc); + gd_update_caption(s); + } +} + static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); if (!s->full_screen) { gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); gtk_widget_set_size_request(s->menu_bar, 0, 0); - gtk_widget_set_size_request(s->drawing_area, -1, -1); - gtk_window_fullscreen(GTK_WINDOW(s->window)); - if (gd_on_vga(s)) { - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE); + if (vc->type == GD_VC_GFX) { + gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), + TRUE); } + gtk_window_fullscreen(GTK_WINDOW(s->window)); s->full_screen = TRUE; } else { gtk_window_unfullscreen(GTK_WINDOW(s->window)); gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s); gtk_widget_set_size_request(s->menu_bar, -1, -1); - gtk_widget_set_size_request(s->drawing_area, - surface_width(s->ds), - surface_height(s->ds)); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE); s->full_screen = FALSE; - s->scale_x = 1.0; - s->scale_y = 1.0; + if (vc->type == GD_VC_GFX) { + vc->gfx.scale_x = 1.0; + vc->gfx.scale_y = 1.0; + gd_update_windowsize(vc); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), + FALSE); + } } - gd_update_cursor(s, FALSE); + gd_update_cursor(vc); } static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), FALSE); - s->scale_x += .25; - s->scale_y += .25; + vc->gfx.scale_x += VC_SCALE_STEP; + vc->gfx.scale_y += VC_SCALE_STEP; - gd_update_windowsize(s); + gd_update_windowsize(vc); } static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), FALSE); - s->scale_x -= .25; - s->scale_y -= .25; + vc->gfx.scale_x -= VC_SCALE_STEP; + vc->gfx.scale_y -= VC_SCALE_STEP; - s->scale_x = MAX(s->scale_x, .25); - s->scale_y = MAX(s->scale_y, .25); + vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN); + vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN); - gd_update_windowsize(s); + gd_update_windowsize(vc); } static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); - s->scale_x = 1.0; - s->scale_y = 1.0; + vc->gfx.scale_x = 1.0; + vc->gfx.scale_y = 1.0; - gd_update_windowsize(s); + gd_update_windowsize(vc); } static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) { s->free_scale = TRUE; } else { s->free_scale = FALSE; - s->scale_x = 1.0; - s->scale_y = 1.0; - gd_update_windowsize(s); + vc->gfx.scale_x = 1.0; + vc->gfx.scale_y = 1.0; } - gd_update_full_redraw(s); + gd_update_windowsize(vc); + gd_update_full_redraw(vc); } -static void gd_grab_keyboard(GtkDisplayState *s) +static void gd_grab_keyboard(VirtualConsole *vc) { #if GTK_CHECK_VERSION(3, 0, 0) - GdkDisplay *display = gtk_widget_get_display(s->drawing_area); + GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); GdkDeviceManager *mgr = gdk_display_get_device_manager(display); GList *devices = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER); @@ -927,7 +1153,7 @@ static void gd_grab_keyboard(GtkDisplayState *s) GdkDevice *dev = tmp->data; if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) { gdk_device_grab(dev, - gtk_widget_get_window(s->drawing_area), + gtk_widget_get_window(vc->gfx.drawing_area), GDK_OWNERSHIP_NONE, FALSE, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, @@ -938,16 +1164,25 @@ static void gd_grab_keyboard(GtkDisplayState *s) } g_list_free(devices); #else - gdk_keyboard_grab(gtk_widget_get_window(s->drawing_area), + gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area), FALSE, GDK_CURRENT_TIME); #endif + vc->s->kbd_owner = vc; + trace_gd_grab(vc->label, "kbd", true); } static void gd_ungrab_keyboard(GtkDisplayState *s) { + VirtualConsole *vc = s->kbd_owner; + + if (vc == NULL) { + return; + } + s->kbd_owner = NULL; + #if GTK_CHECK_VERSION(3, 0, 0) - GdkDisplay *display = gtk_widget_get_display(s->drawing_area); + GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); GdkDeviceManager *mgr = gdk_display_get_device_manager(display); GList *devices = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER); @@ -964,11 +1199,12 @@ static void gd_ungrab_keyboard(GtkDisplayState *s) #else gdk_keyboard_ungrab(GDK_CURRENT_TIME); #endif + trace_gd_grab(vc->label, "kbd", false); } -static void gd_grab_pointer(GtkDisplayState *s) +static void gd_grab_pointer(VirtualConsole *vc) { - GdkDisplay *display = gtk_widget_get_display(s->drawing_area); + GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); #if GTK_CHECK_VERSION(3, 0, 0) GdkDeviceManager *mgr = gdk_display_get_device_manager(display); GList *devices = gdk_device_manager_list_devices(mgr, @@ -978,7 +1214,7 @@ static void gd_grab_pointer(GtkDisplayState *s) GdkDevice *dev = tmp->data; if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) { gdk_device_grab(dev, - gtk_widget_get_window(s->drawing_area), + gtk_widget_get_window(vc->gfx.drawing_area), GDK_OWNERSHIP_NONE, FALSE, /* All events to come to our window directly */ @@ -987,16 +1223,16 @@ static void gd_grab_pointer(GtkDisplayState *s) GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK | GDK_SCROLL_MASK, - s->null_cursor, + vc->s->null_cursor, GDK_CURRENT_TIME); } tmp = tmp->next; } g_list_free(devices); gdk_device_get_position(gdk_device_manager_get_client_pointer(mgr), - NULL, &s->grab_x_root, &s->grab_y_root); + NULL, &vc->s->grab_x_root, &vc->s->grab_y_root); #else - gdk_pointer_grab(gtk_widget_get_window(s->drawing_area), + gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area), FALSE, /* All events to come to our window directly */ GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | @@ -1004,16 +1240,25 @@ static void gd_grab_pointer(GtkDisplayState *s) GDK_BUTTON_MOTION_MASK | GDK_SCROLL_MASK, NULL, /* Allow cursor to move over entire desktop */ - s->null_cursor, + vc->s->null_cursor, GDK_CURRENT_TIME); gdk_display_get_pointer(display, NULL, - &s->grab_x_root, &s->grab_y_root, NULL); + &vc->s->grab_x_root, &vc->s->grab_y_root, NULL); #endif + vc->s->ptr_owner = vc; + trace_gd_grab(vc->label, "ptr", true); } static void gd_ungrab_pointer(GtkDisplayState *s) { - GdkDisplay *display = gtk_widget_get_display(s->drawing_area); + VirtualConsole *vc = s->ptr_owner; + + if (vc == NULL) { + return; + } + s->ptr_owner = NULL; + + GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); #if GTK_CHECK_VERSION(3, 0, 0) GdkDeviceManager *mgr = gdk_display_get_device_manager(display); GList *devices = gdk_device_manager_list_devices(mgr, @@ -1029,51 +1274,65 @@ static void gd_ungrab_pointer(GtkDisplayState *s) } g_list_free(devices); gdk_device_warp(gdk_device_manager_get_client_pointer(mgr), - gtk_widget_get_screen(s->drawing_area), - s->grab_x_root, s->grab_y_root); + gtk_widget_get_screen(vc->gfx.drawing_area), + vc->s->grab_x_root, vc->s->grab_y_root); #else gdk_pointer_ungrab(GDK_CURRENT_TIME); gdk_display_warp_pointer(display, - gtk_widget_get_screen(s->drawing_area), - s->grab_x_root, s->grab_y_root); + gtk_widget_get_screen(vc->gfx.drawing_area), + vc->s->grab_x_root, vc->s->grab_y_root); #endif + trace_gd_grab(vc->label, "ptr", false); } static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); if (gd_is_grab_active(s)) { - gd_grab_keyboard(s); - gd_grab_pointer(s); + if (!gd_grab_on_hover(s)) { + gd_grab_keyboard(vc); + } + gd_grab_pointer(vc); } else { gd_ungrab_keyboard(s); gd_ungrab_pointer(s); } gd_update_caption(s); - gd_update_cursor(s, FALSE); + gd_update_cursor(vc); } static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, gpointer data) { GtkDisplayState *s = data; - guint last_page; + VirtualConsole *vc; gboolean on_vga; if (!gtk_widget_get_realized(s->notebook)) { return; } - last_page = gtk_notebook_get_current_page(nb); - - if (last_page) { - gtk_widget_set_size_request(s->vc[last_page - 1].terminal, -1, -1); +#ifdef VTE_RESIZE_HACK + vc = gd_vc_find_current(s); + if (vc && vc->type == GD_VC_VTE) { + gtk_widget_hide(vc->vte.terminal); } - - on_vga = arg2 == 0; - +#endif + vc = gd_vc_find_by_page(s, arg2); + if (!vc) { + return; + } +#ifdef VTE_RESIZE_HACK + if (vc->type == GD_VC_VTE) { + gtk_widget_show(vc->vte.terminal); + } +#endif + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), + TRUE); + on_vga = (vc->type == GD_VC_GFX); if (!on_vga) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE); @@ -1081,71 +1340,88 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE); } - - if (arg2 == 0) { - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE); - } else { -#if defined(CONFIG_VTE) - VirtualConsole *vc = &s->vc[arg2 - 1]; - VteTerminal *term = VTE_TERMINAL(vc->terminal); - int width, height; - - width = 80 * vte_terminal_get_char_width(term); - height = 25 * vte_terminal_get_char_height(term); - - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); - gtk_widget_set_size_request(vc->terminal, width, height); -#else - g_assert_not_reached(); -#endif - } - gtk_widget_set_sensitive(s->grab_item, on_vga); - gd_update_cursor(s, TRUE); + gd_update_windowsize(vc); + gd_update_cursor(vc); } -static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data) +static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, + gpointer opaque) { - GtkDisplayState *s = data; + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; - if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) { - gd_grab_keyboard(s); + if (gd_grab_on_hover(s)) { + gd_ungrab_keyboard(s); + gd_grab_keyboard(vc); + gd_update_caption(s); } - return TRUE; } -static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data) +static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, + gpointer opaque) { - GtkDisplayState *s = data; + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; - if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) { + if (gd_grab_on_hover(s)) { gd_ungrab_keyboard(s); + gd_update_caption(s); } - return TRUE; } static gboolean gd_focus_out_event(GtkWidget *widget, - GdkEventCrossing *crossing, gpointer data) + GdkEventCrossing *crossing, gpointer opaque) { - GtkDisplayState *s = data; + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; gtk_release_modifiers(s); - return TRUE; } /** Virtual Console Callbacks **/ -static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc, + int idx, GSList *group, GtkWidget *view_menu) { + char path[32]; + + snprintf(path, sizeof(path), "<QEMU>/View/VC%d", idx); + + vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label); + group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); + gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path); + gtk_accel_map_add_entry(path, GDK_KEY_1 + idx, HOTKEY_MODIFIERS); + + g_signal_connect(vc->menu_item, "activate", + G_CALLBACK(gd_menu_switch_vc), s); + gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item); + + return group; +} + #if defined(CONFIG_VTE) +static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque) +{ + VirtualConsole *vc = opaque; + + if (gtk_adjustment_get_upper(adjustment) > + gtk_adjustment_get_page_size(adjustment)) { + gtk_widget_show(vc->vte.scrollbar); + } else { + gtk_widget_hide(vc->vte.scrollbar); + } +} + +static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ VirtualConsole *vc = chr->opaque; - vte_terminal_feed(VTE_TERMINAL(vc->terminal), (const char *)buf, len); -#endif + vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len); return len; } @@ -1166,115 +1442,132 @@ static CharDriverState *gd_vc_handler(ChardevVC *unused) return chr; } -void early_gtk_display_init(void) -{ - register_vc_handler(gd_vc_handler); -} - -#if defined(CONFIG_VTE) static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size, gpointer user_data) { VirtualConsole *vc = user_data; - qemu_chr_be_write(vc->chr, (uint8_t *)text, (unsigned int)size); + qemu_chr_be_write(vc->vte.chr, (uint8_t *)text, (unsigned int)size); return TRUE; } -#endif -static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group, - GtkWidget *view_menu) +static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, + CharDriverState *chr, int idx, + GSList *group, GtkWidget *view_menu) { -#if defined(CONFIG_VTE) - const char *label; char buffer[32]; - char path[32]; - GtkWidget *scrolled_window; + GtkWidget *box; + GtkWidget *scrollbar; GtkAdjustment *vadjustment; - snprintf(buffer, sizeof(buffer), "vc%d", index); - snprintf(path, sizeof(path), "<QEMU>/View/VC%d", index); - - vc->chr = vcs[index]; - - if (vc->chr->label) { - label = vc->chr->label; - } else { - label = buffer; - } + vc->s = s; + vc->vte.chr = chr; - vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label); - group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); - gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path); - gtk_accel_map_add_entry(path, GDK_KEY_2 + index, HOTKEY_MODIFIERS); + snprintf(buffer, sizeof(buffer), "vc%d", idx); + vc->label = g_strdup_printf("%s", vc->vte.chr->label + ? vc->vte.chr->label : buffer); + group = gd_vc_menu_init(s, vc, idx, group, view_menu); - vc->terminal = vte_terminal_new(); - g_signal_connect(vc->terminal, "commit", G_CALLBACK(gd_vc_in), vc); + vc->vte.terminal = vte_terminal_new(); + g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc); - vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1); + vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1); + vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal), + VC_TERM_X_MIN, VC_TERM_Y_MIN); #if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0) - vadjustment = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(vc->terminal)); + vadjustment = gtk_scrollable_get_vadjustment + (GTK_SCROLLABLE(vc->vte.terminal)); #else - vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal)); + vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal)); #endif - scrolled_window = gtk_scrolled_window_new(NULL, vadjustment); - gtk_container_add(GTK_CONTAINER(scrolled_window), vc->terminal); - - vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25); +#if GTK_CHECK_VERSION(3, 0, 0) + box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2); + scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment); +#else + box = gtk_hbox_new(false, 2); + scrollbar = gtk_vscrollbar_new(vadjustment); +#endif - vc->chr->opaque = vc; - vc->scrolled_window = scrolled_window; + gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vc->scrolled_window), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + vc->vte.chr->opaque = vc; + vc->vte.box = box; + vc->vte.scrollbar = scrollbar; - gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), scrolled_window, gtk_label_new(label)); - g_signal_connect(vc->menu_item, "activate", - G_CALLBACK(gd_menu_switch_vc), s); + g_signal_connect(vadjustment, "changed", + G_CALLBACK(gd_vc_adjustment_changed), vc); - gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item); + vc->type = GD_VC_VTE; + vc->tab_item = box; + gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item, + gtk_label_new(vc->label)); - qemu_chr_be_generic_open(vc->chr); - if (vc->chr->init) { - vc->chr->init(vc->chr); + qemu_chr_be_generic_open(vc->vte.chr); + if (vc->vte.chr->init) { + vc->vte.chr->init(vc->vte.chr); } -#endif /* CONFIG_VTE */ return group; } +static void gd_vcs_init(GtkDisplayState *s, GSList *group, + GtkWidget *view_menu) +{ + int i; + + for (i = 0; i < nb_vcs; i++) { + VirtualConsole *vc = &s->vc[s->nb_vcs]; + group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu); + s->nb_vcs++; + } +} +#endif /* CONFIG_VTE */ + /** Window Creation **/ +static void gd_connect_vc_gfx_signals(VirtualConsole *vc) +{ +#if GTK_CHECK_VERSION(3, 0, 0) + g_signal_connect(vc->gfx.drawing_area, "draw", + G_CALLBACK(gd_draw_event), vc); +#else + g_signal_connect(vc->gfx.drawing_area, "expose-event", + G_CALLBACK(gd_expose_event), vc); +#endif + g_signal_connect(vc->gfx.drawing_area, "event", + G_CALLBACK(gd_event), vc); + g_signal_connect(vc->gfx.drawing_area, "button-press-event", + G_CALLBACK(gd_button_event), vc); + g_signal_connect(vc->gfx.drawing_area, "button-release-event", + G_CALLBACK(gd_button_event), vc); + g_signal_connect(vc->gfx.drawing_area, "scroll-event", + G_CALLBACK(gd_scroll_event), vc); + g_signal_connect(vc->gfx.drawing_area, "key-press-event", + G_CALLBACK(gd_key_event), vc); + g_signal_connect(vc->gfx.drawing_area, "key-release-event", + G_CALLBACK(gd_key_event), vc); + + g_signal_connect(vc->gfx.drawing_area, "enter-notify-event", + G_CALLBACK(gd_enter_event), vc); + g_signal_connect(vc->gfx.drawing_area, "leave-notify-event", + G_CALLBACK(gd_leave_event), vc); + g_signal_connect(vc->gfx.drawing_area, "focus-out-event", + G_CALLBACK(gd_focus_out_event), vc); +} + static void gd_connect_signals(GtkDisplayState *s) { g_signal_connect(s->show_tabs_item, "activate", G_CALLBACK(gd_menu_show_tabs), s); + g_signal_connect(s->untabify_item, "activate", + G_CALLBACK(gd_menu_untabify), s); g_signal_connect(s->window, "delete-event", G_CALLBACK(gd_window_close), s); -#if GTK_CHECK_VERSION(3, 0, 0) - g_signal_connect(s->drawing_area, "draw", - G_CALLBACK(gd_draw_event), s); -#else - g_signal_connect(s->drawing_area, "expose-event", - G_CALLBACK(gd_expose_event), s); -#endif - g_signal_connect(s->drawing_area, "event", - G_CALLBACK(gd_event), s); - g_signal_connect(s->drawing_area, "button-press-event", - G_CALLBACK(gd_button_event), s); - g_signal_connect(s->drawing_area, "button-release-event", - G_CALLBACK(gd_button_event), s); - g_signal_connect(s->drawing_area, "scroll-event", - G_CALLBACK(gd_scroll_event), s); - g_signal_connect(s->drawing_area, "key-press-event", - G_CALLBACK(gd_key_event), s); - g_signal_connect(s->drawing_area, "key-release-event", - G_CALLBACK(gd_key_event), s); - g_signal_connect(s->pause_item, "activate", G_CALLBACK(gd_menu_pause), s); g_signal_connect(s->reset_item, "activate", @@ -1293,18 +1586,10 @@ static void gd_connect_signals(GtkDisplayState *s) G_CALLBACK(gd_menu_zoom_fixed), s); g_signal_connect(s->zoom_fit_item, "activate", G_CALLBACK(gd_menu_zoom_fit), s); - g_signal_connect(s->vga_item, "activate", - G_CALLBACK(gd_menu_switch_vc), s); g_signal_connect(s->grab_item, "activate", G_CALLBACK(gd_menu_grab_input), s); g_signal_connect(s->notebook, "switch-page", G_CALLBACK(gd_change_page), s); - g_signal_connect(s->drawing_area, "enter-notify-event", - G_CALLBACK(gd_enter_event), s); - g_signal_connect(s->drawing_area, "leave-notify-event", - G_CALLBACK(gd_leave_event), s); - g_signal_connect(s->drawing_area, "focus-out-event", - G_CALLBACK(gd_focus_out_event), s); } static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *accel_group) @@ -1340,12 +1625,68 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *acce return machine_menu; } +static const DisplayChangeListenerOps dcl_ops = { + .dpy_name = "gtk", + .dpy_gfx_update = gd_update, + .dpy_gfx_switch = gd_switch, + .dpy_refresh = gd_refresh, + .dpy_mouse_set = gd_mouse_set, + .dpy_cursor_define = gd_cursor_define, +}; + +static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, + QemuConsole *con, int idx, + GSList *group, GtkWidget *view_menu) +{ + Error *local_err = NULL; + Object *obj; + + obj = object_property_get_link(OBJECT(con), "device", &local_err); + if (obj) { + vc->label = g_strdup_printf("%s", object_get_typename(obj)); + } else { + vc->label = g_strdup_printf("VGA"); + } + + vc->s = s; + vc->gfx.scale_x = 1.0; + vc->gfx.scale_y = 1.0; + + vc->gfx.drawing_area = gtk_drawing_area_new(); + gtk_widget_add_events(vc->gfx.drawing_area, + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_MOTION_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_SCROLL_MASK | + GDK_KEY_PRESS_MASK); + gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE); + gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE); + + vc->type = GD_VC_GFX; + vc->tab_item = vc->gfx.drawing_area; + gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), + vc->tab_item, gtk_label_new(vc->label)); + gd_connect_vc_gfx_signals(vc); + + group = gd_vc_menu_init(s, vc, idx, group, view_menu); + + vc->gfx.dcl.ops = &dcl_ops; + vc->gfx.dcl.con = con; + register_displaychangelistener(&vc->gfx.dcl); + + return group; +} + static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_group) { GSList *group = NULL; GtkWidget *view_menu; GtkWidget *separator; - int i; + QemuConsole *con; + int vc; view_menu = gtk_menu_new(); gtk_menu_set_accel_group(GTK_MENU(view_menu), accel_group); @@ -1400,26 +1741,31 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s, GtkAccelGroup *accel_g separator = gtk_separator_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); - s->vga_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA"); - group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s->vga_item)); - gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->vga_item), - "<QEMU>/View/VGA"); - gtk_accel_map_add_entry("<QEMU>/View/VGA", GDK_KEY_1, HOTKEY_MODIFIERS); - gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->vga_item); - - for (i = 0; i < nb_vcs; i++) { - VirtualConsole *vc = &s->vc[i]; - - group = gd_vc_init(s, vc, i, group, view_menu); + /* gfx */ + for (vc = 0;; vc++) { + con = qemu_console_lookup_by_index(vc); + if (!con || !qemu_console_is_graphic(con)) { + break; + } + group = gd_vc_gfx_init(s, &s->vc[vc], con, + vc, group, view_menu); s->nb_vcs++; } +#if defined(CONFIG_VTE) + /* vte */ + gd_vcs_init(s, group, view_menu); +#endif + separator = gtk_separator_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator); s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs")); gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item); + s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab")); + gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item); + return view_menu; } @@ -1445,14 +1791,28 @@ static void gd_create_menus(GtkDisplayState *s) s->accel_group = accel_group; } -static const DisplayChangeListenerOps dcl_ops = { - .dpy_name = "gtk", - .dpy_gfx_update = gd_update, - .dpy_gfx_switch = gd_switch, - .dpy_refresh = gd_refresh, - .dpy_mouse_set = gd_mouse_set, - .dpy_cursor_define = gd_cursor_define, -}; +static void gd_set_keycode_type(GtkDisplayState *s) +{ +#ifndef _WIN32 + char *keycodes = NULL; + GdkDisplay *display = gtk_widget_get_display(s->window); + Display *x11_display = gdk_x11_display_get_xdisplay(display); + XkbDescPtr desc = XkbGetKeyboard(x11_display, XkbGBN_AllComponentsMask, + XkbUseCoreKbd); + + if (desc && desc->names) { + keycodes = XGetAtomName(x11_display, desc->names->keycodes); + } + if (keycodes == NULL) { + fprintf(stderr, "could not lookup keycode name\n"); + } else if (strstart(keycodes, "evdev", NULL)) { + s->has_evdev = true; + } else if (!strstart(keycodes, "xfree86", NULL)) { + fprintf(stderr, "unknown keycodes `%s', please report to " + "qemu-devel@nongnu.org\n", keycodes); + } +#endif +} void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) { @@ -1461,9 +1821,6 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) gtk_init(NULL, NULL); - s->dcl.ops = &dcl_ops; - s->dcl.con = qemu_console_lookup_by_index(0); - s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); #if GTK_CHECK_VERSION(3, 2, 0) s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); @@ -1471,11 +1828,8 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) s->vbox = gtk_vbox_new(FALSE, 0); #endif s->notebook = gtk_notebook_new(); - s->drawing_area = gtk_drawing_area_new(); s->menu_bar = gtk_menu_bar_new(); - s->scale_x = 1.0; - s->scale_y = 1.0; s->free_scale = FALSE; setlocale(LC_ALL, ""); @@ -1488,8 +1842,6 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier); qemu_add_vm_change_state_handler(gd_change_runstate, s); - gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), s->drawing_area, gtk_label_new("VGA")); - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg"); if (filename) { GError *error = NULL; @@ -1506,18 +1858,6 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) gd_connect_signals(s); - gtk_widget_add_events(s->drawing_area, - GDK_POINTER_MOTION_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_BUTTON_MOTION_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK | - GDK_SCROLL_MASK | - GDK_KEY_PRESS_MASK); - gtk_widget_set_double_buffered(s->drawing_area, FALSE); - gtk_widget_set_can_focus(s->drawing_area, TRUE); - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE); @@ -1530,6 +1870,21 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) gtk_widget_show_all(s->window); +#ifdef VTE_RESIZE_HACK + { + VirtualConsole *cur = gd_vc_find_current(s); + int i; + + for (i = 0; i < s->nb_vcs; i++) { + VirtualConsole *vc = &s->vc[i]; + if (vc && vc->type == GD_VC_VTE && vc != cur) { + gtk_widget_hide(vc->vte.terminal); + } + } + gd_update_windowsize(cur); + } +#endif + if (full_screen) { gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item)); } @@ -1537,7 +1892,12 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item)); } - register_displaychangelistener(&s->dcl); + gd_set_keycode_type(s); +} - global_state = s; +void early_gtk_display_init(void) +{ +#if defined(CONFIG_VTE) + register_vc_handler(gd_vc_handler); +#endif } diff --git a/ui/input-keymap.c b/ui/input-keymap.c index 6da4495103..5d299353a8 100644 --- a/ui/input-keymap.c +++ b/ui/input-keymap.c @@ -13,6 +13,8 @@ static const int qcode_to_number[] = { [Q_KEY_CODE_CTRL] = 0x1d, [Q_KEY_CODE_CTRL_R] = 0x9d, + [Q_KEY_CODE_META_L] = 0xdb, + [Q_KEY_CODE_META_R] = 0xdc, [Q_KEY_CODE_MENU] = 0xdd, [Q_KEY_CODE_ESC] = 0x01, @@ -129,7 +131,7 @@ static const int qcode_to_number[] = { [Q_KEY_CODE_MAX] = 0, }; -static int number_to_qcode[0xff]; +static int number_to_qcode[0x100]; int qemu_input_key_value_to_number(const KeyValue *value) { @@ -141,7 +143,7 @@ int qemu_input_key_value_to_number(const KeyValue *value) } } -int qemu_input_key_value_to_qcode(const KeyValue *value) +int qemu_input_key_number_to_qcode(uint8_t nr) { static int first = true; @@ -155,11 +157,16 @@ int qemu_input_key_value_to_qcode(const KeyValue *value) } } + return number_to_qcode[nr]; +} + +int qemu_input_key_value_to_qcode(const KeyValue *value) +{ if (value->kind == KEY_VALUE_KIND_QCODE) { return value->qcode; } else { assert(value->kind == KEY_VALUE_KIND_NUMBER); - return number_to_qcode[value->number]; + return qemu_input_key_number_to_qcode(value->number); } } diff --git a/ui/input-legacy.c b/ui/input-legacy.c index 2a538607a2..3025f50f49 100644 --- a/ui/input-legacy.c +++ b/ui/input-legacy.c @@ -74,27 +74,6 @@ int index_from_key(const char *key) return i; } -static KeyValue **keyvalues; -static int keyvalues_size; -static QEMUTimer *key_timer; - -static void free_keyvalues(void) -{ - g_free(keyvalues); - keyvalues = NULL; - keyvalues_size = 0; -} - -static void release_keys(void *opaque) -{ - while (keyvalues_size > 0) { - qemu_input_event_send_key(NULL, keyvalues[--keyvalues_size], - false); - } - - free_keyvalues(); -} - static KeyValue *copy_key_value(KeyValue *src) { KeyValue *dst = g_new(KeyValue, 1); @@ -107,30 +86,18 @@ void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time, { KeyValueList *p; - if (!key_timer) { - key_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, release_keys, NULL); - } - - if (keyvalues != NULL) { - timer_del(key_timer); - release_keys(NULL); - } - if (!has_hold_time) { - hold_time = 100; + hold_time = 0; /* use default */ } for (p = keys; p != NULL; p = p->next) { qemu_input_event_send_key(NULL, copy_key_value(p->value), true); - - keyvalues = g_realloc(keyvalues, sizeof(KeyValue *) * - (keyvalues_size + 1)); - keyvalues[keyvalues_size++] = copy_key_value(p->value); + qemu_input_event_send_key_delay(hold_time); + } + for (p = keys; p != NULL; p = p->next) { + qemu_input_event_send_key(NULL, copy_key_value(p->value), false); + qemu_input_event_send_key_delay(hold_time); } - - /* delayed key up events */ - timer_mod(key_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(get_ticks_per_sec(), hold_time, 1000)); } static void legacy_kbd_event(DeviceState *dev, QemuConsole *src, diff --git a/ui/input.c b/ui/input.c index fc91fba83c..89d9db78c0 100644 --- a/ui/input.c +++ b/ui/input.c @@ -1,3 +1,4 @@ +#include "hw/qdev.h" #include "sysemu/sysemu.h" #include "qapi-types.h" #include "qmp-commands.h" @@ -10,13 +11,34 @@ struct QemuInputHandlerState { QemuInputHandler *handler; int id; int events; + QemuConsole *con; QTAILQ_ENTRY(QemuInputHandlerState) node; }; + +typedef struct QemuInputEventQueue QemuInputEventQueue; +struct QemuInputEventQueue { + enum { + QEMU_INPUT_QUEUE_DELAY = 1, + QEMU_INPUT_QUEUE_EVENT, + QEMU_INPUT_QUEUE_SYNC, + } type; + QEMUTimer *timer; + uint32_t delay_ms; + QemuConsole *src; + InputEvent *evt; + QTAILQ_ENTRY(QemuInputEventQueue) node; +}; + static QTAILQ_HEAD(, QemuInputHandlerState) handlers = QTAILQ_HEAD_INITIALIZER(handlers); static NotifierList mouse_mode_notifiers = NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers); +static QTAILQ_HEAD(QemuInputEventQueueHead, QemuInputEventQueue) kbd_queue = + QTAILQ_HEAD_INITIALIZER(kbd_queue); +static QEMUTimer *kbd_timer; +static uint32_t kbd_default_delay_ms = 10; + QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev, QemuInputHandler *handler) { @@ -53,12 +75,46 @@ void qemu_input_handler_unregister(QemuInputHandlerState *s) qemu_input_check_mode_change(); } +void qemu_input_handler_bind(QemuInputHandlerState *s, + const char *device_id, int head, + Error **errp) +{ + DeviceState *dev; + QemuConsole *con; + + dev = qdev_find_recursive(sysbus_get_default(), device_id); + if (dev == NULL) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device_id); + return; + } + + con = qemu_console_lookup_by_device(dev, head); + if (con == NULL) { + error_setg(errp, "Device %s is not bound to a QemuConsole", device_id); + return; + } + + s->con = con; +} + static QemuInputHandlerState* -qemu_input_find_handler(uint32_t mask) +qemu_input_find_handler(uint32_t mask, QemuConsole *con) { QemuInputHandlerState *s; QTAILQ_FOREACH(s, &handlers, node) { + if (s->con == NULL || s->con != con) { + continue; + } + if (mask & s->handler->mask) { + return s; + } + } + + QTAILQ_FOREACH(s, &handlers, node) { + if (s->con != NULL) { + continue; + } if (mask & s->handler->mask) { return s; } @@ -94,7 +150,7 @@ static void qemu_input_transform_abs_rotate(InputEvent *evt) static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) { const char *name; - int idx = -1; + int qcode, idx = -1; if (src) { idx = qemu_console_get_index(src); @@ -103,8 +159,10 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) case INPUT_EVENT_KIND_KEY: switch (evt->key->key->kind) { case KEY_VALUE_KIND_NUMBER: + qcode = qemu_input_key_number_to_qcode(evt->key->key->number); + name = QKeyCode_lookup[qcode]; trace_input_event_key_number(idx, evt->key->key->number, - evt->key->down); + name, evt->key->down); break; case KEY_VALUE_KIND_QCODE: name = QKeyCode_lookup[evt->key->key->qcode]; @@ -133,6 +191,73 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) } } +static void qemu_input_queue_process(void *opaque) +{ + struct QemuInputEventQueueHead *queue = opaque; + QemuInputEventQueue *item; + + g_assert(!QTAILQ_EMPTY(queue)); + item = QTAILQ_FIRST(queue); + g_assert(item->type == QEMU_INPUT_QUEUE_DELAY); + QTAILQ_REMOVE(queue, item, node); + g_free(item); + + while (!QTAILQ_EMPTY(queue)) { + item = QTAILQ_FIRST(queue); + switch (item->type) { + case QEMU_INPUT_QUEUE_DELAY: + timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + item->delay_ms); + return; + case QEMU_INPUT_QUEUE_EVENT: + qemu_input_event_send(item->src, item->evt); + qapi_free_InputEvent(item->evt); + break; + case QEMU_INPUT_QUEUE_SYNC: + qemu_input_event_sync(); + break; + } + QTAILQ_REMOVE(queue, item, node); + g_free(item); + } +} + +static void qemu_input_queue_delay(struct QemuInputEventQueueHead *queue, + QEMUTimer *timer, uint32_t delay_ms) +{ + QemuInputEventQueue *item = g_new0(QemuInputEventQueue, 1); + bool start_timer = QTAILQ_EMPTY(queue); + + item->type = QEMU_INPUT_QUEUE_DELAY; + item->delay_ms = delay_ms; + item->timer = timer; + QTAILQ_INSERT_TAIL(queue, item, node); + + if (start_timer) { + timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + item->delay_ms); + } +} + +static void qemu_input_queue_event(struct QemuInputEventQueueHead *queue, + QemuConsole *src, InputEvent *evt) +{ + QemuInputEventQueue *item = g_new0(QemuInputEventQueue, 1); + + item->type = QEMU_INPUT_QUEUE_EVENT; + item->src = src; + item->evt = evt; + QTAILQ_INSERT_TAIL(queue, item, node); +} + +static void qemu_input_queue_sync(struct QemuInputEventQueueHead *queue) +{ + QemuInputEventQueue *item = g_new0(QemuInputEventQueue, 1); + + item->type = QEMU_INPUT_QUEUE_SYNC; + QTAILQ_INSERT_TAIL(queue, item, node); +} + void qemu_input_event_send(QemuConsole *src, InputEvent *evt) { QemuInputHandlerState *s; @@ -149,7 +274,7 @@ void qemu_input_event_send(QemuConsole *src, InputEvent *evt) } /* send event */ - s = qemu_input_find_handler(1 << evt->kind); + s = qemu_input_find_handler(1 << evt->kind, src); if (!s) { return; } @@ -192,9 +317,14 @@ void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down) { InputEvent *evt; evt = qemu_input_event_new_key(key, down); - qemu_input_event_send(src, evt); - qemu_input_event_sync(); - qapi_free_InputEvent(evt); + if (QTAILQ_EMPTY(&kbd_queue)) { + qemu_input_event_send(src, evt); + qemu_input_event_sync(); + qapi_free_InputEvent(evt); + } else { + qemu_input_queue_event(&kbd_queue, src, evt); + qemu_input_queue_sync(&kbd_queue); + } } void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down) @@ -213,6 +343,16 @@ void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down) qemu_input_event_send_key(src, key, down); } +void qemu_input_event_send_key_delay(uint32_t delay_ms) +{ + if (!kbd_timer) { + kbd_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, qemu_input_queue_process, + &kbd_queue); + } + qemu_input_queue_delay(&kbd_queue, kbd_timer, + delay_ms ? delay_ms : kbd_default_delay_ms); +} + InputEvent *qemu_input_event_new_btn(InputButton btn, bool down) { InputEvent *evt = g_new0(InputEvent, 1); @@ -250,7 +390,8 @@ bool qemu_input_is_absolute(void) { QemuInputHandlerState *s; - s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS); + s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS, + NULL); return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS); } diff --git a/ui/sdl2.c b/ui/sdl2.c index 361de619fa..fcac87b4be 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -49,6 +49,7 @@ static struct sdl2_state { int idx; int last_vm_running; /* per console for caption reasons */ int x, y; + int hidden; } *sdl2_console; static SDL_Surface *guest_sprite_surface; @@ -136,6 +137,9 @@ static void do_sdl_resize(struct sdl2_state *scon, int width, int height, } else { flags |= SDL_WINDOW_RESIZABLE; } + if (scon->hidden) { + flags |= SDL_WINDOW_HIDDEN; + } scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, @@ -190,30 +194,50 @@ static void sdl_switch(DisplayChangeListener *dcl, } } -static void reset_keys(void) +static void reset_keys(struct sdl2_state *scon) { + QemuConsole *con = scon ? scon->dcl.con : NULL; int i; for (i = 0; i < 256; i++) { if (modifiers_state[i]) { int qcode = sdl2_scancode_to_qcode[i]; - qemu_input_event_send_key_qcode(NULL, qcode, false); + qemu_input_event_send_key_qcode(con, qcode, false); modifiers_state[i] = 0; } } } -static void sdl_process_key(SDL_KeyboardEvent *ev) +static void sdl_process_key(struct sdl2_state *scon, + SDL_KeyboardEvent *ev) { int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode]; + QemuConsole *con = scon ? scon->dcl.con : NULL; + + if (!qemu_console_is_graphic(con)) { + if (ev->type == SDL_KEYDOWN) { + switch (ev->keysym.scancode) { + case SDL_SCANCODE_RETURN: + kbd_put_keysym_console(con, '\n'); + break; + case SDL_SCANCODE_BACKSPACE: + kbd_put_keysym_console(con, QEMU_KEY_BACKSPACE); + break; + default: + kbd_put_qcode_console(con, qcode); + break; + } + } + return; + } switch (ev->keysym.scancode) { #if 0 case SDL_SCANCODE_NUMLOCKCLEAR: case SDL_SCANCODE_CAPSLOCK: /* SDL does not send the key up event, so we generate it */ - qemu_input_event_send_key_qcode(NULL, qcode, true); - qemu_input_event_send_key_qcode(NULL, qcode, false); + qemu_input_event_send_key_qcode(con, qcode, true); + qemu_input_event_send_key_qcode(con, qcode, false); return; #endif case SDL_SCANCODE_LCTRL: @@ -231,7 +255,7 @@ static void sdl_process_key(SDL_KeyboardEvent *ev) } /* fall though */ default: - qemu_input_event_send_key_qcode(NULL, qcode, + qemu_input_event_send_key_qcode(con, qcode, ev->type == SDL_KEYDOWN); } } @@ -302,6 +326,11 @@ static void sdl_show_cursor(void) static void sdl_grab_start(struct sdl2_state *scon) { + QemuConsole *con = scon ? scon->dcl.con : NULL; + + if (!con || !qemu_console_is_graphic(con)) { + return; + } /* * If the application is not active, do not try to enter grab state. This * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the @@ -455,7 +484,7 @@ static void toggle_full_screen(struct sdl2_state *scon) static void handle_keydown(SDL_Event *ev) { - int mod_state; + int mod_state, win; struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); if (alt_grab) { @@ -470,6 +499,27 @@ static void handle_keydown(SDL_Event *ev) if (gui_key_modifier_pressed) { switch (ev->key.keysym.scancode) { + case SDL_SCANCODE_2: + case SDL_SCANCODE_3: + case SDL_SCANCODE_4: + case SDL_SCANCODE_5: + case SDL_SCANCODE_6: + case SDL_SCANCODE_7: + case SDL_SCANCODE_8: + case SDL_SCANCODE_9: + win = ev->key.keysym.scancode - SDL_SCANCODE_1; + if (win < sdl2_num_outputs) { + sdl2_console[win].hidden = !sdl2_console[win].hidden; + if (sdl2_console[win].real_window) { + if (sdl2_console[win].hidden) { + SDL_HideWindow(sdl2_console[win].real_window); + } else { + SDL_ShowWindow(sdl2_console[win].real_window); + } + } + gui_keysym = 1; + } + break; case SDL_SCANCODE_F: toggle_full_screen(scon); gui_keysym = 1; @@ -506,7 +556,7 @@ static void handle_keydown(SDL_Event *ev) } } if (!gui_keysym) { - sdl_process_key(&ev->key); + sdl_process_key(scon, &ev->key); } } @@ -531,16 +581,27 @@ static void handle_keyup(SDL_Event *ev) } /* SDL does not send back all the modifiers key, so we must * correct it. */ - reset_keys(); + reset_keys(scon); return; } gui_keysym = 0; } if (!gui_keysym) { - sdl_process_key(&ev->key); + sdl_process_key(scon, &ev->key); } } +static void handle_textinput(SDL_Event *ev) +{ + struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); + QemuConsole *con = scon ? scon->dcl.con : NULL; + + if (qemu_console_is_graphic(con)) { + return; + } + kbd_put_string_console(con, ev->text.text, strlen(ev->text.text)); +} + static void handle_mousemotion(SDL_Event *ev) { int max_x, max_y; @@ -677,6 +738,9 @@ static void sdl_refresh(DisplayChangeListener *dcl) case SDL_KEYUP: handle_keyup(ev); break; + case SDL_TEXTINPUT: + handle_textinput(ev); + break; case SDL_QUIT: if (!no_quit) { no_shutdown = 0; @@ -805,7 +869,7 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) for (i = 0;; i++) { QemuConsole *con = qemu_console_lookup_by_index(i); - if (!con || !qemu_console_is_graphic(con)) { + if (!con) { break; } } @@ -813,6 +877,9 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs); for (i = 0; i < sdl2_num_outputs; i++) { QemuConsole *con = qemu_console_lookup_by_index(i); + if (!qemu_console_is_graphic(con)) { + sdl2_console[i].hidden = true; + } sdl2_console[i].dcl.ops = &dcl_ops; sdl2_console[i].dcl.con = con; register_displaychangelistener(&sdl2_console[i].dcl); diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index 59b59c0c79..f02352cc46 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -181,6 +181,10 @@ tight_detect_smooth_image24(VncState *vs, int w, int h) } } + if (pixels == 0) { + return 0; + } + /* 95% smooth or more ... */ if (stats[0] * 33 / pixels >= 95) { return 0; @@ -267,7 +271,9 @@ tight_detect_smooth_image24(VncState *vs, int w, int h) y += w; \ } \ } \ - \ + if (pixels == 0) { \ + return 0; \ + } \ if ((stats[0] + stats[1]) * 100 / pixels >= 90) { \ return 0; \ } \ diff --git a/ui/vnc.c b/ui/vnc.c index 2d7def9aa2..1684206184 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -26,6 +26,7 @@ #include "vnc.h" #include "vnc-jobs.h" +#include "trace.h" #include "sysemu/sysemu.h" #include "qemu/sockets.h" #include "qemu/timer.h" @@ -1552,7 +1553,9 @@ static void press_key(VncState *vs, int keysym) { int keycode = keysym2scancode(vs->vd->kbd_layout, keysym) & SCANCODE_KEYMASK; qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, true); + qemu_input_event_send_key_delay(0); qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false); + qemu_input_event_send_key_delay(0); } static int current_led_state(VncState *vs) @@ -1597,6 +1600,10 @@ static void kbd_leds(void *opaque, int ledstate) int caps, num, scr; bool has_changed = (ledstate != current_led_state(vs)); + trace_vnc_key_guest_leds((ledstate & QEMU_CAPS_LOCK_LED), + (ledstate & QEMU_NUM_LOCK_LED), + (ledstate & QEMU_SCROLL_LOCK_LED)); + caps = ledstate & QEMU_CAPS_LOCK_LED ? 1 : 0; num = ledstate & QEMU_NUM_LOCK_LED ? 1 : 0; scr = ledstate & QEMU_SCROLL_LOCK_LED ? 1 : 0; @@ -1659,11 +1666,13 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) */ if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) { if (!vs->modifiers_state[0x45]) { + trace_vnc_key_sync_numlock(true); vs->modifiers_state[0x45] = 1; press_key(vs, 0xff7f); } } else { if (vs->modifiers_state[0x45]) { + trace_vnc_key_sync_numlock(false); vs->modifiers_state[0x45] = 0; press_key(vs, 0xff7f); } @@ -1682,11 +1691,13 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) int capslock = !!(vs->modifiers_state[0x3a]); if (capslock) { if (uppercase == shift) { + trace_vnc_key_sync_capslock(false); vs->modifiers_state[0x3a] = 0; press_key(vs, 0xffe5); } } else { if (uppercase != shift) { + trace_vnc_key_sync_capslock(true); vs->modifiers_state[0x3a] = 1; press_key(vs, 0xffe5); } @@ -1819,6 +1830,11 @@ static void vnc_release_modifiers(VncState *vs) } } +static const char *code2name(int keycode) +{ + return QKeyCode_lookup[qemu_input_key_number_to_qcode(keycode)]; +} + static void key_event(VncState *vs, int down, uint32_t sym) { int keycode; @@ -1829,6 +1845,7 @@ static void key_event(VncState *vs, int down, uint32_t sym) } keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF) & SCANCODE_KEYMASK; + trace_vnc_key_event_map(down, sym, keycode, code2name(keycode)); do_key_event(vs, down, keycode, sym); } @@ -1836,10 +1853,12 @@ static void ext_key_event(VncState *vs, int down, uint32_t sym, uint16_t keycode) { /* if the user specifies a keyboard layout, always use it */ - if (keyboard_layout) + if (keyboard_layout) { key_event(vs, down, sym); - else + } else { + trace_vnc_key_event_ext(down, sym, keycode, code2name(keycode)); do_key_event(vs, down, keycode, sym); + } } static void framebuffer_update_request(VncState *vs, int incremental, @@ -2929,10 +2948,12 @@ void vnc_display_init(DisplayState *ds) QTAILQ_INIT(&vs->clients); vs->expires = TIME_MAX; - if (keyboard_layout) + if (keyboard_layout) { + trace_vnc_key_map_init(keyboard_layout); vs->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout); - else + } else { vs->kbd_layout = init_keyboard_layout(name2keysym, "en-us"); + } if (!vs->kbd_layout) exit(1); @@ -2976,26 +2997,6 @@ static void vnc_display_close(DisplayState *ds) #endif } -static int vnc_display_disable_login(DisplayState *ds) -{ - VncDisplay *vs = vnc_display; - - if (!vs) { - return -1; - } - - if (vs->password) { - g_free(vs->password); - } - - vs->password = NULL; - if (vs->auth == VNC_AUTH_NONE) { - vs->auth = VNC_AUTH_VNC; - } - - return 0; -} - int vnc_display_password(DisplayState *ds, const char *password) { VncDisplay *vs = vnc_display; @@ -3003,20 +3004,18 @@ int vnc_display_password(DisplayState *ds, const char *password) if (!vs) { return -EINVAL; } - - if (!password) { - /* This is not the intention of this interface but err on the side - of being safe */ - return vnc_display_disable_login(ds); + if (vs->auth == VNC_AUTH_NONE) { + error_printf_unless_qmp("If you want use passwords please enable " + "password auth using '-vnc ${dpy},password'."); + return -EINVAL; } if (vs->password) { g_free(vs->password); vs->password = NULL; } - vs->password = g_strdup(password); - if (vs->auth == VNC_AUTH_NONE) { - vs->auth = VNC_AUTH_VNC; + if (password) { + vs->password = g_strdup(password); } return 0; |