[PATCH v2 xserver 9/9] xwayland: add tablet pad support

Peter Hutterer peter.hutterer at who-t.net
Thu Feb 9 23:06:18 UTC 2017


On Thu, Feb 09, 2017 at 12:14:47PM -0800, Jason Gerecke wrote:
> On Tue, Feb 7, 2017 at 6:42 PM, Peter Hutterer <peter.hutterer at who-t.net> wrote:
> > Hooked up a bit differently to the other tools. Those tools can be static for
> > all and be re-used. The wacom driver initializes the pad with the correct
> > number of buttons though and we can't do this until we have the pad done event.
> >
> > If the tablet is removed and we plug a different one in, we should initialize
> > that correctly, so unlike the other tools the pad is properly removed and
> > re-initialized on plug.
> >
> > Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> > ---
> > New in this series
> >
> >  hw/xwayland/xwayland-input.c | 417 +++++++++++++++++++++++++++++++++++++++++++
> >  hw/xwayland/xwayland.h       |  28 +++
> >  2 files changed, 445 insertions(+)
> >
> > diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c
> > index 31e2473..1079ff4 100644
> > --- a/hw/xwayland/xwayland-input.c
> > +++ b/hw/xwayland/xwayland-input.c
> > @@ -1334,6 +1334,7 @@ tablet_handle_removed(void *data, struct zwp_tablet_v2 *tablet)
> >              DisableDevice(xwl_seat->eraser, TRUE);
> >          if (xwl_seat->puck)
> >              DisableDevice(xwl_seat->puck, TRUE);
> > +        /* pads are removed separately */
> >      }
> >
> >      zwp_tablet_v2_destroy(tablet);
> > @@ -1694,6 +1695,418 @@ static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = {
> >  };
> >
> >  static void
> > +tablet_pad_ring_destroy(struct xwl_tablet_pad_ring *ring)
> > +{
> > +    zwp_tablet_pad_ring_v2_destroy(ring->ring);
> > +    xorg_list_del(&ring->link);
> > +    free(ring);
> > +}
> > +
> > +static void
> > +tablet_pad_ring_source(void *data,
> > +                       struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2,
> > +                       uint32_t source)
> > +{
> > +}
> > +
> > +static void
> > +tablet_pad_ring_angle(void *data,
> > +                      struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2,
> > +                      wl_fixed_t degrees)
> > +{
> > +    struct xwl_tablet_pad_ring *ring = data;
> > +    struct xwl_tablet_pad *pad = ring->group->pad;
> > +    double deg = wl_fixed_to_double(degrees);
> > +    ValuatorMask mask;
> > +
> > +    valuator_mask_zero(&mask);
> > +    valuator_mask_set(&mask, 5 + ring->index, deg/360.0  * 71);
> > +    QueuePointerEvents(pad->xdevice, MotionNotify, 0, 0, &mask);
> > +}
> > +
> > +static void
> > +tablet_pad_ring_stop(void *data,
> > +                     struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2)
> > +{
> > +}
> > +
> > +static void
> > +tablet_pad_ring_frame(void *data,
> > +                      struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2,
> > +                      uint32_t time)
> > +{
> > +}
> > +
> > +static const struct zwp_tablet_pad_ring_v2_listener tablet_pad_ring_listener = {
> > +    tablet_pad_ring_source,
> > +    tablet_pad_ring_angle,
> > +    tablet_pad_ring_stop,
> > +    tablet_pad_ring_frame,
> > +};
> > +
> > +
> > +static void
> > +tablet_pad_strip_destroy(struct xwl_tablet_pad_strip *strip)
> > +{
> > +    zwp_tablet_pad_strip_v2_destroy(strip->strip);
> > +    xorg_list_del(&strip->link);
> > +    free(strip);
> > +}
> > +
> > +static void
> > +tablet_pad_strip_source(void *data,
> > +                        struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2,
> > +                        uint32_t source)
> > +{
> > +}
> > +
> > +static void
> > +tablet_pad_strip_position(void *data,
> > +                          struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2,
> > +                          uint32_t position)
> > +{
> > +    struct xwl_tablet_pad_strip *strip = data;
> > +    struct xwl_tablet_pad *pad = strip->group->pad;
> > +    ValuatorMask mask;
> > +
> > +    valuator_mask_zero(&mask);
> > +    valuator_mask_set(&mask, 3 + strip->index, position/65535.0 * 2048);
> 
> Note: the values reported by xf86-input-wacom are the raw hardware
> values which use a walking bit to indicate where the finger is along
> the strip. When your finger is at the top of the strip, we report '1';
> moving down slightly '2', then '4', then '8', and so on until you
> reach the bottom and we report '4096'.
> 
> The linear relationship you use here won't be compatible with software
> that expects the exponential relationship used by xf86-input-wacom.
> You could potentially use something like round(pow(2.0,
> 12*position/65535.0)) to get the "expected" values, along with
> changing the axis maximums to 4096.

hmm. for the libinput xorg driver I also just forward the strip events
as-is, in the hope that eventually we can get rid of the pow2 which is
effectively just a long-standing bug that we never removed for fear of
breaking applications. Do we *know*  about applications that actually rely
on this exact behaviour and that cannot change?

I'm really uncomfortable continuing this in xwayland (and the libinput xorg
driver).

Cheers,
   Peter

> > +    QueuePointerEvents(pad->xdevice, MotionNotify, 0, 0, &mask);
> > +}
> > +
> > +static void
> > +tablet_pad_strip_stop(void *data,
> > +                      struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2)
> > +{
> > +}
> > +
> > +static void
> > +tablet_pad_strip_frame(void *data,
> > +                       struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2,
> > +                       uint32_t time)
> > +{
> > +}
> > +
> > +static const struct zwp_tablet_pad_strip_v2_listener tablet_pad_strip_listener = {
> > +    tablet_pad_strip_source,
> > +    tablet_pad_strip_position,
> > +    tablet_pad_strip_stop,
> > +    tablet_pad_strip_frame,
> > +};
> > +
> > +static void
> > +tablet_pad_group_destroy(struct xwl_tablet_pad_group *group)
> > +{
> > +    struct xwl_tablet_pad_ring *r, *tr;
> > +    struct xwl_tablet_pad_strip *s, *ts;
> > +
> > +    xorg_list_for_each_entry_safe(r, tr,
> > +                                  &group->pad_group_ring_list,
> > +                                  link)
> > +        tablet_pad_ring_destroy(r);
> > +
> > +    xorg_list_for_each_entry_safe(s, ts,
> > +                                  &group->pad_group_strip_list,
> > +                                  link)
> > +        tablet_pad_strip_destroy(s);
> > +
> > +    zwp_tablet_pad_group_v2_destroy(group->group);
> > +    xorg_list_del(&group->link);
> > +    free(group);
> > +}
> > +
> > +static void
> > +tablet_pad_group_buttons(void *data,
> > +                         struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2,
> > +                         struct wl_array *buttons)
> > +{
> > +
> > +}
> > +
> > +static void
> > +tablet_pad_group_ring(void *data,
> > +                      struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2,
> > +                      struct zwp_tablet_pad_ring_v2 *wp_ring)
> > +{
> > +    static unsigned int ring_index = 0;
> > +    struct xwl_tablet_pad_group *group = data;
> > +    struct xwl_tablet_pad_ring *ring;
> > +
> > +    ring = calloc(1, sizeof *ring);
> > +    if (ring == NULL) {
> > +        ErrorF("%s ENOMEM\n", __func__);
> > +        return;
> > +    }
> > +
> > +    ring->index = ring_index++;
> > +    ring->group = group;
> > +    ring->ring = wp_ring;
> > +
> > +    xorg_list_add(&ring->link, &group->pad_group_ring_list);
> > +
> > +    zwp_tablet_pad_ring_v2_add_listener(wp_ring, &tablet_pad_ring_listener,
> > +                                        ring);
> > +}
> > +
> > +static void
> > +tablet_pad_group_strip(void *data,
> > +                       struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2,
> > +                       struct zwp_tablet_pad_strip_v2 *wp_strip)
> > +{
> > +    static unsigned int strip_index = 0;
> > +    struct xwl_tablet_pad_group *group = data;
> > +    struct xwl_tablet_pad_strip *strip;
> > +
> > +    strip = calloc(1, sizeof *strip);
> > +    if (strip == NULL) {
> > +        ErrorF("%s ENOMEM\n", __func__);
> > +        return;
> > +    }
> > +
> > +    strip->index = strip_index++;
> > +    strip->group = group;
> > +    strip->strip = wp_strip;
> > +
> > +    xorg_list_add(&strip->link, &group->pad_group_strip_list);
> > +
> > +    zwp_tablet_pad_strip_v2_add_listener(wp_strip, &tablet_pad_strip_listener,
> > +                                         strip);
> > +}
> > +
> > +static void
> > +tablet_pad_group_modes(void *data,
> > +                       struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2,
> > +                       uint32_t modes)
> > +{
> > +
> > +}
> > +
> > +static void
> > +tablet_pad_group_done(void *data,
> > +                      struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2)
> > +{
> > +
> > +}
> > +
> > +static void
> > +tablet_pad_group_mode_switch(void *data,
> > +                             struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2,
> > +                             uint32_t time,
> > +                             uint32_t serial,
> > +                             uint32_t mode)
> > +{
> > +
> > +}
> > +
> > +static struct zwp_tablet_pad_group_v2_listener tablet_pad_group_listener = {
> > +    tablet_pad_group_buttons,
> > +    tablet_pad_group_ring,
> > +    tablet_pad_group_strip,
> > +    tablet_pad_group_modes,
> > +    tablet_pad_group_done,
> > +    tablet_pad_group_mode_switch,
> > +};
> > +
> > +static int
> > +xwl_tablet_pad_proc(DeviceIntPtr device, int what)
> > +{
> > +    struct xwl_tablet_pad *pad = device->public.devicePrivate;
> > +    /* Axis layout mirrors that of xf86-input-wacom to have better
> > +       compatibility with existing clients */
> > +#define NAXES 7
> > +    Atom axes_labels[NAXES] = { 0 };
> > +    BYTE map[MAX_BUTTONS + 1];
> > +    int i = 0;
> > +    Atom btn_labels[MAX_BUTTONS] = { 0 }; /* btn labels are meaningless */
> > +    int nbuttons;
> > +
> > +    switch (what) {
> > +    case DEVICE_INIT:
> > +        device->public.on = FALSE;
> > +
> > +        axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X);
> > +        axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_Y);
> > +        /* The others have no good mapping */
> > +
> > +        if (!InitValuatorClassDeviceStruct(device, NAXES, axes_labels,
> > +                                           GetMotionHistorySize(), Absolute))
> > +            return BadValue;
> > +
> > +        for (i = 1; i <= MAX_BUTTONS; i++)
> > +            map[i] = i;
> > +
> > +        /* We need at least 7 buttons to allow scrolling */
> > +        nbuttons = min(max(pad->nbuttons + 4, 7), MAX_BUTTONS);
> > +
> > +        if (!InitButtonClassDeviceStruct(device, nbuttons,
> > +                                         btn_labels, map))
> > +            return BadValue;
> > +
> > +        /* Valuators */
> > +        InitValuatorAxisStruct(device, 0, axes_labels[0],
> > +                               0, 100, 1, 0, 1, Absolute);
> > +        InitValuatorAxisStruct(device, 1, axes_labels[1],
> > +                               0, 100, 1, 0, 1, Absolute);
> > +        /* Pressure - unused, for backwards compat only */
> > +        InitValuatorAxisStruct(device, 2, axes_labels[2],
> > +                               0, 2048, 1, 0, 1, Absolute);
> > +        /* strip x */
> > +        InitValuatorAxisStruct(device, 3, axes_labels[3],
> > +                               0, 2048, 1, 0, 1, Absolute);
> > +        /* strip y */
> > +        InitValuatorAxisStruct(device, 4, axes_labels[4],
> > +                               0, 2048, 1, 0, 1, Absolute);
> > +        /* ring */
> > +        InitValuatorAxisStruct(device, 5, axes_labels[5],
> > +                               0, 71, 1, 0, 1, Absolute);
> > +        /* ring2 */
> > +        InitValuatorAxisStruct(device, 6, axes_labels[6],
> > +                               0, 71, 1, 0, 1, Absolute);
> > +
> > +        if (!InitPtrFeedbackClassDeviceStruct(device, xwl_pointer_control))
> > +            return BadValue;
> > +
> > +        return Success;
> > +
> > +    case DEVICE_ON:
> > +        device->public.on = TRUE;
> > +        return Success;
> > +
> > +    case DEVICE_OFF:
> > +    case DEVICE_CLOSE:
> > +        device->public.on = FALSE;
> > +        return Success;
> > +    }
> > +
> > +    return BadMatch;
> > +#undef NAXES
> > +}
> > +
> > +static void
> > +tablet_pad_group(void *data,
> > +                 struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
> > +                 struct zwp_tablet_pad_group_v2 *pad_group)
> > +{
> > +    struct xwl_tablet_pad *pad = data;
> > +    struct xwl_tablet_pad_group *group;
> > +
> > +    group = calloc(1, sizeof *group);
> > +    if (pad == NULL) {
> > +        ErrorF("%s ENOMEM\n", __func__);
> > +        return;
> > +    }
> > +
> > +    group->pad = pad;
> > +    group->group = pad_group;
> > +    xorg_list_init(&group->pad_group_ring_list);
> > +    xorg_list_init(&group->pad_group_strip_list);
> > +
> > +    xorg_list_add(&group->link, &pad->pad_group_list);
> > +
> > +    zwp_tablet_pad_group_v2_add_listener(pad_group,
> > +                                         &tablet_pad_group_listener,
> > +                                         group);
> > +}
> > +
> > +static void
> > +tablet_pad_path(void *data,
> > +                struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
> > +                const char *path)
> > +{
> > +
> > +}
> > +
> > +static void
> > +tablet_pad_buttons(void *data,
> > +                   struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
> > +                   uint32_t buttons)
> > +{
> > +    struct xwl_tablet_pad *pad = data;
> > +
> > +    pad->nbuttons = buttons;
> > +}
> > +
> > +static void
> > +tablet_pad_done(void *data,
> > +                struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2)
> > +{
> > +    struct xwl_tablet_pad *pad = data;
> > +
> > +    pad->xdevice = add_device(pad->seat, "xwayland-pad",
> > +                              xwl_tablet_pad_proc);
> > +    pad->xdevice->public.devicePrivate = pad;
> > +    ActivateDevice(pad->xdevice, TRUE);
> > +    EnableDevice(pad->xdevice, TRUE);
> > +}
> > +
> > +static void
> > +tablet_pad_button(void *data,
> > +                  struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
> > +                  uint32_t time,
> > +                  uint32_t button,
> > +                  uint32_t state)
> > +{
> > +    struct xwl_tablet_pad *pad = data;
> > +    ValuatorMask mask;
> > +
> > +    button++; /* wayland index vs X's 1-offset */
> > +    /* skip scroll wheel buttons 4-7 */
> > +    button = button > 3 ? button + 4 : button;
> > +
> > +    valuator_mask_zero(&mask);
> > +    QueuePointerEvents(pad->xdevice,
> > +                       state ? ButtonPress : ButtonRelease, button, 0, &mask);
> > +}
> > +
> > +static void
> > +tablet_pad_enter(void *data,
> > +                 struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
> > +                 uint32_t serial,
> > +                 struct zwp_tablet_v2 *tablet,
> > +                 struct wl_surface *surface)
> > +{
> > +    /* pairs the pad with the tablet but also to set the focus. We
> > +     * don't care about the pairing and always use X's focus */
> > +}
> > +
> > +static void
> > +tablet_pad_leave(void *data,
> > +                 struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
> > +                 uint32_t serial,
> > +                 struct wl_surface *surface)
> > +{
> > +    /* pairs the pad with the tablet but also to set the focus. We
> > +     * don't care about the pairing and always use X's focus */
> > +}
> > +
> > +static void
> > +tablet_pad_removed(void *data,
> > +                   struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2)
> > +{
> > +    struct xwl_tablet_pad *pad = data;
> > +    struct xwl_tablet_pad_group *g, *tg;
> > +
> > +    xorg_list_for_each_entry_safe(g, tg, &pad->pad_group_list, link)
> > +        tablet_pad_group_destroy(g);
> > +
> > +    RemoveDevice(pad->xdevice, TRUE);
> > +    xorg_list_del(&pad->link);
> > +    zwp_tablet_pad_v2_destroy(pad->pad);
> > +    free(pad);
> > +}
> > +
> > +static const struct zwp_tablet_pad_v2_listener tablet_pad_listener = {
> > +    tablet_pad_group,
> > +    tablet_pad_path,
> > +    tablet_pad_buttons,
> > +    tablet_pad_done,
> > +    tablet_pad_button,
> > +    tablet_pad_enter,
> > +    tablet_pad_leave,
> > +    tablet_pad_removed,
> > +};
> > +
> > +static void
> >  tablet_seat_handle_add_tablet(void *data, struct zwp_tablet_seat_v2 *tablet_seat,
> >                                struct zwp_tablet_v2 *tablet)
> >  {
> > @@ -1762,8 +2175,12 @@ tablet_seat_handle_add_pad(void *data, struct zwp_tablet_seat_v2 *tablet_seat,
> >
> >      xwl_tablet_pad->pad = pad;
> >      xwl_tablet_pad->seat = xwl_seat;
> > +    xorg_list_init(&xwl_tablet_pad->pad_group_list);
> >
> >      xorg_list_add(&xwl_tablet_pad->link, &xwl_seat->tablet_pads);
> > +
> > +    zwp_tablet_pad_v2_add_listener(pad, &tablet_pad_listener,
> > +                                   xwl_tablet_pad);
> >  }
> >
> >  static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
> > diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h
> > index eceac32..40d08b6 100644
> > --- a/hw/xwayland/xwayland.h
> > +++ b/hw/xwayland/xwayland.h
> > @@ -216,10 +216,38 @@ struct xwl_tablet_tool {
> >      struct xwl_cursor cursor;
> >  };
> >
> > +struct xwl_tablet_pad_ring {
> > +    unsigned int index;
> > +    struct xorg_list link;
> > +    struct xwl_tablet_pad_group *group;
> > +    struct zwp_tablet_pad_ring_v2 *ring;
> > +};
> > +
> > +struct xwl_tablet_pad_strip {
> > +    unsigned int index;
> > +    struct xorg_list link;
> > +    struct xwl_tablet_pad_group *group;
> > +    struct zwp_tablet_pad_strip_v2 *strip;
> > +};
> > +
> > +struct xwl_tablet_pad_group {
> > +    struct xorg_list link;
> > +    struct xwl_tablet_pad *pad;
> > +    struct zwp_tablet_pad_group_v2 *group;
> > +
> > +    struct xorg_list pad_group_ring_list;
> > +    struct xorg_list pad_group_strip_list;
> > +};
> > +
> >  struct xwl_tablet_pad {
> >      struct xorg_list link;
> >      struct zwp_tablet_pad_v2 *pad;
> >      struct xwl_seat *seat;
> > +
> > +    DeviceIntPtr xdevice;
> > +
> > +    unsigned int nbuttons;
> > +    struct xorg_list pad_group_list;
> >  };
> >
> >  struct xwl_output {
> > --
> > 2.9.3
> >
> 


More information about the xorg-devel mailing list