@@ -136,6 +136,7 @@ struct GtkDisplayState {
VirtualConsole *ptr_owner;
gboolean full_screen;
+ gboolean ext_abs_mode;
GdkCursor *null_cursor;
Notifier mouse_mode_notifier;
@@ -1201,6 +1201,9 @@
# Since 7.1
# @show-menubar: Display the main window menubar. Defaults to "on".
# Since 8.0
+# @extend-abs-mode: Extend the absolute mode across all monitors or
+# limit it to just one. Defaults to "off".
+# Since 8.0
#
# Since: 2.12
##
@@ -1208,7 +1211,8 @@
'data' : { '*grab-on-hover' : 'bool',
'*zoom-to-fit' : 'bool',
'*show-tabs' : 'bool',
- '*show-menubar' : 'bool' } }
+ '*show-menubar' : 'bool',
+ '*extend-abs-mode' : 'bool' } }
##
# @DisplayEGLHeadless:
@@ -1980,7 +1980,7 @@ DEF("display", HAS_ARG, QEMU_OPTION_display,
#if defined(CONFIG_GTK)
"-display gtk[,full-screen=on|off][,gl=on|off][,grab-on-hover=on|off]\n"
" [,show-tabs=on|off][,show-cursor=on|off][,window-close=on|off]\n"
- " [,show-menubar=on|off]\n"
+ " [,show-menubar=on|off][,extend-abs-mode=on|off]\n"
#endif
#if defined(CONFIG_VNC)
"-display vnc=<display>[,<optargs>]\n"
@@ -2075,6 +2075,9 @@ SRST
``show-menubar=on|off`` : Display the main window menubar, defaults to "on"
+ ``extend-abs-mode=on|off`` : Extend the absolute mode across all
+ monitors or limit it to just one.
+
``curses[,charset=<encoding>]``
Display video output via curses. For graphics device models
which support a text mode, QEMU can display this output using a
@@ -883,12 +883,42 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
return TRUE;
}
+static void gd_calc_width_height(VirtualConsole *vc,
+ uint32_t *max_w, uint32_t *max_h)
+{
+ GtkDisplayState *s = vc->s;
+ GdkDisplay *dpy = gtk_widget_get_display(vc->gfx.drawing_area);
+ GdkRectangle geometry;
+ int i;
+
+ if (s->ext_abs_mode) {
+ for (i = 0; i < gdk_display_get_n_monitors(dpy); i++) {
+ gdk_monitor_get_geometry(gdk_display_get_monitor(dpy, i),
+ &geometry);
+ if (geometry.x + geometry.width > *max_w) {
+ *max_w = geometry.x + geometry.width;
+ }
+ if (geometry.y + geometry.height > *max_h) {
+ *max_h = geometry.y + geometry.height;
+ }
+ }
+ } else {
+ *max_w = surface_width(vc->gfx.ds);
+ *max_h = surface_height(vc->gfx.ds);
+ }
+}
+
static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
void *opaque)
{
VirtualConsole *vc = opaque;
GtkDisplayState *s = vc->s;
- GdkWindow *window;
+ GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
+ GdkDisplay *dpy = gtk_widget_get_display(widget);
+ GdkWindow *window = gtk_widget_get_window(widget);
+ GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, window);
+ GdkRectangle geometry;
+ uint32_t max_w = 0, max_h = 0;
int x, y;
int mx, my;
int fbh, fbw;
@@ -901,7 +931,6 @@ 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;
- window = gtk_widget_get_window(vc->gfx.drawing_area);
ww = gdk_window_get_width(window);
wh = gdk_window_get_height(window);
ws = gdk_window_get_scale_factor(window);
@@ -916,17 +945,20 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
x = (motion->x - mx) / vc->gfx.scale_x * ws;
y = (motion->y - my) / vc->gfx.scale_y * ws;
+ gdk_monitor_get_geometry(monitor, &geometry);
if (qemu_input_is_absolute()) {
- if (x < 0 || y < 0 ||
- x >= surface_width(vc->gfx.ds) ||
- y >= surface_height(vc->gfx.ds)) {
+ if (s->ext_abs_mode) {
+ x = x + geometry.x;
+ y = y + geometry.y;
+ }
+
+ gd_calc_width_height(vc, &max_w, &max_h);
+ if (x < 0 || y < 0 || x >= max_w || y >= max_h) {
return TRUE;
}
- qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
- 0, surface_width(vc->gfx.ds));
- qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
- 0, surface_height(vc->gfx.ds));
+ qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x, 0, max_w);
+ qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y, 0, max_h);
qemu_input_event_sync();
} else if (s->last_set && s->ptr_owner == vc) {
qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
@@ -938,17 +970,9 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
s->last_set = TRUE;
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 x = (int)motion->x_root;
int y = (int)motion->y_root;
- gdk_monitor_get_geometry(monitor, &geometry);
-
/* In relative mode check to see if client pointer hit
* one of the monitor edges, and if so move it back to the
* center of the monitor. This is important because the pointer
@@ -2423,6 +2447,11 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
opts->u.gtk.show_tabs) {
gtk_menu_item_activate(GTK_MENU_ITEM(s->show_tabs_item));
}
+ if (opts->u.gtk.has_extend_abs_mode &&
+ opts->u.gtk.extend_abs_mode &&
+ qemu_input_is_absolute()) {
+ s->ext_abs_mode = TRUE;
+ }
gd_clipboard_init(s);
}
Currently, the range of the absolute pointer device (usb-tablet) is restricted to any one monitor. This presents a problem when there are multiple VCs (Guest GTK windows) located on different monitors. Therefore, it makes sense to extend the range of the absolute pointer device to span all monitors. This would work nicely as long as the monitors (aka outputs/connectors/displays) on the Host and Guest are in alignment with each other (i.e, if the Host has monitor 2, 3, 4 to the right of monitor 1, then Guest's need to be arranged accordingly). Relative mode could also be used in these situations but the user experience is not as seamless as the absolute mode. Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Dongwon Kim <dongwon.kim@intel.com> Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com> --- include/ui/gtk.h | 1 + qapi/ui.json | 6 ++++- qemu-options.hx | 5 +++- ui/gtk.c | 63 +++++++++++++++++++++++++++++++++++------------- 4 files changed, 56 insertions(+), 19 deletions(-)