On 3/13/19 4:49 PM, Martin Pieuchot wrote:
> 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 ;)

Some of the WSCONS_EVENT_* names are very long, and too many capital
letters in a C function can make me nervous ;-)  Initially, I thought
that more *_EV definitions would become macros.

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

I wouldn't know of any obstacle, at least, but I haven't checked that.
Whether the kernel sends a DELTA_Z event or a VSCROLL event with a
multiple of the base unit shouldn't make a difference in the outcome.
It might even be possible to apply an acceleration scheme to wheel
input (I believe Mac OS does that), but I have no idea whether that
would be useful.  Anyway, first we should make sure that the mechanism
is sound; I'm a bit puzzled by Joshua's report and hope there will be
more tests.

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