diff --git a/README.md b/README.md index b218f3e..c4b433a 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,15 @@ This is a standard autoconf package. So: make && make install It is likely that you will need to change system-dependent paths such as the -xorg module directory. See `configure --help` for options. +xorg module directory. Otherwise mtrack may be not installed in xserver search +path. +See `configure --help` for options. + +To build deb package and install in system wide you will usually have to change +installation prefix to /usr like so: + + ./configure --prefix=/usr + dpkg-buildpackage Configuration ------------- @@ -108,7 +116,8 @@ Button emulation depends on this value being correct. Boolean value. Defaults to true. **ButtonMoveEmulate** -Whether or not to count the moving finger when emulating button clicks. Useful to disable if you use two hands on trackpad. Boolean value. Defaults to true. +Whether or not to count the moving finger when emulating button clicks. +Useful to disable if you use two hands on trackpad. Boolean value. Defaults to true. **ButtonZonesEnable** - Whether or not to enable button zones. If button zones are enabled then the @@ -153,10 +162,13 @@ disables three-finger tapping. Defaults to 0. **ClickTime** - When tapping, how much time to hold down the emulated button. Integer value -representing milliseconds. Defaults to 50. +representing milliseconds. Not very usable because button release event will be +sent immediately after releasing any of fingers (touches). +Integer value representing miliseconds. Defaults to 50. **MaxTapTime** - -The amount of time to wait for a tap to release before counting it as a move. +The amount of time to wait for incoming touches after first one before counting +it as emulated button click. Integer value representing milliseconds. Defaults to 120. **MaxTapMove** - @@ -178,6 +190,17 @@ gestures. Integer value representing milliseconds. Defaults to 100. For two finger scrolling. How far you must move your fingers before a button click is triggered. Integer value. Defaults to 150. +**ScrollClickTime** - +For two finger scrolling. How long button triggered by scrolling +will be hold down. A value of 0 will hold button down till end of gesture. +Integer value representing milliseconds. Defaults to 20. + +**ScrollSensitivity** - +For two finger scrolling. Sensitivity (movement speed) of pointer during two +finger scrolling. A value of 0 disables pointer movement during gesture. +Integer value expressed as parts per thousand of normal sensivity. +A value of 1000 results with normal movement speed. Defaults to 0. + **ScrollUpButton** - For two finger scrolling. The button that is triggered by scrolling up. Integer value. A value of 0 disables scrolling up. Defaults to 4. @@ -198,6 +221,16 @@ Integer value. A value of 0 disables scrolling right. Defaults to 7. For three finger swiping. How far you must move your fingers before a button click is triggered. Integer value. Defaults to 700. +**SwipeClickTime** - +For three finger swiping. How long button triggered by swiping +will be hold down. Integer value representing milliseconds. Defaults to 300. + +**SwipeSensitivity** - +For three finger scrolling. Sensitivity (movement speed) of pointer during three +finger scrolling. A value of 0 disables pointer movement during gesture. +Integer value expressed as parts per thousand of normal sensivity. +A value of 1000 results with normal movement speed. Defaults to 0. + **SwipeUpButton** - For three finger swiping. The button that is triggered by swiping up. Integer value. A value of 0 disables swiping up. Defaults to 8. @@ -218,6 +251,16 @@ value. A value of 0 disables swiping right. Defaults to 11. For four finger swiping. How far you must move your fingers before a button click is triggered. Integer value. Defaults to 700. +**Swipe4ClickTime** - +For four finger swiping. How long button triggered by swiping +will be hold down. Integer value representing milliseconds. Defaults to 300. + +**Swipe4Sensitivity** - +For four finger scrolling. Sensitivity (movement speed) of pointer during four +finger scrolling. A value of 0 disables pointer movement during gesture. +Integer value expressed as parts per thousand of normal sensivity. +A value of 1000 results with normal movement speed. Defaults to 0. + **Swipe4UpButton** - For four finger swiping. The button that is triggered by swiping up. Integer value. A value of 0 disables swiping up. Defaults to 8. @@ -284,6 +327,24 @@ Whether or not to invert the X axis. Boolean value. Defaults to false. **AxisYInvert** Whether or not to invert the Y axis. Boolean value. Defaults to false. +Tips +------------- +##### Swipe to drag +To setup swipe to drag functionality you have to choose which swipe gesture (Scroll, Swipe, Swipe4) +will be used for dragging. +Example configuration for three finger drag: +``` + Option "SwipeDistance" "1" + Option "SwipeLeftButton" "1" + Option "SwipeRightButton" "1" + Option "SwipeUpButton" "1" + Option "SwipeDownButton" "1" + Option "SwipeClickTime" "0" + Option "SwipeSensitivity" "1000" +``` +This will enable draging with three fingers. Change sensitivity for faster/slower movements. +Scroll, and Swipe4 are also supported. + [1]: http://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt "Kernel Multitouch Protocol" [2]: http://www.gnu.org/licenses/gpl-2.0.html "GNU General Public License, version 2" [3]: http://bitmath.org/code/multitouch/ "xf86-input-multitouch website" diff --git a/driver/mprops.c b/driver/mprops.c index 3ded861..5a9e605 100644 --- a/driver/mprops.c +++ b/driver/mprops.c @@ -75,6 +75,22 @@ Atom atom_init_float(DeviceIntPtr dev, char* name, int nvalues, float* values, A return atom; } +static void init_swipe_props(DeviceIntPtr dev, struct MPropsSwipe* props_swipe, + struct MConfigSwipe* cfg_swipe, char const* settings_prop_name, + char const* buttons_prop_name){ + int ivals[MAX_INT_VALUES]; + ivals[0] = cfg_swipe->dist; + ivals[1] = cfg_swipe->hold; + ivals[2] = cfg_swipe->drag_sens; + props_swipe->settings = atom_init_integer(dev, (char*)settings_prop_name, 3, ivals, 32); + + ivals[0] = cfg_swipe->up_btn; + ivals[1] = cfg_swipe->dn_btn; + ivals[2] = cfg_swipe->lt_btn; + ivals[3] = cfg_swipe->rt_btn; + props_swipe->buttons = atom_init_integer(dev, (char*)buttons_prop_name, 4, ivals, 8); +} + void mprops_init(struct MConfig* cfg, InputInfoPtr local) { int ivals[MAX_INT_VALUES]; float fvals[MAX_FLOAT_VALUES]; @@ -142,32 +158,11 @@ void mprops_init(struct MConfig* cfg, InputInfoPtr local) { ivals[1] = cfg->gesture_wait; mprops.gesture_settings = atom_init_integer(local->dev, MTRACK_PROP_GESTURE_SETTINGS, 2, ivals, 16); - ivals[0] = cfg->scroll_dist; - mprops.scroll_dist = atom_init_integer(local->dev, MTRACK_PROP_SCROLL_DIST, 1, ivals, 32); - - ivals[0] = cfg->scroll_up_btn; - ivals[1] = cfg->scroll_dn_btn; - ivals[2] = cfg->scroll_lt_btn; - ivals[3] = cfg->scroll_rt_btn; - mprops.scroll_buttons = atom_init_integer(local->dev, MTRACK_PROP_SCROLL_BUTTONS, 4, ivals, 8); + init_swipe_props(local->dev, &mprops.scroll, &cfg->scroll, MTRACK_PROP_SCROLL_SETTINGS, MTRACK_PROP_SCROLL_BUTTONS); - ivals[0] = cfg->swipe_dist; - mprops.swipe_dist = atom_init_integer(local->dev, MTRACK_PROP_SWIPE_DIST, 1, ivals, 32); + init_swipe_props(local->dev, &mprops.swipe3, &cfg->swipe3, MTRACK_PROP_SWIPE_SETTINGS, MTRACK_PROP_SWIPE_BUTTONS); - ivals[0] = cfg->swipe_up_btn; - ivals[1] = cfg->swipe_dn_btn; - ivals[2] = cfg->swipe_lt_btn; - ivals[3] = cfg->swipe_rt_btn; - mprops.swipe_buttons = atom_init_integer(local->dev, MTRACK_PROP_SWIPE_BUTTONS, 4, ivals, 8); - - ivals[0] = cfg->swipe4_dist; - mprops.swipe4_dist = atom_init_integer(local->dev, MTRACK_PROP_SWIPE4_DIST, 1, ivals, 32); - - ivals[0] = cfg->swipe4_up_btn; - ivals[1] = cfg->swipe4_dn_btn; - ivals[2] = cfg->swipe4_lt_btn; - ivals[3] = cfg->swipe4_rt_btn; - mprops.swipe4_buttons = atom_init_integer(local->dev, MTRACK_PROP_SWIPE4_BUTTONS, 4, ivals, 8); + init_swipe_props(local->dev, &mprops.swipe4, &cfg->swipe4, MTRACK_PROP_SWIPE4_SETTINGS, MTRACK_PROP_SWIPE4_BUTTONS); ivals[0] = cfg->scale_dist; mprops.scale_dist = atom_init_integer(local->dev, MTRACK_PROP_SCALE_DIST, 1, ivals, 32); @@ -194,6 +189,63 @@ void mprops_init(struct MConfig* cfg, InputInfoPtr local) { mprops.axis_invert = atom_init_integer(local->dev, MTRACK_PROP_AXIS_INVERT, 2, ivals, 8); } +/* Return: + * 1 - property was recognized and handled with or without error, check error code for details + * 0 - property not recognized, don't trust returned error code - it's invalid + */ +static int set_swipe_properties(Atom property, BOOL checkonly, XIPropertyValuePtr prop, + struct MPropsSwipe* props_swipe, + struct MConfigSwipe* cfg_swipe, int* error_code){ + + uint8_t* ivals8; + uint16_t* ivals16; + uint32_t* ivals32; + + *error_code = Success; + + if (property == props_swipe->settings) { + if (prop->size != 3 || prop->format != 32 || prop->type != XA_INTEGER) + return *error_code = BadMatch, 1; + + ivals32 = (uint32_t*)prop->data; + if (ivals32[0] < 1 || (int)(ivals32[1]) < 0) + return *error_code = BadMatch, 1; + + if (!checkonly) { + cfg_swipe->dist = ivals32[0]; + cfg_swipe->hold = ivals32[1]; + cfg_swipe->drag_sens = ivals32[2]; +#ifdef DEBUG_PROPS + xf86Msg(X_INFO, "mtrack: set swipe settings: dist: %d hold: %d\n", + cfg_swipe->dist, cfg_swipe->hold); +#endif + } + } + else if (property == props_swipe->buttons) { + if (prop->size != 4 || prop->format != 8 || prop->type != XA_INTEGER) + return *error_code = BadMatch, 1; + + ivals8 = (uint8_t*)prop->data; + if (!VALID_BUTTON(ivals8[0]) || !VALID_BUTTON(ivals8[1]) || !VALID_BUTTON(ivals8[2]) || !VALID_BUTTON(ivals8[3])) + return *error_code = BadMatch, 1; + + if (!checkonly) { + cfg_swipe->up_btn = ivals8[0]; + cfg_swipe->dn_btn = ivals8[1]; + cfg_swipe->lt_btn = ivals8[2]; + cfg_swipe->rt_btn = ivals8[3]; +#ifdef DEBUG_PROPS + xf86Msg(X_INFO, "mtrack: set swipe buttons to %d %d %d %d\n", + cfg_swipe->up_btn, cfg_swipe->dn_btn, cfg_swipe->lt_btn, cfg_swipe->rt_btn); +#endif + } + } + else{ + return 0; + } + return 1; +} + int mprops_set_property(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, BOOL checkonly) { InputInfoPtr local = dev->public.devicePrivate; struct MConfig* cfg = &((struct MTouch*)local->private)->cfg; @@ -203,6 +255,8 @@ int mprops_set_property(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop uint32_t* ivals32; float* fvals; + int error_code; + if (property == mprops.trackpad_disable) { if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; @@ -237,7 +291,7 @@ int mprops_set_property(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop } } else if (property == mprops.pressure) { - if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER) + if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; @@ -254,7 +308,7 @@ int mprops_set_property(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop } } else if (property == mprops.button_settings) { - if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER) + if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER) return BadMatch; ivals8 = (uint8_t*)prop->data; @@ -332,7 +386,7 @@ int mprops_set_property(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop if (!VALID_BUTTON(ivals8[0]) || !VALID_BUTTON(ivals8[1]) || !VALID_BUTTON(ivals8[2]) || !VALID_BUTTON(ivals8[3])) return BadMatch; - if (!checkonly) { + if (!checkonly) { cfg->tap_1touch = ivals8[0]; cfg->tap_2touch = ivals8[1]; cfg->tap_3touch = ivals8[2]; @@ -427,110 +481,14 @@ int mprops_set_property(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop #endif } } - else if (property == mprops.scroll_dist) { - if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER) - return BadMatch; - - ivals32 = (uint32_t*)prop->data; - if (ivals32[0] < 1) - return BadMatch; - - if (!checkonly) { - cfg->scroll_dist = ivals32[0]; -#ifdef DEBUG_PROPS - xf86Msg(X_INFO, "mtrack: set scroll distance to %d\n", - cfg->scroll_dist); -#endif - } + else if (set_swipe_properties(property, checkonly, prop, &mprops.scroll, &cfg->scroll, &error_code)) { + return error_code; } - else if (property == mprops.scroll_buttons) { - if (prop->size != 4 || prop->format != 8 || prop->type != XA_INTEGER) - return BadMatch; - - ivals8 = (uint8_t*)prop->data; - if (!VALID_BUTTON(ivals8[0]) || !VALID_BUTTON(ivals8[1]) || !VALID_BUTTON(ivals8[2]) || !VALID_BUTTON(ivals8[3])) - return BadMatch; - - if (!checkonly) { - cfg->scroll_up_btn = ivals8[0]; - cfg->scroll_dn_btn = ivals8[1]; - cfg->scroll_lt_btn = ivals8[2]; - cfg->scroll_rt_btn = ivals8[3]; -#ifdef DEBUG_PROPS - xf86Msg(X_INFO, "mtrack: set scroll buttons to %d %d %d %d\n", - cfg->scroll_up_btn, cfg->scroll_dn_btn, cfg->scroll_lt_btn, cfg->scroll_rt_btn); -#endif - } - } - else if (property == mprops.swipe_dist) { - if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER) - return BadMatch; - - ivals32 = (uint32_t*)prop->data; - if (ivals32[0] < 1) - return BadMatch; - - if (!checkonly) { - cfg->swipe_dist = ivals32[0]; -#ifdef DEBUG_PROPS - xf86Msg(X_INFO, "mtrack: set swipe distance to %d\n", - cfg->swipe_dist); -#endif - } - } - else if (property == mprops.swipe_buttons) { - if (prop->size != 4 || prop->format != 8 || prop->type != XA_INTEGER) - return BadMatch; - - ivals8 = (uint8_t*)prop->data; - if (!VALID_BUTTON(ivals8[0]) || !VALID_BUTTON(ivals8[1]) || !VALID_BUTTON(ivals8[2]) || !VALID_BUTTON(ivals8[3])) - return BadMatch; - - if (!checkonly) { - cfg->swipe_up_btn = ivals8[0]; - cfg->swipe_dn_btn = ivals8[1]; - cfg->swipe_lt_btn = ivals8[2]; - cfg->swipe_rt_btn = ivals8[3]; -#ifdef DEBUG_PROPS - xf86Msg(X_INFO, "mtrack: set swipe buttons to %d %d %d %d\n", - cfg->swipe_up_btn, cfg->swipe_dn_btn, cfg->swipe_lt_btn, cfg->swipe_rt_btn); -#endif - } + else if (set_swipe_properties(property, checkonly, prop, &mprops.swipe3, &cfg->swipe3, &error_code)) { + return error_code; } - else if (property == mprops.swipe4_dist) { - if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER) - return BadMatch; - - ivals32 = (uint32_t*)prop->data; - if (ivals32[0] < 1) - return BadMatch; - - if (!checkonly) { - cfg->swipe4_dist = ivals32[0]; -#ifdef DEBUG_PROPS - xf86Msg(X_INFO, "mtrack: set swipe4 distance to %d\n", - cfg->swipe4_dist); -#endif - } - } - else if (property == mprops.swipe4_buttons) { - if (prop->size != 4 || prop->format != 8 || prop->type != XA_INTEGER) - return BadMatch; - - ivals8 = (uint8_t*)prop->data; - if (!VALID_BUTTON(ivals8[0]) || !VALID_BUTTON(ivals8[1]) || !VALID_BUTTON(ivals8[2]) || !VALID_BUTTON(ivals8[3])) - return BadMatch; - - if (!checkonly) { - cfg->swipe4_up_btn = ivals8[0]; - cfg->swipe4_dn_btn = ivals8[1]; - cfg->swipe4_lt_btn = ivals8[2]; - cfg->swipe4_rt_btn = ivals8[3]; -#ifdef DEBUG_PROPS - xf86Msg(X_INFO, "mtrack: set swipe4 buttons to %d %d %d %d\n", - cfg->swipe4_up_btn, cfg->swipe4_dn_btn, cfg->swipe4_lt_btn, cfg->swipe4_rt_btn); -#endif - } + else if (set_swipe_properties(property, checkonly, prop, &mprops.swipe4, &cfg->swipe4, &error_code)) { + return error_code; } else if (property == mprops.scale_dist) { if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER) diff --git a/driver/mtrack.c b/driver/mtrack.c index 6bf1821..3e0a531 100644 --- a/driver/mtrack.c +++ b/driver/mtrack.c @@ -22,6 +22,7 @@ #include "mtouch.h" #include "mprops.h" +#include "os.h" /* xorg/os.h for timers */ #include #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 @@ -156,6 +157,9 @@ static int device_init(DeviceIntPtr dev, LocalDevicePtr local) mprops_init(&mt->cfg, local); XIRegisterPropertyHandler(dev, mprops_set_property, NULL, NULL); + TimerInit(); + mt->timer = NULL; /* allocated later */ + mt->is_timer_installed = 0; return Success; } @@ -172,6 +176,10 @@ static int device_on(LocalDevicePtr local) return !Success; } xf86AddEnabledDevice(local); + if(mt->timer != NULL) + TimerFree(mt->timer); // release any existing timer + mt->timer = NULL; + mt->is_timer_installed = 0; return Success; } @@ -182,6 +190,10 @@ static int device_off(LocalDevicePtr local) if (mtouch_close(mt)) xf86Msg(X_WARNING, "mtrack: cannot ungrab device\n"); xf86CloseSerial(local->fd); + if(mt->timer != NULL) + TimerFree(mt->timer); // release any existing timer + mt->timer = NULL; + mt->is_timer_installed = 0; return Success; } @@ -218,14 +230,63 @@ static void handle_gestures(LocalDevicePtr local, xf86PostMotionEvent(local->dev, 0, 0, 2, gs->move_dx, gs->move_dy); } -/* called for each full received packet from the touchpad */ +/* + * Timers documentation: + * http://www.x.org/releases/X11R7.7/doc/xorg-server/Xserver-spec.html#id2536042 + * + * This function indirectly may call itself recursively using timer to guarantee correct + * event delivery time. Ususally recursion ends after first recursive call. + */ +static CARD32 check_resolve_delayed(OsTimerPtr timer, CARD32 time, void *arg){ + LocalDevicePtr local = arg; + struct MTouch *mt = local->private; + mstime_t delta_millis; + struct timeval delta; + + // If it was to early to trigger delayed button, next timer will be set, + // but when called by timer such situation shouldn't take place. + switch (mtouch_delayed(mt)){ + case 1: + if(mt->is_timer_installed == 0){ + mt->is_timer_installed = 1; + timersub(&mt->gs.button_delayed_time, &mt->gs.time, &delta); + delta_millis = timertoms(&delta); + mt->timer = TimerSet(mt->timer, 0, delta_millis, check_resolve_delayed, local); + } + break; + case 2: + TimerCancel(mt->timer); + mt->is_timer_installed = 0; + handle_gestures(local, &mt->gs); + break; + case 0: break; + } + return 0; +} + +/* + * Called for each full received packet from the touchpad. + * Any xf86 input event generated by int this callback function fill be queued by + * xorg server, and fired when control return from this function. + * So to fire event as early as possible this function should return quickly. + * For delayed events we can't simply wait in this function, because it will delay + * all events generated by 'handle_gestures'. + * Moreover we don't know when next input event will occur, so to guarantee proper + * timing I have to use timer. + * + * If mtouch_delayed() retured 1 this means it was to early to trigger delayed button, + * and new timer have to be installed. Otherwise events generated inside can be handled + * as usual. + * + * More on input event processing: + * http://www.x.org/wiki/Development/Documentation/InputEventProcessing/ + */ static void read_input(LocalDevicePtr local) { struct MTouch *mt = local->private; while (mtouch_read(mt) > 0) handle_gestures(local, &mt->gs); - if (mtouch_delayed(mt)) - handle_gestures(local, &mt->gs); + check_resolve_delayed(NULL, 0, local); } static Bool device_control(DeviceIntPtr dev, int mode) @@ -266,10 +327,10 @@ static int preinit(InputDriverPtr drv, InputInfoPtr pInfo, int flags) pInfo->read_input = read_input; pInfo->switch_mode = 0; - xf86CollectInputOptions(pInfo, NULL); - xf86OptionListReport(pInfo->options); - xf86ProcessCommonOptions(pInfo, pInfo->options); - mconfig_configure(&mt->cfg, pInfo->options); + xf86CollectInputOptions(pInfo, NULL); + xf86OptionListReport(pInfo->options); + xf86ProcessCommonOptions(pInfo, pInfo->options); + mconfig_configure(&mt->cfg, pInfo->options); return Success; } @@ -310,6 +371,9 @@ static void uninit(InputDriverPtr drv, InputInfoPtr local, int flags) xf86DeleteInput(local, 0); } +/* About xorg drivers, modules: + * http://www.x.org/wiki/Development/Documentation/XorgInputHOWTO/ + */ static InputDriverRec MTRACK = { 1, "mtrack", diff --git a/include/common.h b/include/common.h index 1021067..d80302a 100644 --- a/include/common.h +++ b/include/common.h @@ -53,7 +53,7 @@ typedef unsigned int bitmask_t; #define MODBIT(m, x, b) ((b) ? SETBIT(m, x) : CLEARBIT(m, x)) #define ABSVAL(x) ((x) < 0 ? -1*(x) : (x)) -#define MINVAL(x, y) ((x) < (y) ? (x) : (y)) +#define MINVAL(x, y) ((x) < (y) ? (x) : (y)) #define MAXVAL(x, y) ((x) > (y) ? (x) : (y)) #define MODVAL(x, y) ((x) - ((int)((x) / (y))) * (y)) #define SQRVAL(x) ((x) * (x)) @@ -105,6 +105,16 @@ static inline void timeraddms(const struct timeval* a, const mstime_t b, struct timeradd(a, &tv, dest); } +/* Check if given timeval a is set to epoch time. + */ +static inline int isepochtime(const struct timeval* a) +{ + struct timeval epoch; + timerclear(&epoch); + + return timercmp(a, &epoch, ==); +} + /* Clamp value to 15 bits. */ static inline int clamp15(int x) diff --git a/include/gestures.h b/include/gestures.h index dc5784b..f8526f4 100644 --- a/include/gestures.h +++ b/include/gestures.h @@ -36,12 +36,14 @@ struct MTouch; #define GS_NONE 0 #define GS_MOVE 1 #define GS_SCROLL 2 -#define GS_SWIPE 3 -#define GS_SCALE 4 -#define GS_ROTATE 5 -#define GS_DRAG_READY 6 -#define GS_DRAG_WAIT 7 -#define GS_DRAG_ACTIVE 8 +#define GS_SWIPE2 GS_SCROLL +#define GS_SWIPE3 3 +#define GS_SWIPE4 4 +#define GS_SCALE 5 +#define GS_ROTATE 6 +#define GS_DRAG_READY 7 +#define GS_DRAG_WAIT 8 +#define GS_DRAG_ACTIVE 9 struct Gestures { /* Taps, physical buttons, and gestures will trigger @@ -63,9 +65,12 @@ struct Gestures { /* Internal state tracking. Not for direct access. */ int button_emulate; + /* Invalid button_delayed means that there's no delayed button. + */ int button_delayed; + /* If equals to epoch time then button is delayed till gesture end. + */ struct timeval button_delayed_time; - struct timeval button_delayed_delta; int tap_touching; int tap_released; diff --git a/include/mconfig.h b/include/mconfig.h index 6cead2a..a845d5d 100644 --- a/include/mconfig.h +++ b/include/mconfig.h @@ -57,16 +57,19 @@ #define DEFAULT_SCROLL_DN_BTN 5 #define DEFAULT_SCROLL_LT_BTN 6 #define DEFAULT_SCROLL_RT_BTN 7 +#define DEFAULT_SCROLL_HOLD 20 #define DEFAULT_SWIPE_DIST 700 #define DEFAULT_SWIPE_UP_BTN 8 #define DEFAULT_SWIPE_DN_BTN 9 #define DEFAULT_SWIPE_LT_BTN 10 #define DEFAULT_SWIPE_RT_BTN 11 +#define DEFAULT_SWIPE_HOLD 300 #define DEFAULT_SWIPE4_DIST 700 #define DEFAULT_SWIPE4_UP_BTN 0 #define DEFAULT_SWIPE4_DN_BTN 0 #define DEFAULT_SWIPE4_LT_BTN 0 #define DEFAULT_SWIPE4_RT_BTN 0 +#define DEFAULT_SWIPE_SENS 0 #define DEFAULT_SCALE_DIST 150 #define DEFAULT_SCALE_UP_BTN 12 #define DEFAULT_SCALE_DN_BTN 13 @@ -131,26 +134,21 @@ struct MConfig { int tap_2touch; // What button to emulate for two touch taps? 0 to 32 int tap_3touch; // What button to emulate for three touch taps? 0 to 32 int tap_4touch; // What button to emulate for four touch taps? 0 to 32 - int tap_timeout; // Window for touches when counting for the button. > 0 + int tap_timeout; // Window for touches when counting for the button. + // How long to wait for incoming touches after first one. > 0 int tap_hold; // How long to "hold down" the emulated button on tap. > 0 int tap_dist; // How far to allow a touch to move before it's a moving touch. > 0 int gesture_hold; // How long to "hold down" the emulated button for gestures. > 0 int gesture_wait; // How long after a gesture to wait before movement is allowed. >= 0 - int scroll_dist; // Distance needed to trigger a button. >= 0, 0 disables - int scroll_up_btn; // Button to use for scroll up. >= 0, 0 is none - int scroll_dn_btn; // Button to use for scroll down. >= 0, 0 is none - int scroll_lt_btn; // Button to use for scroll left. >= 0, 0 is none - int scroll_rt_btn; // Button to use for scroll right. >= 0, 0 is none - int swipe_dist; // Distance needed to trigger a button. >= 0, 0 disables - int swipe_up_btn; // Button to use for swipe up. >= 0, 0 is none - int swipe_dn_btn; // Button to use for swipe down. >= 0, 0 is none - int swipe_lt_btn; // Button to use for swipe left. >= 0, 0 is none - int swipe_rt_btn; // Button to use for swipe right. >= 0, 0 is none - int swipe4_dist; // Distance needed to trigger a button. >= 0, 0 disables - int swipe4_up_btn; // Button to use for four finger swipe up. >= 0, 0 is none - int swipe4_dn_btn; // Button to use for four finger swipe down. >= 0, 0 is none - int swipe4_lt_btn; // Button to use for four finger swipe left. >= 0, 0 is none - int swipe4_rt_btn; // Button to use for four finger swipe right. >= 0, 0 is none + struct MConfigSwipe{ + int dist; // Distance needed to trigger a button. >= 0, 0 disables + int hold; // How long to "hold down" the emulated button for swipe gesture. > 0 + int drag_sens; // Should this gesture emit movement events? 0 disables movement, 1000 set speed same as during normal movement + int up_btn; // Button to use for swipe up. >= 0, 0 is none + int dn_btn; // Button to use for swipe down. >= 0, 0 is none + int lt_btn; // Button to use for swipe left. >= 0, 0 is none + int rt_btn; // Button to use for swipe right. >= 0, 0 is none + } scroll, swipe3, swipe4/*, swipe5*/; int scale_dist; // Distance needed to trigger a button. >= 0, 0 disables int scale_up_btn; // Button to use for scale up. >= 0, 0 is none int scale_dn_btn; // Button to use for scale down. >= 0, 0 is none diff --git a/include/mprops.h b/include/mprops.h index 6672741..9ca5162 100644 --- a/include/mprops.h +++ b/include/mprops.h @@ -62,16 +62,22 @@ #define MTRACK_PROP_PALM_SIZE "Trackpad Palm Size" // int, 2 value - button hold, wait time #define MTRACK_PROP_GESTURE_SETTINGS "Trackpad Gesture Settings" -// int, 1 value - distance before a scroll event is triggered -#define MTRACK_PROP_SCROLL_DIST "Trackpad Scroll Distance" +// int, 2 values +// first: distance before a scroll event (two finger swipe) is triggered +// second: how much milliseconds button will be hold after {up,down,left,right} scroll +#define MTRACK_PROP_SCROLL_SETTINGS "Trackpad Scroll Settings" // int, 4 values - up button, down button, left button, right button #define MTRACK_PROP_SCROLL_BUTTONS "Trackpad Scroll Buttons" -// int, 1 value - distance before a swipe event is triggered -#define MTRACK_PROP_SWIPE_DIST "Trackpad Swipe Distance" +// int, 2 values +// first: distance before a three finger swipe event is triggered +// second: how much milliseconds button will be hold after {up,down,left,right} swipe +#define MTRACK_PROP_SWIPE_SETTINGS "Trackpad Swipe Settings" // int, 4 values - up button, down button, left button, right button #define MTRACK_PROP_SWIPE_BUTTONS "Trackpad Swipe Buttons" -// int, 1 value - distance before a swipe event is triggered -#define MTRACK_PROP_SWIPE4_DIST "Trackpad Swipe4 Distance" +// int, 2 values +// first: distance before a four finger swipe event is triggered +// second: how much milliseconds button will be hold after {up,down,left,right} swipe4 +#define MTRACK_PROP_SWIPE4_SETTINGS "Trackpad Swipe4 Settings" // int, 4 values - up button, down button, left button, right button #define MTRACK_PROP_SWIPE4_BUTTONS "Trackpad Swipe4 Buttons" // int, 1 value - distance before a scale event is triggered @@ -106,12 +112,10 @@ struct MProps { Atom palm_detect; Atom palm_size; Atom gesture_settings; - Atom scroll_dist; - Atom scroll_buttons; - Atom swipe_dist; - Atom swipe_buttons; - Atom swipe4_dist; - Atom swipe4_buttons; + struct MPropsSwipe{ + Atom settings; + Atom buttons; + } scroll, swipe3, swipe4/*, swipe5*/; Atom scale_dist; Atom scale_buttons; Atom rotate_dist; diff --git a/include/mtouch.h b/include/mtouch.h index 91fc41c..df91b5a 100644 --- a/include/mtouch.h +++ b/include/mtouch.h @@ -29,6 +29,7 @@ #include "mtstate.h" #include "mconfig.h" #include "gestures.h" +#include "os.h" /* xorg/os.h for timers */ struct MTouch { int fd; @@ -38,6 +39,8 @@ struct MTouch { struct MTState state; struct MConfig cfg; struct Gestures gs; + int is_timer_installed; // to avoid setting timer multiple times + OsTimerPtr timer; }; int mtouch_configure(struct MTouch* mt, int fd); diff --git a/required_packages.txt b/required_packages.txt new file mode 100644 index 0000000..f849d6a --- /dev/null +++ b/required_packages.txt @@ -0,0 +1 @@ +xserver-xorg-dev mtdev-dev xutils-dev diff --git a/src/gestures.c b/src/gestures.c index 5262fbd..10114ce 100644 --- a/src/gestures.c +++ b/src/gestures.c @@ -31,6 +31,8 @@ #define IS_VALID_BUTTON(x) (x >= 0 && x <= 31) +#define TOUCHES_MAX 4 + static void trigger_button_up(struct Gestures* gs, int button) { if (IS_VALID_BUTTON(button)) { @@ -50,7 +52,7 @@ static void trigger_button_down(struct Gestures* gs, int button) struct timeval epoch; timerclear(&epoch); - if (IS_VALID_BUTTON(button) && (button != gs->button_delayed || timercmp(&gs->button_delayed_time, &epoch, ==))) { + if (IS_VALID_BUTTON(button) && (button != gs->button_delayed || !IS_VALID_BUTTON(gs->button_delayed))) { SETBIT(gs->buttons, button); #ifdef DEBUG_GESTURES xf86Msg(X_INFO, "trigger_button_down: %d down\n", button); @@ -74,23 +76,50 @@ static void trigger_button_emulation(struct Gestures* gs, int button) } } +static void trigger_delayed_button_unsafe(struct Gestures* gs) +{ + int button; + + // clear button before timer (unless compiler decide otherwise) + button = gs->button_delayed; + + gs->button_delayed = -1; + timerclear(&gs->button_delayed_time); +#ifdef DEBUG_GESTURES + xf86Msg(X_INFO, "trigger_delayed_button: %d up, timer expired\n", button); +#endif + trigger_button_up(gs, button); +} + +/* + * If trigger_up_time is NULL or epoch time it will set timer to infinity - button up will + * be send when user finish gesture. + */ static void trigger_button_click(struct Gestures* gs, int button, struct timeval* trigger_up_time) { - struct timeval epoch; - timerclear(&epoch); +#ifdef DEBUG_GESTURES + struct timeval delta; +#endif - if (IS_VALID_BUTTON(button) && timercmp(&gs->button_delayed_time, &epoch, ==)) { + if (!IS_VALID_BUTTON(button)) + return; + + if (!IS_VALID_BUTTON(gs->button_delayed)) { trigger_button_down(gs, button); gs->button_delayed = button; - timercp(&gs->button_delayed_time, trigger_up_time); - timerclear(&gs->button_delayed_delta); -#ifdef DEBUG_GESTRUES - xf86Msg(X_INFO, "trigger_button_click: %d placed in delayed mode\n"); + if(trigger_up_time == NULL) + timerclear(&gs->button_delayed_time); // "infinite timer", wait for gesture end + else + timercp(&gs->button_delayed_time, trigger_up_time); // may be also "infinite" + +#ifdef DEBUG_GESTURES + timersub(&gs->button_delayed_time, &gs->time, &delta); + xf86Msg(X_INFO, "trigger_button_click: %d placed in delayed mode; delta: %d ms\n", button, timertoms(&delta)); #endif } #ifdef DEBUG_GESTURES - else if (IS_VALID_BUTTON(button)) + else xf86Msg(X_INFO, "trigger_button_click: %d ignored, in delayed mode\n", button); #endif } @@ -164,7 +193,7 @@ static void trigger_drag_stop(struct Gestures* gs, int force) #ifdef DEBUG_GESTURES xf86Msg(X_INFO, "trigger_drag_stop: drag stopped\n"); #endif - } + } } static void buttons_update(struct Gestures* gs, @@ -300,6 +329,7 @@ static void tapping_update(struct Gestures* gs, timerclear(&epoch); timeraddms(&gs->tap_time_down, cfg->tap_timeout, &tv_tmp); if (!timercmp(&gs->tap_time_down, &epoch, ==) && !timercmp(&gs->time, &tv_tmp, <)) { + // too much time passed by from first touch, stop waiting for incoming touches gs->tap_touching = 0; gs->tap_released = 0; timerclear(&gs->tap_time_down); @@ -355,11 +385,13 @@ static void tapping_update(struct Gestures* gs, } if ((gs->tap_touching == 0 && gs->tap_released > 0) || gs->tap_released >= released_max) { + // in this branch tap was recognized as button click + // clear tap flags from touches foreach_bit(i, ms->touch_used) { if (GETBIT(ms->touch[i].flags, GS_TAP)) CLEARBIT(ms->touch[i].flags, GS_TAP); } - + // determinate which button was "tapped" by counting touches if (gs->tap_released == 1) n = cfg->tap_1touch - 1; else if (gs->tap_released == 2) @@ -369,6 +401,8 @@ static void tapping_update(struct Gestures* gs, else n = cfg->tap_4touch - 1; + // how long button should be hold down + timeraddms(&gs->time, cfg->tap_hold, &tv_tmp); trigger_button_click(gs, n, &tv_tmp); if (cfg->drag_enable && n == 0) trigger_drag_ready(gs, cfg); @@ -403,94 +437,122 @@ static void trigger_move(struct Gestures* gs, } } -static void trigger_scroll(struct Gestures* gs, - const struct MConfig* cfg, - double dist, int dir) +static int get_scroll_dir(const struct Touch* t1, + const struct Touch* t2) { - if (gs->move_type == GS_SCROLL || !timercmp(&gs->time, &gs->move_wait, <)) { - struct timeval tv_tmp; - trigger_drag_stop(gs, 1); - if (gs->move_type != GS_SCROLL || gs->move_dir != dir) - gs->move_dist = 0; - gs->move_dx = 0; - gs->move_dy = 0; - gs->move_type = GS_SCROLL; - gs->move_dist += (int)ABSVAL(dist); - gs->move_dir = dir; - gs->move_speed = dist/timertomicro(&gs->dt); - timeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait); + if (trig_angles_acute(t1->direction, t2->direction) < 2.0) { + double angles[2]; + angles[0] = t1->direction; + angles[1] = t2->direction; + return trig_generalize(trig_angles_avg(angles, 2)); + } + return TR_NONE; +} - if (gs->move_dist >= cfg->scroll_dist) { - gs->move_dist = MODVAL(gs->move_dist, cfg->scroll_dist); - timeraddms(&gs->time, cfg->gesture_hold, &tv_tmp); - if (dir == TR_DIR_UP) - trigger_button_click(gs, cfg->scroll_up_btn - 1, &tv_tmp); - else if (dir == TR_DIR_DN) - trigger_button_click(gs, cfg->scroll_dn_btn - 1, &tv_tmp); - else if (dir == TR_DIR_LT) - trigger_button_click(gs, cfg->scroll_lt_btn - 1, &tv_tmp); - else if (dir == TR_DIR_RT) - trigger_button_click(gs, cfg->scroll_rt_btn - 1, &tv_tmp); - } -#ifdef DEBUG_GESTURES - xf86Msg(X_INFO, "trigger_scroll: scrolling %+f in direction %d (at %d of %d) (speed %f)\n", - dist, dir, gs->move_dist, cfg->scroll_dist, gs->move_speed); -#endif +static int get_swipe_dir_n(const struct Touch* touches[TOUCHES_MAX], int count) +{ + if(count > TOUCHES_MAX) + return TR_NONE; + double angles[TOUCHES_MAX]; + int i; + for (i = 0; i < count; i++) { + angles[i] = touches[i]->direction; } + return trig_generalize(trig_angles_avg(angles, count)); } -static void trigger_swipe(struct Gestures* gs, - const struct MConfig* cfg, - double dist, int dir, int isfour) +static void get_swipe_avg_xy(const struct Touch* touches[TOUCHES_MAX], int count, double* out_x, double* out_y){ + double x, y; + x = y = 0.0; + int i; + for (i = 0; i < count; ++i) { + x += touches[i]->dx; + y += touches[i]->dy; + } + *out_x = x/(double)count; + *out_y = y/(double)count; +} + +/* Return: + * 0 - it wasn't swipe + * other value - it was swipe + */ +static int trigger_swipe(struct Gestures* gs, + const struct MConfig* cfg, const struct Touch* touches[4], int touches_count) { - if (gs->move_type == GS_SWIPE || !timercmp(&gs->time, &gs->move_wait, <)) { - struct timeval tv_tmp; + int move_type_to_trigger, dir; + double avg_move_x, avg_move_y, dist; + const struct MConfigSwipe* cfg_swipe; + struct timeval tv_tmp; + + switch(touches_count){ + case 2: + cfg_swipe = &cfg->scroll; + move_type_to_trigger = GS_SCROLL; + dir = get_scroll_dir(touches[0], touches[1]); + break; + case 3: + cfg_swipe = &cfg->swipe3; + move_type_to_trigger = GS_SWIPE3; + dir = get_swipe_dir_n(touches, touches_count); + break; + case 4: + cfg_swipe = &cfg->swipe4; + move_type_to_trigger = GS_SWIPE4; + dir = get_swipe_dir_n(touches, touches_count); + break; + default: + return 2; + } + + if(dir == TR_NONE) + return 0; + + if (gs->move_type == move_type_to_trigger || timercmp(&gs->time, &gs->move_wait, >=)) { trigger_drag_stop(gs, 1); - if (gs->move_type != GS_SWIPE || gs->move_dir != dir) + get_swipe_avg_xy(touches, touches_count, &avg_move_x, &avg_move_y); + // hypot(1/n * (x0 + ... + xn); 1/n * (y0 + ... + yn)) <=> 1/n * hypot(x0 + ... + xn; y0 + ... + yn) + dist = hypot(avg_move_x, avg_move_y); + if(cfg_swipe->drag_sens){ + gs->move_dx = (int)(cfg->sensitivity * avg_move_x * cfg_swipe->drag_sens * 0.001); + gs->move_dy = (int)(cfg->sensitivity * avg_move_y * cfg_swipe->drag_sens * 0.001); + } else{ + gs->move_dx = 0; + gs->move_dy = 0; + } + if (gs->move_type != move_type_to_trigger){ + trigger_delayed_button_unsafe(gs); gs->move_dist = 0; - gs->move_dx = 0; - gs->move_dy = 0; - gs->move_type = GS_SWIPE; + } + else if (gs->move_dir != dir) + gs->move_dist = 0; + gs->move_type = move_type_to_trigger; gs->move_dist += (int)ABSVAL(dist); gs->move_dir = dir; gs->move_speed = dist/timertomicro(&gs->dt); timeraddms(&gs->time, cfg->gesture_wait, &gs->move_wait); - timeraddms(&gs->time, cfg->gesture_hold, &tv_tmp); - - if (isfour) { - if (cfg->swipe4_dist > 0 && gs->move_dist >= cfg->swipe4_dist) { - gs->move_dist = MODVAL(gs->move_dist, cfg->swipe4_dist); - if (dir == TR_DIR_UP) - trigger_button_click(gs, cfg->swipe4_up_btn - 1, &tv_tmp); - else if (dir == TR_DIR_DN) - trigger_button_click(gs, cfg->swipe4_dn_btn - 1, &tv_tmp); - else if (dir == TR_DIR_LT) - trigger_button_click(gs, cfg->swipe4_lt_btn - 1, &tv_tmp); - else if (dir == TR_DIR_RT) - trigger_button_click(gs, cfg->swipe4_rt_btn - 1, &tv_tmp); - } -#ifdef DEBUG_GESTURES - xf86Msg(X_INFO, "trigger_swipe4: swiping %+f in direction %d (at %d of %d) (speed %f)\n", - dist, dir, gs->move_dist, cfg->swipe_dist, gs->move_speed); -#endif + + if (cfg_swipe->dist > 0 && gs->move_dist >= cfg_swipe->dist) { + if(cfg_swipe->hold != 0) + timeraddms(&gs->time, cfg_swipe->hold, &tv_tmp); + else + timerclear(&tv_tmp); // wait for gesture end + gs->move_dist = MODVAL(gs->move_dist, cfg_swipe->dist); + if (dir == TR_DIR_UP) + trigger_button_click(gs, cfg_swipe->up_btn - 1, &tv_tmp); + else if (dir == TR_DIR_DN) + trigger_button_click(gs, cfg_swipe->dn_btn - 1, &tv_tmp); + else if (dir == TR_DIR_LT) + trigger_button_click(gs, cfg_swipe->lt_btn - 1, &tv_tmp); + else if (dir == TR_DIR_RT) + trigger_button_click(gs, cfg_swipe->rt_btn - 1, &tv_tmp); } - else { - if (cfg->swipe_dist > 0 && gs->move_dist >= cfg->swipe_dist) { - gs->move_dist = MODVAL(gs->move_dist, cfg->swipe_dist); - if (dir == TR_DIR_UP) - trigger_button_click(gs, cfg->swipe_up_btn - 1, &tv_tmp); - else if (dir == TR_DIR_DN) - trigger_button_click(gs, cfg->swipe_dn_btn - 1, &tv_tmp); - else if (dir == TR_DIR_LT) - trigger_button_click(gs, cfg->swipe_lt_btn - 1, &tv_tmp); - else if (dir == TR_DIR_RT) - trigger_button_click(gs, cfg->swipe_rt_btn - 1, &tv_tmp); - } #ifdef DEBUG_GESTURES - xf86Msg(X_INFO, "trigger_swipe: swiping %+f in direction %d (at %d of %d)\n", dist, dir, gs->move_dist, cfg->swipe_dist); + xf86Msg(X_INFO, "trigger_swipe_button: swiping %+f in direction %d (at %d of %d) (speed %f)\n", + dist, dir, gs->move_dist, cfg_swipe->dist, gs->move_speed); #endif - } } + return 1; } static void trigger_scale(struct Gestures* gs, @@ -566,18 +628,6 @@ static void trigger_reset(struct Gestures* gs) timerclear(&gs->move_wait); } -static int get_scroll_dir(const struct Touch* t1, - const struct Touch* t2) -{ - if (trig_angles_acute(t1->direction, t2->direction) < 2.0) { - double angles[2]; - angles[0] = t1->direction; - angles[1] = t2->direction; - return trig_generalize(trig_angles_avg(angles, 2)); - } - return TR_NONE; -} - static int get_rotate_dir(const struct Touch* t1, const struct Touch* t2) { @@ -606,37 +656,13 @@ static int get_scale_dir(const struct Touch* t1, return TR_NONE; } -static int get_swipe_dir(const struct Touch* t1, - const struct Touch* t2, - const struct Touch* t3) -{ - double angles[3]; - angles[0] = t1->direction; - angles[1] = t2->direction; - angles[2] = t3->direction; - return trig_generalize(trig_angles_avg(angles, 3)); -} - -static int get_swipe4_dir(const struct Touch* t1, - const struct Touch* t2, - const struct Touch* t3, - const struct Touch* t4) -{ - double angles[4]; - angles[0] = t1->direction; - angles[1] = t2->direction; - angles[2] = t3->direction; - angles[3] = t4->direction; - return trig_generalize(trig_angles_avg(angles, 4)); -} - static void moving_update(struct Gestures* gs, const struct MConfig* cfg, struct MTState* ms) { int i, count, btn_count, dx, dy, dir; double dist; - struct Touch* touches[4]; + const struct Touch* touches[TOUCHES_MAX]; count = btn_count = 0; dx = dy = 0; dir = 0; @@ -655,7 +681,7 @@ static void moving_update(struct Gestures* gs, dy += ms->touch[i].dy; } else if (!GETBIT(ms->touch[i].flags, GS_TAP)) { - if (count < 4) + if (count < TOUCHES_MAX) touches[count++] = &ms->touch[i]; } } @@ -674,11 +700,8 @@ static void moving_update(struct Gestures* gs, } else if (count == 2 && cfg->trackpad_disable < 1) { // scroll, scale, or rotate - if ((dir = get_scroll_dir(touches[0], touches[1])) != TR_NONE) { - dist = hypot( - touches[0]->dx + touches[1]->dx, - touches[0]->dy + touches[1]->dy); - trigger_scroll(gs, cfg, dist/2, dir); + if (trigger_swipe(gs, cfg, touches, count)) { + /* nothing to do */ } else if ((dir = get_rotate_dir(touches[0], touches[1])) != TR_NONE) { dist = ABSVAL(hypot(touches[0]->dx, touches[0]->dy)) + @@ -692,20 +715,10 @@ static void moving_update(struct Gestures* gs, } } else if (count == 3 && cfg->trackpad_disable < 1) { - if ((dir = get_swipe_dir(touches[0], touches[1], touches[2])) != TR_NONE) { - dist = hypot( - touches[0]->dx + touches[1]->dx + touches[2]->dx, - touches[0]->dy + touches[1]->dy + touches[2]->dy); - trigger_swipe(gs, cfg, dist/3, dir, 0); - } + trigger_swipe(gs, cfg, touches, count); } else if (count == 4 && cfg->trackpad_disable < 1) { - if ((dir = get_swipe4_dir(touches[0], touches[1], touches[2], touches[3])) != TR_NONE) { - dist = hypot( - touches[0]->dx + touches[1]->dx + touches[2]->dx + touches[3]->dx, - touches[0]->dy + touches[1]->dy + touches[2]->dy + touches[3]->dy); - trigger_swipe(gs, cfg, dist/4, dir, 1); - } + trigger_swipe(gs, cfg, touches, count); } } @@ -719,25 +732,28 @@ static void dragging_update(struct Gestures* gs) } } +static int is_timer_infinite(struct Gestures* gs){ + return isepochtime(&gs->button_delayed_time); +} + static void delayed_update(struct Gestures* gs) { - struct timeval epoch; - timerclear(&epoch); - - if (timercmp(&gs->button_delayed_time, &epoch, ==)) + // if there's no delayed button - return + if(!IS_VALID_BUTTON(gs->button_delayed)) return; - if (!timercmp(&gs->time, &gs->button_delayed_time, <)) { + if (!is_timer_infinite(gs) && timercmp(&gs->time, &gs->button_delayed_time, >=)) { #ifdef DEBUG_GESTURES xf86Msg(X_INFO, "delayed_update: %d delay expired, triggering up\n", gs->button_delayed); #endif - trigger_button_up(gs, gs->button_delayed); - gs->button_delayed = 0; - timerclear(&gs->button_delayed_time); - timerclear(&gs->button_delayed_delta); + trigger_delayed_button_unsafe(gs); } else { - timersub(&gs->button_delayed_time, &gs->time, &gs->button_delayed_delta); +#ifdef DEBUG_GESTURES + struct timeval delta; + timersub(&gs->button_delayed_time, &gs->time, &delta); + xf86Msg(X_INFO, "delayed_update: %d still waiting, new delta %d ms\n", gs->button_delayed, timertoms(&delta)); +#endif } } @@ -758,39 +774,69 @@ void gestures_extract(struct MTouch* mt) delayed_update(&mt->gs); } -static int gestures_sleep(struct MTouch* mt, const struct timeval* sleep) -{ - if (mtdev_empty(&mt->dev)) { - struct timeval now; - mtdev_idle(&mt->dev, mt->fd, timertoms(sleep)); - microtime(&now); - timersub(&now, &mt->gs.time, &mt->gs.dt); - timercp(&mt->gs.time, &now); - return 1; - } - return 0; -} - +/* + * Executed every input time frame, at least once. First time from 'read_input' to check if + * timer is needed. + * Return value 1 means that next timer should be installed with this function as a + * callabck and gs->button_delayed_delta as delay time. + * + * Return vale meaning: + * - 0 - no delay to handle, don't install timer, do nothing + * - 1 - only install timer + * - 2 - state was changed, so caller have to cancel timer and handle state change + * by calling TimerCancel() and handle_gestures() + */ int gestures_delayed(struct MTouch* mt) { struct Gestures* gs = &mt->gs; - struct timeval epoch; - timerclear(&epoch); + struct MTState* ms = &mt->state; + struct timeval now, delta; + int i, taps_released; + + taps_released = 0; + + // if there's no delayed button - return + if(!IS_VALID_BUTTON(gs->button_delayed)) + return 0; + + // count released fingers + foreach_bit(i, ms->touch_used) { + if (GETBIT(ms->touch[i].state, MT_RELEASED)) + ++taps_released; + } - if (timercmp(&gs->button_delayed_time, &epoch, >)) { - if (gestures_sleep(mt, &gs->button_delayed_delta)) { + // if one of fingers were released it means that gesture was finisfed so + // send "button up" event immediately without checking for delivery time + if(taps_released != 0){ + trigger_delayed_button_unsafe(gs); + gs->move_dx = gs->move_dy = 0; + gs->move_type = GS_NONE; + return 2; + } + + if(is_timer_infinite(gs)) + return 0; + + microtime(&now); + timersub(&now, &mt->gs.time, &mt->gs.dt); + timercp(&mt->gs.time, &now); + + if(timercmp(&gs->button_delayed_time, &now, >)){ + timersub(&gs->button_delayed_time, &now, &delta); + // That second check may seem unnecessary, but it is not. + // Even if button delayed time is > than now time, timertoms may still return 0 + // because it truncates time to miliseconds. It's important because truncated time + // is used to setup timer. + if(timertoms(&delta) > 1){ #ifdef DEBUG_GESTURES - xf86Msg(X_INFO, "gestures_delayed: %d up, timer expired\n", gs->button_delayed); + xf86Msg(X_INFO, "gestures_delayed: %d delayed, new delta: %d ms\n", gs->button_delayed, timertoms(&delta)); #endif - trigger_button_up(gs, gs->button_delayed); - gs->move_dx = 0; - gs->move_dy = 0; - gs->button_delayed = 0; - timerclear(&gs->button_delayed_time); - timerclear(&gs->button_delayed_delta); - return 1; - } + return 1; // install timer + } } - return 0; + + trigger_delayed_button_unsafe(gs); + gs->move_dx = gs->move_dy = 0; + return 2; } diff --git a/src/mconfig.c b/src/mconfig.c index 4ecbd87..e9dbc1f 100644 --- a/src/mconfig.c +++ b/src/mconfig.c @@ -53,21 +53,27 @@ void mconfig_defaults(struct MConfig* cfg) cfg->tap_dist = DEFAULT_TAP_DIST; cfg->gesture_hold = DEFAULT_GESTURE_HOLD; cfg->gesture_wait = DEFAULT_GESTURE_WAIT; - cfg->scroll_dist = DEFAULT_SCROLL_DIST; - cfg->scroll_up_btn = DEFAULT_SCROLL_UP_BTN; - cfg->scroll_dn_btn = DEFAULT_SCROLL_DN_BTN; - cfg->scroll_lt_btn = DEFAULT_SCROLL_LT_BTN; - cfg->scroll_rt_btn = DEFAULT_SCROLL_RT_BTN; - cfg->swipe_dist = DEFAULT_SWIPE_DIST; - cfg->swipe_up_btn = DEFAULT_SWIPE_UP_BTN; - cfg->swipe_dn_btn = DEFAULT_SWIPE_DN_BTN; - cfg->swipe_lt_btn = DEFAULT_SWIPE_LT_BTN; - cfg->swipe_rt_btn = DEFAULT_SWIPE_RT_BTN; - cfg->swipe4_dist = DEFAULT_SWIPE4_DIST; - cfg->swipe4_up_btn = DEFAULT_SWIPE4_UP_BTN; - cfg->swipe4_dn_btn = DEFAULT_SWIPE4_DN_BTN; - cfg->swipe4_lt_btn = DEFAULT_SWIPE4_LT_BTN; - cfg->swipe4_rt_btn = DEFAULT_SWIPE4_RT_BTN; + cfg->scroll.dist = DEFAULT_SCROLL_DIST; + cfg->scroll.hold = DEFAULT_SCROLL_HOLD; + cfg->scroll.drag_sens = DEFAULT_SWIPE_SENS; + cfg->scroll.up_btn = DEFAULT_SCROLL_UP_BTN; + cfg->scroll.dn_btn = DEFAULT_SCROLL_DN_BTN; + cfg->scroll.lt_btn = DEFAULT_SCROLL_LT_BTN; + cfg->scroll.rt_btn = DEFAULT_SCROLL_RT_BTN; + cfg->swipe3.dist = DEFAULT_SWIPE_DIST; + cfg->swipe3.hold = DEFAULT_SWIPE_HOLD; + cfg->swipe3.drag_sens = DEFAULT_SWIPE_SENS; + cfg->swipe3.up_btn = DEFAULT_SWIPE_UP_BTN; + cfg->swipe3.dn_btn = DEFAULT_SWIPE_DN_BTN; + cfg->swipe3.lt_btn = DEFAULT_SWIPE_LT_BTN; + cfg->swipe3.rt_btn = DEFAULT_SWIPE_RT_BTN; + cfg->swipe4.dist = DEFAULT_SWIPE4_DIST; + cfg->swipe4.hold = DEFAULT_SWIPE_HOLD; + cfg->swipe4.drag_sens = DEFAULT_SWIPE_SENS; + cfg->swipe4.up_btn = DEFAULT_SWIPE4_UP_BTN; + cfg->swipe4.dn_btn = DEFAULT_SWIPE4_DN_BTN; + cfg->swipe4.lt_btn = DEFAULT_SWIPE4_LT_BTN; + cfg->swipe4.rt_btn = DEFAULT_SWIPE4_RT_BTN; cfg->scale_dist = DEFAULT_SCALE_DIST; cfg->scale_up_btn = DEFAULT_SCALE_UP_BTN; cfg->scale_dn_btn = DEFAULT_SCALE_DN_BTN; @@ -85,7 +91,7 @@ void mconfig_init(struct MConfig* cfg, cfg->touch_minor = caps->has_abs[MTDEV_TOUCH_MINOR]; cfg->pad_width = get_cap_xsize(caps); cfg->pad_height = get_cap_ysize(caps); - + if (caps->has_abs[MTDEV_TOUCH_MAJOR] && caps->has_abs[MTDEV_WIDTH_MAJOR]) { cfg->touch_type = MCFG_SCALE; cfg->touch_min = caps->abs[MTDEV_TOUCH_MAJOR].minimum; @@ -150,21 +156,27 @@ void mconfig_configure(struct MConfig* cfg, cfg->tap_dist = MAXVAL(xf86SetIntOption(opts, "MaxTapMove", DEFAULT_TAP_DIST), 1); cfg->gesture_hold = MAXVAL(xf86SetIntOption(opts, "GestureClickTime", DEFAULT_GESTURE_HOLD), 1); cfg->gesture_wait = MAXVAL(xf86SetIntOption(opts, "GestureWaitTime", DEFAULT_GESTURE_WAIT), 0); - cfg->scroll_dist = MAXVAL(xf86SetIntOption(opts, "ScrollDistance", DEFAULT_SCROLL_DIST), 1); - cfg->scroll_up_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollUpButton", DEFAULT_SCROLL_UP_BTN), 0, 32); - cfg->scroll_dn_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollDownButton", DEFAULT_SCROLL_DN_BTN), 0, 32); - cfg->scroll_lt_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollLeftButton", DEFAULT_SCROLL_LT_BTN), 0, 32); - cfg->scroll_rt_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollRightButton", DEFAULT_SCROLL_RT_BTN), 0, 32); - cfg->swipe_dist = MAXVAL(xf86SetIntOption(opts, "SwipeDistance", DEFAULT_SWIPE_DIST), 1); - cfg->swipe_up_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeUpButton", DEFAULT_SWIPE_UP_BTN), 0, 32); - cfg->swipe_dn_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeDownButton", DEFAULT_SWIPE_DN_BTN), 0, 32); - cfg->swipe_lt_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeLeftButton", DEFAULT_SWIPE_LT_BTN), 0, 32); - cfg->swipe_rt_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeRightButton", DEFAULT_SWIPE_RT_BTN), 0, 32); - cfg->swipe4_dist = MAXVAL(xf86SetIntOption(opts, "Swipe4Distance", DEFAULT_SWIPE4_DIST), 1); - cfg->swipe4_up_btn = CLAMPVAL(xf86SetIntOption(opts, "Swipe4UpButton", DEFAULT_SWIPE4_UP_BTN), 0, 32); - cfg->swipe4_dn_btn = CLAMPVAL(xf86SetIntOption(opts, "Swipe4DownButton", DEFAULT_SWIPE4_DN_BTN), 0, 32); - cfg->swipe4_lt_btn = CLAMPVAL(xf86SetIntOption(opts, "Swipe4LeftButton", DEFAULT_SWIPE4_LT_BTN), 0, 32); - cfg->swipe4_rt_btn = CLAMPVAL(xf86SetIntOption(opts, "Swipe4RightButton", DEFAULT_SWIPE4_RT_BTN), 0, 32); + cfg->scroll.dist = MAXVAL(xf86SetIntOption(opts, "ScrollDistance", DEFAULT_SCROLL_DIST), 1); + cfg->scroll.hold = MAXVAL(xf86SetIntOption(opts, "ScrollClickTime", DEFAULT_SCROLL_HOLD), 0); + cfg->scroll.drag_sens = MAXVAL(xf86SetIntOption(opts, "ScrollSensitivity", DEFAULT_SWIPE_SENS), 0); + cfg->scroll.up_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollUpButton", DEFAULT_SCROLL_UP_BTN), 0, 32); + cfg->scroll.dn_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollDownButton", DEFAULT_SCROLL_DN_BTN), 0, 32); + cfg->scroll.lt_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollLeftButton", DEFAULT_SCROLL_LT_BTN), 0, 32); + cfg->scroll.rt_btn = CLAMPVAL(xf86SetIntOption(opts, "ScrollRightButton", DEFAULT_SCROLL_RT_BTN), 0, 32); + cfg->swipe3.dist = MAXVAL(xf86SetIntOption(opts, "SwipeDistance", DEFAULT_SWIPE_DIST), 1); + cfg->swipe3.hold = MAXVAL(xf86SetIntOption(opts, "SwipeClickTime", DEFAULT_SWIPE_HOLD), 0); + cfg->swipe3.drag_sens = MAXVAL(xf86SetIntOption(opts, "SwipeSensitivity", DEFAULT_SWIPE_SENS), 0); + cfg->swipe3.up_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeUpButton", DEFAULT_SWIPE_UP_BTN), 0, 32); + cfg->swipe3.dn_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeDownButton", DEFAULT_SWIPE_DN_BTN), 0, 32); + cfg->swipe3.lt_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeLeftButton", DEFAULT_SWIPE_LT_BTN), 0, 32); + cfg->swipe3.rt_btn = CLAMPVAL(xf86SetIntOption(opts, "SwipeRightButton", DEFAULT_SWIPE_RT_BTN), 0, 32); + cfg->swipe4.dist = MAXVAL(xf86SetIntOption(opts, "Swipe4Distance", DEFAULT_SWIPE4_DIST), 1); + cfg->swipe4.hold = MAXVAL(xf86SetIntOption(opts, "Swipe4ClickTime", DEFAULT_SWIPE_HOLD), 0); + cfg->swipe4.drag_sens = MAXVAL(xf86SetIntOption(opts, "Swipe4Sensitivity", DEFAULT_SWIPE_SENS), 0); + cfg->swipe4.up_btn = CLAMPVAL(xf86SetIntOption(opts, "Swipe4UpButton", DEFAULT_SWIPE4_UP_BTN), 0, 32); + cfg->swipe4.dn_btn = CLAMPVAL(xf86SetIntOption(opts, "Swipe4DownButton", DEFAULT_SWIPE4_DN_BTN), 0, 32); + cfg->swipe4.lt_btn = CLAMPVAL(xf86SetIntOption(opts, "Swipe4LeftButton", DEFAULT_SWIPE4_LT_BTN), 0, 32); + cfg->swipe4.rt_btn = CLAMPVAL(xf86SetIntOption(opts, "Swipe4RightButton", DEFAULT_SWIPE4_RT_BTN), 0, 32); cfg->scale_dist = MAXVAL(xf86SetIntOption(opts, "ScaleDistance", DEFAULT_SCALE_DIST), 1); cfg->scale_up_btn = CLAMPVAL(xf86SetIntOption(opts, "ScaleUpButton", DEFAULT_SCALE_UP_BTN), 0, 32); cfg->scale_dn_btn = CLAMPVAL(xf86SetIntOption(opts, "ScaleDownButton", DEFAULT_SCALE_DN_BTN), 0, 32); diff --git a/src/mtstate.c b/src/mtstate.c index 2cc0c32..6b30c0f 100644 --- a/src/mtstate.c +++ b/src/mtstate.c @@ -41,7 +41,7 @@ static int is_touch(const struct MConfig* cfg, if (cfg->touch_type == MCFG_SCALE) return percentage(hw->touch_major, hw->width_major) > cfg->touch_down; else if (cfg->touch_type == MCFG_SIZE) - return touch_range_ratio(cfg, hw->touch_major) > cfg->touch_down; + return touch_range_ratio(cfg, hw->touch_major) >= cfg->touch_down; else if (cfg->touch_type == MCFG_PRESSURE) return touch_range_ratio(cfg, hw->pressure) > cfg->touch_down; else @@ -231,24 +231,24 @@ static void touches_update(struct MTState* ms, SETBIT(ms->touch[n].state, MT_THUMB); else CLEARBIT(ms->touch[n].state, MT_THUMB); - + if (is_palm(cfg, &hs->data[i])) SETBIT(ms->touch[n].state, MT_PALM); else CLEARBIT(ms->touch[n].state, MT_PALM); - + if (ms->touch[n].y > (100 - cfg->bottom_edge)*cfg->pad_height/100) { if (GETBIT(ms->touch[n].state, MT_NEW)) SETBIT(ms->touch[n].state, MT_BOTTOM_EDGE); } else CLEARBIT(ms->touch[n].state, MT_BOTTOM_EDGE); - + MODBIT(ms->touch[n].state, MT_INVALID, GETBIT(ms->touch[n].state, MT_THUMB) && cfg->ignore_thumb || GETBIT(ms->touch[n].state, MT_PALM) && cfg->ignore_palm || GETBIT(ms->touch[n].state, MT_BOTTOM_EDGE)); - + disable |= cfg->disable_on_thumb && GETBIT(ms->touch[n].state, MT_THUMB); disable |= cfg->disable_on_palm && GETBIT(ms->touch[n].state, MT_PALM); }