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

Reply via email to