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

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