From 827d4c8f2c785ac8e4bbebf068202ece2223089c Mon Sep 17 00:00:00 2001 From: Maarten van Gompel Date: Sun, 9 Aug 2020 16:23:45 +0200 Subject: [PATCH] implementing support for gestures with edge and corner detection Signed-off-by: Maarten van Gompel --- Makefile | 5 +- README.md | 25 +++--- config.def.h | 26 ++++--- lisgd.1 | 17 ++-- lisgd.c | 214 ++++++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 233 insertions(+), 54 deletions(-) diff --git a/Makefile b/Makefile index b2ab358..4c86c8f 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,9 @@ SRC = lisgd.c OBJ = ${SRC:.c=.o} LDFLAGS = -linput -lm +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + all: options lisgd options: @@ -19,7 +22,7 @@ config.h: cp config.def.h $@ lisgd: ${OBJ} - ${CC} -g -o $@ ${OBJ} ${LDFLAGS} + ${CC} -g -o $@ ${OBJ} -I${X11INC} -lX11 ${LDFLAGS} install: all mkdir -p ${DESTDIR}${PREFIX}/bin diff --git a/README.md b/README.md index 05072b1..5c5fef3 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Lisgd (libinput **synthetic** gesture daemon) lets you bind gestures based on libinput touch events to run specific commands to execute. For example, dragging left to right with one finger could execute a particular command like launching a terminal. Directional L-R, R-L, U-D, and D-U gestures and -diagnol LD-RU, RD-LU, UR-DL, UL-DR gestures are supported with 1 through +diagnol LD-RU, RD-LU, UR-DL, UL-DR gestures are supported with 1 through n fingers. Unlike other libinput gesture daemons, lisgd uses touch events to @@ -32,25 +32,24 @@ Flags: - **-d [devicenodepath]**: Defines the dev filesystem device to monitor - Example: `lisgd -d /dev/input/input1` -- **-g [nfingers,gesture,command]**: Allows you to bind a gesture wherein - nfingers is an integer, gesture is one of {LR,RL,DU,UD,DLUR,URDL,ULDR,DLUR}, - and command is the shell command to be executed. The -g option can be used +- **-g [nfingers,gesture,edge,distance,command]**: Allows you to bind a gesture wherein nfingers is an integer, gesture is +one of {LR,RL,DU,UD,DLUR,URDL,ULDR,DLUR}, edge is one of * (any), N (none), L (left), R (right), T (top), B (bottom), TL (top left), TR (top right), BL (bottom left), BR (bottom right) and distance is one of * (any), S (short), M (medium), L (large). command is the shell command to be executed. The -g option can be used multiple times to bind multiple gestures. - - Single Gesture Example: `lisgd -g "1,LR,notify-send swiped lr"` - - Multiple Gestures Example: `lisgd -g "1,LR,notify-send swiped lr" -g "1,RL,noitfy-send swiped rl"` -- **-m [timeoutms]**: Number of milliseconds gestures must be performed within + - Single Gesture Example: `lisgd -g "1,LR,*,*,notify-send swiped lr"` + - Multiple Gestures Example: `lisgd -g "1,LR,*,*,notify-send swiped lr" -g "1,RL,R,*,noitfy-send swiped rl from right edge"` +- **-m [timeoutms]**: Number of milliseconds gestures must be performed within to be registered. After the timeoutms value; the gesture won't be registered. - Example: `lisgd -m 1200` -- **-o [orientation]**: Number of 90-degree rotations to translate gestures by. - Can be set to 0-3. For example using 1; a L-R gesture would become a U-D +- **-o [orientation]**: Number of 90-degree rotations to translate gestures by. + Can be set to 0-3. For example using 1; a L-R gesture would become a U-D gesture. Meant to be used for screen-rotation. - Example `lisgd -o 1` -- **-r [degrees]**: Number of degrees offset each 45-degree interval may still - be recognized within. Maximum value is 45. Default value is 15. E.g. U-D - is a 180 degree gesture but with 15 degrees of leniency will be recognized +- **-r [degrees]**: Number of degrees offset each 45-degree interval may still + be recognized within. Maximum value is 45. Default value is 15. E.g. U-D + is a 180 degree gesture but with 15 degrees of leniency will be recognized between 165-195 degrees. - Example: `lisgd -r 20` -- **-t [threshold_units]**: Threshold in libinput units (pixels) after which a +- **-t [threshold_units]**: Threshold in libinput units (pixels) after which a gesture registers. Defaults to 300. - Example: `lisgd -t 400` - **-v**: Verbose mode, useful for debugging diff --git a/config.def.h b/config.def.h index 641de0d..3a3ca3e 100644 --- a/config.def.h +++ b/config.def.h @@ -1,5 +1,5 @@ -/* - distancethreshold: Minimum cutoff for a gestures to take effect +/* + distancethreshold: Minimum cutoff for a gestures to take effect degreesleniency: Offset degrees within which gesture is recognized (max=45) timeoutms: Maximum duration for a gesture to take place in miliseconds orientation: Number of 90 degree turns to shift gestures by @@ -15,18 +15,20 @@ unsigned int degreesleniency = 15; unsigned int timeoutms = 800; unsigned int orientation = 0; unsigned int verbose = 0; +double edgesizex = 50.0; +double edgesizey = 50.0; char *device = "/dev/input/event1"; Gesture gestures[] = { /* nfingers gesturetype command */ - { 1, SwipeLR, "xdotool key --clearmodifiers Alt+Shift+e" }, - { 1, SwipeRL, "xdotool key --clearmodifiers Alt+Shift+r" }, - { 1, SwipeDLUR, "sxmo_vol.sh up" }, - { 1, SwipeURDL, "sxmo_vol.sh down" }, - { 1, SwipeDRUL, "sxmo_brightness.sh up" }, - { 1, SwipeULDR, "sxmo_brightness.sh down" }, - { 2, SwipeLR, "xdotool key --clearmodifiers Alt+e" }, - { 2, SwipeRL, "xdotool key --clearmodifiers Alt+r" }, - { 2, SwipeDU, "pidof svkbd-sxmo || svkbd-sxmo &" }, - { 2, SwipeUD, "pkill -9 svkbd-sxmo" }, + { 1, SwipeLR, EdgeAny, DistanceAny, "xdotool key --clearmodifiers Alt+Shift+e" }, + { 1, SwipeRL, EdgeAny, DistanceAny, "xdotool key --clearmodifiers Alt+Shift+r" }, + { 1, SwipeDLUR, EdgeAny, DistanceAny, "sxmo_vol.sh up" }, + { 1, SwipeURDL, EdgeAny, DistanceAny, "sxmo_vol.sh down" }, + { 1, SwipeDRUL, EdgeAny, DistanceAny, "sxmo_brightness.sh up" }, + { 1, SwipeULDR, EdgeAny, DistanceAny, "sxmo_brightness.sh down" }, + { 2, SwipeLR, EdgeAny, DistanceAny, "xdotool key --clearmodifiers Alt+e" }, + { 2, SwipeRL, EdgeAny, DistanceAny, "xdotool key --clearmodifiers Alt+r" }, + { 2, SwipeDU, EdgeAny, DistanceAny, "pidof svkbd-sxmo || svkbd-sxmo &" }, + { 2, SwipeUD, EdgeAny, DistanceAny, "pkill -9 svkbd-sxmo" }, }; diff --git a/lisgd.1 b/lisgd.1 index aa104e4..3d28d71 100644 --- a/lisgd.1 +++ b/lisgd.1 @@ -20,8 +20,9 @@ lisgd \- libinput synthetic gesture daemon libinput touch events to run specific commands to execute. For example, dragging left to right with one finger could execute a particular command like launching a terminal. Directional L-R, R-L, U-D, and D-U gestures and -diagnol LD-RU, RD-LU, UR-DL, UL-DR gestures are supported with 1 through -n fingers. +diagnol LD-RU, RD-LU, UR-DL, UL-DR gestures are supported with 1 through +n fingers and can be bound to the screen's edges and/or made sensitive to +the distance of the gesture. Unlike other libinput gesture daemons, lisgd uses touch events to recognize synthetic swipe gestures rather than using the libinput's @@ -29,9 +30,9 @@ gesture events. The advantage of this is that the synthetic gestures you define via lisgd can be used on touchscreens, which normal libinput gestures don't support. -This program was built for use on the Pinephone however it could be used in -general for any device that supports touch events, like laptop touchscreens -or similar. You may want to adjust the threshold depending on the device +This program was built for use on the Pinephone however it could be used in +general for any device that supports touch events, like laptop touchscreens +or similar. You may want to adjust the threshold depending on the device you're using. @@ -41,9 +42,9 @@ you're using. Path of the dev filesystem device to monitor (like /dev/input/event1). .TP -.BR \-g ", " \-g\ nfingers,gesture,command\fR -Allow you to bind a gesture wherein nfingers is an integer, gesture is -one of {LR,RL,DU,UD,DLUR,URDL,ULDR,DLUR}, and the shell command to be executed. +.BR \-g ", " \-g\ nfingers,gesture,edge,distance,command\fR +Allows you to bind a gesture wherein nfingers is an integer, gesture is +one of {LR,RL,DU,UD,DLUR,URDL,ULDR,DLUR}, edge is one of * (any), N (none), L (left), R (right), T (top), B (bottom), TL (top left), TR (top right), BL (bottom left), BR (bottom right) and distance is one of * (any), S (short), M (medium), L (large). command is the shell command to be executed. The -g option can be used multiple times to bind multiple gestures. diff --git a/lisgd.c b/lisgd.c index 5adfbfa..8be78cf 100644 --- a/lisgd.c +++ b/lisgd.c @@ -10,6 +10,7 @@ #include #include #include +#include /* Defines */ #define MAXSLOTS 20 @@ -26,11 +27,35 @@ enum { SwipeURDL, SwipeULDR }; - typedef int Swipe; + +enum { + EdgeAny, + EdgeNone, + EdgeLeft, + EdgeRight, + EdgeTop, + EdgeBottom, + CornerTopLeft, + CornerTopRight, + CornerBottomLeft, + CornerBottomRight, +}; +typedef int Edge; + +enum { + DistanceAny, + DistanceShort, + DistanceMedium, + DistanceLong, +}; +typedef int Distance; + typedef struct { int nfswipe; Swipe swipe; + Edge edge; + Distance distance; char *command; } Gesture; @@ -41,9 +66,14 @@ typedef struct { Gesture *gestsarr; int gestsarrlen; Swipe pendingswipe; +Edge pendingedge; +Distance pendingdistance; double xstart[MAXSLOTS], xend[MAXSLOTS], ystart[MAXSLOTS], yend[MAXSLOTS]; unsigned nfdown = 0, nfpendingswipe = 0; struct timespec timedown; +static Display *dpy; +static int screen; +static int screenwidth, screenheight; void die(char * msg) @@ -91,31 +121,119 @@ gesturecalculateswipe(double x0, double y0, double x1, double y1) { return -1; } +Distance +gesturecalculatedistance(double x0, double y0, double x1, double y1, Swipe swipe) { + double dist = sqrt(pow(x1 - x0, 2) + pow(y1 - y0, 2)); + double diag = sqrt(pow(screenwidth,2) + pow(screenheight,2)); + switch (swipe) { + case SwipeDU: + case SwipeUD: + if (dist >= screenheight * 0.66) { + return DistanceLong; + } else if (dist >= screenheight * 0.33) { + return DistanceMedium; + } else { + return DistanceShort; + } + break; + case SwipeLR: + case SwipeRL: + if (dist >= screenwidth * 0.66) { + return DistanceLong; + } else if (dist >= screenwidth * 0.33) { + return DistanceMedium; + } else { + return DistanceShort; + } + break; + case SwipeULDR: + case SwipeDRUL: + case SwipeDLUR: + case SwipeURDL: + if (dist >= diag * 0.66) { + return DistanceLong; + } else if (dist >= diag * 0.33) { + return DistanceMedium; + } else { + return DistanceShort; + } + break; + } + + return 0; //shouldn't happen +} + +Edge +gesturecalculateedge(double x0, double y0, double x1, double y1) { + Edge horizontal = EdgeNone; + Edge vertical = EdgeNone; + if (x0 <= edgesizex) { + horizontal = EdgeLeft; + } else if (x0 >= screenwidth - edgesizex) { + horizontal = EdgeRight; + } else if (x1 <= edgesizex) { + horizontal = EdgeLeft; + } else if (x1 >= screenwidth - edgesizex) { + horizontal = EdgeRight; + } + if (y0 <= edgesizey) { + vertical = EdgeTop; + } else if (y0 >= screenheight - edgesizey) { + vertical = EdgeBottom; + } else if (y1 <= edgesizey) { + vertical = EdgeTop; + } else if (y1 >= screenheight - edgesizey) { + vertical = EdgeBottom; + } + if (horizontal == EdgeLeft && vertical == EdgeTop) { + return CornerTopLeft; + } else if (horizontal == EdgeRight && vertical == EdgeTop) { + return CornerTopRight; + } else if (horizontal == EdgeLeft && vertical == EdgeBottom) { + return CornerBottomLeft; + } else if (horizontal == EdgeRight && vertical == EdgeBottom) { + return CornerBottomRight; + } else if (horizontal != EdgeNone) { + return horizontal; + } else { + return vertical; + } +} + void -gestureexecute(Swipe swipe, int nfingers) { +gestureexecute(Swipe swipe, int nfingers, Edge edge, Distance distance) { int i; for (i = 0; i < gestsarrlen; i++) { if (verbose) { - fprintf(stderr, - "[Nfswipe/SwipeId]: Cfg (%d/%d) <=> Evt (%d/%d)\n", - gestsarr[i].nfswipe, gestsarr[i].swipe, nfingers, swipe + fprintf(stderr, + "[swipe]: Cfg(f=%d/s=%d/e=%d/d=%d) <=> Evt(f=%d/s=%d/e=%d/d=%d)\n", + gestsarr[i].nfswipe, gestsarr[i].swipe, gestsarr[i].edge, gestsarr[i].distance, nfingers, swipe, edge, distance ); } - if (gestsarr[i].nfswipe == nfingers && gestsarr[i].swipe == swipe) { + if (gestsarr[i].nfswipe == nfingers && gestsarr[i].swipe == swipe + && gestsarr[i].distance <= distance + && (gestsarr[i].edge == EdgeAny || gestsarr[i].edge == edge || + ((edge == CornerTopLeft || edge == CornerTopRight) && gestsarr[i].edge == EdgeTop) || + ((edge == CornerBottomLeft || edge == CornerBottomRight) && gestsarr[i].edge == EdgeBottom) || + ((edge == CornerTopLeft || edge == CornerBottomLeft) && gestsarr[i].edge == EdgeLeft) || + ((edge == CornerTopRight || edge == CornerBottomRight) && gestsarr[i].edge == EdgeRight) + ) + ) { if (verbose) fprintf(stderr, "Execute %s\n", gestsarr[i].command); execcommand(gestsarr[i].command); + break; //execute first match only } } } -static int +static int libinputopenrestricted(const char *path, int flags, void *user_data) { int fd = open(path, flags); return fd < 0 ? -errno : fd; } - + static void libinputcloserestricted(int fd, void *user_data) { @@ -141,6 +259,25 @@ swipereorient(Swipe swipe, int orientation) { return swipe; } +Edge +edgereorient(Edge edge, int orientation) { + while (orientation > 0) { + switch(edge) { + // 90deg per turn + case EdgeLeft: edge = EdgeTop; break; + case EdgeRight: edge = EdgeBottom; break; + case EdgeTop: edge = EdgeRight; break; + case EdgeBottom: edge = EdgeLeft; break; + case CornerTopLeft: edge = CornerTopRight; break; + case CornerTopRight: edge = CornerBottomRight; break; + case CornerBottomLeft: edge = CornerTopLeft; break; + case CornerBottomRight: edge = CornerBottomLeft; break; + } + orientation--; + } + return edge; +} + void touchdown(struct libinput_event *e) { @@ -197,17 +334,27 @@ touchup(struct libinput_event *e) Swipe swipe = gesturecalculateswipe( xstart[slot], ystart[slot], xend[slot], yend[slot] ); - if (nfpendingswipe == 0) pendingswipe = swipe; + Edge edge = gesturecalculateedge( + xstart[slot], ystart[slot], xend[slot], yend[slot] + ); + Distance distance = gesturecalculatedistance( + xstart[slot], ystart[slot], xend[slot], yend[slot], swipe + ); + if (nfpendingswipe == 0) { + pendingswipe = swipe; + pendingedge = edge; + pendingdistance = distance; + } if (pendingswipe == swipe) nfpendingswipe++; resetslot(slot); // All fingers up - check if within milisecond limit, exec, & reset if (nfdown == 0) { if ( - timeoutms > + timeoutms > ((now.tv_sec - timedown.tv_sec) * 1000000 + (now.tv_nsec - timedown.tv_nsec) / 1000) / 1000 - ) gestureexecute(swipe, nfpendingswipe); - + ) gestureexecute(swipe, nfpendingswipe, edge, distance); + nfpendingswipe = 0; } } @@ -238,7 +385,7 @@ run() die("Couldn't set mode to capture events"); } - // E.g. initially invalidate every slot + // E.g. initially invalidate every slot for (i = 0; i < MAXSLOTS; i++) { xend[i] = NOMOTION; yend[i] = NOMOTION; @@ -266,7 +413,7 @@ run() } libinput_unref(li); } - + int main(int argc, char *argv[]) { @@ -300,10 +447,10 @@ main(int argc, char *argv[]) exit(EXIT_FAILURE); } gestpt = strtok(argv[++i], ","); - for (j = 0; gestpt != NULL && j < 3; gestpt = strtok(NULL, ","), j++) { + for (j = 0; gestpt != NULL && j < 5; gestpt = strtok(NULL, ","), j++) { switch(j) { case 0: gestsarr[gestsarrlen - 1].nfswipe = atoi(gestpt); break; - case 1: + case 1: if (!strcmp(gestpt, "LR")) gestsarr[gestsarrlen-1].swipe = SwipeLR; if (!strcmp(gestpt, "RL")) gestsarr[gestsarrlen-1].swipe = SwipeRL; if (!strcmp(gestpt, "DU")) gestsarr[gestsarrlen-1].swipe = SwipeDU; @@ -313,15 +460,40 @@ main(int argc, char *argv[]) if (!strcmp(gestpt, "ULDR")) gestsarr[gestsarrlen-1].swipe = SwipeULDR; if (!strcmp(gestpt, "DRUL")) gestsarr[gestsarrlen-1].swipe = SwipeDRUL; break; - case 2: gestsarr[gestsarrlen - 1].command = gestpt; break; + case 2: + if (!strcmp(gestpt, "L")) gestsarr[gestsarrlen-1].edge = EdgeLeft; + if (!strcmp(gestpt, "R")) gestsarr[gestsarrlen-1].edge = EdgeRight; + if (!strcmp(gestpt, "T")) gestsarr[gestsarrlen-1].edge = EdgeTop; + if (!strcmp(gestpt, "B")) gestsarr[gestsarrlen-1].edge = EdgeBottom; + if (!strcmp(gestpt, "TL")) gestsarr[gestsarrlen-1].edge = CornerTopLeft; + if (!strcmp(gestpt, "TR")) gestsarr[gestsarrlen-1].edge = CornerTopRight; + if (!strcmp(gestpt, "BL")) gestsarr[gestsarrlen-1].edge = CornerBottomLeft; + if (!strcmp(gestpt, "BR")) gestsarr[gestsarrlen-1].edge = CornerBottomRight; + if (!strcmp(gestpt, "N")) gestsarr[gestsarrlen-1].edge = EdgeNone; + if (!strcmp(gestpt, "*")) gestsarr[gestsarrlen-1].edge = EdgeAny; + break; + case 3: + if (!strcmp(gestpt, "L")) gestsarr[gestsarrlen-1].distance = DistanceLong; + if (!strcmp(gestpt, "M")) gestsarr[gestsarrlen-1].distance = DistanceMedium; + if (!strcmp(gestpt, "S")) gestsarr[gestsarrlen-1].distance = DistanceShort; + if (!strcmp(gestpt, "*")) gestsarr[gestsarrlen-1].distance = DistanceAny; + break; + case 4: gestsarr[gestsarrlen - 1].command = gestpt; break; } } } else { - fprintf(stderr, "lisgd [-v] [-d /dev/input/0] [-o 0] [-t 200] [-r 20] [-m 400] [-g '1,LR,notify-send swiped left to right']\n"); + fprintf(stderr, "lisgd [-v] [-d /dev/input/0] [-o 0] [-t 200] [-r 20] [-m 400] [-g '1,LR,L,notify-send swiped left to right from left edge']\n"); exit(1); } } + //get display size + if (!(dpy = XOpenDisplay(0))) + die("cannot open display"); + screen = DefaultScreen(dpy); + screenwidth = DisplayWidth(dpy, screen); + screenheight = DisplayHeight(dpy, screen); + // E.g. no gestures passed on CLI - used gestures defined in config.def.h if (gestsarrlen == 0) { gestsarr = malloc(sizeof(gestures)); @@ -329,9 +501,11 @@ main(int argc, char *argv[]) memcpy(gestsarr, gestures, sizeof(gestures)); } - // Modify gestures swipes based on orientation provided - for (i = 0; i < gestsarrlen; i++) + // Modify gestures swipes based on orientation provided + for (i = 0; i < gestsarrlen; i++) { gestsarr[i].swipe = swipereorient(gestsarr[i].swipe, orientation); + gestsarr[i].edge = edgereorient(gestsarr[i].edge, orientation); + } run(); return 0;