On Mon, Dec 18, 2017 at 4:59 PM, Peter Hutterer <peter.hutte...@who-t.net> wrote: > On Mon, Dec 18, 2017 at 11:46:21AM -0800, Jason Gerecke wrote: >> When enabled through `xsetwacom set <id> button <n> pan`, this causes the >> driver >> to appear to "drag" scrollable window contents by emitting appropriate scroll >> events as the pen is dragged around. >> >> Signed-off-by: Jason Gerecke <jason.gere...@wacom.com> > > Shouldn't this be named drag-scroll? or maybe 'button-scroll'? Aside from > 'panscroll' looking weird after about 5 occurances it also doesn't really > specify what it does. >
The Windows/Mac drivers use the term "pan" for this action. Something like "drag scroll" would also be clear, but I'd prefer to use the same/similar terms. > The code itself here is > Reviewed-by: Peter Hutterer <peter.hutte...@who-t.net> > but I do wonder if it's better to move the driver to a scroll axis and have > at least this feature use the smooth scrolling. Because otherwise it's not > going to be fine-grained enough by just sending click events. > > Cheers, > Peter > I'd completely forgotten about smooth scrolling. That does indeed sound like a good idea, and a quick proof-of-concept seems to indicate that it could work. Since there doesn't appear to be any reason we couldn't switch from discrete to smooth at a later point, I think I'll push this as the initial implementation and then work on cleaning up my (currently-ugly) patch switchover patch next year. Jason --- Now instead of four in the eights place / you’ve got three, ‘Cause you added one / (That is to say, eight) to the two, / But you can’t take seven from three, / So you look at the sixty-fours.... >> --- >> include/Xwacom.h | 1 + >> include/wacom-properties.h | 3 ++ >> man/wacom.man | 7 ++++ >> man/xsetwacom.man | 14 ++++++++ >> src/wcmCommon.c | 90 >> ++++++++++++++++++++++++++++++++++++++++++---- >> src/wcmValidateDevice.c | 3 ++ >> src/wcmXCommand.c | 21 +++++++++++ >> src/xf86WacomDefs.h | 3 ++ >> tools/xsetwacom.c | 24 +++++++++++++ >> 9 files changed, 160 insertions(+), 6 deletions(-) >> >> diff --git a/include/Xwacom.h b/include/Xwacom.h >> index 49489ff..c2af381 100644 >> --- a/include/Xwacom.h >> +++ b/include/Xwacom.h >> @@ -60,6 +60,7 @@ >> #define AC_MODETOGGLE 0x00020000 /* Toggle absolute/relative >> mode */ >> #define AC_DBLCLICK 0x00030000 /* DEPRECATED: use two button >> events instead */ >> #define AC_DISPLAYTOGGLE 0x00040000 /* DEPRECATED: has no effect (used >> to toggle among screens) */ >> +#define AC_PANSCROLL 0x00050000 /* Enter/exit panscroll mode */ >> #define AC_BUTTON 0x00080000 /* Emit button events */ >> #define AC_TYPE 0x000f0000 /* The mask to isolate event >> type bits */ >> #define AC_KEYBTNPRESS 0x00100000 /* bit set for key/button presses */ >> diff --git a/include/wacom-properties.h b/include/wacom-properties.h >> index b845083..0796ab0 100644 >> --- a/include/wacom-properties.h >> +++ b/include/wacom-properties.h >> @@ -111,6 +111,9 @@ >> */ >> #define WACOM_PROP_PRESSURE_RECAL "Wacom Pressure Recalibration" >> >> +/* 32 bit, 1 values */ >> +#define WACOM_PROP_PANSCROLL_THRESHOLD "Wacom Panscroll Threshold" >> + >> /* The following are tool types used by the driver in WACOM_PROP_TOOL_TYPE >> * or in the 'type' field for XI1 clients. Clients may check for one of >> * these types to identify tool types. >> diff --git a/man/wacom.man b/man/wacom.man >> index 3693c94..3e9c7bd 100644 >> --- a/man/wacom.man >> +++ b/man/wacom.man >> @@ -277,6 +277,13 @@ initial pressure reading may be unequal to zero even >> for a perfectly >> good pen. If the consecutive pressure readings are not higher than >> the initial pressure by a threshold no button event will be generated. >> This option allows to disable the recalibration. >> +.TP 4 >> +.B Option \fI"PanScrollThreshold"\fP \fI"number"\fP >> +Specifies the distance the pen must move (in tablet units) before a >> +scroll event is generated when using the "pan" action. Smaller values >> +will require less distance and be more sensitive. Larger values will >> +require more distance and be less sensitive. Default: 1300 or 2600 >> +depending on tablet resolution (corresponds to 13 mm of distance). >> .RE >> .SH "TOUCH GESTURES" >> .SS Single finger (1FG) >> diff --git a/man/xsetwacom.man b/man/xsetwacom.man >> index 234e9ba..6e82301 100644 >> --- a/man/xsetwacom.man >> +++ b/man/xsetwacom.man >> @@ -134,6 +134,13 @@ numbers. >> The "modetoggle" keyword is also recognized; it takes no arguments, >> and toggles the device mode between relative and absolute pointer tracking. >> >> +The "pan" keyword causes the driver to send scroll events while the pen >> +is dragged. This makes it easy to scroll through lists and documents, >> +pan around 2D canvases, and zoom in/out of 3D scenes (exact behavior >> +depends on application interpretation of scrollwheel events). Dragging >> +the pen up/down will send scrollwheel down/up events; dragging it left/right >> +will send scrollwheel right/left events. >> + >> The events in the action mapping are sent when the physical button is >> pressed. >> If the action mapping leaves any buttons or keys pressed (such as a modifier >> key), they will be released when the physical button is released. >> @@ -273,6 +280,13 @@ initial pressure reading may be unequal to zero even >> for a perfectly >> good pen. If the consecutive pressure readings are not higher than >> the initial pressure by a threshold no button event will be generated. >> This option allows to disable the recalibration. Default: on >> +.TP >> +\fBPanScrollThreshold\fR distance >> +This specifies the distance the pen must move (in tablet units) before >> +a scroll event is generated when using the "pan" action. Smaller values >> +will require less distance and be more sensitive. Larger values will >> +require more distance and be less sensitive. Default: 1300 or 2600 >> +depending on tablet resolution (corresponds to 13 mm of distance). >> >> >> .SH "AUTHORS" >> diff --git a/src/wcmCommon.c b/src/wcmCommon.c >> index b816cdd..f7620af 100644 >> --- a/src/wcmCommon.c >> +++ b/src/wcmCommon.c >> @@ -89,6 +89,50 @@ void set_absolute(InputInfoPtr pInfo, Bool absolute) >> priv->flags &= ~ABSOLUTE_FLAG; >> } >> >> +static int wcmButtonPerNotch(WacomDevicePtr priv, int value, int threshold, >> int btn_positive, int btn_negative) >> +{ >> + int mode = is_absolute(priv->pInfo); >> + int notches = value / threshold; >> + int button = (notches > 0) ? btn_positive : btn_negative; >> + int i; >> + >> + for (i = 0; i < abs(notches); i++) { >> + xf86PostButtonEventP(priv->pInfo->dev, mode, button, 1, 0, 0, >> 0); >> + xf86PostButtonEventP(priv->pInfo->dev, mode, button, 0, 0, 0, >> 0); >> + } >> + >> + return value % threshold; >> +} >> + >> +static void wcmPanscroll(WacomDevicePtr priv, const WacomDeviceState *ds) >> +{ >> + WacomCommonPtr common = priv->common; >> + int threshold = common->wcmPanscrollThreshold; >> + int *accumulated_x, *accumulated_y; >> + >> + if (!(priv->flags & SCROLLMODE_FLAG) || !(ds->buttons & 1)) >> + return; >> + >> + /* Tip has gone down down; store state for dragging */ >> + if (!(priv->oldState.buttons & 1)) { >> + priv->wcmPanscrollState = *ds; >> + priv->wcmPanscrollState.x = 0; >> + priv->wcmPanscrollState.y = 0; >> + return; >> + } >> + >> + accumulated_x = &priv->wcmPanscrollState.x; >> + accumulated_y = &priv->wcmPanscrollState.y; >> + >> + *accumulated_x += (ds->x - priv->oldState.x); >> + *accumulated_y += (ds->y - priv->oldState.y); >> + >> + DBG(6, priv, "pan x = %d, pan y = %d\n", *accumulated_x, >> *accumulated_y); >> + >> + *accumulated_x = wcmButtonPerNotch(priv, *accumulated_x, threshold, 6, >> 7); >> + *accumulated_y = wcmButtonPerNotch(priv, *accumulated_y, threshold, 4, >> 5); >> +} >> + >> >> /***************************************************************************** >> * wcmSendButtons -- >> * Send button events by comparing the current button mask with the >> @@ -170,6 +214,7 @@ static void sendAction(InputInfoPtr pInfo, const >> WacomDeviceState* ds, >> int press, unsigned int *keys, int nkeys, >> int first_val, int num_val, int *valuators) >> { >> + WacomDevicePtr priv = (WacomDevicePtr) pInfo->private; >> int i; >> >> /* Actions only trigger on press, not release */ >> @@ -186,10 +231,15 @@ static void sendAction(InputInfoPtr pInfo, const >> WacomDeviceState* ds, >> { >> int btn_no = (action & AC_CODE); >> int is_press = (action & >> AC_KEYBTNPRESS); >> - xf86PostButtonEventP(pInfo->dev, >> - >> is_absolute(pInfo), btn_no, >> - is_press, >> first_val, num_val, >> - VCOPY(valuators, >> num_val)); >> + if (btn_no == 1 && (priv->flags & >> SCROLLMODE_FLAG)) { >> + /* Don't send clicks in scroll >> mode */ >> + } >> + else { >> + >> xf86PostButtonEventP(pInfo->dev, >> + >> is_absolute(pInfo), btn_no, >> + is_press, >> first_val, num_val, >> + >> VCOPY(valuators, num_val)); >> + } >> } >> break; >> case AC_KEY: >> @@ -204,6 +254,12 @@ static void sendAction(InputInfoPtr pInfo, const >> WacomDeviceState* ds, >> wcmDevSwitchModeCall(pInfo, >> (is_absolute(pInfo)) ? >> Relative : Absolute); /* not a typo! */ >> break; >> + case AC_PANSCROLL: >> + priv->flags |= SCROLLMODE_FLAG; >> + priv->wcmPanscrollState = *ds; >> + priv->wcmPanscrollState.x = 0; >> + priv->wcmPanscrollState.y = 0; >> + break; >> } >> } >> >> @@ -240,8 +296,11 @@ static void sendAction(InputInfoPtr pInfo, const >> WacomDeviceState* ds, >> if (countPresses(key_code, &keys[i], >> nkeys - i)) >> wcmEmitKeycode(pInfo->dev, >> key_code, 0); >> } >> + break; >> + case AC_PANSCROLL: >> + priv->flags &= ~SCROLLMODE_FLAG; >> + break; >> } >> - >> } >> } >> >> @@ -428,6 +487,9 @@ static void sendCommonEvents(InputInfoPtr pInfo, const >> WacomDeviceState* ds, >> WacomDevicePtr priv = (WacomDevicePtr) pInfo->private; >> int buttons = ds->buttons; >> >> + /* send scrolling events if necessary */ >> + wcmPanscroll(priv, ds); >> + >> /* send button events when state changed or first time in prox and >> button unpresses */ >> if (priv->oldState.buttons != buttons || (!priv->oldState.proximity && >> !buttons)) >> wcmSendButtons(pInfo, ds, buttons, first_val, num_vals, >> valuators); >> @@ -568,7 +630,8 @@ wcmSendNonPadEvents(InputInfoPtr pInfo, const >> WacomDeviceState *ds, >> VCOPY(valuators, num_vals)); >> >> /* Move the cursor to where it should be before sending button >> events */ >> - if(!(priv->flags & BUTTONS_ONLY_FLAG)) >> + if(!(priv->flags & BUTTONS_ONLY_FLAG) && >> + !(priv->flags & SCROLLMODE_FLAG && priv->oldState.buttons & >> 1)) >> { >> xf86PostMotionEventP(pInfo->dev, is_absolute(pInfo), >> first_val, num_vals, >> @@ -650,6 +713,10 @@ void wcmSendEvents(InputInfoPtr pInfo, const >> WacomDeviceState* ds) >> ty = ds->stripy; >> } >> >> + /* cancel panscroll */ >> + if (!ds->proximity) >> + priv->flags &= ~SCROLLMODE_FLAG; >> + >> DBG(7, priv, "[%s] o_prox=%s x=%d y=%d z=%d " >> "b=%s b=%d tx=%d ty=%d wl=%d wl2=%d rot=%d th=%d\n", >> pInfo->type_name, >> @@ -1356,6 +1423,16 @@ int wcmInitTablet(InputInfoPtr pInfo, const char* id, >> float version) >> pInfo->name, common->wcmThreshold); >> } >> >> + /* Calculate default panscroll threshold if not set */ >> + xf86Msg(X_CONFIG, "%s: panscroll is %d\n", pInfo->name, >> common->wcmPanscrollThreshold); >> + if (common->wcmPanscrollThreshold < 1) { >> + common->wcmPanscrollThreshold = common->wcmResolY * 13 / 1000; >> /* 13mm */ >> + } >> + if (common->wcmPanscrollThreshold < 1) { >> + common->wcmPanscrollThreshold = 1000; >> + } >> + xf86Msg(X_CONFIG, "%s: panscroll modified to %d\n", pInfo->name, >> common->wcmPanscrollThreshold); >> + >> /* output tablet state as probed */ >> if (IsPen(priv)) >> xf86Msg(X_PROBED, "%s: maxX=%d maxY=%d maxZ=%d " >> @@ -1468,6 +1545,7 @@ WacomCommonPtr wcmNewCommon(void) >> /* transmit position if increment is superior */ >> common->wcmRawSample = DEFAULT_SAMPLES; >> /* number of raw data to be used to for filtering */ >> + common->wcmPanscrollThreshold = 0; >> common->wcmPressureRecalibration = 1; >> return common; >> } >> diff --git a/src/wcmValidateDevice.c b/src/wcmValidateDevice.c >> index 21ccd5f..486235b 100644 >> --- a/src/wcmValidateDevice.c >> +++ b/src/wcmValidateDevice.c >> @@ -912,6 +912,9 @@ Bool wcmPreInitParseOptions(InputInfoPtr pInfo, Bool >> is_primary, >> tool = priv->tool; >> tool->serial = priv->serial; >> >> + common->wcmPanscrollThreshold = xf86SetIntOption(pInfo->options, >> "PanScrollThreshold", >> + common->wcmPanscrollThreshold); >> + >> /* The first device doesn't need to add any tools/areas as it >> * will be the first anyway. So if different, add tool >> * and/or area to the existing lists >> diff --git a/src/wcmXCommand.c b/src/wcmXCommand.c >> index e18fb8f..63d1b3d 100644 >> --- a/src/wcmXCommand.c >> +++ b/src/wcmXCommand.c >> @@ -99,6 +99,7 @@ static Atom prop_tooltype; >> static Atom prop_btnactions; >> static Atom prop_product_id; >> static Atom prop_pressure_recal; >> +static Atom prop_panscroll_threshold; >> #ifdef DEBUG >> static Atom prop_debuglevels; >> #endif >> @@ -336,6 +337,9 @@ void InitWcmDeviceProperties(InputInfoPtr pInfo) >> XA_INTEGER, 8, 1, values); >> } >> >> + values[0] = common->wcmPanscrollThreshold; >> + prop_panscroll_threshold = InitWcmAtom(pInfo->dev, >> WACOM_PROP_PANSCROLL_THRESHOLD, XA_INTEGER, 32, 1, values); >> + >> values[0] = common->vendor_id; >> values[1] = common->tablet_id; >> prop_product_id = InitWcmAtom(pInfo->dev, XI_PROP_PRODUCT_ID, >> XA_INTEGER, 32, 2, values); >> @@ -455,6 +459,8 @@ static int wcmCheckActionProperty(WacomDevicePtr priv, >> Atom property, XIProperty >> break; >> case AC_MODETOGGLE: >> break; >> + case AC_PANSCROLL: >> + break; >> default: >> DBG(3, priv, "ERROR: Unknown command\n"); >> return BadValue; >> @@ -972,6 +978,21 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, >> XIPropertyValuePtr prop, >> >> if (!checkonly) >> common->wcmPressureRecalibration = values[0]; >> + } else if (property == prop_panscroll_threshold) >> + { >> + CARD32 *values = (CARD32*)prop->data; >> + >> + if (prop->size != 1 || prop->format != 32) >> + return BadValue; >> + >> + if (values[0] <= 0) >> + return BadValue; >> + >> + if (IsTouch(priv)) >> + return BadMatch; >> + >> + if (!checkonly) >> + common->wcmPanscrollThreshold = values[0]; >> } else >> { >> Atom *handler = NULL; >> diff --git a/src/xf86WacomDefs.h b/src/xf86WacomDefs.h >> index a772597..a2053a8 100644 >> --- a/src/xf86WacomDefs.h >> +++ b/src/xf86WacomDefs.h >> @@ -175,6 +175,7 @@ struct _WacomModel >> #define ABSOLUTE_FLAG 0x00000100 >> #define BAUD_19200_FLAG 0x00000400 >> #define BUTTONS_ONLY_FLAG 0x00000800 >> +#define SCROLLMODE_FLAG 0x00001000 >> >> #define IsCursor(priv) (DEVICE_ID((priv)->flags) == CURSOR_ID) >> #define IsStylus(priv) (DEVICE_ID((priv)->flags) == STYLUS_ID) >> @@ -288,6 +289,7 @@ struct _WacomDeviceRec >> WacomCommonPtr common; /* common info pointer */ >> >> /* state fields in device coordinates */ >> + struct _WacomDeviceState wcmPanscrollState; /* panscroll state >> tracking */ >> struct _WacomDeviceState oldState; /* previous state information */ >> int oldCursorHwProx; /* previous cursor hardware proximity */ >> >> @@ -467,6 +469,7 @@ struct _WacomCommonRec >> int wcmRawSample; /* Number of raw data used to filter an >> event */ >> int wcmPressureRecalibration; /* Determine if pressure recalibration of >> worn pens should be performed */ >> + int wcmPanscrollThreshold; /* distance pen must move to send a >> panscroll event */ >> >> int bufpos; /* position with buffer */ >> unsigned char buffer[BUFFER_SIZE]; /* data read from device */ >> diff --git a/tools/xsetwacom.c b/tools/xsetwacom.c >> index 1051868..5615836 100644 >> --- a/tools/xsetwacom.c >> +++ b/tools/xsetwacom.c >> @@ -471,6 +471,15 @@ static param_t parameters[] = >> .arg_count = 1, >> .prop_flags = PROP_FLAG_BOOLEAN >> }, >> + { >> + .name = "PanScrollThreshold", >> + .x11name = "PanScrollThreshold", >> + .desc = "Adjusts distance required for pan actions to generate >> a scroll event", >> + .prop_name = WACOM_PROP_PANSCROLL_THRESHOLD, >> + .prop_format = 32, >> + .prop_offset = 0, >> + .arg_count = 1, >> + }, >> { >> .name = "MapToOutput", >> .desc = "Map the device to the given output. ", >> @@ -1012,6 +1021,7 @@ static int special_map_button(Display *dpy, int argc, >> char **argv, unsigned long >> static int special_map_core(Display *dpy, int argc, char **argv, unsigned >> long *ndata, unsigned long *data, const size_t size); >> static int special_map_modetoggle(Display *dpy, int argc, char **argv, >> unsigned long *ndata, unsigned long *data, const size_t size); >> static int special_map_displaytoggle(Display *dpy, int argc, char **argv, >> unsigned long *ndata, unsigned long *data, const size_t size); >> +static int special_map_panscroll(Display *dpy, int argc, char **argv, >> unsigned long *ndata, unsigned long *data, const size_t size); >> >> /* Valid keywords for the --set ButtonX options */ >> static struct keywords { >> @@ -1023,6 +1033,7 @@ static struct keywords { >> {"core", special_map_core}, >> {"modetoggle", special_map_modetoggle}, >> {"displaytoggle", special_map_displaytoggle}, >> + {"pan", special_map_panscroll}, >> { NULL, NULL } >> }; >> >> @@ -1067,6 +1078,19 @@ static int special_map_displaytoggle(Display *dpy, >> int argc, char **argv, unsign >> return 0; >> } >> >> +static int special_map_panscroll(Display *dpy, int argc, char **argv, >> unsigned long *ndata, unsigned long *data, const size_t size) >> +{ >> + if (*ndata + 1 > size) { >> + fprintf(stderr, "Insufficient space to store all commands.\n"); >> + return 0; >> + } >> + data[*ndata] = AC_PANSCROLL; >> + >> + *ndata += 1; >> + >> + return 0; >> +} >> + >> static inline int is_valid_keyword(const char *keyword) >> { >> struct keywords *kw = keywords; >> -- >> 2.15.1 >> >> >> ------------------------------------------------------------------------------ >> Check out the vibrant tech community on one of the world's most >> engaging tech sites, Slashdot.org! http://sdm.link/slashdot >> _______________________________________________ >> Linuxwacom-devel mailing list >> Linuxwacom-devel@lists.sourceforge.net >> https://lists.sourceforge.net/lists/listinfo/linuxwacom-devel >> ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ Linuxwacom-devel mailing list Linuxwacom-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxwacom-devel