summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPing Cheng <pingc@wacom.com>2009-12-15 21:05:38 -0800
committerPeter Hutterer <peter.hutterer@who-t.net>2009-12-21 10:07:50 +1000
commit463c5d1effa3505d54a5a520d143919ad1befc69 (patch)
treee070ca23730e119119cc40a83ff14f72b4f011c4
parentdea6383b30e1695acc6183aa098a4e056640596c (diff)
downloadxf86-input-wacom-463c5d1effa3505d54a5a520d143919ad1befc69.tar.gz
Support 2FGT gesture
There are 3 gesture features: second finger tap to right click (GESTURE_TAP_MODE) vertical/horizontal scroll (GESTURE_SCROLL_MODE) and zoom in/out (GESTURE_ZOOM_MODE) Signed-off-by: Ping Cheng <pinglinux@gmail.com> Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
-rw-r--r--src/Makefile.am2
-rw-r--r--src/wcmCommon.c44
-rw-r--r--src/wcmTouchFilter.c406
-rw-r--r--src/xf86WacomDefs.h3
4 files changed, 454 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index df1481b..461089f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -36,5 +36,5 @@ INCLUDES=-I$(top_srcdir)/include/
wcmCommon.c wcmConfig.c wcmISDV4.c \
wcmFilter.c wcmFilter.h xf86WacomDefs.h \
wcmTilt2Rotation.c wcmUSB.c wcmXCommand.c \
- wcmValidateDevice.c
+ wcmValidateDevice.c wcmTouchFilter.c
diff --git a/src/wcmCommon.c b/src/wcmCommon.c
index c5c4a57..434715d 100644
--- a/src/wcmCommon.c
+++ b/src/wcmCommon.c
@@ -34,6 +34,7 @@ extern void xf86WcmInitialCoordinates(LocalDevicePtr local, int axes);
extern void xf86WcmVirtualTabletSize(LocalDevicePtr local);
extern void xf86WcmVirtualTabletPadding(LocalDevicePtr local);
extern void xf86WcmTilt2R(WacomDeviceStatePtr ds);
+extern void xf86WcmFingerTapToClick(WacomCommonPtr common);
/*****************************************************************************
* Static functions
@@ -1211,7 +1212,50 @@ void xf86WcmEvent(WacomCommonPtr common, unsigned int channel,
pChannel->valid.state = ds; /*save last raw sample */
if (pChannel->nSamples < common->wcmRawSample) ++pChannel->nSamples;
+ /* process second finger data if exists
+ * and both touch and geature are enabled */
+ if ((ds.device_type == TOUCH_ID) &&
+ common->wcmTouch && common->wcmGesture)
+ {
+ WacomChannelPtr pOtherChannel;
+ WacomDeviceState dsOther;
+
+ /* exit gesture mode when both fingers are out */
+ if (channel)
+ pOtherChannel = common->wcmChannel;
+ else
+ pOtherChannel = common->wcmChannel + 1;
+ dsOther = pOtherChannel->valid.state;
+
+ /* This is the only place to reset gesture mode
+ * once a gesture mode is entered */
+ if (!ds.proximity && !dsOther.proximity)
+ {
+ common->wcmGestureMode = 0;
+
+ /* send a touch out-prox event here
+ * in case the FF was out before the SF */
+ channel = 0;
+ }
+ else
+ {
+ /* don't move the cursor if in gesture mode
+ * wait for second finger data to process gestures */
+ if (!channel && common->wcmGestureMode)
+ goto ret;
+
+ /* process gesture */
+ if (channel)
+ {
+ xf86WcmFingerTapToClick(common);
+ goto ret;
+ }
+ }
+ }
+
+ /* everything else falls here */
commonDispatchDevice(common,channel,pChannel, suppress);
+ret:
resetSampleCounter(pChannel);
}
diff --git a/src/wcmTouchFilter.c b/src/wcmTouchFilter.c
new file mode 100644
index 0000000..ebf7ba7
--- /dev/null
+++ b/src/wcmTouchFilter.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2009 by Ping Cheng, Wacom Technology. <pingc@wacom.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "xf86Wacom.h"
+#include <math.h>
+
+/* Defines for 2FC Gesture */
+#define WACOM_DIST_IN_POINT 300
+#define WACOM_APART_IN_POINT 350
+#define WACOM_MOTION_IN_POINT 50
+#define WACOM_PARA_MOTION_IN_POINT 50
+#define WACOM_DOWN_TIME_IN_MS 800
+#define WACOM_TIME_BETWEEN_IN_MS 400
+#define WACOM_HORIZ_ALLOWED 1
+#define WACOM_VERT_ALLOWED 2
+
+#define GESTURE_TAP_MODE 1
+#define GESTURE_SCROLL_MODE 2
+#define GESTURE_ZOOM_MODE 4
+
+#define WCM_SCROLL_UP 5 /* vertical up */
+#define WCM_SCROLL_DOWN 4 /* vertical down */
+
+
+/* Defines for Tap Add-a-Finger to Click */
+#define WACOM_TAP_TIME_IN_MS 150
+
+void xf86WcmFingerTapToClick(WacomCommonPtr common);
+
+extern void xf86WcmRotateCoordinates(LocalDevicePtr local, int x, int y);
+extern void emitKeysym (DeviceIntPtr keydev, int keysym, int state);
+
+static void xf86WcmFingerScroll(WacomDevicePtr priv);
+static void xf86WcmFingerZoom(WacomDevicePtr priv);
+
+static double touchDistance(WacomDeviceState ds0, WacomDeviceState ds1)
+{
+ int xDelta = ds0.x - ds1.x;
+ int yDelta = ds0.y - ds1.y;
+ double distance = sqrt((double)(xDelta*xDelta + yDelta*yDelta));
+ return distance;
+}
+
+static Bool pointsInLine(WacomDeviceState ds0, WacomDeviceState ds1,
+ int *direction)
+{
+ Bool ret = FALSE;
+
+ if (*direction == 0)
+ {
+ if (abs(ds0.x - ds1.x) < WACOM_PARA_MOTION_IN_POINT)
+ {
+ *direction = WACOM_VERT_ALLOWED;
+ ret = TRUE;
+ }
+ else if (abs(ds0.y - ds1.y) < WACOM_PARA_MOTION_IN_POINT)
+ {
+ *direction = WACOM_HORIZ_ALLOWED;
+ ret = TRUE;
+ }
+ }
+ else if (*direction == WACOM_HORIZ_ALLOWED)
+ {
+ if (abs(ds0.y - ds1.y) < WACOM_PARA_MOTION_IN_POINT)
+ ret = TRUE;
+ }
+ else if (*direction == WACOM_VERT_ALLOWED)
+ {
+ if (abs(ds0.x - ds1.x) < WACOM_PARA_MOTION_IN_POINT)
+ ret = TRUE;
+ }
+ return ret;
+}
+
+static Bool pointsInLineAfter(int p1, int p2)
+{
+ Bool ret = FALSE;
+
+ if (abs(p1 - p2) < WACOM_PARA_MOTION_IN_POINT)
+ ret = TRUE;
+
+ return ret;
+}
+
+static void xf86WcmSwitchLeftClick(WacomDevicePtr priv)
+{
+ WacomCommonPtr common = priv->common;
+
+ if (common->wcmGestureMode)
+ {
+ /* send button one up */
+ xf86PostButtonEvent(priv->local->dev,
+ priv->flags & ABSOLUTE_FLAG,
+ 1,0,0,priv->naxes, priv->oldX,
+ priv->oldY,0,0,0,0);
+ priv->oldButtons = 0;
+ }
+}
+
+/*****************************************************************************
+ * translate second finger tap to right click
+ ****************************************************************************/
+
+void xf86WcmFingerTapToClick(WacomCommonPtr common)
+{
+ WacomDevicePtr priv = common->wcmDevices;
+ WacomChannelPtr firstChannel = common->wcmChannel;
+ WacomChannelPtr secondChannel = common->wcmChannel + 1;
+ WacomDeviceState ds[2] = { firstChannel->valid.states[0],
+ secondChannel->valid.states[0] };
+ WacomDeviceState dsLast[2] = { firstChannel->valid.states[1],
+ secondChannel->valid.states[1] };
+ int direction = 0;
+
+ DBG(10, priv->debugLevel, ErrorF("xf86WcmFingerTapToClick \n"));
+
+ /* skip initial second finger event */
+ if (!dsLast[1].proximity)
+ goto skipGesture;
+
+ if (!IsTouch(priv))
+ {
+ /* go through the shared port */
+ for (; priv; priv = priv->next)
+ if (IsTouch(priv))
+ break;
+ }
+
+ if (priv) /* found the first finger */
+ {
+ /* allow only second finger tap */
+ if ((dsLast[0].sample < dsLast[1].sample) && ((GetTimeInMillis() -
+ dsLast[1].sample) <= WACOM_TAP_TIME_IN_MS))
+ {
+ /* send right click when second finger taps within WACOM_TAP_TIMEms
+ * and both fingers stay within WACOM_DIST */
+ if (!ds[1].proximity && dsLast[1].proximity)
+ {
+ if (touchDistance(ds[0], dsLast[1]) <= WACOM_DIST_IN_POINT)
+ {
+ /* send left up before sending right down */
+ if (!common->wcmGestureMode)
+ {
+ common->wcmGestureMode = GESTURE_TAP_MODE;
+ xf86WcmSwitchLeftClick(priv);
+ }
+
+ /* right button down */
+ xf86PostButtonEvent(priv->local->dev,
+ priv->flags & ABSOLUTE_FLAG,
+ 3,1,0,priv->naxes, priv->oldX,
+ priv->oldY,0,0,0,0);
+ /* right button up */
+ xf86PostButtonEvent(priv->local->dev,
+ priv->flags & ABSOLUTE_FLAG,
+ 3,0,0,priv->naxes, priv->oldX,
+ priv->oldY,0,0,0,0);
+ }
+ }
+ }
+ else if ((WACOM_TAP_TIME_IN_MS < (GetTimeInMillis() - dsLast[0].sample))
+ && (WACOM_TAP_TIME_IN_MS < (GetTimeInMillis() - dsLast[1].sample))
+ && ds[0].proximity && ds[1].proximity)
+ {
+ if (abs(touchDistance(ds[0], ds[1])) >= WACOM_APART_IN_POINT &&
+ common->wcmGestureMode != GESTURE_TAP_MODE &&
+ common->wcmGestureMode != GESTURE_SCROLL_MODE)
+ {
+ /* fingers moved apart more than WACOM_APART_IN_POINT
+ * zoom mode is entered */
+ if (!common->wcmGestureMode)
+ {
+ common->wcmGestureMode = GESTURE_ZOOM_MODE;
+ xf86WcmSwitchLeftClick(priv);
+ }
+ xf86WcmFingerZoom(priv);
+ }
+
+ if ( pointsInLine(ds[0], dsLast[0], &direction) &&
+ pointsInLine(ds[1], dsLast[1], &direction) &&
+ common->wcmGestureMode != GESTURE_ZOOM_MODE &&
+ common->wcmGestureMode != GESTURE_TAP_MODE)
+ {
+ /* send scroll event when both fingers move in
+ * the same direction */
+ if (!common->wcmGestureMode)
+ {
+ common->wcmGestureMode = GESTURE_SCROLL_MODE;
+ xf86WcmSwitchLeftClick(priv);
+ }
+ xf86WcmFingerScroll(priv);
+ }
+ }
+ }
+ else
+ /* this should never happen */
+ xf86Msg(X_ERROR, "WACOM: No touch device found for %s \n", common->wcmDevice);
+
+skipGesture:
+ /* keep the initial in-prox time */
+ ds[0].sample = dsLast[0].sample;
+ ds[1].sample = dsLast[1].sample;
+
+ /* keep the initial states for both fingers */
+ if ( !(common->wcmGestureMode && (GESTURE_SCROLL_MODE | GESTURE_ZOOM_MODE))
+ && ds[0].proximity && ds[1].proximity)
+ {
+ common->wcmGestureState[0] = ds[0];
+ common->wcmGestureState[1] = ds[1];
+ }
+}
+
+static void xf86WcmSendVerticalScrollEvent(WacomDevicePtr priv,
+ int dist, int up, int dn)
+{
+ int i = 0;
+
+ for (i=0; i<(int)(((double)abs(dist)/
+ (double)WACOM_MOTION_IN_POINT) + 0.5); i++)
+ {
+ if (dist > 0)
+ {
+ /* button down */
+ xf86PostButtonEvent(priv->local->dev,
+ priv->flags & ABSOLUTE_FLAG,
+ up,1,0,priv->naxes, priv->oldX,
+ priv->oldY,0,0,0,0);
+ /* button up */
+ xf86PostButtonEvent(priv->local->dev,
+ priv->flags & ABSOLUTE_FLAG,
+ up,0,0,priv->naxes, priv->oldX,
+ priv->oldY,0,0,0,0);
+ }
+ else
+ {
+ /* button down */
+ xf86PostButtonEvent(priv->local->dev,
+ priv->flags & ABSOLUTE_FLAG,
+ dn,1,0,priv->naxes, priv->oldX,
+ priv->oldY,0,0,0,0);
+ /* button up */
+ xf86PostButtonEvent(priv->local->dev,
+ priv->flags & ABSOLUTE_FLAG,
+ dn,0,0,priv->naxes, priv->oldX,
+ priv->oldY,0,0,0,0);
+ }
+ }
+}
+
+static void xf86WcmSendHorizontalScrollEvent(WacomDevicePtr priv,
+ int dist, int left, int right)
+{
+ int i = 0;
+
+ for (i=0; i<(int)(((double)abs(dist)/
+ (double)WACOM_MOTION_IN_POINT) + 0.5); i++)
+ {
+ if (dist > 0)
+ {
+ emitKeysym (priv->local->dev, left, 1);
+ emitKeysym (priv->local->dev, left, 0);
+ }
+ else
+ {
+ emitKeysym (priv->local->dev, right, 1);
+ emitKeysym (priv->local->dev, right, 0);
+ }
+ }
+}
+
+static void xf86WcmFingerScroll(WacomDevicePtr priv)
+{
+ WacomCommonPtr common = priv->common;
+ WacomChannelPtr firstChannel = common->wcmChannel;
+ WacomChannelPtr secondChannel = common->wcmChannel + 1;
+ WacomDeviceState ds[2] = { firstChannel->valid.states[0],
+ secondChannel->valid.states[0] };
+ WacomDeviceState dsLast[2] = { firstChannel->valid.states[1],
+ secondChannel->valid.states[1] };
+ int midPoint_new = 0;
+ int midPoint_old = 0;
+ int i = 0, dist =0;
+ int gesture = 0;
+ WacomFilterState filterd; /* borrow this struct */
+
+ DBG(10, priv->debugLevel, ErrorF("xf86WcmFingerScroll \n"));
+
+ /* initialize the points so we can rotate them */
+ filterd.x[0] = ds[0].x;
+ filterd.y[0] = ds[0].y;
+ filterd.x[1] = ds[1].x;
+ filterd.y[1] = ds[1].y;
+ filterd.x[2] = common->wcmGestureState[0].x;
+ filterd.y[2] = common->wcmGestureState[0].y;
+ filterd.x[3] = common->wcmGestureState[1].x;
+ filterd.y[3] = common->wcmGestureState[1].y;
+ filterd.x[4] = dsLast[0].x;
+ filterd.y[4] = dsLast[0].y;
+ filterd.x[5] = dsLast[1].x;
+ filterd.y[5] = dsLast[1].y;
+
+ /* rotate the coordinates first */
+ for (i=0; i<6; i++)
+ xf86WcmRotateCoordinates(priv->local, filterd.x[i], filterd.y[i]);
+
+ /* check vertical direction */
+ midPoint_old = (((double)filterd.x[4] + (double)filterd.x[5]) / 2.);
+ midPoint_new = (((double)filterd.x[0] + (double)filterd.x[1]) / 2.);
+ if (pointsInLineAfter(midPoint_old, midPoint_new))
+ {
+ midPoint_old = (((double)filterd.y[2] + (double)filterd.y[3]) / 2.);
+ midPoint_new = (((double)filterd.y[0] + (double)filterd.y[1]) / 2.);
+ dist = midPoint_old - midPoint_new;
+
+ if (abs(dist) > WACOM_PARA_MOTION_IN_POINT)
+ {
+ gesture = 1;
+ xf86WcmSendVerticalScrollEvent(priv, dist,
+ WCM_SCROLL_UP, WCM_SCROLL_DOWN);
+ }
+
+ /* check horizontal direction */
+ if (!gesture)
+ {
+ midPoint_old = (((double)filterd.y[4] + (double)filterd.y[5]) / 2.);
+ midPoint_new = (((double)filterd.y[0] + (double)filterd.y[1]) / 2.);
+ if (pointsInLineAfter(midPoint_old, midPoint_new))
+ {
+ midPoint_old = (((double)filterd.x[2] + (double)filterd.x[3]) / 2.);
+ midPoint_new = (((double)filterd.x[0] + (double)filterd.x[1]) / 2.);
+ dist = midPoint_old - midPoint_new;
+
+ if (abs(dist) > WACOM_PARA_MOTION_IN_POINT)
+ {
+ gesture = 1;
+ xf86WcmSendHorizontalScrollEvent(priv, dist, XK_Left, XK_Right);
+ }
+ }
+ }
+ if (gesture)
+ {
+ /* reset initial states */
+ common->wcmGestureState[0] = ds[0];
+ common->wcmGestureState[1] = ds[1];
+ }
+ }
+}
+
+static void xf86WcmFingerZoom(WacomDevicePtr priv)
+{
+ WacomCommonPtr common = priv->common;
+ WacomChannelPtr firstChannel = common->wcmChannel;
+ WacomChannelPtr secondChannel = common->wcmChannel + 1;
+ WacomDeviceState ds[2] = { firstChannel->valid.states[0],
+ secondChannel->valid.states[0] };
+ int i = 0;
+ int dist = touchDistance(common->wcmGestureState[0],
+ common->wcmGestureState[1]);
+
+ DBG(10, priv->debugLevel, ErrorF("xf86WcmFingerZoom \n"));
+
+ dist = touchDistance(ds[0], ds[1]) - dist;
+
+ /* zooming? */
+ if (abs(dist) > WACOM_MOTION_IN_POINT)
+ {
+ for (i=0; i<(int)(((double)abs(dist)/
+ (double)WACOM_MOTION_IN_POINT) + 0.5); i++)
+ {
+ emitKeysym (priv->local->dev, XK_Control_L, 1);
+ /* zooming in */
+ if (dist > 0)
+ {
+ emitKeysym (priv->local->dev, XK_plus, 1);
+ emitKeysym (priv->local->dev, XK_plus, 0);
+ }
+ else /* zooming out */
+ {
+ emitKeysym (priv->local->dev, XK_minus, 1);
+ emitKeysym (priv->local->dev, XK_minus, 0);
+ }
+ emitKeysym (priv->local->dev, XK_Control_L, 0);
+ }
+
+ /* reset initial states */
+ common->wcmGestureState[0] = ds[0];
+ common->wcmGestureState[1] = ds[1];
+ }
+}
+
+/* vim: set noexpandtab shiftwidth=8: */
diff --git a/src/xf86WacomDefs.h b/src/xf86WacomDefs.h
index 2412692..6634d5c 100644
--- a/src/xf86WacomDefs.h
+++ b/src/xf86WacomDefs.h
@@ -366,6 +366,7 @@ struct _WacomDeviceClass
#define DEVICE_ISDV4 0x000C
#define MAX_CHANNELS 2
+#define MAX_FINGERS 2
struct _WacomCommonRec
{
@@ -426,6 +427,8 @@ struct _WacomCommonRec
int wcmTouchDefault; /* default to disable when not supported */
int wcmGesture; /* disable/enable touch gesture */
int wcmGestureDefault; /* default touch gesture to disable when not supported */
+ int wcmGestureMode; /* data is in Gesture Mode? */
+ WacomDeviceState wcmGestureState[MAX_FINGERS]; /* inital state when in gesture mode */
int wcmCapacity; /* disable/enable capacity */
int wcmCapacityDefault; /* default to -1 when capacity isn't supported/disabled */
/* 3 when capacity is supported */