[PATCH xserver 12/12] xwayland: Add pointer warp emulator

Jonas Ådahl jadahl at gmail.com
Thu Sep 8 10:46:41 UTC 2016


Emulate pointer warps by locking the pointer and sending relative
motion events instead of absolute. X will keep track of the "fake"
pointer cursor position given the relative motion events, and the
client warping the cursor will warp the faked cursor position.

Various requirements need to be met for the pointer warp emulator to
enable:

The cursor must be invisible: since it would not be acceptable that a
fake cursor position would be different from the visual representation
of the cursor, emulation can only be done when there is no visual
representation done by the Wayland compositor. Thus, for the emulator
to enable, the cursor must be hidden, and would the cursor be displayed
while the emulator is active, the emulator would be destroyed.

The window that is warped within must be likely to have pointer focus.
For example, warping outside of the window region will be ignored.

The pointer warp emulator will disable itself once the fake cursor
position leaves the window region, or the cursor is made visible.

This makes various games depending on pointer warping (such as 3D
first-person shooters and stategy games using click-to-drag-map like
things) work.

Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
---
 hw/xwayland/xwayland-cursor.c |   7 ++
 hw/xwayland/xwayland-input.c  | 267 +++++++++++++++++++++++++++++++++++++++++-
 hw/xwayland/xwayland.c        |  27 +++++
 hw/xwayland/xwayland.h        |  17 +++
 4 files changed, 317 insertions(+), 1 deletion(-)

diff --git a/hw/xwayland/xwayland-cursor.c b/hw/xwayland/xwayland-cursor.c
index 7d14a3d..20ddf6e 100644
--- a/hw/xwayland/xwayland-cursor.c
+++ b/hw/xwayland/xwayland-cursor.c
@@ -168,12 +168,19 @@ xwl_set_cursor(DeviceIntPtr device,
                ScreenPtr screen, CursorPtr cursor, int x, int y)
 {
     struct xwl_seat *xwl_seat;
+    Bool cursor_visibility_changed;
 
     xwl_seat = device->public.devicePrivate;
     if (xwl_seat == NULL)
         return;
 
+    cursor_visibility_changed = !!xwl_seat->x_cursor ^ !!cursor;
+
     xwl_seat->x_cursor = cursor;
+
+    if (cursor_visibility_changed)
+        xwl_seat_cursor_visibility_changed(xwl_seat);
+
     xwl_seat_set_cursor(xwl_seat);
 }
 
diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c
index d73448a..aa67a00 100644
--- a/hw/xwayland/xwayland-input.c
+++ b/hw/xwayland/xwayland-input.c
@@ -47,6 +47,21 @@ struct sync_pending {
 };
 
 static void
+xwl_pointer_warp_emulator_handle_motion(struct xwl_pointer_warp_emulator *warp_emulator,
+                                        double dx,
+                                        double dy,
+                                        double dx_unaccel,
+                                        double dy_unaccel);
+static void
+xwl_pointer_warp_emulator_maybe_lock(struct xwl_pointer_warp_emulator *warp_emulator,
+                                     struct xwl_window *xwl_window,
+                                     SpritePtr sprite,
+                                     int x, int y);
+
+static void
+xwl_seat_destroy_confined_pointer(struct xwl_seat *xwl_seat);
+
+static void
 xwl_pointer_control(DeviceIntPtr device, PtrCtrl *ctrl)
 {
     /* Nothing to do, dix handles all settings */
@@ -320,6 +335,12 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer,
         xwl_seat->cursor_frame_cb = NULL;
         xwl_seat_set_cursor(xwl_seat);
     }
+
+    if (xwl_seat->pointer_warp_emulator) {
+        xwl_pointer_warp_emulator_maybe_lock(xwl_seat->pointer_warp_emulator,
+                                             xwl_seat->focus_window,
+                                             NULL, 0, 0);
+    }
 }
 
 static void
@@ -340,8 +361,22 @@ dispatch_pointer_motion_event(struct xwl_seat *xwl_seat)
 {
     ValuatorMask mask;
 
-    if (xwl_seat->pending_pointer_event.has_absolute ||
+    if (xwl_seat->pointer_warp_emulator &&
         xwl_seat->pending_pointer_event.has_relative) {
+        double dx;
+        double dy;
+        double dx_unaccel;
+        double dy_unaccel;
+
+        dx = xwl_seat->pending_pointer_event.dx;
+        dy = xwl_seat->pending_pointer_event.dy;
+        dx_unaccel = xwl_seat->pending_pointer_event.dx_unaccel;
+        dy_unaccel = xwl_seat->pending_pointer_event.dy_unaccel;
+        xwl_pointer_warp_emulator_handle_motion(xwl_seat->pointer_warp_emulator,
+                                                dx, dy,
+                                                dx_unaccel, dy_unaccel);
+    } else if (xwl_seat->pending_pointer_event.has_absolute ||
+               xwl_seat->pending_pointer_event.has_relative) {
         int x;
         int y;
 
@@ -1236,6 +1271,233 @@ xwl_seat_clear_touch(struct xwl_seat *xwl_seat, WindowPtr window)
     }
 }
 
+static void
+xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_emulator,
+                                       int x,
+                                       int y)
+{
+    struct zwp_locked_pointer_v1 *locked_pointer =
+        warp_emulator->locked_pointer;
+    WindowPtr window;
+    int sx, sy;
+
+    if (!warp_emulator->locked_pointer)
+        return;
+
+    window = warp_emulator->xwl_seat->focus_window->window;
+    if (x >= window->drawable.x ||
+        y >= window->drawable.y ||
+        x < (window->drawable.x + window->drawable.width) ||
+        y < (window->drawable.x + window->drawable.height)) {
+        sx = x - window->drawable.x;
+        sy = y - window->drawable.y;
+        zwp_locked_pointer_v1_set_cursor_position_hint(locked_pointer,
+                                                       wl_fixed_from_int(sx),
+                                                       wl_fixed_from_int(sy));
+        wl_surface_commit(warp_emulator->xwl_seat->focus_window->surface);
+    }
+}
+
+static Bool
+xwl_pointer_warp_emulator_is_locked(struct xwl_pointer_warp_emulator *warp_emulator)
+{
+    if (warp_emulator->locked_pointer)
+        return TRUE;
+    else
+        return FALSE;
+}
+
+static void
+xwl_pointer_warp_emulator_lock(struct xwl_pointer_warp_emulator *warp_emulator)
+{
+    struct xwl_seat *xwl_seat = warp_emulator->xwl_seat;
+    struct xwl_screen *xwl_screen = xwl_seat->xwl_screen;
+    struct zwp_pointer_constraints_v1 *pointer_constraints =
+        xwl_screen->pointer_constraints;
+    struct xwl_window *lock_window = xwl_seat->focus_window;
+
+    warp_emulator->locked_window = lock_window;
+
+    warp_emulator->locked_pointer =
+        zwp_pointer_constraints_v1_lock_pointer(pointer_constraints,
+                                                lock_window->surface,
+                                                xwl_seat->wl_pointer,
+                                                NULL,
+                                                ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
+}
+
+static void
+xwl_pointer_warp_emulator_maybe_lock(struct xwl_pointer_warp_emulator *warp_emulator,
+                                     struct xwl_window *xwl_window,
+                                     SpritePtr sprite,
+                                     int x, int y)
+{
+    struct xwl_seat *xwl_seat = warp_emulator->xwl_seat;
+    GrabPtr pointer_grab = xwl_seat->pointer->deviceGrab.grab;
+
+    if (warp_emulator->locked_pointer)
+        return;
+
+    /*
+     * If there is no grab, and the window doesn't have pointer focus, ignore
+     * the warp, as under Wayland it won't receive input anyway.
+     */
+    if (!pointer_grab && xwl_seat->focus_window != xwl_window)
+        return;
+
+    /*
+     * If there is a grab, but it's not an ownerEvents grab and the destination
+     * is not the pointer focus, ignore it, as events wouldn't be delivered
+     * there anyway.
+     */
+    if (pointer_grab &&
+        !pointer_grab->ownerEvents &&
+        XYToWindow(sprite, x, y) != xwl_seat->focus_window->window)
+        return;
+
+    xwl_pointer_warp_emulator_lock(warp_emulator);
+}
+
+static void
+xwl_pointer_warp_emulator_warp(struct xwl_pointer_warp_emulator *warp_emulator,
+                               struct xwl_window *xwl_window,
+                               SpritePtr sprite,
+                               int x, int y)
+{
+    xwl_pointer_warp_emulator_maybe_lock(warp_emulator,
+                                         xwl_window,
+                                         sprite,
+                                         x, y);
+    xwl_pointer_warp_emulator_set_fake_pos(warp_emulator, x, y);
+}
+
+static void
+xwl_pointer_warp_emulator_handle_motion(struct xwl_pointer_warp_emulator *warp_emulator,
+                                        double dx,
+                                        double dy,
+                                        double dx_unaccel,
+                                        double dy_unaccel)
+{
+    struct xwl_seat *xwl_seat = warp_emulator->xwl_seat;
+    ValuatorMask mask;
+    WindowPtr window;
+    int x, y;
+
+    valuator_mask_zero(&mask);
+    valuator_mask_set_unaccelerated(&mask, 0, dx, dx_unaccel);
+    valuator_mask_set_unaccelerated(&mask, 1, dy, dy_unaccel);
+
+    QueuePointerEvents(xwl_seat->relative_pointer, MotionNotify, 0,
+                       POINTER_RELATIVE, &mask);
+
+    window = xwl_seat->focus_window->window;
+    miPointerGetPosition(xwl_seat->pointer, &x, &y);
+
+    if (xwl_pointer_warp_emulator_is_locked(warp_emulator) &&
+        xwl_seat->cursor_confinement_window != warp_emulator->locked_window &&
+        (x < window->drawable.x ||
+         y < window->drawable.y ||
+         x >= (window->drawable.x + window->drawable.width) ||
+         y >= (window->drawable.x + window->drawable.height)))
+        xwl_seat_destroy_pointer_warp_emulator(xwl_seat);
+    else
+        xwl_pointer_warp_emulator_set_fake_pos(warp_emulator, x, y);
+}
+
+static struct xwl_pointer_warp_emulator *
+xwl_pointer_warp_emulator_create(struct xwl_seat *xwl_seat)
+{
+    struct xwl_pointer_warp_emulator *warp_emulator;
+
+    warp_emulator = calloc(sizeof *warp_emulator, 1);
+    if (!warp_emulator) {
+        ErrorF("xwl_pointer_warp_emulator_create ENOMEM");
+        return NULL;
+    }
+
+    warp_emulator->xwl_seat = xwl_seat;
+
+    return warp_emulator;
+}
+
+static void
+xwl_pointer_warp_emulator_destroy(struct xwl_pointer_warp_emulator *warp_emulator)
+{
+    if (warp_emulator->locked_pointer)
+        zwp_locked_pointer_v1_destroy(warp_emulator->locked_pointer);
+    free(warp_emulator);
+}
+
+static void
+xwl_seat_create_pointer_warp_emulator(struct xwl_seat *xwl_seat)
+{
+    if (xwl_seat->confined_pointer)
+        xwl_seat_destroy_confined_pointer(xwl_seat);
+
+    xwl_seat->pointer_warp_emulator =
+        xwl_pointer_warp_emulator_create(xwl_seat);
+}
+
+static Bool
+xwl_seat_can_emulate_pointer_warp(struct xwl_seat *xwl_seat)
+{
+    struct xwl_screen *xwl_screen = xwl_seat->xwl_screen;
+
+    if (!xwl_screen->relative_pointer_manager)
+        return FALSE;
+
+    if (!xwl_screen->pointer_constraints)
+        return FALSE;
+
+    return TRUE;
+}
+
+void
+xwl_seat_emulate_pointer_warp(struct xwl_seat *xwl_seat,
+                              struct xwl_window *xwl_window,
+                              SpritePtr sprite,
+                              int x, int y)
+{
+    if (!xwl_seat_can_emulate_pointer_warp(xwl_seat))
+        return;
+
+    if (xwl_seat->x_cursor != NULL)
+        return;
+
+    if (!xwl_seat->pointer_warp_emulator)
+        xwl_seat_create_pointer_warp_emulator(xwl_seat);
+
+    if (!xwl_seat->pointer_warp_emulator)
+        return;
+
+    xwl_pointer_warp_emulator_warp(xwl_seat->pointer_warp_emulator,
+                                   xwl_window,
+                                   sprite,
+                                   x, y);
+}
+
+void
+xwl_seat_cursor_visibility_changed(struct xwl_seat *xwl_seat)
+{
+    if (xwl_seat->pointer_warp_emulator && xwl_seat->x_cursor != NULL)
+        xwl_seat_destroy_pointer_warp_emulator(xwl_seat);
+}
+
+void
+xwl_seat_destroy_pointer_warp_emulator(struct xwl_seat *xwl_seat)
+{
+    if (!xwl_seat->pointer_warp_emulator)
+        return;
+
+    xwl_pointer_warp_emulator_destroy(xwl_seat->pointer_warp_emulator);
+    xwl_seat->pointer_warp_emulator = NULL;
+
+    if (xwl_seat->cursor_confinement_window) {
+        xwl_seat_confine_pointer(xwl_seat,
+                                 xwl_seat->cursor_confinement_window);
+    }
+}
+
 void
 xwl_seat_confine_pointer(struct xwl_seat *xwl_seat,
                          struct xwl_window *xwl_window)
@@ -1253,6 +1515,9 @@ xwl_seat_confine_pointer(struct xwl_seat *xwl_seat,
 
     xwl_seat->cursor_confinement_window = xwl_window;
 
+    if (xwl_seat->pointer_warp_emulator)
+        return;
+
     xwl_seat->confined_pointer =
         zwp_pointer_constraints_v1_confine_pointer(pointer_constraints,
                                                    xwl_window->surface,
diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c
index d22aba3..02c2250 100644
--- a/hw/xwayland/xwayland.c
+++ b/hw/xwayland/xwayland.c
@@ -164,6 +164,28 @@ xwl_screen_get_default_seat(struct xwl_screen *xwl_screen)
 }
 
 static void
+xwl_cursor_warped_to(DeviceIntPtr device,
+                     ScreenPtr screen,
+                     ClientPtr client,
+                     WindowPtr window,
+                     SpritePtr sprite,
+                     int x, int y)
+{
+    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
+    struct xwl_seat *xwl_seat = device->public.devicePrivate;
+    struct xwl_window *xwl_window;
+
+    if (!xwl_seat)
+        xwl_seat = xwl_screen_get_default_seat(xwl_screen);
+
+    xwl_window = xwl_window_from_window(window);
+    if (!xwl_window)
+        return;
+
+    xwl_seat_emulate_pointer_warp(xwl_seat, xwl_window, sprite, x, y);
+}
+
+static void
 xwl_cursor_confined_to(DeviceIntPtr device,
                        ScreenPtr screen,
                        WindowPtr window)
@@ -383,6 +405,10 @@ xwl_unrealize_window(WindowPtr window)
         if (xwl_seat->cursor_confinement_window &&
             xwl_seat->cursor_confinement_window->window == window)
             xwl_seat_unconfine_pointer(xwl_seat);
+        if (xwl_seat->pointer_warp_emulator &&
+            xwl_seat->pointer_warp_emulator->locked_window &&
+            xwl_seat->pointer_warp_emulator->locked_window->window == window)
+            xwl_seat_destroy_pointer_warp_emulator(xwl_seat);
         xwl_seat_clear_touch(xwl_seat, window);
     }
 
@@ -782,6 +808,7 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
     xwl_screen->CloseScreen = pScreen->CloseScreen;
     pScreen->CloseScreen = xwl_close_screen;
 
+    pScreen->CursorWarpedTo = xwl_cursor_warped_to;
     pScreen->CursorConfinedTo = xwl_cursor_confined_to;
 
     return ret;
diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h
index d62f49a..2e59d7f 100644
--- a/hw/xwayland/xwayland.h
+++ b/hw/xwayland/xwayland.h
@@ -119,6 +119,12 @@ struct xwl_touch {
     struct xorg_list link_touch;
 };
 
+struct xwl_pointer_warp_emulator {
+    struct xwl_seat *xwl_seat;
+    struct xwl_window *locked_window;
+    struct zwp_locked_pointer_v1 *locked_pointer;
+};
+
 struct xwl_seat {
     DeviceIntPtr pointer;
     DeviceIntPtr relative_pointer;
@@ -149,6 +155,8 @@ struct xwl_seat {
 
     struct xorg_list sync_pending;
 
+    struct xwl_pointer_warp_emulator *pointer_warp_emulator;
+
     struct xwl_window *cursor_confinement_window;
     struct zwp_confined_pointer_v1 *confined_pointer;
 
@@ -190,6 +198,15 @@ void xwl_seat_destroy(struct xwl_seat *xwl_seat);
 
 void xwl_seat_clear_touch(struct xwl_seat *xwl_seat, WindowPtr window);
 
+void xwl_seat_emulate_pointer_warp(struct xwl_seat *xwl_seat,
+                                   struct xwl_window *xwl_window,
+                                   SpritePtr sprite,
+                                   int x, int y);
+
+void xwl_seat_destroy_pointer_warp_emulator(struct xwl_seat *xwl_seat);
+
+void xwl_seat_cursor_visibility_changed(struct xwl_seat *xwl_seat);
+
 void xwl_seat_confine_pointer(struct xwl_seat *xwl_seat,
                               struct xwl_window *xwl_window);
 void xwl_seat_unconfine_pointer(struct xwl_seat *xwl_seat);
-- 
2.7.4



More information about the xorg-devel mailing list