On 13/03/19(Wed) 00:41, Ulf Brosziewski wrote:
> The standard method of scrolling in X is tailored to mouse wheels and
> proceeds in coarse steps.  Wheel events are mapped to button events, and on
> receiving such an event, an application moves the view of its data by some
> fixed distance - usually the height of a line of text, or of a couple of
> lines.
> 
> Version 2.1 of the X Input Protocol has introduced a more precise
> alternative.  It defines additional types of motion events.  In essence,
> their values represent fractions of a complete scroll unit, and newer
> applications may move their views by distances that are proportional to the
> event values.  For applications that don't support this, X generates the
> standard button events whenever the values add up to the complete unit.
> 
> synaptics(4) supports the newer method since long.
> 
> The diffs below add the feature to ws and wstpad.  The kernel part defines
> two new event types in wsconsio.h, and it adapts the scrolling functions of
> the touchpad input driver.  The xenocara part adds the new "axes" and event
> handlers to ws.
> 
> There is a little twist to the implementation.  While synaptics(4)
> initializes the scroll axes with the scroll distance in device units, the
> constant 4096 is used in the new ws code, and event values represent the
> fraction (motion_delta / scroll_unit) in [*.12] fixed-point format.  That
> way, no queries for the device- and configuration-dependent scroll unit
> are necessary.
> 
> The X Input Protocol calls the method "smooth scrolling", but it seems
> that nowadays, this term is used exclusively for the rendering technique
> that displays a little animation when the document position changes, so
> "precision scrolling" might be a better choice.
> 
> Tests, comments, and OKs would be welcome.

I like it.  Implementation is nice.  I find the *_EV defines confusing.
Why not use the WSCONS_* defines directly, it would make easier for the
reader to find which code generates which event in a single grep.  But
that's not new ;)

Do you know if it would make sense to get rid of the standard scrolling
method?  Could we generate such events for all input devices?  Does
other X drivers do that already or plan to do it?

> Index: dev/wscons/wsconsio.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/wscons/wsconsio.h,v
> retrieving revision 1.90
> diff -u -p -r1.90 wsconsio.h
> --- dev/wscons/wsconsio.h     10 Nov 2018 14:27:51 -0000      1.90
> +++ dev/wscons/wsconsio.h     12 Mar 2019 21:55:11 -0000
> @@ -112,6 +112,12 @@ struct wscons_event {
>  #define      WSCONS_EVENT_TOUCH_RESET        25      /* (no value) */
> 
>  /*
> + * Precision Scrolling
> + */
> +#define WSCONS_EVENT_HSCROLL         26      /* dx * 4096 / scroll_unit */
> +#define WSCONS_EVENT_VSCROLL         27      /* dy * 4096 / scroll_unit */
> +
> +/*
>   * Keyboard ioctls (0 - 31)
>   */
> 
> Index: dev/wscons/wsmouse.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/wscons/wsmouse.c,v
> retrieving revision 1.51
> diff -u -p -r1.51 wsmouse.c
> --- dev/wscons/wsmouse.c      19 Feb 2019 07:01:02 -0000      1.51
> +++ dev/wscons/wsmouse.c      12 Mar 2019 21:55:11 -0000
> @@ -1034,10 +1034,18 @@ wsmouse_motion_sync(struct wsmouseinput
>                       wsmouse_evq_put(evq, DELTA_X_EV(input), dx);
>               if (dy)
>                       wsmouse_evq_put(evq, DELTA_Y_EV(input), dy);
> -             if (motion->dz)
> -                     wsmouse_evq_put(evq, DELTA_Z_EV, motion->dz);
> -             if (motion->dw)
> -                     wsmouse_evq_put(evq, DELTA_W_EV, motion->dw);
> +             if (motion->dz) {
> +                     if (IS_TOUCHPAD(input))
> +                             wsmouse_evq_put(evq, VSCROLL_EV, motion->dz);
> +                     else
> +                             wsmouse_evq_put(evq, DELTA_Z_EV, motion->dz);
> +             }
> +             if (motion->dw) {
> +                     if (IS_TOUCHPAD(input))
> +                             wsmouse_evq_put(evq, HSCROLL_EV, motion->dw);
> +                     else
> +                             wsmouse_evq_put(evq, DELTA_W_EV, motion->dw);
> +             }
>       }
>       if (motion->sync & SYNC_POSITION) {
>               if (motion->sync & SYNC_X) {
> Index: dev/wscons/wsmouseinput.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/wscons/wsmouseinput.h,v
> retrieving revision 1.12
> diff -u -p -r1.12 wsmouseinput.h
> --- dev/wscons/wsmouseinput.h 10 Nov 2018 14:27:51 -0000      1.12
> +++ dev/wscons/wsmouseinput.h 12 Mar 2019 21:55:12 -0000
> @@ -210,6 +210,8 @@ int wstpad_set_param(struct wsmouseinput
>      WSCONS_EVENT_MOUSE_ABSOLUTE_X : WSCONS_EVENT_MOUSE_ABSOLUTE_Y)
>  #define DELTA_Z_EV   WSCONS_EVENT_MOUSE_DELTA_Z
>  #define DELTA_W_EV   WSCONS_EVENT_MOUSE_DELTA_W
> +#define VSCROLL_EV   WSCONS_EVENT_VSCROLL
> +#define HSCROLL_EV   WSCONS_EVENT_HSCROLL
>  #define ABS_Z_EV     WSCONS_EVENT_TOUCH_PRESSURE
>  #define ABS_W_EV     WSCONS_EVENT_TOUCH_CONTACTS
>  #define BTN_DOWN_EV  WSCONS_EVENT_MOUSE_DOWN
> Index: dev/wscons/wstpad.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/wscons/wstpad.c,v
> retrieving revision 1.22
> diff -u -p -r1.22 wstpad.c
> --- dev/wscons/wstpad.c       29 Dec 2018 21:03:58 -0000      1.22
> +++ dev/wscons/wstpad.c       12 Mar 2019 21:55:12 -0000
> @@ -167,8 +167,6 @@ struct wstpad {
>       u_int mtcycle;
>       u_int ignore;
> 
> -     int dx;
> -     int dy;
>       int contacts;
>       int prev_contacts;
>       u_int btns;
> @@ -223,12 +221,11 @@ struct wstpad {
>       } tap;
> 
>       struct {
> -             int acc_dx;
> -             int acc_dy;
>               int dz;
>               int dw;
>               int hdist;
>               int vdist;
> +             int mag;
>       } scroll;
>  };
> 
> @@ -435,8 +432,8 @@ set_freeze_ts(struct wstpad *tp, int sec
> 
> 
>  /* Return TRUE if two-finger- or edge-scrolling would be valid. */
> -static inline int
> -chk_scroll_state(struct wsmouseinput *input)
> +int
> +wstpad_scroll_coords(struct wsmouseinput *input, int *dx, int *dy)
>  {
>       struct wstpad *tp = input->tp;
> 
> @@ -451,40 +448,43 @@ chk_scroll_state(struct wsmouseinput *in
>        * a short delay, is only applied initially, a touch that stops and
>        * resumes scrolling is not affected.
>        */
> -     if (tp->scroll.dz || tp->scroll.dw || wstpad_is_stable(input, tp->t))
> -             return (tp->dx || tp->dy);
> +     if (tp->scroll.dz || tp->scroll.dw || wstpad_is_stable(input, tp->t)) {
> +             *dx = normalize_rel(&input->filter.h, input->motion.pos.dx);
> +             *dy = normalize_rel(&input->filter.v, input->motion.pos.dy);
> +             return (*dx || *dy);
> +     }
> 
>       return (0);
>  }
> 
>  void
> -wstpad_scroll(struct wstpad *tp, int dx, int dy, u_int *cmds)
> +wstpad_scroll(struct wstpad *tp, int dx, int dy, int mag, u_int *cmds)
>  {
> -     int sign;
> +     int dz, dw, n = 1;
> 
> -     /* Scrolling is either horizontal or vertical, but not both. */
> -
> -     sign = (dy > 0) - (dy < 0);
> -     if (sign) {
> -             if (tp->scroll.dz != -sign) {
> -                     tp->scroll.dz = -sign;
> -                     tp->scroll.acc_dy = -tp->scroll.vdist;
> -             }
> -             tp->scroll.acc_dy += abs(dy);
> -             if (tp->scroll.acc_dy >= 0) {
> -                     tp->scroll.acc_dy -= tp->scroll.vdist;
> -                     *cmds |= 1 << VSCROLL;
> -             }
> -     } else if ((sign = (dx > 0) - (dx < 0))) {
> -             if (tp->scroll.dw != sign) {
> -                     tp->scroll.dw = sign;
> -                     tp->scroll.acc_dx = -tp->scroll.hdist;
> -             }
> -             tp->scroll.acc_dx += abs(dx);
> -             if (tp->scroll.acc_dx >= 0) {
> -                     tp->scroll.acc_dx -= tp->scroll.hdist;
> -                     *cmds |= 1 << HSCROLL;
> -             }
> +     /*
> +      * The function applies strong deceleration, but only to input with
> +      * very low speeds.  A higher threshold might make applications
> +      * without support for precision scrolling appear unresponsive.
> +      */
> +     mag = tp->scroll.mag = imin(MAG_MEDIUM,
> +         (mag + 3 * tp->scroll.mag) / 4);
> +     if (mag < MAG_LOW)
> +             n = (MAG_LOW - mag) / 4096 + 1;
> +
> +     if (dy && tp->scroll.vdist) {
> +             dz = -dy * 4096 / (tp->scroll.vdist * n);
> +             if (tp->scroll.dz && (dy < 0 == tp->scroll.dz > 0))
> +                     dz = (dz + 3 * tp->scroll.dz) / 4;
> +             tp->scroll.dz = dz;
> +             *cmds |= 1 << VSCROLL;
> +
> +     } else if (dx && tp->scroll.hdist) {
> +             dw = dx * 4096 / (tp->scroll.hdist * n);
> +             if (tp->scroll.dw && (dx > 0 == tp->scroll.dw > 0))
> +                     dw = (dw + 3 * tp->scroll.dw) / 4;
> +             tp->scroll.dw = dw;
> +             *cmds |= 1 << HSCROLL;
>       }
>  }
> 
> @@ -502,12 +502,14 @@ wstpad_f2scroll(struct wsmouseinput *inp
>               return;
>       }
> 
> -     if (!chk_scroll_state(input))
> +     if (!wstpad_scroll_coords(input, &dx, &dy))
>               return;
> 
>       dir = tp->t->dir;
> -     dy = NORTH(dir) || SOUTH(dir) ? tp->dy : 0;
> -     dx = EAST(dir) || WEST(dir) ? tp->dx : 0;
> +     if (!(NORTH(dir) || SOUTH(dir)))
> +             dy = 0;
> +     if (!(EAST(dir) || WEST(dir)))
> +             dx = 0;
> 
>       if (dx || dy) {
>               centered = CENTERED(tp->t);
> @@ -526,7 +528,8 @@ wstpad_f2scroll(struct wsmouseinput *inp
>                       centered |= CENTERED(t2);
>               }
>               if (centered) {
> -                     wstpad_scroll(tp, dx, dy, cmds);
> +                     wstpad_scroll(tp, dx, dy,
> +                         magnitude(input, dx, dy), cmds);
>                       set_freeze_ts(tp, 0, FREEZE_MS);
>               }
>       }
> @@ -540,17 +543,19 @@ wstpad_edgescroll(struct wsmouseinput *i
>       u_int v_edge, b_edge;
>       int dx, dy;
> 
> -     if (tp->contacts != 1 || !chk_scroll_state(input))
> +     if (!wstpad_scroll_coords(input, &dx, &dy) || tp->contacts != 1)
>               return;
> 
>       v_edge = (tp->features & WSTPAD_SWAPSIDES) ? L_EDGE : R_EDGE;
>       b_edge = (tp->features & WSTPAD_HORIZSCROLL) ? B_EDGE : 0;
> 
> -     dy = (t->flags & v_edge) ? tp->dy : 0;
> -     dx = (t->flags & b_edge) ? tp->dx : 0;
> +     if ((t->flags & v_edge) == 0)
> +             dy = 0;
> +     if ((t->flags & b_edge) == 0)
> +             dx = 0;
> 
>       if (dx || dy)
> -             wstpad_scroll(tp, dx, dy, cmds);
> +             wstpad_scroll(tp, dx, dy, magnitude(input, dx, dy), cmds);
>  }
> 
>  static inline u_int
> @@ -1121,20 +1126,7 @@ wstpad_touch_inputs(struct wsmouseinput
>  {
>       struct wstpad *tp = input->tp;
>       struct tpad_touch *t;
> -     int slot;
> -
> -     /*
> -      * Use the normalized, hysteresis-filtered, but otherwise untransformed
> -      * relative coordinates of the pointer-controlling touch for filtering
> -      * and scrolling.
> -      */
> -     if ((input->motion.sync & SYNC_POSITION)
> -         && !wsmouse_hysteresis(input, &input->motion.pos)) {
> -             tp->dx = normalize_rel(&input->filter.h, input->motion.pos.dx);
> -             tp->dy = normalize_rel(&input->filter.v, input->motion.pos.dy);
> -     } else {
> -             tp->dx = tp->dy = 0;
> -     }
> +     int slot, x, y, dx, dy;
> 
>       tp->btns = input->btn.buttons;
>       tp->btns_sync = input->btn.sync;
> @@ -1158,8 +1150,6 @@ wstpad_touch_inputs(struct wsmouseinput
>               wstpad_mt_masks(input);
>       } else {
>               t = tp->t;
> -             t->x = normalize_abs(&input->filter.h, t->pos->x);
> -             t->y = normalize_abs(&input->filter.v, t->pos->y);
>               if (tp->contacts)
>                       t->state = (tp->prev_contacts ?
>                           TOUCH_UPDATE : TOUCH_BEGIN);
> @@ -1167,17 +1157,25 @@ wstpad_touch_inputs(struct wsmouseinput
>                       t->state = (tp->prev_contacts ?
>                           TOUCH_END : TOUCH_NONE);
> 
> +             dx = dy = 0;
> +             x = normalize_abs(&input->filter.h, t->pos->x);
> +             y = normalize_abs(&input->filter.v, t->pos->y);
>               if (t->state == TOUCH_BEGIN) {
> -                     t->orig.x = t->x;
> -                     t->orig.y = t->y;
> +                     t->x = t->orig.x = x;
> +                     t->y = t->orig.y = y;
>                       memcpy(&t->orig.time, &tp->time,
>                           sizeof(struct timespec));
> -                     t->flags = edge_flags(tp, t->x, t->y);
> -             } else {
> -                     t->flags &= (~EDGES | edge_flags(tp, t->x, t->y));
> +                     t->flags = edge_flags(tp, x, y);
> +             } else if (input->motion.sync & SYNC_POSITION) {
> +                     if (!wsmouse_hysteresis(input, t->pos)) {
> +                             dx = x - t->x;
> +                             dy = y - t->y;
> +                     }
> +                     t->x = x;
> +                     t->y = y;
> +                     t->flags &= (~EDGES | edge_flags(tp, x, y));
>               }
> -
> -             wstpad_set_direction(tp, t, tp->dx, tp->dy);
> +             wstpad_set_direction(tp, t, dx, dy);
>       }
>  }
> 
> 
> Index: driver/xf86-input-ws/src/ws.c
> ===================================================================
> RCS file: /cvs/xenocara/driver/xf86-input-ws/src/ws.c,v
> retrieving revision 1.63
> diff -u -p -r1.63 ws.c
> --- driver/xf86-input-ws/src/ws.c     31 Dec 2017 23:31:41 -0000      1.63
> +++ driver/xf86-input-ws/src/ws.c     11 Mar 2019 21:09:28 -0000
> @@ -363,6 +363,10 @@ wsDeviceInit(DeviceIntPtr pWS)
>               axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X);
>               axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y);
>       }
> +     axes_labels[HSCROLL_AXIS] =
> +         XIGetKnownProperty(AXIS_LABEL_PROP_REL_HSCROLL);
> +     axes_labels[VSCROLL_AXIS] =
> +         XIGetKnownProperty(AXIS_LABEL_PROP_REL_VSCROLL);
>       if (!InitValuatorClassDeviceStruct(pWS,
>           NAXES, axes_labels, GetMotionHistorySize(),
>           priv->type == WSMOUSE_TYPE_TPANEL ? Absolute : Relative))
> @@ -382,6 +386,25 @@ wsDeviceInit(DeviceIntPtr pWS)
>           priv->type == WSMOUSE_TYPE_TPANEL ? Absolute : Relative);
>       xf86InitValuatorDefaults(pWS, 1);
> 
> +     xf86InitValuatorAxisStruct(pWS, HSCROLL_AXIS,
> +         axes_labels[HSCROLL_AXIS], 0, -1, 0, 0, 0, Relative);
> +     xf86InitValuatorAxisStruct(pWS, VSCROLL_AXIS,
> +         axes_labels[VSCROLL_AXIS], 0, -1, 0, 0, 0, Relative);
> +     priv->scroll_mask = valuator_mask_new(MAX_VALUATORS);
> +     if (!priv->scroll_mask) {
> +             free(axes_labels);
> +             return !Success;
> +     }
> +
> +     /*
> +      * The value of an HSCROLL or VSCROLL event is the fraction
> +      *         motion_delta / scroll_distance
> +      * in [*.12] fixed-point format.  The 'increment' attribute of the
> +      * scroll axes is constant:
> +      */
> +     SetScrollValuator(pWS, HSCROLL_AXIS, SCROLL_TYPE_HORIZONTAL, 4096, 0);
> +     SetScrollValuator(pWS, VSCROLL_AXIS, SCROLL_TYPE_VERTICAL, 4096, 0);
> +
>       pWS->public.on = FALSE;
>       if (wsOpen(pInfo) != Success) {
>               return !Success;
> @@ -579,6 +602,14 @@ wsReadHwState(InputInfoPtr pInfo, wsHwSt
>               case WSCONS_EVENT_SYNC:
>                       DBG(4, ErrorF("Sync\n"));
>                       return TRUE;
> +             case WSCONS_EVENT_HSCROLL:
> +                     hw->hscroll = event->value;
> +                     DBG(4, ErrorF("Horiz. Scrolling %d\n", event->value));
> +                     return TRUE;
> +             case WSCONS_EVENT_VSCROLL:
> +                     hw->vscroll = event->value;
> +                     DBG(4, ErrorF("Vert. Scrolling %d\n", event->value));
> +                     return TRUE;
>               default:
>                       xf86IDrvMsg(pInfo, X_WARNING,
>                           "bad wsmouse event type=%d\n", event->type);
> @@ -623,6 +654,14 @@ wsReadInput(InputInfoPtr pInfo)
>               wbutton = (hw.dw < 0) ? priv->W.negative : priv->W.positive;
>               DBG(4, ErrorF("W -> button %d (%d)\n", wbutton, abs(hw.dw)));
>               wsButtonClicks(pInfo, wbutton, abs(hw.dw));
> +     }
> +     if (hw.hscroll || hw.vscroll) {
> +             valuator_mask_zero(priv->scroll_mask);
> +             valuator_mask_set_double(priv->scroll_mask,
> +                 HSCROLL_AXIS, (double) hw.hscroll);
> +             valuator_mask_set_double(priv->scroll_mask,
> +                 VSCROLL_AXIS, (double) hw.vscroll);
> +             xf86PostMotionEventM(pInfo->dev, FALSE, priv->scroll_mask);
>       }
>       if (priv->lastButtons != hw.buttons) {
>               /* button event */
> Index: driver/xf86-input-ws/src/ws.h
> ===================================================================
> RCS file: /cvs/xenocara/driver/xf86-input-ws/src/ws.h,v
> retrieving revision 1.15
> diff -u -p -r1.15 ws.h
> --- driver/xf86-input-ws/src/ws.h     31 Dec 2017 23:31:41 -0000      1.15
> +++ driver/xf86-input-ws/src/ws.h     11 Mar 2019 21:09:28 -0000
> @@ -26,7 +26,10 @@ extern int ws_debug_level;
>  # define DBG(lvl, f)
>  #endif
> 
> -#define NAXES                2       /* X and Y axes only */
> +#define NAXES                4       /* X, Y, horizontal and vertical 
> scrolling */
> +#define HSCROLL_AXIS 2
> +#define VSCROLL_AXIS 3
> +
>  #define NBUTTONS     32      /* max theoretical buttons */
>  #define DFLTBUTTONS  3       /* default number of buttons */
> 
> @@ -45,6 +48,7 @@ typedef struct {
>       unsigned int buttons;
>       int dx, dy, dz, dw;
>       int ax, ay;
> +     int hscroll, vscroll;
>  } wsHwState;
> 
>  typedef struct WSDevice {
> @@ -86,6 +90,8 @@ typedef struct WSDevice {
>               Time expires;           /* time of expiry */
>               Time timeout;
>       } emulateWheel;
> +
> +     ValuatorMask *scroll_mask;
> 
>       OsTimerPtr      remove_timer;   /* Callback for removal on EIO */
> 

Reply via email to