xserver: Branch 'xwayland-22.1' - 11 commits

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Wed Sep 14 08:21:25 UTC 2022


 .gitlab-ci/debian-install.sh  |    2 
 dix/resource.c                |   11 ++-
 dix/window.c                  |    4 -
 hw/xwayland/xwayland-input.c  |  116 +++++++++++++++++++++++-------------------
 hw/xwayland/xwayland-input.h  |   10 +++
 hw/xwayland/xwayland-screen.c |   49 +++++++++++++++++
 hw/xwayland/xwayland-screen.h |    3 +
 xkb/xkb.c                     |   54 +++++++++++++++----
 8 files changed, 179 insertions(+), 70 deletions(-)

New commits:
commit 0f1c39de0404699e5290457d77501ca76f5a50cb
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Mon Sep 5 16:12:57 2022 +0200

    xwayland: Prevent Xserver grabs with rootless
    
    Because of the design of most Wayland compositors, where the compositor
    is both a Wayland server and an X11 window manager, any X11 client
    issuing a server grab (i.e. XGrabServer()) can possibly hang the whole
    desktop when Xwayland is running rootless.
    
    This can happen with e.g. ImageMagick's import command with mutter.
    
    1. "import" is launched and issues an XServerGrab(),
    2. Xwayland restricts access to that "import" X11 client alone,
    3. mutter continues to process events until it needs to sync with
       Xwayland (there's variability in time before the hang occurs),
    4. When mutter does an XSync() (explicitly or implicitly through some
       other Xlib call), it will stop waiting for Xwayland to reply,
    5. Xwayland waits for the XServerGrab() to be released by import,
    6. "import" waits for a user input to release the XServerGrab(),
    7. mutter is stuck waiting on Xwayland and does not process input
       events...
    
    To prevent this, re-route the GrabServer/UngrabServer requests and
    pretend the grab works but actually does nothing at all for all clients
    but the X11 window manager (which can still issue X11 server grabs, at
    its own risks).
    
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
    Closes: https://bugzilla.redhat.com/1914021
    (cherry picked from commit a77d95af61c09c91927d1aa4b9860b728b357cdb)

diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c
index 5c8b388f0..17280659e 100644
--- a/hw/xwayland/xwayland-screen.c
+++ b/hw/xwayland/xwayland-screen.c
@@ -580,6 +580,53 @@ xwl_screen_roundtrip(struct xwl_screen *xwl_screen)
         xwl_give_up("could not connect to wayland server\n");
 }
 
+static int
+xwl_server_grab(ClientPtr client)
+{
+    struct xwl_screen *xwl_screen;
+
+    /* Allow GrabServer for the X11 window manager.
+     * Xwayland only has 1 screen (no Zaphod for Xwayland) so we check
+     * for the first and only screen here.
+     */
+    xwl_screen = xwl_screen_get(screenInfo.screens[0]);
+    if (xwl_screen->wm_client_id == client->index)
+        return xwl_screen->GrabServer(client);
+
+    /* For all other clients, just pretend it works for compatibility,
+       but do nothing */
+    return Success;
+}
+
+static int
+xwl_server_ungrab(ClientPtr client)
+{
+    struct xwl_screen *xwl_screen;
+
+    /* Same as above, allow UngrabServer for the X11 window manager only */
+    xwl_screen = xwl_screen_get(screenInfo.screens[0]);
+    if (xwl_screen->wm_client_id == client->index)
+        return xwl_screen->UngrabServer(client);
+
+    /* For all other clients, just pretend it works for compatibility,
+       but do nothing */
+    return Success;
+}
+
+static void
+xwl_screen_setup_custom_vector(struct xwl_screen *xwl_screen)
+{
+    /* Rootfull Xwayland does not need a custom ProcVector (yet?) */
+    if (xwl_screen->rootless)
+        return;
+
+    xwl_screen->GrabServer = ProcVector[X_GrabServer];
+    xwl_screen->UngrabServer = ProcVector[X_UngrabServer];
+
+    ProcVector[X_GrabServer] = xwl_server_grab;
+    ProcVector[X_UngrabServer] = xwl_server_ungrab;
+}
+
 Bool
 xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
 {
@@ -791,6 +838,8 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
     AddCallback(&PropertyStateCallback, xwl_property_callback, pScreen);
     AddCallback(&RootWindowFinalizeCallback, xwl_root_window_finalized_callback, pScreen);
 
+    xwl_screen_setup_custom_vector(xwl_screen);
+
     xwl_screen_roundtrip(xwl_screen);
 
     return ret;
diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h
index f04d431c7..32dfd0ddb 100644
--- a/hw/xwayland/xwayland-screen.h
+++ b/hw/xwayland/xwayland-screen.h
@@ -68,6 +68,9 @@ struct xwl_screen {
     ResizeWindowProcPtr ResizeWindow;
     MoveWindowProcPtr MoveWindow;
 
+    int (*GrabServer) (ClientPtr client);
+    int (*UngrabServer) (ClientPtr client);
+
     struct xorg_list output_list;
     struct xorg_list seat_list;
     struct xorg_list damage_window_list;
commit 42559ea039ad3b47669f1a4c33c39ecdef71ae64
Author: Michel Dänzer <mdaenzer at redhat.com>
Date:   Fri Aug 19 12:51:20 2022 +0200

    dix: Skip more code in SetRootClip for ROOT_CLIP_INPUT_ONLY
    
    Despite e957a2e5dd28 ("dix: Add hybrid full-size/empty-clip mode to
    SetRootClip"), I was still seeing all X11 client windows flashing when
    the root window size changes with rootless Xwayland (e.g. due to
    hotplugging a monitor).
    
    Skipping this code for ROOT_CLIP_INPUT_ONLY fixes the issue for me.
    
    (cherry picked from commit f778b56a742cf9df6641f2ec525120e859c74fa5)

diff --git a/dix/window.c b/dix/window.c
index cd917f006..284aa6dd7 100644
--- a/dix/window.c
+++ b/dix/window.c
@@ -3642,7 +3642,7 @@ SetRootClip(ScreenPtr pScreen, int enable)
     if (!pWin)
         return;
     WasViewable = (Bool) (pWin->viewable);
-    if (WasViewable) {
+    if (WasViewable && mode != ROOT_CLIP_INPUT_ONLY) {
         for (pChild = pWin->firstChild; pChild; pChild = pChild->nextSib) {
             (void) (*pScreen->MarkOverlappedWindows) (pChild,
                                                       pChild, &pLayerWin);
@@ -3696,7 +3696,7 @@ SetRootClip(ScreenPtr pScreen, int enable)
 
     ResizeChildrenWinSize(pWin, 0, 0, 0, 0);
 
-    if (WasViewable) {
+    if (WasViewable && mode != ROOT_CLIP_INPUT_ONLY) {
         if (pWin->firstChild) {
             anyMarked |= (*pScreen->MarkOverlappedWindows) (pWin->firstChild,
                                                             pWin->firstChild,
commit 8b739579ed8e43d91f6d09768b54858e4068ef85
Author: Luc Ma <luc at sietium.com>
Date:   Tue Aug 30 08:16:22 2022 +0800

    ci: remove redundant slash in libxcvt repository url
    
    Fixes: 84897891e5e7 ("ci: Install libxcvt from git")
    Signed-off-by: Luc Ma <luc at sietium.com>
    (cherry picked from commit 4603d64483bd16966da59fc9c3cf5a7a9591da0c)

diff --git a/.gitlab-ci/debian-install.sh b/.gitlab-ci/debian-install.sh
index dbb361785..f44b53af7 100644
--- a/.gitlab-ci/debian-install.sh
+++ b/.gitlab-ci/debian-install.sh
@@ -102,7 +102,7 @@ apt-get install -y \
 cd /root
 
 # xserver requires libxcvt
-git clone https://gitlab.freedesktop.org/xorg/lib//libxcvt.git --depth 1 --branch=libxcvt-0.1.0
+git clone https://gitlab.freedesktop.org/xorg/lib/libxcvt.git --depth 1 --branch=libxcvt-0.1.0
 cd libxcvt
 meson _build
 ninja -C _build -j${FDO_CI_CONCURRENT:-4} install
commit be614dbfa66e7d80f34926f12b52e7909c9f0e48
Author: FeepingCreature <feepingcreature at gmail.com>
Date:   Sat Aug 20 20:12:31 2022 +0200

    xkb: Avoid length-check failure on empty strings.
    
    _XkbCheckRequestBounds assumes that from..to is at least one byte.
    However, request strings can be empty, causing spurious failures in
    XkbGetKbdByName calls. To avoid this, before checking bounds make
    sure that the length is nonzero.
    
    (cherry picked from commit 79c572fbd349cb6373f685a502e430552f856995)

diff --git a/xkb/xkb.c b/xkb/xkb.c
index 0f5739b58..83c6250ad 100644
--- a/xkb/xkb.c
+++ b/xkb/xkb.c
@@ -5818,11 +5818,11 @@ GetComponentSpec(ClientPtr client, xkbGetKbdByNameReq *stuff,
         return NULL;
     }
     len = (*(unsigned char *) wire++);
-    if (!_XkbCheckRequestBounds(client, stuff, wire, wire + len)) {
-        *errRtrn = BadLength;
-        return NULL;
-    }
     if (len > 0) {
+        if (!_XkbCheckRequestBounds(client, stuff, wire, wire + len)) {
+            *errRtrn = BadLength;
+            return NULL;
+        }
         str = calloc(1, len + 1);
         if (str) {
             tmp = str;
commit 6e61cdc0955e8054a70a0238a8c6fb4c16a14bff
Author: Olivier Fourdan <ofourdan at redhat.com>
Date:   Tue Jul 12 14:44:48 2022 +0200

    dix: Fix overzealous caching of ResourceClientBits()
    
    Commit c7311654 cached the value of ResourceClientBits(), but that value
    depends on the `MaxClients` value set either from the command line or
    from the configuration file.
    
    For the latter, a call to ResourceClientBits() is issued before the
    configuration file is read, meaning that the cached value is from the
    default, not from the maximum number of clients set in the configuration
    file.
    
    That obviously causes all sort of issues, including memory corruption
    and crashes of the Xserver when reaching the default limit value.
    
    To avoid that issue, also keep the LimitClient value, and recompute the
    ilog2() value if that changes, as on startup when the value is set from
    the the xorg.conf ServerFlags section.
    
    v2: Drop the `cache == 0` test
        Rename cache vars
    
    Fixes: c7311654 - dix: cache ResourceClientBits() value
    Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1310
    Signed-off-by: Olivier Fourdan <ofourdan at redhat.com>
    Reviewed-by: Adam Jackson <ajax at redhat.com>
    (cherry picked from commit 2efa6d659508346358a1ef27b2393e18843f66a3)

diff --git a/dix/resource.c b/dix/resource.c
index 6c0be2e04..055e6934e 100644
--- a/dix/resource.c
+++ b/dix/resource.c
@@ -620,12 +620,15 @@ ilog2(int val)
 unsigned int
 ResourceClientBits(void)
 {
-    static unsigned int cached = 0;
+    static unsigned int cache_ilog2 = 0;
+    static unsigned int cache_limit = 0;
 
-    if (cached == 0)
-      cached = ilog2(LimitClients);
+    if (LimitClients != cache_limit) {
+        cache_limit = LimitClients;
+        cache_ilog2 = ilog2(LimitClients);
+    }
 
-    return cached;
+    return cache_ilog2;
 }
 
 /*****************
commit cb4fd4d06ee8bd71b7176f58ecad70b69e3702d8
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Tue Jul 5 12:06:20 2022 +1000

    xkb: proof GetCountedString against request length attacks
    
    GetCountedString did a check for the whole string to be within the
    request buffer but not for the initial 2 bytes that contain the length
    field. A swapped client could send a malformed request to trigger a
    swaps() on those bytes, writing into random memory.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    (cherry picked from commit 11beef0b7f1ed290348e45618e5fa0d2bffcb72e)

diff --git a/xkb/xkb.c b/xkb/xkb.c
index 3084a535b..0f5739b58 100644
--- a/xkb/xkb.c
+++ b/xkb/xkb.c
@@ -5138,6 +5138,11 @@ _GetCountedString(char **wire_inout, ClientPtr client, char **str)
     CARD16 len;
 
     wire = *wire_inout;
+
+    if (client->req_len <
+        bytes_to_int32(wire + 2 - (char *) client->requestBuffer))
+        return BadValue;
+
     len = *(CARD16 *) wire;
     if (client->swapped) {
         swaps(&len);
commit eed7f2b1c8f6744546bbc92f1d90205b19c8d707
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Wed Jul 13 11:38:16 2022 +1000

    xkb: length-check XkbListComponents before accessing the fields
    
    Each string length field was accessed before checking whether that byte
    was actually part of the client request. No real harm here since it
    would immediately fail with BadLength anyway, but let's be correct here.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    (cherry picked from commit 1bb7767f19969ee6b109f7424ff97738752d18c9)

diff --git a/xkb/xkb.c b/xkb/xkb.c
index 887d30f6d..3084a535b 100644
--- a/xkb/xkb.c
+++ b/xkb/xkb.c
@@ -5871,6 +5871,8 @@ ProcXkbListComponents(ClientPtr client)
      * length wrong. */
     str = (unsigned char *) &stuff[1];
     for (i = 0; i < 6; i++) {
+        if (!_XkbCheckRequestBounds(client, stuff, str, str + 1))
+            return BadLength;
         size = *((uint8_t *)str);
         len = (str + size + 1) - ((unsigned char *) stuff);
         if ((XkbPaddedSize(len) / 4) > stuff->length)
commit bd7b4cf64d965eccfae8fd462b83ab5ae57e13ea
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Wed Jul 13 11:33:00 2022 +1000

    xkb: length-check XkbGetKbdByName before accessing the fields
    
    This request accessed &stuff[1] before length-checking everything. The
    check was performed afterwards so invalid requests would return
    BadLength anyway, but let's do this before we actually access the
    memory.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    (cherry picked from commit 44ae6f44192f5edeaf5ac082870bbdf3262071ef)

diff --git a/xkb/xkb.c b/xkb/xkb.c
index 116b7b665..887d30f6d 100644
--- a/xkb/xkb.c
+++ b/xkb/xkb.c
@@ -5795,7 +5795,8 @@ static unsigned char componentExprLegal[] = {
 };
 
 static char *
-GetComponentSpec(unsigned char **pWire, Bool allowExpr, int *errRtrn)
+GetComponentSpec(ClientPtr client, xkbGetKbdByNameReq *stuff,
+                 unsigned char **pWire, Bool allowExpr, int *errRtrn)
 {
     int len;
     register int i;
@@ -5807,7 +5808,15 @@ GetComponentSpec(unsigned char **pWire, Bool allowExpr, int *errRtrn)
         legal = &componentSpecLegal[0];
 
     wire = *pWire;
+    if (!_XkbCheckRequestBounds(client, stuff, wire, wire + 1)) {
+        *errRtrn = BadLength;
+        return NULL;
+    }
     len = (*(unsigned char *) wire++);
+    if (!_XkbCheckRequestBounds(client, stuff, wire, wire + len)) {
+        *errRtrn = BadLength;
+        return NULL;
+    }
     if (len > 0) {
         str = calloc(1, len + 1);
         if (str) {
@@ -5937,17 +5946,17 @@ ProcXkbGetKbdByName(ClientPtr client)
     status = Success;
     str = (unsigned char *) &stuff[1];
     {
-        char *keymap = GetComponentSpec(&str, TRUE, &status);  /* keymap, unsupported */
+        char *keymap = GetComponentSpec(client, stuff, &str, TRUE, &status);  /* keymap, unsupported */
         if (keymap) {
             free(keymap);
             return BadMatch;
         }
     }
-    names.keycodes = GetComponentSpec(&str, TRUE, &status);
-    names.types = GetComponentSpec(&str, TRUE, &status);
-    names.compat = GetComponentSpec(&str, TRUE, &status);
-    names.symbols = GetComponentSpec(&str, TRUE, &status);
-    names.geometry = GetComponentSpec(&str, TRUE, &status);
+    names.keycodes = GetComponentSpec(client, stuff, &str, TRUE, &status);
+    names.types = GetComponentSpec(client, stuff, &str, TRUE, &status);
+    names.compat = GetComponentSpec(client, stuff, &str, TRUE, &status);
+    names.symbols = GetComponentSpec(client, stuff, &str, TRUE, &status);
+    names.geometry = GetComponentSpec(client, stuff, &str, TRUE, &status);
     if (status == Success) {
         len = str - ((unsigned char *) stuff);
         if ((XkbPaddedSize(len) / 4) != stuff->length)
commit baad076c4df664092158d2822b244ef69ff8edaa
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Wed Jul 13 11:23:09 2022 +1000

    xkb: fix some possible memleaks in XkbGetKbdByName
    
    GetComponentByName returns an allocated string, so let's free that if we
    fail somewhere.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    (cherry picked from commit 18f91b950e22c2a342a4fbc55e9ddf7534a707d2)

diff --git a/xkb/xkb.c b/xkb/xkb.c
index d896073ca..116b7b665 100644
--- a/xkb/xkb.c
+++ b/xkb/xkb.c
@@ -5936,18 +5936,32 @@ ProcXkbGetKbdByName(ClientPtr client)
     xkb = dev->key->xkbInfo->desc;
     status = Success;
     str = (unsigned char *) &stuff[1];
-    if (GetComponentSpec(&str, TRUE, &status))  /* keymap, unsupported */
-        return BadMatch;
+    {
+        char *keymap = GetComponentSpec(&str, TRUE, &status);  /* keymap, unsupported */
+        if (keymap) {
+            free(keymap);
+            return BadMatch;
+        }
+    }
     names.keycodes = GetComponentSpec(&str, TRUE, &status);
     names.types = GetComponentSpec(&str, TRUE, &status);
     names.compat = GetComponentSpec(&str, TRUE, &status);
     names.symbols = GetComponentSpec(&str, TRUE, &status);
     names.geometry = GetComponentSpec(&str, TRUE, &status);
-    if (status != Success)
+    if (status == Success) {
+        len = str - ((unsigned char *) stuff);
+        if ((XkbPaddedSize(len) / 4) != stuff->length)
+            status = BadLength;
+    }
+
+    if (status != Success) {
+        free(names.keycodes);
+        free(names.types);
+        free(names.compat);
+        free(names.symbols);
+        free(names.geometry);
         return status;
-    len = str - ((unsigned char *) stuff);
-    if ((XkbPaddedSize(len) / 4) != stuff->length)
-        return BadLength;
+    }
 
     CHK_MASK_LEGAL(0x01, stuff->want, XkbGBN_AllComponentsMask);
     CHK_MASK_LEGAL(0x02, stuff->need, XkbGBN_AllComponentsMask);
commit 900b0ed0df0d4c3e96be7e45bd47ddeeb4ccb574
Author: Peter Hutterer <peter.hutterer at who-t.net>
Date:   Tue Jul 26 16:04:41 2022 +1000

    xwayland: correct the type for the discrete scroll events
    
    Not that it actually matters since the typedef is int32_t anyway, but
    this theoretically avoids an erroneous call to wl_fixed_to_double() on
    that value.
    
    Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
    (cherry picked from commit 354e39eefa010cad2c05ccbf80729bb942b9536a)

diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c
index a5bcc735d..e1bacb9fc 100644
--- a/hw/xwayland/xwayland-input.c
+++ b/hw/xwayland/xwayland-input.c
@@ -611,8 +611,8 @@ dispatch_scroll_motion(struct xwl_seat *xwl_seat)
     const int divisor = 10;
     wl_fixed_t dy = xwl_seat->pending_pointer_event.scroll_dy;
     wl_fixed_t dx = xwl_seat->pending_pointer_event.scroll_dx;
-    wl_fixed_t discrete_dy = xwl_seat->pending_pointer_event.scroll_discrete_dy;
-    wl_fixed_t discrete_dx = xwl_seat->pending_pointer_event.scroll_discrete_dx;
+    int32_t discrete_dy = xwl_seat->pending_pointer_event.scroll_discrete_dy;
+    int32_t discrete_dx = xwl_seat->pending_pointer_event.scroll_discrete_dx;
 
     valuator_mask_zero(&mask);
     if (xwl_seat->pending_pointer_event.has_vertical_scroll)
commit 2db46363542bfd0f62f20f5a898fef07424ffccc
Author: David Jacewicz <david.jacewicz27 at protonmail.com>
Date:   Fri Jun 17 15:13:13 2022 -0400

    xwayland: Aggregate scroll axis events to fix kinetic scrolling
    
    Pointer scroll events are collected in xwl_seat->pending_pointer_event
    as they are received in the pointer_handle_axis and
    pointer_handle_axis_discrete callbacks. They are dispatched together as a
    single event when pointer_handle_frame is called which "Indicates the end of a
    set of events that logically belong together" [1]. This patch also sends an
    event with dx=0, dy=0 when pointer_handle_axis_stop is called, which is what
    allows XWayland clients to recognise the end of a touchpad scroll.
    
    [1] https://wayland.app/protocols/wayland#wl_pointer:event:frame
    
    Signed-off-by: David Jacewicz <david.jacewicz27 at protonmail.com>
    Fixes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/926
    Reviewed-by: Peter Hutterer <peter.hutterer at who-t.net>
    Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1367
    (cherry picked from commit e37eeb7af2a84d32f0a74feb00613efda6bc966e)
    (cherry picked from commit f0b2eeaf2fe406cc0b788bb3e4ff8c7a7653613d)

diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c
index eda69a193..a5bcc735d 100644
--- a/hw/xwayland/xwayland-input.c
+++ b/hw/xwayland/xwayland-input.c
@@ -50,12 +50,6 @@
 #include "pointer-gestures-unstable-v1-client-protocol.h"
 #include "xwayland-keyboard-grab-unstable-v1-client-protocol.h"
 
-struct axis_discrete_pending {
-    struct xorg_list l;
-    uint32_t axis;
-    int32_t discrete;
-};
-
 struct sync_pending {
     struct xorg_list l;
     DeviceIntPtr pending_dev;
@@ -610,6 +604,36 @@ dispatch_relative_motion(struct xwl_seat *xwl_seat)
                        POINTER_RAWONLY, &mask);
 }
 
+static void
+dispatch_scroll_motion(struct xwl_seat *xwl_seat)
+{
+    ValuatorMask mask;
+    const int divisor = 10;
+    wl_fixed_t dy = xwl_seat->pending_pointer_event.scroll_dy;
+    wl_fixed_t dx = xwl_seat->pending_pointer_event.scroll_dx;
+    wl_fixed_t discrete_dy = xwl_seat->pending_pointer_event.scroll_discrete_dy;
+    wl_fixed_t discrete_dx = xwl_seat->pending_pointer_event.scroll_discrete_dx;
+
+    valuator_mask_zero(&mask);
+    if (xwl_seat->pending_pointer_event.has_vertical_scroll)
+        valuator_mask_set_double(&mask,
+                                 3,
+                                 wl_fixed_to_double(dy) / divisor);
+    else if (xwl_seat->pending_pointer_event.has_vertical_scroll_discrete)
+        valuator_mask_set(&mask, 3, discrete_dy);
+
+    if (xwl_seat->pending_pointer_event.has_horizontal_scroll)
+        valuator_mask_set_double(&mask,
+                                 2,
+                                 wl_fixed_to_double(dx) / divisor);
+    else if (xwl_seat->pending_pointer_event.has_horizontal_scroll_discrete)
+        valuator_mask_set(&mask, 2, discrete_dx);
+
+    QueuePointerEvents(get_pointer_device(xwl_seat),
+                       MotionNotify, 0, POINTER_RELATIVE, &mask);
+}
+
+
 static void
 dispatch_pointer_motion_event(struct xwl_seat *xwl_seat)
 {
@@ -626,8 +650,18 @@ dispatch_pointer_motion_event(struct xwl_seat *xwl_seat)
             dispatch_absolute_motion(xwl_seat);
     }
 
+    if (xwl_seat->pending_pointer_event.has_vertical_scroll ||
+        xwl_seat->pending_pointer_event.has_horizontal_scroll ||
+        xwl_seat->pending_pointer_event.has_vertical_scroll_discrete ||
+        xwl_seat->pending_pointer_event.has_horizontal_scroll_discrete)
+        dispatch_scroll_motion(xwl_seat);
+
     xwl_seat->pending_pointer_event.has_absolute = FALSE;
     xwl_seat->pending_pointer_event.has_relative = FALSE;
+    xwl_seat->pending_pointer_event.has_vertical_scroll = FALSE;
+    xwl_seat->pending_pointer_event.has_horizontal_scroll = FALSE;
+    xwl_seat->pending_pointer_event.has_vertical_scroll_discrete = FALSE;
+    xwl_seat->pending_pointer_event.has_horizontal_scroll_discrete = FALSE;
 }
 
 static void
@@ -684,42 +718,17 @@ pointer_handle_axis(void *data, struct wl_pointer *pointer,
                     uint32_t time, uint32_t axis, wl_fixed_t value)
 {
     struct xwl_seat *xwl_seat = data;
-    int index;
-    const int divisor = 10;
-    ValuatorMask mask;
-    struct axis_discrete_pending *pending = NULL;
-    struct axis_discrete_pending *iter;
 
     switch (axis) {
     case WL_POINTER_AXIS_VERTICAL_SCROLL:
-        index = 3;
+        xwl_seat->pending_pointer_event.has_vertical_scroll = TRUE;
+        xwl_seat->pending_pointer_event.scroll_dy = value;
         break;
     case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
-        index = 2;
+        xwl_seat->pending_pointer_event.has_horizontal_scroll = TRUE;
+        xwl_seat->pending_pointer_event.scroll_dx = value;
         break;
-    default:
-        return;
-    }
-
-    xorg_list_for_each_entry(iter, &xwl_seat->axis_discrete_pending, l) {
-        if (iter->axis == axis) {
-            pending = iter;
-            break;
-        }
     }
-
-    valuator_mask_zero(&mask);
-
-    if (pending) {
-        valuator_mask_set(&mask, index, pending->discrete);
-        xorg_list_del(&pending->l);
-        free(pending);
-    } else {
-        valuator_mask_set_double(&mask, index, wl_fixed_to_double(value) / divisor);
-    }
-
-    QueuePointerEvents(get_pointer_device(xwl_seat),
-                       MotionNotify, 0, POINTER_RELATIVE, &mask);
 }
 
 static void
@@ -742,6 +751,18 @@ static void
 pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
                          uint32_t time, uint32_t axis)
 {
+    struct xwl_seat *xwl_seat = data;
+
+    switch (axis) {
+    case WL_POINTER_AXIS_VERTICAL_SCROLL:
+        xwl_seat->pending_pointer_event.has_vertical_scroll = TRUE;
+        xwl_seat->pending_pointer_event.scroll_dy = 0;
+        break;
+    case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
+        xwl_seat->pending_pointer_event.has_horizontal_scroll = TRUE;
+        xwl_seat->pending_pointer_event.scroll_dx = 0;
+        break;
+    }
 }
 
 static void
@@ -750,14 +771,16 @@ pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer,
 {
     struct xwl_seat *xwl_seat = data;
 
-    struct axis_discrete_pending *pending = malloc(sizeof *pending);
-    if (!pending)
-        return;
-
-    pending->axis = axis;
-    pending->discrete = discrete;
-
-    xorg_list_add(&pending->l, &xwl_seat->axis_discrete_pending);
+    switch (axis) {
+    case WL_POINTER_AXIS_VERTICAL_SCROLL:
+        xwl_seat->pending_pointer_event.has_vertical_scroll_discrete = TRUE;
+        xwl_seat->pending_pointer_event.scroll_discrete_dy = discrete;
+        break;
+    case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
+        xwl_seat->pending_pointer_event.has_horizontal_scroll_discrete = TRUE;
+        xwl_seat->pending_pointer_event.scroll_discrete_dx = discrete;
+        break;
+    }
 }
 
 static const struct wl_pointer_listener pointer_listener = {
@@ -1718,7 +1741,6 @@ create_input_device(struct xwl_screen *xwl_screen, uint32_t id, uint32_t version
     wl_array_init(&xwl_seat->keys);
 
     xorg_list_init(&xwl_seat->touches);
-    xorg_list_init(&xwl_seat->axis_discrete_pending);
     xorg_list_init(&xwl_seat->sync_pending);
 }
 
@@ -1727,7 +1749,6 @@ xwl_seat_destroy(struct xwl_seat *xwl_seat)
 {
     struct xwl_touch *xwl_touch, *next_xwl_touch;
     struct sync_pending *p, *npd;
-    struct axis_discrete_pending *ad, *ad_next;
 
     xorg_list_for_each_entry_safe(xwl_touch, next_xwl_touch,
                                   &xwl_seat->touches, link_touch) {
@@ -1740,11 +1761,6 @@ xwl_seat_destroy(struct xwl_seat *xwl_seat)
         free (p);
     }
 
-    xorg_list_for_each_entry_safe(ad, ad_next, &xwl_seat->axis_discrete_pending, l) {
-        xorg_list_del(&ad->l);
-        free(ad);
-    }
-
     release_tablet_manager_seat(xwl_seat);
 
     release_grab(xwl_seat);
diff --git a/hw/xwayland/xwayland-input.h b/hw/xwayland/xwayland-input.h
index dbe215bdb..4fd175b13 100644
--- a/hw/xwayland/xwayland-input.h
+++ b/hw/xwayland/xwayland-input.h
@@ -93,7 +93,6 @@ struct xwl_seat {
     char *keymap;
     struct wl_surface *keyboard_focus;
 
-    struct xorg_list axis_discrete_pending;
     struct xorg_list sync_pending;
 
     struct xwl_pointer_warp_emulator *pointer_warp_emulator;
@@ -111,6 +110,15 @@ struct xwl_seat {
         double dy;
         double dx_unaccel;
         double dy_unaccel;
+
+        wl_fixed_t scroll_dy;
+        wl_fixed_t scroll_dx;
+        int32_t scroll_discrete_dy;
+        int32_t scroll_discrete_dx;
+        Bool has_vertical_scroll;
+        Bool has_horizontal_scroll;
+        Bool has_vertical_scroll_discrete;
+        Bool has_horizontal_scroll_discrete;
     } pending_pointer_event;
 
     struct xorg_list tablets;


More information about the xorg-commit mailing list