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