aboutsummaryrefslogtreecommitdiff
path: root/ui/gtk.c
diff options
context:
space:
mode:
Diffstat (limited to 'ui/gtk.c')
-rw-r--r--ui/gtk.c835
1 files changed, 514 insertions, 321 deletions
diff --git a/ui/gtk.c b/ui/gtk.c
index 579990b865..810d7fc796 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -6,42 +6,45 @@
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
*
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * Portions from gtk-vnc:
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Portions from gtk-vnc (originally licensed under the LGPL v2+):
*
* GTK VNC Widget
*
* Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
* Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.0 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define GETTEXT_PACKAGE "qemu"
#define LOCALEDIR "po"
#include "qemu/osdep.h"
-#include "qemu-common.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 "qemu/error-report.h"
+#include "qemu/main-loop.h"
#include "ui/console.h"
#include "ui/gtk.h"
+#ifdef G_OS_WIN32
+#include <gdk/gdkwin32.h>
+#endif
+#include "ui/win32-kbd-hook.h"
#include <glib/gi18n.h>
#include <locale.h>
@@ -52,12 +55,12 @@
#include "trace.h"
#include "ui/input.h"
+#include "sysemu/runstate.h"
#include "sysemu/sysemu.h"
#include "keymaps.h"
#include "chardev/char.h"
#include "qom/object.h"
-#define MAX_VCS 10
#define VC_WINDOW_X_MIN 320
#define VC_WINDOW_Y_MIN 240
#define VC_TERM_X_MIN 80
@@ -111,96 +114,23 @@
# 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 int modifier_keycode[] = {
- Q_KEY_CODE_SHIFT,
- Q_KEY_CODE_SHIFT_R,
- Q_KEY_CODE_CTRL,
- Q_KEY_CODE_CTRL_R,
- Q_KEY_CODE_ALT,
- Q_KEY_CODE_ALT_R,
- Q_KEY_CODE_META_L,
- Q_KEY_CODE_META_R,
-};
-
static const guint16 *keycode_map;
static size_t keycode_maplen;
-struct GtkDisplayState {
- GtkWidget *window;
-
- GtkWidget *menu_bar;
-
- GtkAccelGroup *accel_group;
-
- GtkWidget *machine_menu_item;
- GtkWidget *machine_menu;
- GtkWidget *pause_item;
- GtkWidget *reset_item;
- GtkWidget *powerdown_item;
- GtkWidget *quit_item;
-
- GtkWidget *view_menu_item;
- GtkWidget *view_menu;
- GtkWidget *full_screen_item;
- GtkWidget *copy_item;
- GtkWidget *zoom_in_item;
- GtkWidget *zoom_out_item;
- GtkWidget *zoom_fixed_item;
- GtkWidget *zoom_fit_item;
- GtkWidget *grab_item;
- GtkWidget *grab_on_hover_item;
-
- int nb_vcs;
- VirtualConsole vc[MAX_VCS];
-
- GtkWidget *show_tabs_item;
- GtkWidget *untabify_item;
- GtkWidget *show_menubar_item;
-
- GtkWidget *vbox;
- GtkWidget *notebook;
- 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;
-
- gboolean full_screen;
-
- GdkCursor *null_cursor;
- Notifier mouse_mode_notifier;
- gboolean free_scale;
-
- bool external_pause_update;
-
- bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
- 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)
+
+static struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX];
bool gtk_use_gl_area;
@@ -274,7 +204,7 @@ static void gd_update_cursor(VirtualConsole *vc)
}
window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area));
- if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) {
+ if (s->full_screen || qemu_input_is_absolute(vc->gfx.dcl.con) || s->ptr_owner == vc) {
gdk_window_set_cursor(window, s->null_cursor);
} else {
gdk_window_set_cursor(window, NULL);
@@ -414,7 +344,7 @@ static void gd_update_full_redraw(VirtualConsole *vc)
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;
@@ -426,20 +356,12 @@ static void gd_update_full_redraw(VirtualConsole *vc)
static void gtk_release_modifiers(GtkDisplayState *s)
{
VirtualConsole *vc = gd_vc_find_current(s);
- int i, qcode;
if (vc->type != GD_VC_GFX ||
!qemu_console_is_graphic(vc->gfx.dcl.con)) {
return;
}
- for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
- qcode = modifier_keycode[i];
- if (!s->modifier_pressed[i]) {
- continue;
- }
- qemu_input_event_send_key_qcode(vc->gfx.dcl.con, qcode, false);
- s->modifier_pressed[i] = false;
- }
+ qkbd_state_lift_all_keys(vc->gfx.kbd);
}
static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
@@ -451,6 +373,16 @@ static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
g_object_unref(G_OBJECT(widget));
}
+static void *gd_win32_get_hwnd(VirtualConsole *vc)
+{
+#ifdef G_OS_WIN32
+ return gdk_win32_window_get_impl_hwnd(
+ gtk_widget_get_window(vc->window ? vc->window : vc->s->window));
+#else
+ return NULL;
+#endif
+}
+
/** DisplayState Callbacks **/
static void gd_update(DisplayChangeListener *dcl,
@@ -510,12 +442,7 @@ static void gd_refresh(DisplayChangeListener *dcl)
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,
@@ -525,7 +452,8 @@ static void gd_mouse_set(DisplayChangeListener *dcl,
GdkDisplay *dpy;
gint x_root, y_root;
- if (qemu_input_is_absolute()) {
+ if (!gtk_widget_get_realized(vc->gfx.drawing_area) ||
+ qemu_input_is_absolute(dcl->con)) {
return;
}
@@ -568,9 +496,7 @@ static void gd_switch(DisplayChangeListener *dcl,
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
bool resized = true;
- trace_gd_switch(vc->label,
- surface ? surface_width(surface) : 0,
- surface ? surface_height(surface) : 0);
+ trace_gd_switch(vc->label, surface_width(surface), surface_height(surface));
if (vc->gfx.surface) {
cairo_surface_destroy(vc->gfx.surface);
@@ -581,18 +507,14 @@ static void gd_switch(DisplayChangeListener *dcl,
vc->gfx.convert = NULL;
}
- if (vc->gfx.ds && surface &&
+ if (vc->gfx.ds &&
surface_width(vc->gfx.ds) == surface_width(surface) &&
surface_height(vc->gfx.ds) == surface_height(surface)) {
resized = false;
}
vc->gfx.ds = surface;
- if (!surface) {
- return;
- }
-
- if (surface->format == PIXMAN_x8r8g8b8) {
+ if (surface_format(surface) == PIXMAN_x8r8g8b8) {
/*
* PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24
*
@@ -644,9 +566,44 @@ static const DisplayChangeListenerOps dcl_ops = {
#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;
+}
+
+static void gd_gl_release_dmabuf(DisplayChangeListener *dcl,
+ QemuDmaBuf *dmabuf)
+{
+#ifdef CONFIG_GBM
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ egl_dmabuf_release_texture(dmabuf);
+ if (vc->gfx.guest_fb.dmabuf == dmabuf) {
+ vc->gfx.guest_fb.dmabuf = NULL;
+ }
+#endif
+}
+
+void gd_hw_gl_flushed(void *vcon)
+{
+ VirtualConsole *vc = vcon;
+ QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
+
+ qemu_set_fd_handler(dmabuf->fence_fd, NULL, NULL, NULL);
+ close(dmabuf->fence_fd);
+ dmabuf->fence_fd = -1;
+ graphic_hw_gl_block(vc->gfx.dcl.con, false);
+}
+
+/** DisplayState Callbacks (opengl version) **/
static const DisplayChangeListenerOps dcl_gl_area_ops = {
.dpy_name = "gtk-egl",
@@ -657,16 +614,29 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
.dpy_mouse_set = gd_mouse_set,
.dpy_cursor_define = gd_cursor_define,
- .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_gl_release_dmabuf = gd_gl_release_dmabuf,
+ .dpy_has_dmabuf = gd_has_dmabuf,
};
-#endif /* CONFIG_GTK_GL */
+static bool
+gd_gl_area_is_compatible_dcl(DisplayGLCtx *dgc,
+ DisplayChangeListener *dcl)
+{
+ return dcl->ops == &dcl_gl_area_ops;
+}
+static const DisplayGLCtxOps gl_area_ctx_ops = {
+ .dpy_gl_ctx_is_compatible_dcl = gd_gl_area_is_compatible_dcl,
+ .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,
+};
+
+#ifdef CONFIG_X11
static const DisplayChangeListenerOps dcl_egl_ops = {
.dpy_name = "gtk-egl",
.dpy_gfx_update = gd_egl_update,
@@ -676,24 +646,36 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
.dpy_mouse_set = gd_mouse_set,
.dpy_cursor_define = gd_cursor_define,
- .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_dmabuf = gd_egl_cursor_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_gl_update = gd_egl_flush,
+ .dpy_gl_release_dmabuf = gd_gl_release_dmabuf,
+ .dpy_has_dmabuf = gd_has_dmabuf,
+};
+
+static bool
+gd_egl_is_compatible_dcl(DisplayGLCtx *dgc,
+ DisplayChangeListener *dcl)
+{
+ return dcl->ops == &dcl_egl_ops;
+}
+
+static const DisplayGLCtxOps egl_ctx_ops = {
+ .dpy_gl_ctx_is_compatible_dcl = gd_egl_is_compatible_dcl,
+ .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,
};
+#endif
#endif /* CONFIG_OPENGL */
/** QEMU Events **/
-static void gd_change_runstate(void *opaque, int running, RunState state)
+static void gd_change_runstate(void *opaque, bool running, RunState state)
{
GtkDisplayState *s = opaque;
@@ -707,9 +689,13 @@ static void gd_mouse_mode_change(Notifier *notify, void *data)
s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
/* release the grab at switching to absolute mode */
- if (qemu_input_is_absolute() && gd_is_grab_active(s)) {
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
- FALSE);
+ if (s->ptr_owner && qemu_input_is_absolute(s->ptr_owner->gfx.dcl.con)) {
+ if (!s->ptr_owner->window) {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
+ FALSE);
+ } else {
+ gd_ungrab_pointer(s);
+ }
}
for (i = 0; i < s->nb_vcs; i++) {
VirtualConsole *vc = &s->vc[i];
@@ -736,17 +722,34 @@ static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
return TRUE;
}
-static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height)
+static void gd_set_ui_refresh_rate(VirtualConsole *vc, int refresh_rate)
{
QemuUIInfo info;
- memset(&info, 0, sizeof(info));
+ if (!dpy_ui_info_supported(vc->gfx.dcl.con)) {
+ return;
+ }
+
+ info = *dpy_get_ui_info(vc->gfx.dcl.con);
+ info.refresh_rate = refresh_rate;
+ dpy_set_ui_info(vc->gfx.dcl.con, &info, true);
+}
+
+static void gd_set_ui_size(VirtualConsole *vc, gint width, gint height)
+{
+ QemuUIInfo info;
+
+ if (!dpy_ui_info_supported(vc->gfx.dcl.con)) {
+ return;
+ }
+
+ info = *dpy_get_ui_info(vc->gfx.dcl.con);
info.width = width;
info.height = height;
- dpy_set_ui_info(vc->gfx.dcl.con, &info);
+ dpy_set_ui_info(vc->gfx.dcl.con, &info, true);
}
-#if defined(CONFIG_GTK_GL)
+#if defined(CONFIG_OPENGL)
static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
void *opaque)
@@ -764,11 +767,34 @@ static void gd_resize_event(GtkGLArea *area,
{
VirtualConsole *vc = (void *)opaque;
- gd_set_ui_info(vc, width, height);
+ gd_set_ui_size(vc, width, height);
}
#endif
+void gd_update_monitor_refresh_rate(VirtualConsole *vc, GtkWidget *widget)
+{
+#ifdef GDK_VERSION_3_22
+ GdkWindow *win = gtk_widget_get_window(widget);
+ int refresh_rate;
+
+ if (win) {
+ GdkDisplay *dpy = gtk_widget_get_display(widget);
+ GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
+ refresh_rate = gdk_monitor_get_refresh_rate(monitor); /* [mHz] */
+ } else {
+ refresh_rate = 0;
+ }
+
+ gd_set_ui_refresh_rate(vc, refresh_rate);
+
+ /* T = 1 / f = 1 [s*Hz] / f = 1000*1000 [ms*mHz] / f */
+ vc->gfx.dcl.update_interval = refresh_rate ?
+ MIN(1000 * 1000 / refresh_rate, GUI_REFRESH_INTERVAL_DEFAULT) :
+ GUI_REFRESH_INTERVAL_DEFAULT;
+#endif
+}
+
static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
{
VirtualConsole *vc = opaque;
@@ -783,8 +809,12 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
/* invoke render callback please */
return FALSE;
} else {
+#ifdef CONFIG_X11
gd_egl_draw(vc);
return TRUE;
+#else
+ abort();
+#endif
}
}
#endif
@@ -795,6 +825,11 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
if (!vc->gfx.ds) {
return FALSE;
}
+ if (!vc->gfx.surface) {
+ return FALSE;
+ }
+
+ gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : s->window);
fbw = surface_width(vc->gfx.ds);
fbh = surface_height(vc->gfx.ds);
@@ -852,7 +887,7 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
int x, y;
int mx, my;
int fbh, fbw;
- int ww, wh;
+ int ww, wh, ws;
if (!vc->gfx.ds) {
return TRUE;
@@ -861,8 +896,9 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
- ww = gdk_window_get_width(gtk_widget_get_window(vc->gfx.drawing_area));
- wh = gdk_window_get_height(gtk_widget_get_window(vc->gfx.drawing_area));
+ ww = gtk_widget_get_allocated_width(widget);
+ wh = gtk_widget_get_allocated_height(widget);
+ ws = gtk_widget_get_scale_factor(widget);
mx = my = 0;
if (ww > fbw) {
@@ -872,10 +908,10 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
my = (wh - fbh) / 2;
}
- x = (motion->x - mx) / vc->gfx.scale_x;
- y = (motion->y - my) / vc->gfx.scale_y;
+ x = (motion->x - mx) / vc->gfx.scale_x * ws;
+ y = (motion->y - my) / vc->gfx.scale_y * ws;
- if (qemu_input_is_absolute()) {
+ if (qemu_input_is_absolute(vc->gfx.dcl.con)) {
if (x < 0 || y < 0 ||
x >= surface_width(vc->gfx.ds) ||
y >= surface_height(vc->gfx.ds)) {
@@ -895,53 +931,32 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
s->last_y = y;
s->last_set = TRUE;
- if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
+ if (!qemu_input_is_absolute(vc->gfx.dcl.con) && s->ptr_owner == vc) {
GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
- int screen_width, screen_height;
+ 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 x = (int)motion->x_root;
- int y = (int)motion->y_root;
+ int xr = (int)motion->x_root;
+ int yr = (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);
/* In relative mode check to see if client pointer hit
- * one of the screen edges, and if so move it back by
- * 200 pixels. This is important because the pointer
+ * one of the monitor edges, and if so move it back to the
+ * center of the monitor. This is important because the pointer
* in the server doesn't correspond 1-for-1, and so
* may still be only half way across the screen. Without
* this warp, the server pointer would thus appear to hit
* an invisible wall */
- if (x == 0) {
- x += 200;
- }
- if (y == 0) {
- y += 200;
- }
- if (x == (screen_width - 1)) {
- x -= 200;
- }
- if (y == (screen_height - 1)) {
- y -= 200;
- }
-
- if (x != (int)motion->x_root || y != (int)motion->y_root) {
+ if (xr <= geometry.x || xr - geometry.x >= geometry.width - 1 ||
+ yr <= geometry.y || yr - geometry.y >= geometry.height - 1) {
GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
- gdk_device_warp(dev, screen, x, y);
+ xr = geometry.x + geometry.width / 2;
+ yr = geometry.y + geometry.height / 2;
+
+ gdk_device_warp(dev, screen, xr, yr);
s->last_set = FALSE;
return FALSE;
}
@@ -958,7 +973,7 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
/* 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() && s->ptr_owner != vc) {
+ !qemu_input_is_absolute(vc->gfx.dcl.con) && s->ptr_owner != vc) {
if (!vc->window) {
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
TRUE);
@@ -982,6 +997,10 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
return TRUE;
}
+ if (button->type == GDK_2BUTTON_PRESS || button->type == GDK_3BUTTON_PRESS) {
+ return TRUE;
+ }
+
qemu_input_queue_btn(vc->gfx.dcl.con, btn,
button->type == GDK_BUTTON_PRESS);
qemu_input_event_sync();
@@ -992,35 +1011,97 @@ static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
void *opaque)
{
VirtualConsole *vc = opaque;
- InputButton btn;
+ InputButton btn_vertical;
+ InputButton btn_horizontal;
+ bool has_vertical = false;
+ bool has_horizontal = false;
if (scroll->direction == GDK_SCROLL_UP) {
- btn = INPUT_BUTTON_WHEEL_UP;
+ btn_vertical = INPUT_BUTTON_WHEEL_UP;
+ has_vertical = true;
} else if (scroll->direction == GDK_SCROLL_DOWN) {
- btn = INPUT_BUTTON_WHEEL_DOWN;
+ btn_vertical = INPUT_BUTTON_WHEEL_DOWN;
+ has_vertical = true;
+ } else if (scroll->direction == GDK_SCROLL_LEFT) {
+ btn_horizontal = INPUT_BUTTON_WHEEL_LEFT;
+ has_horizontal = true;
+ } else if (scroll->direction == GDK_SCROLL_RIGHT) {
+ btn_horizontal = INPUT_BUTTON_WHEEL_RIGHT;
+ has_horizontal = true;
} else if (scroll->direction == GDK_SCROLL_SMOOTH) {
gdouble delta_x, delta_y;
if (!gdk_event_get_scroll_deltas((GdkEvent *)scroll,
&delta_x, &delta_y)) {
return TRUE;
}
+
if (delta_y > 0) {
- btn = INPUT_BUTTON_WHEEL_DOWN;
+ btn_vertical = INPUT_BUTTON_WHEEL_DOWN;
+ has_vertical = true;
+ } else if (delta_y < 0) {
+ btn_vertical = INPUT_BUTTON_WHEEL_UP;
+ has_vertical = true;
+ } else if (delta_x > 0) {
+ btn_horizontal = INPUT_BUTTON_WHEEL_RIGHT;
+ has_horizontal = true;
+ } else if (delta_x < 0) {
+ btn_horizontal = INPUT_BUTTON_WHEEL_LEFT;
+ has_horizontal = true;
} else {
- btn = INPUT_BUTTON_WHEEL_UP;
+ return TRUE;
}
} else {
return TRUE;
}
- qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
- qemu_input_event_sync();
- qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
- qemu_input_event_sync();
+ if (has_vertical) {
+ qemu_input_queue_btn(vc->gfx.dcl.con, btn_vertical, true);
+ qemu_input_event_sync();
+ qemu_input_queue_btn(vc->gfx.dcl.con, btn_vertical, false);
+ qemu_input_event_sync();
+ }
+
+ if (has_horizontal) {
+ qemu_input_queue_btn(vc->gfx.dcl.con, btn_horizontal, true);
+ qemu_input_event_sync();
+ qemu_input_queue_btn(vc->gfx.dcl.con, btn_horizontal, false);
+ qemu_input_event_sync();
+ }
+
return TRUE;
}
+static gboolean gd_touch_event(GtkWidget *widget, GdkEventTouch *touch,
+ void *opaque)
+{
+ VirtualConsole *vc = opaque;
+ uint64_t num_slot = GPOINTER_TO_UINT(touch->sequence);
+ int type = -1;
+
+ switch (touch->type) {
+ case GDK_TOUCH_BEGIN:
+ type = INPUT_MULTI_TOUCH_TYPE_BEGIN;
+ break;
+ case GDK_TOUCH_UPDATE:
+ type = INPUT_MULTI_TOUCH_TYPE_UPDATE;
+ break;
+ case GDK_TOUCH_END:
+ case GDK_TOUCH_CANCEL:
+ type = INPUT_MULTI_TOUCH_TYPE_END;
+ break;
+ default:
+ warn_report("gtk: unexpected touch event type\n");
+ return FALSE;
+ }
+
+ console_handle_touch_event(vc->gfx.dcl.con, touch_slots,
+ num_slot, surface_width(vc->gfx.ds),
+ surface_height(vc->gfx.ds), touch->x,
+ touch->y, type, &error_warn);
+ return TRUE;
+}
+
static const guint16 *gd_get_keymap(size_t *maplen)
{
GdkDisplay *dpy = gdk_display_get_default();
@@ -1044,8 +1125,8 @@ static const guint16 *gd_get_keymap(size_t *maplen)
#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
@@ -1091,19 +1172,38 @@ static int gd_map_keycode(int scancode)
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)
{
VirtualConsole *vc = opaque;
- QemuConsole *con = vc->gfx.dcl.con;
+ QemuTextConsole *con = QEMU_TEXT_CONSOLE(vc->gfx.dcl.con);
if (key->keyval == GDK_KEY_Delete) {
- kbd_put_qcode_console(con, Q_KEY_CODE_DELETE, false);
+ qemu_text_console_put_qcode(con, Q_KEY_CODE_DELETE, false);
} else if (key->length) {
- kbd_put_string_console(con, key->string, key->length);
+ qemu_text_console_put_string(con, key->string, key->length);
} else {
- int qcode = gd_map_keycode(key->hardware_keycode);
- kbd_put_qcode_console(con, qcode, false);
+ int qcode = gd_map_keycode(gd_get_keycode(key));
+ qemu_text_console_put_qcode(con, qcode, false);
}
return TRUE;
}
@@ -1111,19 +1211,19 @@ static gboolean gd_text_key_down(GtkWidget *widget,
static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
{
VirtualConsole *vc = opaque;
- GtkDisplayState *s = vc->s;
- int qcode;
- int i;
-
- 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
@@ -1134,25 +1234,39 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
|| key->hardware_keycode == VK_PAUSE
#endif
) {
- qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE,
- key->type == GDK_KEY_PRESS);
+ qkbd_state_key_event(vc->gfx.kbd, Q_KEY_CODE_PAUSE,
+ key->type == GDK_KEY_PRESS);
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");
- for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
- if (qcode == modifier_keycode[i]) {
- s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
- }
- }
+ qkbd_state_key_event(vc->gfx.kbd, qcode,
+ key->type == GDK_KEY_PRESS);
+
+ return TRUE;
+}
- qemu_input_event_send_key_qcode(vc->gfx.dcl.con, qcode,
- key->type == GDK_KEY_PRESS);
+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;
}
@@ -1208,7 +1322,6 @@ static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
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)
@@ -1243,6 +1356,16 @@ static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
vc->tab_item, vc->label);
gtk_widget_destroy(vc->window);
vc->window = NULL;
+#if defined(CONFIG_OPENGL)
+ if (vc->gfx.esurface) {
+ eglDestroySurface(qemu_egl_display, vc->gfx.esurface);
+ vc->gfx.esurface = NULL;
+ }
+ if (vc->gfx.ectx) {
+ eglDestroyContext(qemu_egl_display, vc->gfx.ectx);
+ vc->gfx.ectx = NULL;
+ }
+#endif
return TRUE;
}
@@ -1272,6 +1395,16 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
if (!vc->window) {
gtk_widget_set_sensitive(vc->menu_item, false);
vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+#if defined(CONFIG_OPENGL)
+ if (vc->gfx.esurface) {
+ eglDestroySurface(qemu_egl_display, vc->gfx.esurface);
+ vc->gfx.esurface = NULL;
+ }
+ if (vc->gfx.ectx) {
+ eglDestroyContext(qemu_egl_display, vc->gfx.ectx);
+ vc->gfx.ectx = NULL;
+ }
+#endif
gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
g_signal_connect(vc->window, "delete-event",
@@ -1418,7 +1551,6 @@ static void gd_menu_zoom_fit(GtkMenuItem *item, 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);
@@ -1442,32 +1574,6 @@ static void gd_grab_update(VirtualConsole *vc, bool kbd, bool ptr)
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)
{
@@ -1479,13 +1585,8 @@ static void gd_grab_keyboard(VirtualConsole *vc, const char *reason)
}
}
-#if GTK_CHECK_VERSION(3, 20, 0)
+ win32_kbd_set_grab(true);
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);
@@ -1500,11 +1601,8 @@ static void gd_ungrab_keyboard(GtkDisplayState *s)
}
s->kbd_owner = NULL;
-#if GTK_CHECK_VERSION(3, 20, 0)
+ win32_kbd_set_grab(false);
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");
}
@@ -1521,21 +1619,9 @@ static void gd_grab_pointer(VirtualConsole *vc, const char *reason)
}
}
-#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);
@@ -1552,17 +1638,10 @@ static void gd_ungrab_pointer(GtkDisplayState *s)
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");
}
@@ -1642,12 +1721,22 @@ static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
return TRUE;
}
+static gboolean gd_focus_in_event(GtkWidget *widget,
+ GdkEventFocus *event, gpointer opaque)
+{
+ VirtualConsole *vc = opaque;
+
+ win32_kbd_set_window(gd_win32_get_hwnd(vc));
+ return TRUE;
+}
+
static gboolean gd_focus_out_event(GtkWidget *widget,
- GdkEventCrossing *crossing, gpointer opaque)
+ GdkEventFocus *event, gpointer opaque)
{
VirtualConsole *vc = opaque;
GtkDisplayState *s = vc->s;
+ win32_kbd_set_window(NULL);
gtk_release_modifiers(s);
return TRUE;
}
@@ -1657,7 +1746,7 @@ static gboolean gd_configure(GtkWidget *widget,
{
VirtualConsole *vc = opaque;
- gd_set_ui_info(vc, cfg->width, cfg->height);
+ gd_set_ui_size(vc, cfg->width, cfg->height);
return FALSE;
}
@@ -1678,8 +1767,7 @@ static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
G_CALLBACK(gd_menu_switch_vc), s);
gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
- group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
- return group;
+ return gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
}
#if defined(CONFIG_VTE)
@@ -1708,6 +1796,23 @@ static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
}
}
+static void gd_vc_send_chars(VirtualConsole *vc)
+{
+ uint32_t len, avail;
+
+ len = qemu_chr_be_can_write(vc->vte.chr);
+ avail = fifo8_num_used(&vc->vte.out_fifo);
+ while (len > 0 && avail > 0) {
+ const uint8_t *buf;
+ uint32_t size;
+
+ buf = fifo8_pop_buf(&vc->vte.out_fifo, MIN(len, avail), &size);
+ qemu_chr_be_write(vc->vte.chr, buf, size);
+ len = qemu_chr_be_can_write(vc->vte.chr);
+ avail -= size;
+ }
+}
+
static int gd_vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
{
VCChardev *vcd = VC_CHARDEV(chr);
@@ -1717,6 +1822,16 @@ static int gd_vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
return len;
}
+static void gd_vc_chr_accept_input(Chardev *chr)
+{
+ VCChardev *vcd = VC_CHARDEV(chr);
+ VirtualConsole *vc = vcd->console;
+
+ if (vc) {
+ gd_vc_send_chars(vc);
+ }
+}
+
static void gd_vc_chr_set_echo(Chardev *chr, bool echo)
{
VCChardev *vcd = VC_CHARDEV(chr);
@@ -1753,9 +1868,9 @@ static void char_gd_vc_class_init(ObjectClass *oc, void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
- cc->parse = qemu_chr_parse_vc;
cc->open = gd_vc_open;
cc->chr_write = gd_vc_chr_write;
+ cc->chr_accept_input = gd_vc_chr_accept_input;
cc->chr_set_echo = gd_vc_chr_set_echo;
}
@@ -1770,6 +1885,7 @@ static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
gpointer user_data)
{
VirtualConsole *vc = user_data;
+ uint32_t free;
if (vc->vte.echo) {
VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
@@ -1789,7 +1905,10 @@ static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
}
}
- qemu_chr_be_write(vc->vte.chr, (uint8_t *)text, (unsigned int)size);
+ free = fifo8_num_free(&vc->vte.out_fifo);
+ fifo8_push_all(&vc->vte.out_fifo, (uint8_t *)text, MIN(free, size));
+ gd_vc_send_chars(vc);
+
return TRUE;
}
@@ -1806,6 +1925,7 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
vc->s = s;
vc->vte.echo = vcd->echo;
vc->vte.chr = chr;
+ fifo8_create(&vc->vte.out_fifo, 4096);
vcd->console = vc;
snprintf(buffer, sizeof(buffer), "vc%d", idx);
@@ -1880,7 +2000,7 @@ static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
{
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",
@@ -1902,15 +2022,21 @@ static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
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, "touch-event",
+ G_CALLBACK(gd_touch_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-in-event",
+ G_CALLBACK(gd_focus_in_event), vc);
g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
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);
@@ -1990,11 +2116,24 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
return machine_menu;
}
+#if defined(CONFIG_OPENGL)
+static void gl_area_realize(GtkGLArea *area, VirtualConsole *vc)
+{
+ 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
+
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 i;
vc->label = qemu_console_get_label(con);
vc->s = s;
@@ -2003,13 +2142,14 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
#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 */
- {
+ vc->gfx.dgc.ops = &gl_area_ctx_ops;
+ } else {
+#ifdef CONFIG_X11
vc->gfx.drawing_area = gtk_drawing_area_new();
/*
* gtk_widget_set_double_buffered() was deprecated in 3.14.
@@ -2017,15 +2157,16 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
* 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.dgc.ops = &egl_ctx_ops;
+ vc->gfx.has_dmabuf = qemu_egl_has_dmabuf();
+#else
+ abort();
+#endif
}
} else
#endif
@@ -2040,9 +2181,11 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_BUTTON_MOTION_MASK |
+ GDK_TOUCH_MASK |
GDK_ENTER_NOTIFY_MASK |
GDK_LEAVE_NOTIFY_MASK |
GDK_SCROLL_MASK |
+ GDK_SMOOTH_SCROLL_MASK |
GDK_KEY_PRESS_MASK);
gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
@@ -2052,7 +2195,12 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
vc->tab_item, gtk_label_new(vc->label));
+ vc->gfx.kbd = qkbd_state_init(con);
vc->gfx.dcl.con = con;
+
+ if (display_opengl) {
+ qemu_console_set_display_gl_ctx(con, &vc->gfx.dgc);
+ }
register_displaychangelistener(&vc->gfx.dcl);
gd_connect_vc_gfx_signals(vc);
@@ -2069,10 +2217,15 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
s->free_scale = true;
}
+ for (i = 0; i < INPUT_EVENT_SLOTS_MAX; i++) {
+ struct touch_slot *slot = &touch_slots[i];
+ slot->tracking_id = -1;
+ }
+
return group;
}
-static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
+static GtkWidget *gd_create_menu_view(GtkDisplayState *s, DisplayOptions *opts)
{
GSList *group = NULL;
GtkWidget *view_menu;
@@ -2170,7 +2323,8 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
s->show_menubar_item = gtk_check_menu_item_new_with_mnemonic(
_("Show Menubar"));
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->show_menubar_item),
- TRUE);
+ !opts->u.gtk.has_show_menubar ||
+ opts->u.gtk.show_menubar);
gtk_accel_group_connect(s->accel_group, GDK_KEY_m, HOTKEY_MODIFIERS, 0,
g_cclosure_new_swap(G_CALLBACK(gd_accel_show_menubar), s, NULL));
gtk_accel_label_set_accel(
@@ -2181,13 +2335,13 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
return view_menu;
}
-static void gd_create_menus(GtkDisplayState *s)
+static void gd_create_menus(GtkDisplayState *s, DisplayOptions *opts)
{
GtkSettings *settings;
s->accel_group = gtk_accel_group_new();
s->machine_menu = gd_create_menu_machine(s);
- s->view_menu = gd_create_menu_view(s);
+ s->view_menu = gd_create_menu_view(s, opts);
s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
@@ -2213,17 +2367,26 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
{
VirtualConsole *vc;
- GtkDisplayState *s = g_malloc0(sizeof(*s));
- char *filename;
+ GtkDisplayState *s;
GdkDisplay *window_display;
+ GtkIconTheme *theme;
+ char *dir;
+ int idx;
if (!gtkinit) {
fprintf(stderr, "gtk initialization failed\n");
exit(1);
}
assert(opts->type == DISPLAY_TYPE_GTK);
+ s = g_malloc0(sizeof(*s));
s->opts = opts;
+ theme = gtk_icon_theme_get_default();
+ 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);
s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
s->notebook = gtk_notebook_new();
@@ -2237,30 +2400,27 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
* 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");
window_display = gtk_widget_get_display(s->window);
- s->null_cursor = gdk_cursor_new_for_display(window_display,
- GDK_BLANK_CURSOR);
+ if (s->opts->has_show_cursor && s->opts->show_cursor) {
+ s->null_cursor = NULL; /* default pointer */
+ } else {
+ s->null_cursor = gdk_cursor_new_for_display(window_display,
+ GDK_BLANK_CURSOR);
+ }
s->mouse_mode_notifier.notify = gd_mouse_mode_change;
qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
qemu_add_vm_change_state_handler(gd_change_runstate, s);
- filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg");
- if (filename) {
- GError *error = NULL;
- GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error);
- if (pixbuf) {
- gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf);
- } else {
- g_error_free(error);
- }
- g_free(filename);
- }
+ gtk_window_set_icon_name(GTK_WINDOW(s->window), "qemu");
- gd_create_menus(s);
+ gd_create_menus(s, opts);
gd_connect_signals(s);
@@ -2276,6 +2436,19 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
gtk_widget_show_all(s->window);
+ for (idx = 0;; idx++) {
+ QemuConsole *con = qemu_console_lookup_by_index(idx);
+ if (!con) {
+ break;
+ }
+ gtk_widget_realize(s->vc[idx].gfx.drawing_area);
+ }
+
+ if (opts->u.gtk.has_show_menubar &&
+ !opts->u.gtk.show_menubar) {
+ gtk_widget_hide(s->menu_bar);
+ }
+
vc = gd_vc_find_current(s);
gtk_widget_set_sensitive(s->view_menu, vc != NULL);
#ifdef CONFIG_VTE
@@ -2291,6 +2464,13 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
opts->u.gtk.grab_on_hover) {
gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
}
+ if (opts->u.gtk.has_show_tabs &&
+ opts->u.gtk.show_tabs) {
+ gtk_menu_item_activate(GTK_MENU_ITEM(s->show_tabs_item));
+ }
+#ifdef CONFIG_GTK_CLIPBOARD
+ gd_clipboard_init(s);
+#endif /* CONFIG_GTK_CLIPBOARD */
}
static void early_gtk_display_init(DisplayOptions *opts)
@@ -2322,15 +2502,23 @@ static void early_gtk_display_init(DisplayOptions *opts)
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
+#if defined(GDK_WINDOWING_WIN32)
+ if (GDK_IS_WIN32_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
}
@@ -2346,6 +2534,7 @@ static QemuDisplay qemu_display_gtk = {
.type = DISPLAY_TYPE_GTK,
.early_init = early_gtk_display_init,
.init = gtk_display_init,
+ .vc = "vc",
};
static void register_gtk(void)
@@ -2354,3 +2543,7 @@ static void register_gtk(void)
}
type_init(register_gtk);
+
+#ifdef CONFIG_OPENGL
+module_dep("ui-opengl");
+#endif