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

Peter Hutterer peter.hutterer at who-t.net
Mon Sep 12 03:38:57 UTC 2016


On Thu, Sep 08, 2016 at 06:46:41PM +0800, Jonas Ådahl wrote:
> 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)) {

typo, drawable.y

> +        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)))

typo, drawable.y

> +        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");

use __func__ here please.

Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net> otherwise

Cheers,
   Peter

> +        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