Both src and tools/include are updated. This "feature" will be removed when gesture is supported in X server or in desktop environment.
Peter Huterer's comment: "just for the record: I am deeply opposed to implementing gestures in the driver. This way every driver needs to cook their own soup, there is hardly any possibility to let clients configure the gestures, clients won't know when a gesture has been applied or when a gesture should be skipped instead, there is virtually no chance of standardization of gestures as all drivers will end up using slighlty different parameters, etc. the list is rather long. that's also why I kinda pretend that the code doesn't exist and do cursory checks only (I can't test it anyway). having said that, I realise that the lack of gesture support in the server is currently blocking any other attempts. So I'll reluctantly keep saying yes to this gesture approach, but expect it to be temporary only." Signed-off-by: Ping Cheng <[email protected]> --- include/wacom-properties.h | 6 + src/wcmCommon.c | 100 ++++----- src/wcmConfig.c | 7 + src/wcmTouchFilter.c | 552 ++++++++++++++++++++++++-------------------- src/wcmUSB.c | 12 +- src/wcmValidateDevice.c | 20 ++ src/wcmXCommand.c | 39 +++ src/xf86Wacom.h | 3 +- src/xf86WacomDefs.h | 13 + tools/xsetwacom.c | 33 +++ 10 files changed, 482 insertions(+), 303 deletions(-) diff --git a/include/wacom-properties.h b/include/wacom-properties.h index d4b1112..5ac20aa 100644 --- a/include/wacom-properties.h +++ b/include/wacom-properties.h @@ -66,6 +66,12 @@ /* BOOL, 1 value */ #define WACOM_PROP_TOUCH "Wacom Enable Touch" +/* 8 bit, 1 values */ +#define WACOM_PROP_ENABLE_GESTURE "Wacom Enable Touch Gesture" + +/* 32 bit, 3 values, zoom, rotate, tap parameters */ +#define WACOM_PROP_GESTURE_PARAMETERS "Wacom Touch Gesture Parameters" + /* BOOL, 1 value */ #define WACOM_PROP_HOVER "Wacom Hover Click" diff --git a/src/wcmCommon.c b/src/wcmCommon.c index 8f669b3..a7b5286 100644 --- a/src/wcmCommon.c +++ b/src/wcmCommon.c @@ -30,11 +30,13 @@ #define THRESHOLD_TOLERANCE (FILTER_PRESSURE_RES / 125) #define DEFAULT_THRESHOLD (FILTER_PRESSURE_RES / 75) +/* Tested result for Bamboo touch jump */ +#define BAMBOO_TOUCH_JUMPED 30 + /***************************************************************************** * Static functions ****************************************************************************/ -static void wcmSoftOutEvent(LocalDevicePtr local); static void transPressureCurve(WacomDevicePtr pDev, WacomDeviceStatePtr pState); static void commonDispatchDevice(WacomCommonPtr common, unsigned int channel, const WacomChannelPtr pChannel, int suppress); @@ -1084,7 +1086,7 @@ void wcmEvent(WacomCommonPtr common, unsigned int channel, WacomChannelPtr pChannel; WacomFilterState* fs; int i, suppress = 0; - + WacomDevicePtr priv = common->wcmDevices; pChannel = common->wcmChannel + channel; pLast = &pChannel->valid.state; @@ -1098,8 +1100,6 @@ void wcmEvent(WacomCommonPtr common, unsigned int channel, * will need to change the values (ie. for error correction) */ ds = *pState; - /* timestamp the state for velocity and acceleration analysis */ - ds.sample = (int)GetTimeInMillis(); DBG(10, common, "c=%d i=%d t=%d s=%u x=%d y=%d b=%d " "p=%d rz=%d tx=%d ty=%d aw=%d rw=%d " @@ -1114,6 +1114,22 @@ void wcmEvent(WacomCommonPtr common, unsigned int channel, ds.discard_first, ds.proximity, ds.sample, pChannel->nSamples); + /* touch device is needed for gesture later */ + if ((ds.device_type == TOUCH_ID) && !IsTouch(priv) && + TabletHasFeature(common, WCM_2FGT)) + { + + for (; priv != NULL && !IsTouch(priv); priv = priv->next); + + if (priv == NULL || !IsTouch(priv)) + { + priv = common->wcmDevices; + /* this error will likely cause the driver crash */ + xf86Msg(X_ERROR, "%s: wcmEvent could not " + "find touch device.", priv->name); + } + } + /* Discard the first 2 USB packages due to events delay */ if ( (pChannel->nSamples < 2) && (common->wcmDevCls == &gWacomUSBDevice) && ds.device_type != PAD_ID && (ds.device_type != TOUCH_ID) ) @@ -1125,6 +1141,19 @@ void wcmEvent(WacomCommonPtr common, unsigned int channel, return; /* discard */ } + /* ignore Bamboo touch data if point is abnormal */ + if ((ds.device_type == TOUCH_ID) && (common->tablet_id >= 0xd0 + && common->tablet_id <= 0xd3) && ds.proximity) + { + if (!(ds.x * ds.y) || (pLast->proximity && + (abs(ds.x - pLast->x) > BAMBOO_TOUCH_JUMPED || + abs(ds.y - pLast->y) > BAMBOO_TOUCH_JUMPED))) + { + /* ignore the data */ + goto ret; + } + } + if (TabletHasFeature(common, WCM_ROTATION) && TabletHasFeature(common, WCM_RING)) /* I4 */ { @@ -1197,49 +1226,17 @@ void wcmEvent(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; + if ((ds.device_type == TOUCH_ID) && common->wcmTouch) + wcmGestureFilter(priv, channel); - /* exit gesture mode when both fingers are out */ - if (channel) - pOtherChannel = common->wcmChannel; - else - pOtherChannel = common->wcmChannel + 1; - dsOther = pOtherChannel->valid.state; + /* don't move the cursor if in gesture mode */ + if (common->wcmGestureMode) + goto ret; - /* 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) - { - wcmFingerTapToClick(common); - goto ret; - } - } - } - - /* everything else falls here */ - commonDispatchDevice(common,channel,pChannel, suppress); + /* For touch, only first finger moves the cursor */ + if ((ds.device_type == TOUCH_ID && common->wcmTouch && !channel) || + (ds.device_type != TOUCH_ID)) + commonDispatchDevice(common,channel,pChannel, suppress); ret: resetSampleCounter(pChannel); } @@ -1500,14 +1497,6 @@ static void commonDispatchDevice(WacomCommonPtr common, unsigned int channel, transPressureCurve(priv,&filtered); } - /* touch capacity is supported */ - if (IsTouch(priv) && (common->wcmCapacityDefault >= 0) && !priv->hardProx) - { - if (((double)(filtered.capacity * 5) / - (double)common->wcmMaxZ) > - (5 - common->wcmCapacity)) - filtered.buttons |= button; - } else if (IsCursor(priv) && !priv->hardProx) { /* initial current max distance for Intuos series */ @@ -1664,7 +1653,7 @@ int wcmInitTablet(LocalDevicePtr local, const char* id, float version) } /* Send a soft prox-out event for the device */ -static void wcmSoftOutEvent(LocalDevicePtr local) +void wcmSoftOutEvent(LocalDevicePtr local) { WacomDeviceState out = { 0 }; WacomDevicePtr priv = (WacomDevicePtr) local->private; @@ -1673,6 +1662,9 @@ static void wcmSoftOutEvent(LocalDevicePtr local) out.device_id = wcmGetPhyDeviceID(priv); DBG(2, priv->common, "send a soft prox-out\n"); wcmSendEvents(local, &out); + + if (out.device_type == TOUCH_ID) + priv->common->wcmTouchpadMode = 0; } /***************************************************************************** diff --git a/src/wcmConfig.c b/src/wcmConfig.c index 6262e4d..4931a3d 100644 --- a/src/wcmConfig.c +++ b/src/wcmConfig.c @@ -116,6 +116,13 @@ static int wcmAllocate(LocalDevicePtr local) common->wcmCapacity = -1; /* Capacity is disabled */ common->wcmCapacityDefault = -1; /* default to -1 when capacity isn't supported */ /* 3 when capacity is supported */ + common->wcmGestureParameters.wcmZoomDistance = 50; + common->wcmGestureParameters.wcmZoomDistanceDefault = 50; + common->wcmGestureParameters.wcmScrollDirection = 0; + common->wcmGestureParameters.wcmScrollDistance = 20; + common->wcmGestureParameters.wcmScrollDistanceDefault = 20; + common->wcmGestureParameters.wcmTapTime = 250; + common->wcmGestureParameters.wcmTapTimeDefault = 250; common->wcmRotate = ROTATE_NONE; /* default tablet rotation to off */ common->wcmMaxX = 0; /* max digitizer logical X value */ common->wcmMaxY = 0; /* max digitizer logical Y value */ diff --git a/src/wcmTouchFilter.c b/src/wcmTouchFilter.c index f31d948..d52fb5b 100644 --- a/src/wcmTouchFilter.c +++ b/src/wcmTouchFilter.c @@ -24,28 +24,21 @@ #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_INLINE_DISTANCE 40 #define WACOM_HORIZ_ALLOWED 1 #define WACOM_VERT_ALLOWED 2 +#define WACOM_GESTURE_LAG_TIME 10 #define GESTURE_TAP_MODE 1 #define GESTURE_SCROLL_MODE 2 #define GESTURE_ZOOM_MODE 4 +#define GESTURE_LAG_MODE 8 #define WCM_SCROLL_UP 5 /* vertical up */ #define WCM_SCROLL_DOWN 4 /* vertical down */ #define WCM_SCROLL_LEFT 6 /* horizontal left */ #define WCM_SCROLL_RIGHT 7 /* horizontal right */ - -/* Defines for Tap Add-a-Finger to Click */ -#define WACOM_TAP_TIME_IN_MS 150 - static void wcmFingerScroll(WacomDevicePtr priv); static void wcmFingerZoom(WacomDevicePtr priv); @@ -57,246 +50,275 @@ static double touchDistance(WacomDeviceState ds0, WacomDeviceState ds1) return distance; } -static Bool pointsInLine(WacomDeviceState ds0, WacomDeviceState ds1, - int *direction) +static Bool pointsInLine(WacomCommonPtr common, WacomDeviceState ds0, + WacomDeviceState ds1) { Bool ret = FALSE; - if (*direction == 0) + if (!common->wcmGestureParameters.wcmScrollDirection) { - if (abs(ds0.x - ds1.x) < WACOM_PARA_MOTION_IN_POINT) + if ((abs(ds0.x - ds1.x) < WACOM_INLINE_DISTANCE) && + (abs(ds0.y - ds1.y) > WACOM_INLINE_DISTANCE)) { - *direction = WACOM_VERT_ALLOWED; + common->wcmGestureParameters.wcmScrollDirection = WACOM_VERT_ALLOWED; ret = TRUE; } - else if (abs(ds0.y - ds1.y) < WACOM_PARA_MOTION_IN_POINT) + if ((abs(ds0.y - ds1.y) < WACOM_INLINE_DISTANCE) && + (abs(ds0.x - ds1.x) > WACOM_INLINE_DISTANCE)) { - *direction = WACOM_HORIZ_ALLOWED; + common->wcmGestureParameters.wcmScrollDirection = WACOM_HORIZ_ALLOWED; ret = TRUE; } } - else if (*direction == WACOM_HORIZ_ALLOWED) + else if (common->wcmGestureParameters.wcmScrollDirection == WACOM_HORIZ_ALLOWED) { - if (abs(ds0.y - ds1.y) < WACOM_PARA_MOTION_IN_POINT) + if (abs(ds0.y - ds1.y) < WACOM_INLINE_DISTANCE) ret = TRUE; } - else if (*direction == WACOM_VERT_ALLOWED) + else if (common->wcmGestureParameters.wcmScrollDirection == WACOM_VERT_ALLOWED) { - if (abs(ds0.x - ds1.x) < WACOM_PARA_MOTION_IN_POINT) + if (abs(ds0.x - ds1.x) < WACOM_INLINE_DISTANCE) ret = TRUE; } return ret; } -static Bool pointsInLineAfter(int p1, int p2) +/* send a button event */ +static void wcmSendButtonClick(WacomDevicePtr priv, int button, int state) { - Bool ret = FALSE; + int x = 0; + int y = 0; + int mode = priv->flags & ABSOLUTE_FLAG; - if (abs(p1 - p2) < WACOM_PARA_MOTION_IN_POINT) - ret = TRUE; + if (mode) + { + x = priv->oldX; + y = priv->oldY; + } - return ret; -} + /* send button event in state */ + xf86PostButtonEvent(priv->local->dev, mode,button, + state,0,priv->naxes,x,y,0,0,0,0); -static void wcmSwitchLeftClick(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); + /* We have changed the button state (from down to up) for the device + * so we need to update the record */ + if (button == 1) priv->oldButtons = 0; - } } /***************************************************************************** * translate second finger tap to right click ****************************************************************************/ -void wcmFingerTapToClick(WacomCommonPtr common) +static void wcmFingerTapToClick(WacomDevicePtr priv) { - WacomDevicePtr priv = common->wcmDevices; + 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 direction = 0; DBG(10, priv, "\n"); - /* skip initial second finger event */ - if (!dsLast[1].proximity) - goto skipGesture; - - if (!IsTouch(priv)) + /* process second finger tap if matched */ + if ((ds[0].sample < ds[1].sample) && + ((GetTimeInMillis() - + dsLast[1].sample) <= common->wcmGestureParameters.wcmTapTime) && + !ds[1].proximity && dsLast[1].proximity) { - /* go through the shared port */ - for (; priv; priv = priv->next) - if (IsTouch(priv)) - break; + /* send left up before sending right down */ + wcmSendButtonClick(priv, 1, 0); + common->wcmGestureMode = GESTURE_TAP_MODE; + + /* right button down */ + wcmSendButtonClick(priv, 3, 1); + + /* right button up */ + wcmSendButtonClick(priv, 3, 0); } +} + - if (priv) /* found the first finger */ +/* process single finger Relative mode events + * if touch is not in an active gesture mode. + */ +static void wcmFirstFingerClick(WacomCommonPtr common) +{ + static int tmpStamp = 0; + WacomChannelPtr aChannel = common->wcmChannel; + WacomDeviceState ds = aChannel->valid.states[0]; + WacomDeviceState dsLast = aChannel->valid.states[1]; + if (ds.proximity) { - /* 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; - wcmSwitchLeftClick(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 (common->wcmTouchpadMode) + /* continuing left button down */ + aChannel->valid.states[0].buttons |= 1; + else if (!dsLast.proximity && + (abs(tmpStamp - ds.sample) <= common->wcmGestureParameters.wcmTapTime)) { - 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; - wcmSwitchLeftClick(priv); - } - wcmFingerZoom(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; - wcmSwitchLeftClick(priv); - } - wcmFingerScroll(priv); - } + /* initial left button down */ + aChannel->valid.states[0].buttons |= 1; + common->wcmTouchpadMode = 1; } + } else { + tmpStamp = GetTimeInMillis(); + if (common->wcmTouchpadMode) + aChannel->valid.states[0].buttons &= ~1; + common->wcmTouchpadMode = 0; } - else +} + +/* parsing gesture mode according to 2FGT data */ +void wcmGestureFilter(WacomDevicePtr priv, int channel) +{ + 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] }; + + DBG(10, priv, "\n"); + + if (!IsTouch(priv)) + { /* this should never happen */ - xf86Msg(X_ERROR, "WACOM: No touch device found for %s \n", common->wcmDevice); + xf86Msg(X_ERROR, "WACOM: No touch device found for %s \n", + common->wcmDevice); + return; + } -skipGesture: - /* keep the initial in-prox time */ - ds[0].sample = dsLast[0].sample; - ds[1].sample = dsLast[1].sample; + if (!common->wcmGesture) + goto ret; - /* keep the initial states for both fingers */ - if ( !(common->wcmGestureMode && (GESTURE_SCROLL_MODE | GESTURE_ZOOM_MODE)) - && ds[0].proximity && ds[1].proximity) + /* second finger in prox. wait for gesture event if first finger + * was in in prox */ + if (ds[1].proximity && !common->wcmGestureMode && dsLast[0].proximity) { - common->wcmGestureState[0] = ds[0]; + common->wcmTouchpadMode = 0; + common->wcmGestureMode = GESTURE_LAG_MODE; + } + + /* first finger recently came in prox. But not the first time + * wait for the second one for a certain time */ + else if (dsLast[0].proximity && + ((GetTimeInMillis() - ds[0].sample) < WACOM_GESTURE_LAG_TIME)) + { + if (!common->wcmGestureMode) + common->wcmGestureMode = GESTURE_LAG_MODE; + } + + /* we've waited enough time */ + else if (common->wcmGestureMode == GESTURE_LAG_MODE) + common->wcmGestureMode = 0; + + if (ds[1].proximity && !dsLast[1].proximity) + { + /* keep the initial states for gesture mode */ common->wcmGestureState[1] = ds[1]; + + /* reset the initial count for a new getsure */ + common->wcmGestureParameters.wcmGestureUsed = 0; } -} -static void wcmSendVerticalScrollEvent(WacomDevicePtr priv, - int dist, int up, int dn) -{ - int i = 0; + if (ds[0].proximity && !dsLast[0].proximity) + { + /* keep the initial states for gesture mode */ + common->wcmGestureState[0] = ds[0]; - for (i=0; i<(int)(((double)abs(dist)/ - (double)WACOM_MOTION_IN_POINT) + 0.5); i++) + /* reset the initial count for a new getsure */ + common->wcmGestureParameters.wcmGestureUsed = 0; + + /* initialize the cursor position */ + if (!common->wcmGestureMode && !channel) + goto ret; + } + + if (!ds[0].proximity && !ds[1].proximity) { - 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); - } + /* first finger was out-prox when GestureMode was still on */ + if (!dsLast[0].proximity && common->wcmGestureMode) + /* send first finger out prox */ + wcmSoftOutEvent(priv->local); + + /* exit gesture mode when both fingers are out */ + common->wcmGestureMode = 0; + common->wcmGestureParameters.wcmScrollDirection = 0; + + common->wcmTouchpadMode = 0; + goto ret; } + + if (!(common->wcmGestureMode & (GESTURE_SCROLL_MODE | GESTURE_ZOOM_MODE))) + wcmFingerTapToClick(priv); + + /* Change mode happens only when both fingers are out */ + if (common->wcmGestureMode & GESTURE_TAP_MODE) + goto ret; + + /* skip initial finger event for scroll and zoom */ + if (!dsLast[0].proximity || !dsLast[1].proximity) + goto ret; + + /* was in zoom mode no time check needed */ + if ((common->wcmGestureMode & GESTURE_ZOOM_MODE) && + ds[0].proximity && ds[1].proximity) + wcmFingerZoom(priv); + + /* was in scroll mode no time check needed */ + else if (common->wcmGestureMode & GESTURE_SCROLL_MODE) + wcmFingerScroll(priv); + + /* process complex two finger gestures */ + else if ((2*common->wcmGestureParameters.wcmTapTime < + (GetTimeInMillis() - ds[0].sample)) && + (2*common->wcmGestureParameters.wcmTapTime < + (GetTimeInMillis() - ds[1].sample)) + && ds[0].proximity && ds[1].proximity) + { + /* scroll should be considered first since it requires + * a finger distance check */ + wcmFingerScroll(priv); + + if (!(common->wcmGestureMode & GESTURE_SCROLL_MODE)) + wcmFingerZoom(priv); + } +ret: + if (!common->wcmGestureMode && !channel && !(priv->flags & ABSOLUTE_FLAG)) + wcmFirstFingerClick(common); } -static void wcmSendHorizontalScrollEvent(WacomDevicePtr priv, - int dist, int left, int right) +static void wcmSendScrollEvent(WacomDevicePtr priv, int dist, + int buttonUp, int buttonDn) { - int i = 0; + int button = (dist > 0) ? buttonUp : buttonDn; + WacomCommonPtr common = priv->common; + int count = (int)(((double)abs(dist)/ + common->wcmGestureParameters.wcmScrollDistance) + 0.5); + WacomChannelPtr firstChannel = common->wcmChannel; + WacomChannelPtr secondChannel = common->wcmChannel + 1; + WacomDeviceState ds[2] = { firstChannel->valid.states[0], + secondChannel->valid.states[0] }; - for (i=0; i<(int)(((double)abs(dist)/ - (double)WACOM_MOTION_IN_POINT) + 0.5); i++) + /* user might have changed from up to down or vice versa */ + if (count < common->wcmGestureParameters.wcmGestureUsed) { - if (dist > 0) - { - /* button down */ - xf86PostButtonEvent(priv->local->dev, - priv->flags & ABSOLUTE_FLAG, - left,1,0,priv->naxes, priv->oldX, - priv->oldY,0,0,0,0); - /* button up */ - xf86PostButtonEvent(priv->local->dev, - priv->flags & ABSOLUTE_FLAG, - left,0,0,priv->naxes, priv->oldX, - priv->oldY,0,0,0,0); - } - else - { - /* button down */ - xf86PostButtonEvent(priv->local->dev, - priv->flags & ABSOLUTE_FLAG, - right,1,0,priv->naxes, priv->oldX, - priv->oldY,0,0,0,0); - /* button up */ - xf86PostButtonEvent(priv->local->dev, - priv->flags & ABSOLUTE_FLAG, - right,0,0,priv->naxes, priv->oldX, - priv->oldY,0,0,0,0); - } + common->wcmGestureState[0] = ds[0]; + common->wcmGestureState[1] = ds[1]; + common->wcmGestureParameters.wcmGestureUsed = 0; + return; + } + + count -= common->wcmGestureParameters.wcmGestureUsed; + common->wcmGestureParameters.wcmGestureUsed += count; + while (count--) + { + wcmSendButtonClick(priv, button, 1); + wcmSendButtonClick(priv, button, 0); + DBG(10, priv, "loop count = %d \n", i); } } @@ -307,16 +329,38 @@ static void wcmFingerScroll(WacomDevicePtr priv) 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; + int i = 0, dist = 0; WacomFilterState filterd; /* borrow this struct */ DBG(10, priv, "\n"); + if (common->wcmGestureMode != GESTURE_SCROLL_MODE) + { + if (abs(touchDistance(ds[0], ds[1]) - + touchDistance(common->wcmGestureState[0], + common->wcmGestureState[1])) < WACOM_INLINE_DISTANCE) + { + /* two fingers stay close to each other all the time and + * move in vertical or horizontal direction together + */ + if (pointsInLine(common, ds[0], common->wcmGestureState[0]) + && pointsInLine(common, ds[1], common->wcmGestureState[1]) + && common->wcmGestureParameters.wcmScrollDirection) + { + /* left button might be down. Send it up first */ + wcmSendButtonClick(priv, 1, 0); + common->wcmGestureMode = GESTURE_SCROLL_MODE; + } + } + } + + /* still not a scroll event yet? */ + if (common->wcmGestureMode != GESTURE_SCROLL_MODE) + return; + /* initialize the points so we can rotate them */ filterd.x[0] = ds[0].x; filterd.y[0] = ds[0].y; @@ -326,56 +370,54 @@ static void wcmFingerScroll(WacomDevicePtr priv) 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 */ + /* scrolling has directions so rotation has to be considered first */ for (i=0; i<6; i++) wcmRotateCoordinates(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)) + if (common->wcmGestureParameters.wcmScrollDirection == WACOM_VERT_ALLOWED) { 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) + /* allow one finger scroll */ + if (!ds[0].proximity) { - gesture = 1; - wcmSendVerticalScrollEvent(priv, dist, - WCM_SCROLL_UP, WCM_SCROLL_DOWN); + midPoint_old = filterd.y[3]; + midPoint_new = filterd.y[1]; } - /* check horizontal direction */ - if (!gesture) + if (!ds[1].proximity) { - 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; - wcmSendHorizontalScrollEvent(priv, dist, - WCM_SCROLL_LEFT, WCM_SCROLL_RIGHT); - } - } + midPoint_old = filterd.y[2]; + midPoint_new = filterd.y[0]; + } + + dist = midPoint_old - midPoint_new; + wcmSendScrollEvent(priv, dist, WCM_SCROLL_UP, WCM_SCROLL_DOWN); + } + + if (common->wcmGestureParameters.wcmScrollDirection == WACOM_HORIZ_ALLOWED) + { + midPoint_old = (((double)filterd.x[2] + (double)filterd.x[3]) / 2.); + midPoint_new = (((double)filterd.x[0] + (double)filterd.x[1]) / 2.); + + /* allow one finger scroll */ + if (!ds[0].proximity) + { + midPoint_old = filterd.x[3]; + midPoint_new = filterd.x[1]; } - if (gesture) + + if (!ds[1].proximity) { - /* reset initial states */ - common->wcmGestureState[0] = ds[0]; - common->wcmGestureState[1] = ds[1]; + midPoint_old = filterd.x[2]; + midPoint_new = filterd.x[0]; } + + dist = midPoint_old - midPoint_new; + wcmSendScrollEvent(priv, dist, WCM_SCROLL_RIGHT, WCM_SCROLL_LEFT); } } @@ -386,38 +428,56 @@ static void wcmFingerZoom(WacomDevicePtr priv) WacomChannelPtr secondChannel = common->wcmChannel + 1; WacomDeviceState ds[2] = { firstChannel->valid.states[0], secondChannel->valid.states[0] }; - int i = 0; + int count, key; int dist = touchDistance(common->wcmGestureState[0], common->wcmGestureState[1]); DBG(10, priv, "\n"); - dist = touchDistance(ds[0], ds[1]) - dist; - - /* zooming? */ - if (abs(dist) > WACOM_MOTION_IN_POINT) + if (common->wcmGestureMode != GESTURE_ZOOM_MODE) { - for (i=0; i<(int)(((double)abs(dist)/ - (double)WACOM_MOTION_IN_POINT) + 0.5); i++) + /* two fingers moved apart from each other */ + if (abs(touchDistance(ds[0], ds[1]) - + touchDistance(common->wcmGestureState[0], + common->wcmGestureState[1])) > + (3 * WACOM_INLINE_DISTANCE)) { - wcmEmitKeysym (priv->local->dev, XK_Control_L, 1); - /* zooming in */ - if (dist > 0) - { - wcmEmitKeysym (priv->local->dev, XK_plus, 1); - wcmEmitKeysym (priv->local->dev, XK_plus, 0); - } - else /* zooming out */ - { - wcmEmitKeysym (priv->local->dev, XK_minus, 1); - wcmEmitKeysym (priv->local->dev, XK_minus, 0); - } - wcmEmitKeysym (priv->local->dev, XK_Control_L, 0); + /* left button might be down, send it up first */ + wcmSendButtonClick(priv, 1, 0); + + /* fingers moved apart more than 3 times + * WACOM_INLINE_DISTANCE, zoom mode is entered */ + common->wcmGestureMode = GESTURE_ZOOM_MODE; } + } + + if (common->wcmGestureMode != GESTURE_ZOOM_MODE) + return; - /* reset initial states */ + dist = touchDistance(ds[0], ds[1]) - dist; + count = (int)(((double)abs(dist)/common->wcmGestureParameters.wcmZoomDistance) + 0.5); + + /* user might have changed from left to right or vice versa */ + if (count < common->wcmGestureParameters.wcmGestureUsed) + { + /* reset the initial states for the new getsure */ common->wcmGestureState[0] = ds[0]; common->wcmGestureState[1] = ds[1]; + common->wcmGestureParameters.wcmGestureUsed = 0; + return; + } + + /* zooming? */ + key = (dist > 0) ? XK_plus : XK_minus; + + count -= common->wcmGestureParameters.wcmGestureUsed; + common->wcmGestureParameters.wcmGestureUsed += count; + while (count--) + { + wcmEmitKeysym (priv->local->dev, XK_Control_L, 1); + wcmEmitKeysym (priv->local->dev, key, 1); + wcmEmitKeysym (priv->local->dev, key, 0); + wcmEmitKeysym (priv->local->dev, XK_Control_L, 0); } } diff --git a/src/wcmUSB.c b/src/wcmUSB.c index 90df2d8..3323030 100644 --- a/src/wcmUSB.c +++ b/src/wcmUSB.c @@ -667,9 +667,17 @@ static int usbChooseChannel(WacomCommonPtr common, int serial) if (common->wcmProtocolLevel == 4) { - /* Protocol 4 doesn't support tool serial numbers */ + /* Protocol 4 doesn't support tool serial numbers. + * However, we pass finger index into serial + * numbers for tablets with multi-touch capabilities + * to track individual fingers in proper channels. + * serial number 0xf0 is reserved for the pad and is + * always the last supported channel (i.e. MAX_CHANNELS-1). + */ if (serial == 0xf0) - channel = 1; + channel = MAX_CHANNELS-1; + else if (serial) + channel = serial-1; else channel = 0; } diff --git a/src/wcmValidateDevice.c b/src/wcmValidateDevice.c index df1375c..bfc8fcf 100644 --- a/src/wcmValidateDevice.c +++ b/src/wcmValidateDevice.c @@ -625,6 +625,26 @@ int wcmParseOptions(LocalDevicePtr local, int hotplugged) else if (oldOption != common->wcmGesture) xf86Msg(X_WARNING, "%s: Touch gesture option can only " "be set by a touch tool.\n", local->name); + + if ((common->wcmDevCls == &gWacomUSBDevice) && + TabletHasFeature(common, WCM_LCD) && + TabletHasFeature(common, WCM_2FGT)) { + common->wcmGestureParameters.wcmZoomDistanceDefault = 30; + common->wcmGestureParameters.wcmScrollDistanceDefault = 30; + common->wcmGestureParameters.wcmTapTimeDefault = 250; + } + + common->wcmGestureParameters.wcmZoomDistance = + xf86SetIntOption(local->options, "ZoomDistance", + common->wcmGestureParameters.wcmZoomDistanceDefault); + + common->wcmGestureParameters.wcmScrollDistance = + xf86SetIntOption(local->options, "ScrollDistance", + common->wcmGestureParameters.wcmScrollDistanceDefault); + + common->wcmGestureParameters.wcmTapTime = + xf86SetIntOption(local->options, "TapTime", + common->wcmGestureParameters.wcmTapTimeDefault); } /* Mouse cursor stays in one monitor in a multimonitor setup */ diff --git a/src/wcmXCommand.c b/src/wcmXCommand.c index 40207a3..d41512e 100644 --- a/src/wcmXCommand.c +++ b/src/wcmXCommand.c @@ -116,6 +116,8 @@ Atom prop_capacity; Atom prop_threshold; Atom prop_suppress; Atom prop_touch; +Atom prop_gesture; +Atom prop_gesture_param; Atom prop_hover; Atom prop_tooltype; Atom prop_btnactions; @@ -247,6 +249,13 @@ void InitWcmDeviceProperties(LocalDevicePtr local) values[0] = !common->wcmTPCButton; prop_hover = InitWcmAtom(local->dev, WACOM_PROP_HOVER, 8, 1, values); + values[0] = common->wcmGesture; + prop_gesture = InitWcmAtom(local->dev, WACOM_PROP_ENABLE_GESTURE, 8, 1, values); + + values[0] = common->wcmGestureParameters.wcmZoomDistance; + values[1] = common->wcmGestureParameters.wcmScrollDistance; + values[2] = common->wcmGestureParameters.wcmTapTime; + prop_gesture_param = InitWcmAtom(local->dev, WACOM_PROP_GESTURE_PARAMETERS, 32, 3, values); values[0] = MakeAtom(local->type_name, strlen(local->type_name), TRUE); prop_tooltype = InitWcmAtom(local->dev, WACOM_PROP_TOOL_TYPE, -32, 1, values); @@ -653,6 +662,36 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, if (!checkonly && common->wcmTouch != values[0]) common->wcmTouch = values[0]; + } else if (property == prop_gesture) + { + CARD8 *values = (CARD8*)prop->data; + + if (prop->size != 1 || prop->format != 8) + return BadValue; + + if ((values[0] != 0) && (values[0] != 1)) + return BadValue; + + if (!checkonly && common->wcmGesture != values[0]) + common->wcmGesture = values[0]; + } else if (property == prop_gesture_param) + { + CARD32 *values; + + if (prop->size != 3 || prop->format != 32) + return BadValue; + + values = (CARD32*)prop->data; + + if (!checkonly) + { + if (common->wcmGestureParameters.wcmZoomDistance != values[0]) + common->wcmGestureParameters.wcmZoomDistance = values[0]; + if (common->wcmGestureParameters.wcmScrollDistance != values[1]) + common->wcmGestureParameters.wcmScrollDistance = values[1]; + if (common->wcmGestureParameters.wcmTapTime != values[2]) + common->wcmGestureParameters.wcmTapTime = values[2]; + } } else if (property == prop_hover) { CARD8 *values = (CARD8*)prop->data; diff --git a/src/xf86Wacom.h b/src/xf86Wacom.h index 17fd793..d05b66f 100644 --- a/src/xf86Wacom.h +++ b/src/xf86Wacom.h @@ -161,8 +161,9 @@ extern int wcmDevSwitchMode(ClientPtr client, DeviceIntPtr dev, int mode); /* run-time modifications */ extern void wcmChangeScreen(LocalDevicePtr local, int value); extern void wcmTilt2R(WacomDeviceStatePtr ds); -extern void wcmFingerTapToClick(WacomCommonPtr common); +extern void wcmGestureFilter(WacomDevicePtr priv, int channel); extern void wcmEmitKeysym(DeviceIntPtr keydev, int keysym, int state); +extern void wcmSoftOutEvent(LocalDevicePtr local); extern void wcmRotateTablet(LocalDevicePtr local, int value); extern void wcmRotateCoordinates(LocalDevicePtr local, int* x, int* y); diff --git a/src/xf86WacomDefs.h b/src/xf86WacomDefs.h index ddddf26..d919a14 100644 --- a/src/xf86WacomDefs.h +++ b/src/xf86WacomDefs.h @@ -359,6 +359,17 @@ extern WacomDeviceClass gWacomISDV4Device; #define MAX_CHANNELS 2 #define MAX_FINGERS 2 +typedef struct { + int wcmZoomDistance; /* minimum distance for a zoom touch gesture */ + int wcmZoomDistanceDefault; /* default minimum distance for a zoom touch gesture */ + int wcmScrollDistance; /* minimum motion before sending a scroll gesture */ + int wcmScrollDirection; /* store the vertical or horizontal bit in use */ + int wcmScrollDistanceDefault; /* default minimum motion before sending a scroll gesture */ + int wcmGestureUsed; /* retain used gesture count within one in-prox event */ + int wcmTapTime; /* minimum time between taps for a right click */ + int wcmTapTimeDefault; /* default minimum time between taps for a right click */ +} WacomGesturesParameters; + struct _WacomCommonRec { /* Do not move wcmDevice, same offset as priv->name */ @@ -415,10 +426,12 @@ struct _WacomCommonRec int wcmGesture; /* disable/enable touch gesture */ int wcmGestureDefault; /* default touch gesture to disable when not supported */ int wcmGestureMode; /* data is in Gesture Mode? */ + int wcmTouchpadMode; /* in touchpad 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 */ + WacomGesturesParameters wcmGestureParameters; int wcmMaxCursorDist; /* Max mouse distance reported so far */ int wcmCursorProxoutDist; /* Max mouse distance for proxy-out max/256 units */ int wcmCursorProxoutDistDefault; /* Default max mouse distance for proxy-out */ diff --git a/tools/xsetwacom.c b/tools/xsetwacom.c index 874f42f..2e7a0f2 100644 --- a/tools/xsetwacom.c +++ b/tools/xsetwacom.c @@ -412,6 +412,39 @@ static param_t parameters[] = .prop_flags = PROP_FLAG_BOOLEAN }, { + .name = "Gesture", + .desc = "Turns on/off multi-touch gesture events " + "(default is enable/on). ", + .prop_name = WACOM_PROP_ENABLE_GESTURE, + .prop_format = 8, + .prop_offset = 0, + .prop_flags = PROP_FLAG_BOOLEAN + }, + { + .name = "ZoomDistance", + .desc = "Minimum distance for a zoom gesture " + "(default is 50). ", + .prop_name = WACOM_PROP_GESTURE_PARAMETERS, + .prop_format = 32, + .prop_offset = 0, + }, + { + .name = "ScrollDistance", + .desc = "Minimum motion before sending a scroll gesture " + "(default is 20). ", + .prop_name = WACOM_PROP_GESTURE_PARAMETERS, + .prop_format = 32, + .prop_offset = 0, + }, + { + .name = "TapTime", + .desc = "Minimum time between taps for a right click " + "(default is 250). ", + .prop_name = WACOM_PROP_GESTURE_PARAMETERS, + .prop_format = 32, + .prop_offset = 0, + }, + { .name = "Capacity", .desc = "Touch sensitivity level (default is 3, " "-1 for none capacitive tools).", -- 1.6.6.1 ------------------------------------------------------------------------------ _______________________________________________ Linuxwacom-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linuxwacom-devel
