diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index ae0f53740d..01eb560b0b 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -136,6 +136,7 @@ struct GtkDisplayState {
     VirtualConsole *ptr_owner;
 
     gboolean full_screen;
+    gboolean ext_abs_mode;
 
     GdkCursor *null_cursor;
     Notifier mouse_mode_notifier;
diff --git a/qapi/ui.json b/qapi/ui.json
index 0abba3e930..1ce2945c84 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -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:
diff --git a/qemu-options.hx b/qemu-options.hx
index eb38e5dc40..e6ef0634cd 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -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
diff --git a/ui/gtk.c b/ui/gtk.c
index 2be9755b0a..a4644675b2 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -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);
 }
 
