Initial commit
This commit is contained in:
commit
657331d4b9
5 changed files with 393 additions and 0 deletions
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT/X Consortium License
|
||||||
|
|
||||||
|
© 2020 Miles Alan <m@milesalan.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
30
Makefile
Normal file
30
Makefile
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
SRC = lisgd.c
|
||||||
|
OBJ = ${SRC:.c=.o}
|
||||||
|
LDFLAGS = -linput
|
||||||
|
|
||||||
|
all: options lisgd
|
||||||
|
|
||||||
|
options:
|
||||||
|
@echo lisgd build options:
|
||||||
|
@echo "CFLAGS = ${CFLAGS}"
|
||||||
|
@echo "LDFLAGS = ${LDFLAGS}"
|
||||||
|
@echo "CC = ${CC}"
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
${CC} -c ${CFLAGS} $<
|
||||||
|
|
||||||
|
${OBJ}: config.h
|
||||||
|
|
||||||
|
config.h:
|
||||||
|
cp config.def.h $@
|
||||||
|
|
||||||
|
lisgd: ${OBJ}
|
||||||
|
${CC} -g -o $@ ${OBJ} ${LDFLAGS}
|
||||||
|
|
||||||
|
install: all
|
||||||
|
mkdir -p ${DESTDIR}${PREFIX}/bin
|
||||||
|
cp -f lisgd ${DESTDIR}${PREFIX}/bin
|
||||||
|
chmod 755 ${DESTDIR}${PREFIX}/bin/lisgd
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f config.h
|
47
README.md
Normal file
47
README.md
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# lisgd
|
||||||
|
|
||||||
|
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. L-R, R-L, U-D, and D-U swipe gestures are
|
||||||
|
supported with 1 through n fingers.
|
||||||
|
|
||||||
|
Unlike other libinput gesture daemons, lisgd uses touch events to
|
||||||
|
recognize **synthetic swipe gestures** rather than using the *libinput*'s
|
||||||
|
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 you're using.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
Configuration can be done in two ways:
|
||||||
|
|
||||||
|
1. Through a suckless style `config.h`; see the `config.def.h`
|
||||||
|
2. Through commandline flags which override the default config.h values
|
||||||
|
|
||||||
|
### Suckless-style config.h based configuration
|
||||||
|
Example `config.def.h` configuration -- e.g. copy this to `config.h`:
|
||||||
|
|
||||||
|
### Commandline flags based configuration
|
||||||
|
Flags:
|
||||||
|
- **-g [fingers,start,end,command]**: Defines a gestures wherein fingers is a integer, start/end are {l,r,d,u}, and command is the command to execute
|
||||||
|
- Example: `lisgd -g "1,l,r,notify-send swiped lr" -g "1,r,l,noitfy-send swiped rl"`
|
||||||
|
- **-d [devicenodepath]**: Defines the dev filesystem device to monitor
|
||||||
|
- Example: `lisgd -d /dev/input/input1`
|
||||||
|
- **-t [threshold_units]**: Number of libinput units (number) minimum to recognize a gesture
|
||||||
|
- Example: `lisgd -t 400`
|
||||||
|
- **-v**: Verbose mode, useful for debugging
|
||||||
|
- Example: `lisgd -v`
|
||||||
|
|
||||||
|
Full commandline-based configuration example:
|
||||||
|
```
|
||||||
|
lisgd -d /dev/input/input1 -g "1,left,right,notify-send swiped lr" -t 200 -v
|
||||||
|
```
|
||||||
|
|
||||||
|
### TODO
|
||||||
|
- Diagnol swipe gestures
|
||||||
|
- Gestures recognition based on screenspace executed in
|
27
config.def.h
Normal file
27
config.def.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/* Minimum cutoff for a gestures to take effect */
|
||||||
|
unsigned int threshold = 300;
|
||||||
|
|
||||||
|
/* Verbose mode, 1 = enabled, 0 = disabled */
|
||||||
|
int verbose = 0;
|
||||||
|
|
||||||
|
/* Device libinput should read from */
|
||||||
|
char *device = "/dev/input/event1";
|
||||||
|
|
||||||
|
/* Commands to execute upon recieving a swipe gesture */
|
||||||
|
Gesture gestures[] = {
|
||||||
|
/* fingers start end command */
|
||||||
|
{ 1, Left, Right, "xdotool key --clearmodifiers Alt+Shift+h" },
|
||||||
|
{ 1, Right, Left, "xdotool key --clearmodifiers Alt+Shift+l" },
|
||||||
|
|
||||||
|
{ 2, Left, Right, "xdotool key --clearmodifiers Alt+h" },
|
||||||
|
{ 2, Right, Left, "xdotool key --clearmodifiers Alt+l" },
|
||||||
|
|
||||||
|
{ 2, Down, Up, "pidof svkbd-sxmo || svkbd-sxmo &" },
|
||||||
|
{ 2, Up, Down, "pkill -9 svkbd-sxmo" },
|
||||||
|
|
||||||
|
{ 3, Down, Up, "sxmo_vol.sh up" },
|
||||||
|
{ 3, Up, Down, "sxmo_vol.sh down" },
|
||||||
|
|
||||||
|
{ 4, Down, Up, "sxmo_brightness.sh up" },
|
||||||
|
{ 4, Up, Down, "sxmo_brightness.sh down" },
|
||||||
|
};
|
268
lisgd.c
Normal file
268
lisgd.c
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <libinput.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#define MAXSLOTS 20
|
||||||
|
#define INVALID -999999
|
||||||
|
|
||||||
|
/* Types */
|
||||||
|
enum { Left, Right, Down, Up };
|
||||||
|
typedef int direction;
|
||||||
|
typedef struct {
|
||||||
|
int nfingers;
|
||||||
|
direction start;
|
||||||
|
direction end;
|
||||||
|
char *command;
|
||||||
|
} Gesture;
|
||||||
|
|
||||||
|
/* Config */
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
/* Globals */
|
||||||
|
Gesture *gestsarr;
|
||||||
|
int gestsarrlen;
|
||||||
|
direction pendingstart, pendingend;
|
||||||
|
double xstart[MAXSLOTS], xend[MAXSLOTS], ystart[MAXSLOTS], yend[MAXSLOTS];
|
||||||
|
unsigned fingsdown = 0, fingspending = 0;
|
||||||
|
|
||||||
|
void
|
||||||
|
die(char * msg)
|
||||||
|
{
|
||||||
|
fprintf(stderr, msg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
execcommand(char *c)
|
||||||
|
{
|
||||||
|
system(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
touchdown(struct libinput_event *e)
|
||||||
|
{
|
||||||
|
struct libinput_event_touch *tevent;
|
||||||
|
int slot;
|
||||||
|
|
||||||
|
tevent = libinput_event_get_touch_event(e);
|
||||||
|
slot = libinput_event_touch_get_slot(tevent);
|
||||||
|
|
||||||
|
xstart[slot] = libinput_event_touch_get_x(tevent);
|
||||||
|
ystart[slot] = libinput_event_touch_get_y(tevent);
|
||||||
|
fingsdown++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
touchmotion(struct libinput_event *e)
|
||||||
|
{
|
||||||
|
struct libinput_event_touch *tevent;
|
||||||
|
int slot;
|
||||||
|
|
||||||
|
tevent = libinput_event_get_touch_event(e);
|
||||||
|
slot = libinput_event_touch_get_slot(tevent);
|
||||||
|
xend[slot] = libinput_event_touch_get_x(tevent);
|
||||||
|
yend[slot] = libinput_event_touch_get_y(tevent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
touchup(struct libinput_event *e)
|
||||||
|
{
|
||||||
|
struct libinput_event_touch *tevent;
|
||||||
|
direction start;
|
||||||
|
direction end;
|
||||||
|
int i;
|
||||||
|
int slot;
|
||||||
|
|
||||||
|
tevent = libinput_event_get_touch_event(e);
|
||||||
|
slot = libinput_event_touch_get_slot(tevent);
|
||||||
|
|
||||||
|
fingsdown--;
|
||||||
|
if (xend[slot] == INVALID || xstart[slot] == INVALID) return;
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"(%d down fingers) (%d pending fingers) [start: x %lf y %lf] to [end x %lf y %lf]\n",
|
||||||
|
fingsdown, fingspending, xstart[slot], ystart[slot], xend[slot], yend[slot]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xend[slot] > xstart[slot] && fabs(xend[slot] - xstart[slot]) > threshold) {
|
||||||
|
start = Left;
|
||||||
|
end = Right;
|
||||||
|
} else if (xend[slot] < xstart[slot] && fabs(xend[slot] - xstart[slot]) > threshold) {
|
||||||
|
start = Right;
|
||||||
|
end = Left;
|
||||||
|
} else if (yend[slot] > ystart[slot] && fabs(yend[slot] - ystart[slot]) > threshold) {
|
||||||
|
start = Up;
|
||||||
|
end = Down;
|
||||||
|
} else if (yend[slot] < ystart[slot] && fabs(yend[slot] - ystart[slot]) > threshold) {
|
||||||
|
start = Down;
|
||||||
|
end = Up;
|
||||||
|
} else {
|
||||||
|
if (verbose) {
|
||||||
|
fprintf(stderr, "Input didn't match a known gesture\n");
|
||||||
|
}
|
||||||
|
start = INVALID;
|
||||||
|
end = INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fingspending == 0) {
|
||||||
|
pendingstart = start;
|
||||||
|
pendingend = end;
|
||||||
|
}
|
||||||
|
if (pendingstart == start && pendingend == end) {
|
||||||
|
fingspending++;
|
||||||
|
}
|
||||||
|
|
||||||
|
xend[slot] = INVALID;
|
||||||
|
yend[slot] = INVALID;
|
||||||
|
xstart[slot] = INVALID;
|
||||||
|
ystart[slot] = INVALID;
|
||||||
|
|
||||||
|
if (fingsdown == 0) {
|
||||||
|
for (i = 0; i < gestsarrlen; i++) {
|
||||||
|
if (verbose) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"[Fingers/Start/End]: Cfg (%d/%d/%d) <=> Evt (%d/%d/%d)\n",
|
||||||
|
gestsarr[i].nfingers, gestsarr[i].start, gestsarr[i].end,
|
||||||
|
fingspending, pendingstart, pendingend
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
gestsarr[i].nfingers == fingspending &&
|
||||||
|
gestsarr[i].start == pendingstart &&
|
||||||
|
gestsarr[i].end == pendingend
|
||||||
|
) {
|
||||||
|
if (verbose) {
|
||||||
|
fprintf(stderr, "Execute %s\n", gestsarr[i].command);
|
||||||
|
}
|
||||||
|
execcommand(gestsarr[i].command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fingspending = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run()
|
||||||
|
{
|
||||||
|
struct libinput *li;
|
||||||
|
struct libinput_event *event;
|
||||||
|
struct libinput_event_touch *tevent;
|
||||||
|
struct libinput_device *d;
|
||||||
|
int selectresult;
|
||||||
|
fd_set fdset;
|
||||||
|
|
||||||
|
const static struct libinput_interface interface = {
|
||||||
|
.open_restricted = libinputopenrestricted,
|
||||||
|
.close_restricted = libinputcloserestricted,
|
||||||
|
};
|
||||||
|
|
||||||
|
li = libinput_path_create_context(&interface, NULL);
|
||||||
|
|
||||||
|
if ((d = libinput_path_add_device(li, device)) == NULL) {
|
||||||
|
die("Couldn't bind event from dev filesystem\n");
|
||||||
|
} else if (LIBINPUT_CONFIG_STATUS_SUCCESS != libinput_device_config_send_events_set_mode(
|
||||||
|
d, LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
|
||||||
|
)) {
|
||||||
|
die("Couldn't set mode to capture events\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
FD_ZERO(&fdset);
|
||||||
|
FD_SET(libinput_get_fd(li), &fdset);
|
||||||
|
for (;;) {
|
||||||
|
selectresult = select(FD_SETSIZE, &fdset, NULL, NULL, NULL);
|
||||||
|
if (selectresult == -1) {
|
||||||
|
die("Can't select on device node?\n");
|
||||||
|
} else {
|
||||||
|
libinput_dispatch(li);
|
||||||
|
while ((event = libinput_get_event(li)) != NULL) {
|
||||||
|
switch(libinput_event_get_type(event)) {
|
||||||
|
case LIBINPUT_EVENT_TOUCH_DOWN: touchdown(event); break;
|
||||||
|
case LIBINPUT_EVENT_TOUCH_UP: touchup(event); break;
|
||||||
|
case LIBINPUT_EVENT_TOUCH_MOTION: touchmotion(event); break;
|
||||||
|
}
|
||||||
|
libinput_event_destroy(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
libinput_unref(li);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
char *gestpt;
|
||||||
|
|
||||||
|
gestsarr = malloc(0);
|
||||||
|
gestsarrlen = 0;
|
||||||
|
|
||||||
|
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
||||||
|
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||||
|
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (!strcmp(argv[i], "-v")) {
|
||||||
|
verbose = 1;
|
||||||
|
} else if (!strcmp(argv[i], "-d")) {
|
||||||
|
device = argv[++i];
|
||||||
|
} else if (!strcmp(argv[i], "-t")) {
|
||||||
|
threshold = atoi(argv[++i]);
|
||||||
|
} else if (!strcmp(argv[i], "-g")) {
|
||||||
|
gestsarrlen++;
|
||||||
|
realloc(gestsarr, (gestsarrlen * sizeof(Gesture)));
|
||||||
|
gestpt = strtok(argv[++i], ",");
|
||||||
|
for (j = 0; gestpt != NULL && j < 4; gestpt = strtok(NULL, ","), j++) {
|
||||||
|
switch(j) {
|
||||||
|
case 0: gestsarr[gestsarrlen - 1].nfingers = atoi(gestpt); break;
|
||||||
|
case 1:
|
||||||
|
if (!strcmp(gestpt, "l")) gestsarr[gestsarrlen-1].start = Left;
|
||||||
|
if (!strcmp(gestpt, "r")) gestsarr[gestsarrlen-1].start = Right;
|
||||||
|
if (!strcmp(gestpt, "d")) gestsarr[gestsarrlen-1].start = Down;
|
||||||
|
if (!strcmp(gestpt, "u")) gestsarr[gestsarrlen-1].start = Up;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (!strcmp(gestpt, "l")) gestsarr[gestsarrlen-1].end = Left;
|
||||||
|
if (!strcmp(gestpt, "r")) gestsarr[gestsarrlen-1].end = Right;
|
||||||
|
if (!strcmp(gestpt, "d")) gestsarr[gestsarrlen-1].end = Down;
|
||||||
|
if (!strcmp(gestpt, "u")) gestsarr[gestsarrlen-1].end = Up;
|
||||||
|
break;
|
||||||
|
case 3: gestsarr[gestsarrlen - 1].command = gestpt; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// E.g. no gestures passed on CLI - used gestures defined in config.def.h
|
||||||
|
if (gestsarrlen == 0) {
|
||||||
|
gestsarr = malloc(sizeof(gestures));
|
||||||
|
gestsarrlen = sizeof(gestures) / sizeof(Gesture);
|
||||||
|
memcpy(gestsarr, gestures, sizeof(gestures));
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue