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