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