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

Reply via email to