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