#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-control.h"
+#include "qapi/qapi-commands-machine.h"
#include "qapi/qapi-commands-misc.h"
#include "qemu/cutils.h"
#include <math.h>
#include "trace.h"
+#include "qemu/cutils.h"
#include "ui/input.h"
#include "sysemu/runstate.h"
#include "sysemu/sysemu.h"
# define VTE_CHECK_VERSION(a, b, c) 0
#endif
-/* Some older mingw versions lack this constant or have
- * it conditionally defined */
-#ifdef _WIN32
-# ifndef MAPVK_VK_TO_VSC
-# define MAPVK_VK_TO_VSC 0
-# endif
-#endif
-
-
#define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK)
static const guint16 *keycode_map;
bool external_pause_update;
- bool ignore_keys;
-
DisplayOptions *opts;
};
-typedef struct VCChardev {
+struct VCChardev {
Chardev parent;
VirtualConsole *console;
bool echo;
-} VCChardev;
+};
+typedef struct VCChardev VCChardev;
#define TYPE_CHARDEV_VC "chardev-vc"
-#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
+DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
+ TYPE_CHARDEV_VC)
bool gtk_use_gl_area;
int ww, wh;
ww = gdk_window_get_width(gtk_widget_get_window(area));
wh = gdk_window_get_height(gtk_widget_get_window(area));
-#if defined(CONFIG_GTK_GL)
+#if defined(CONFIG_OPENGL)
if (vc->gfx.gls && gtk_use_gl_area) {
gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
return;
static GdkDevice *gd_get_pointer(GdkDisplay *dpy)
{
-#if GTK_CHECK_VERSION(3, 20, 0)
return gdk_seat_get_pointer(gdk_display_get_default_seat(dpy));
-#else
- return gdk_device_manager_get_client_pointer(
- gdk_display_get_device_manager(dpy));
-#endif
}
static void gd_mouse_set(DisplayChangeListener *dcl,
}
vc->gfx.ds = surface;
- if (!surface) {
- return;
- }
-
if (surface->format == PIXMAN_x8r8g8b8) {
/*
* PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24
#if defined(CONFIG_OPENGL)
-/** DisplayState Callbacks (opengl version) **/
+static bool gd_has_dmabuf(DisplayChangeListener *dcl)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
-#if defined(CONFIG_GTK_GL)
+ if (gtk_use_gl_area && !gtk_widget_get_realized(vc->gfx.drawing_area)) {
+ /* FIXME: Assume it will work, actual check done after realize */
+ /* fixing this would require delaying listener registration */
+ return true;
+ }
+
+ return vc->gfx.has_dmabuf;
+}
+
+/** DisplayState Callbacks (opengl version) **/
static const DisplayChangeListenerOps dcl_gl_area_ops = {
.dpy_name = "gtk-egl",
.dpy_gl_ctx_create = gd_gl_area_create_context,
.dpy_gl_ctx_destroy = gd_gl_area_destroy_context,
.dpy_gl_ctx_make_current = gd_gl_area_make_current,
- .dpy_gl_ctx_get_current = gd_gl_area_get_current_context,
.dpy_gl_scanout_texture = gd_gl_area_scanout_texture,
+ .dpy_gl_scanout_disable = gd_gl_area_scanout_disable,
.dpy_gl_update = gd_gl_area_scanout_flush,
+ .dpy_gl_scanout_dmabuf = gd_gl_area_scanout_dmabuf,
+ .dpy_has_dmabuf = gd_has_dmabuf,
};
-#endif /* CONFIG_GTK_GL */
+#ifdef CONFIG_X11
static const DisplayChangeListenerOps dcl_egl_ops = {
.dpy_name = "gtk-egl",
.dpy_gl_ctx_create = gd_egl_create_context,
.dpy_gl_ctx_destroy = qemu_egl_destroy_context,
.dpy_gl_ctx_make_current = gd_egl_make_current,
- .dpy_gl_ctx_get_current = qemu_egl_get_current_context,
.dpy_gl_scanout_disable = gd_egl_scanout_disable,
.dpy_gl_scanout_texture = gd_egl_scanout_texture,
.dpy_gl_scanout_dmabuf = gd_egl_scanout_dmabuf,
.dpy_gl_cursor_position = gd_egl_cursor_position,
.dpy_gl_release_dmabuf = gd_egl_release_dmabuf,
.dpy_gl_update = gd_egl_scanout_flush,
+ .dpy_has_dmabuf = gd_has_dmabuf,
};
+#endif
+
#endif /* CONFIG_OPENGL */
/** QEMU Events **/
dpy_set_ui_info(vc->gfx.dcl.con, &info);
}
-#if defined(CONFIG_GTK_GL)
+#if defined(CONFIG_OPENGL)
static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
void *opaque)
#endif
+/*
+ * If available, return the update interval of the monitor in ms,
+ * else return 0 (the default update interval).
+ */
+int gd_monitor_update_interval(GtkWidget *widget)
+{
+#ifdef GDK_VERSION_3_22
+ GdkWindow *win = gtk_widget_get_window(widget);
+
+ if (win) {
+ GdkDisplay *dpy = gtk_widget_get_display(widget);
+ GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
+ int refresh_rate = gdk_monitor_get_refresh_rate(monitor); /* [mHz] */
+
+ if (refresh_rate) {
+ /* T = 1 / f = 1 [s*Hz] / f = 1000*1000 [ms*mHz] / f */
+ return MIN(1000 * 1000 / refresh_rate,
+ GUI_REFRESH_INTERVAL_DEFAULT);
+ }
+ }
+#endif
+ return 0;
+}
+
static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
{
VirtualConsole *vc = opaque;
/* invoke render callback please */
return FALSE;
} else {
+#ifdef CONFIG_X11
gd_egl_draw(vc);
return TRUE;
+#else
+ abort();
+#endif
}
}
#endif
return FALSE;
}
+ vc->gfx.dcl.update_interval =
+ gd_monitor_update_interval(vc->window ? vc->window : s->window);
+
fbw = surface_width(vc->gfx.ds);
fbh = surface_height(vc->gfx.ds);
if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
+ GdkDisplay *dpy = gtk_widget_get_display(widget);
+ GdkWindow *win = gtk_widget_get_window(widget);
+ GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
+ GdkRectangle geometry;
int screen_width, screen_height;
int x = (int)motion->x_root;
int y = (int)motion->y_root;
-#if GTK_CHECK_VERSION(3, 22, 0)
- {
- GdkDisplay *dpy = gtk_widget_get_display(widget);
- GdkWindow *win = gtk_widget_get_window(widget);
- GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
- GdkRectangle geometry;
- gdk_monitor_get_geometry(monitor, &geometry);
- screen_width = geometry.width;
- screen_height = geometry.height;
- }
-#else
- {
- screen_width = gdk_screen_get_width(screen);
- screen_height = gdk_screen_get_height(screen);
- }
-#endif
+ gdk_monitor_get_geometry(monitor, &geometry);
+ screen_width = geometry.width;
+ screen_height = geometry.height;
/* In relative mode check to see if client pointer hit
* one of the screen edges, and if so move it back by
#ifdef GDK_WINDOWING_WIN32
if (GDK_IS_WIN32_DISPLAY(dpy)) {
trace_gd_keymap_windowing("win32");
- *maplen = qemu_input_map_win32_to_qcode_len;
- return qemu_input_map_win32_to_qcode;
+ *maplen = qemu_input_map_atset1_to_qcode_len;
+ return qemu_input_map_atset1_to_qcode;
}
#endif
return keycode_map[scancode];
}
+static int gd_get_keycode(GdkEventKey *key)
+{
+#ifdef G_OS_WIN32
+ int scancode = gdk_event_get_scancode((GdkEvent *)key);
+
+ /* translate Windows native scancodes to atset1 keycodes */
+ switch (scancode & (KF_EXTENDED | 0xff)) {
+ case 0x145: /* NUMLOCK */
+ return scancode & 0xff;
+ }
+
+ return scancode & KF_EXTENDED ?
+ 0xe000 | (scancode & 0xff) : scancode & 0xff;
+
+#else
+ return key->hardware_keycode;
+#endif
+}
+
static gboolean gd_text_key_down(GtkWidget *widget,
GdkEventKey *key, void *opaque)
{
} else if (key->length) {
kbd_put_string_console(con, key->string, key->length);
} else {
- int qcode = gd_map_keycode(key->hardware_keycode);
+ int qcode = gd_map_keycode(gd_get_keycode(key));
kbd_put_qcode_console(con, qcode, false);
}
return TRUE;
static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
{
VirtualConsole *vc = opaque;
- GtkDisplayState *s = vc->s;
- int qcode;
-
- if (s->ignore_keys) {
- s->ignore_keys = (key->type == GDK_KEY_PRESS);
- return TRUE;
- }
+ int keycode, qcode;
-#ifdef WIN32
+#ifdef G_OS_WIN32
/* on windows, we ought to ignore the reserved key event? */
if (key->hardware_keycode == 0xff)
return false;
+
+ if (!vc->s->kbd_owner) {
+ if (key->hardware_keycode == VK_LWIN ||
+ key->hardware_keycode == VK_RWIN) {
+ return FALSE;
+ }
+ }
#endif
if (key->keyval == GDK_KEY_Pause
return TRUE;
}
- qcode = gd_map_keycode(key->hardware_keycode);
+ keycode = gd_get_keycode(key);
+ qcode = gd_map_keycode(keycode);
- trace_gd_key_event(vc->label, key->hardware_keycode, qcode,
+ trace_gd_key_event(vc->label, keycode, qcode,
(key->type == GDK_KEY_PRESS) ? "down" : "up");
qkbd_state_key_event(vc->gfx.kbd, qcode,
return TRUE;
}
+static gboolean gd_grab_broken_event(GtkWidget *widget,
+ GdkEventGrabBroken *event, void *opaque)
+{
+#ifdef CONFIG_WIN32
+ /*
+ * On Windows the Ctrl-Alt-Del key combination can't be grabbed. This
+ * key combination leaves all three keys in a stuck condition. We use
+ * the grab-broken-event to release all keys.
+ */
+ if (event->keyboard) {
+ VirtualConsole *vc = opaque;
+ GtkDisplayState *s = vc->s;
+
+ gtk_release_modifiers(s);
+ }
+#endif
+ return TRUE;
+}
+
static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
{
if (event->type == GDK_MOTION_NOTIFY) {
gtk_notebook_set_current_page(nb, page);
gtk_widget_grab_focus(vc->focus);
}
- s->ignore_keys = false;
}
static void gd_accel_switch_vc(void *opaque)
gd_update_full_redraw(vc);
}
-#if GTK_CHECK_VERSION(3, 20, 0)
static void gd_grab_update(VirtualConsole *vc, bool kbd, bool ptr)
{
GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
gdk_seat_ungrab(seat);
}
}
-#else
-static void gd_grab_devices(VirtualConsole *vc, bool grab,
- GdkInputSource source, GdkEventMask mask,
- GdkCursor *cursor)
-{
- GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
- GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
- GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
- GList *tmp = devs;
-
- for (tmp = devs; tmp; tmp = tmp->next) {
- GdkDevice *dev = tmp->data;
- if (gdk_device_get_source(dev) != source) {
- continue;
- }
- if (grab) {
- GdkWindow *win = gtk_widget_get_window(vc->gfx.drawing_area);
- gdk_device_grab(dev, win, GDK_OWNERSHIP_NONE, FALSE,
- mask, cursor, GDK_CURRENT_TIME);
- } else {
- gdk_device_ungrab(dev, GDK_CURRENT_TIME);
- }
- }
- g_list_free(devs);
-}
-#endif
static void gd_grab_keyboard(VirtualConsole *vc, const char *reason)
{
}
win32_kbd_set_grab(true);
-#if GTK_CHECK_VERSION(3, 20, 0)
gd_grab_update(vc, true, vc->s->ptr_owner == vc);
-#else
- gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD,
- GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
- NULL);
-#endif
vc->s->kbd_owner = vc;
gd_update_caption(vc->s);
trace_gd_grab(vc->label, "kbd", reason);
s->kbd_owner = NULL;
win32_kbd_set_grab(false);
-#if GTK_CHECK_VERSION(3, 20, 0)
gd_grab_update(vc, false, vc->s->ptr_owner == vc);
-#else
- gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL);
-#endif
gd_update_caption(s);
trace_gd_ungrab(vc->label, "kbd");
}
}
}
-#if GTK_CHECK_VERSION(3, 20, 0)
gd_grab_update(vc, vc->s->kbd_owner == vc, true);
gdk_device_get_position(gd_get_pointer(display),
NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
-#else
- gd_grab_devices(vc, true, GDK_SOURCE_MOUSE,
- GDK_POINTER_MOTION_MASK |
- GDK_BUTTON_PRESS_MASK |
- GDK_BUTTON_RELEASE_MASK |
- GDK_BUTTON_MOTION_MASK |
- GDK_SCROLL_MASK,
- vc->s->null_cursor);
- gdk_device_get_position(gd_get_pointer(display),
- NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
-#endif
vc->s->ptr_owner = vc;
gd_update_caption(vc->s);
trace_gd_grab(vc->label, "ptr", reason);
s->ptr_owner = NULL;
display = gtk_widget_get_display(vc->gfx.drawing_area);
-#if GTK_CHECK_VERSION(3, 20, 0)
gd_grab_update(vc, vc->s->kbd_owner == vc, false);
gdk_device_warp(gd_get_pointer(display),
gtk_widget_get_screen(vc->gfx.drawing_area),
vc->s->grab_x_root, vc->s->grab_y_root);
-#else
- gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL);
- gdk_device_warp(gd_get_pointer(display),
- gtk_widget_get_screen(vc->gfx.drawing_area),
- vc->s->grab_x_root, vc->s->grab_y_root);
-#endif
gd_update_caption(s);
trace_gd_ungrab(vc->label, "ptr");
}
}
}
- qemu_chr_be_write(vc->vte.chr, (uint8_t *)text, (unsigned int)size);
+ int remaining = size;
+ uint8_t* p = (uint8_t *)text;
+ while (remaining > 0) {
+ int can_write = qemu_chr_be_can_write(vc->vte.chr);
+ int written = MIN(remaining, can_write);
+ qemu_chr_be_write(vc->vte.chr, p, written);
+
+ remaining -= written;
+ p += written;
+ }
return TRUE;
}
{
g_signal_connect(vc->gfx.drawing_area, "draw",
G_CALLBACK(gd_draw_event), vc);
-#if defined(CONFIG_GTK_GL)
+#if defined(CONFIG_OPENGL)
if (gtk_use_gl_area) {
/* wire up GtkGlArea events */
g_signal_connect(vc->gfx.drawing_area, "render",
G_CALLBACK(gd_focus_out_event), vc);
g_signal_connect(vc->gfx.drawing_area, "configure-event",
G_CALLBACK(gd_configure), vc);
+ g_signal_connect(vc->gfx.drawing_area, "grab-broken-event",
+ G_CALLBACK(gd_grab_broken_event), vc);
} else {
g_signal_connect(vc->gfx.drawing_area, "key-press-event",
G_CALLBACK(gd_text_key_down), vc);
return machine_menu;
}
-/*
- * If available, return the refresh rate of the display in milli-Hertz,
- * else return 0.
- */
-static int gd_refresh_rate_millihz(GtkWidget *window)
+#if defined(CONFIG_OPENGL)
+static void gl_area_realize(GtkGLArea *area, VirtualConsole *vc)
{
-#ifdef GDK_VERSION_3_22
- GdkWindow *win = gtk_widget_get_window(window);
-
- if (win) {
- GdkDisplay *dpy = gtk_widget_get_display(window);
- GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
-
- return gdk_monitor_get_refresh_rate(monitor);
+ gtk_gl_area_make_current(area);
+ qemu_egl_display = eglGetCurrentDisplay();
+ vc->gfx.has_dmabuf = qemu_egl_has_dmabuf();
+ if (!vc->gfx.has_dmabuf) {
+ error_report("GtkGLArea console lacks DMABUF support.");
}
-#endif
- return 0;
}
+#endif
static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
QemuConsole *con, int idx,
GSList *group, GtkWidget *view_menu)
{
bool zoom_to_fit = false;
- int refresh_rate_millihz;
vc->label = qemu_console_get_label(con);
vc->s = s;
#if defined(CONFIG_OPENGL)
if (display_opengl) {
-#if defined(CONFIG_GTK_GL)
if (gtk_use_gl_area) {
vc->gfx.drawing_area = gtk_gl_area_new();
+ g_signal_connect(vc->gfx.drawing_area, "realize",
+ G_CALLBACK(gl_area_realize), vc);
vc->gfx.dcl.ops = &dcl_gl_area_ops;
- } else
-#endif /* CONFIG_GTK_GL */
- {
+ } else {
+#ifdef CONFIG_X11
vc->gfx.drawing_area = gtk_drawing_area_new();
/*
* gtk_widget_set_double_buffered() was deprecated in 3.14.
* proper replacement (native opengl support) is only
* available in 3.16+. Silence the warning if possible.
*/
-#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#endif
gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
-#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
#pragma GCC diagnostic pop
-#endif
vc->gfx.dcl.ops = &dcl_egl_ops;
+ vc->gfx.has_dmabuf = qemu_egl_has_dmabuf();
+#else
+ abort();
+#endif
}
} else
#endif
vc->gfx.kbd = qkbd_state_init(con);
vc->gfx.dcl.con = con;
- refresh_rate_millihz = gd_refresh_rate_millihz(vc->window ?
- vc->window : s->window);
- if (refresh_rate_millihz) {
- vc->gfx.dcl.update_interval = MILLISEC_PER_SEC / refresh_rate_millihz;
- }
-
register_displaychangelistener(&vc->gfx.dcl);
gd_connect_vc_gfx_signals(vc);
GtkDisplayState *s = g_malloc0(sizeof(*s));
GdkDisplay *window_display;
GtkIconTheme *theme;
+ char *dir;
if (!gtkinit) {
fprintf(stderr, "gtk initialization failed\n");
s->opts = opts;
theme = gtk_icon_theme_get_default();
- gtk_icon_theme_prepend_search_path(theme, CONFIG_QEMU_ICONDIR);
+ dir = get_relocated_path(CONFIG_QEMU_ICONDIR);
+ gtk_icon_theme_prepend_search_path(theme, dir);
+ g_free(dir);
g_set_prgname("qemu");
s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
* sure that we don't accidentally break implicit assumptions. */
setlocale(LC_MESSAGES, "");
setlocale(LC_CTYPE, "C.UTF-8");
- bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
+ dir = get_relocated_path(CONFIG_QEMU_LOCALEDIR);
+ bindtextdomain("qemu", dir);
+ g_free(dir);
bind_textdomain_codeset("qemu", "UTF-8");
textdomain("qemu");
assert(opts->type == DISPLAY_TYPE_GTK);
if (opts->has_gl && opts->gl != DISPLAYGL_MODE_OFF) {
#if defined(CONFIG_OPENGL)
-#if defined(CONFIG_GTK_GL) && defined(GDK_WINDOWING_WAYLAND)
+#if defined(GDK_WINDOWING_WAYLAND)
if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
gtk_use_gl_area = true;
gtk_gl_area_init();
} else
#endif
{
+#ifdef CONFIG_X11
DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON;
gtk_egl_init(mode);
+#endif
}
#endif
}