Skip to content

natask/gestures

Repository files navigation

Gestures

Gesture Application for Linux supporting up to 5 finger gestures on Touchscreen and Touchpad.


gestures.png

Demonstrating fluid gestures, five finger gestures, tap gestures and touchscreen gestures.

Click to watch.

Table of Contents

  • Newly Created [2020-05-12 Tue 21:49:27]

Motivation

  • Newly Created [2020-01-17 Fri 03:02:06]
  • I wanted an application that will allow me to fluidly express my intent through the touchpad and touchscreen.
  • I felt shackled by one-shot gestures from libinput-gestures but from it I found the utility of eight-directional gestures.
  • I felt fusuma was lacking Although it was more fluid.
  • I really missed keeping my fingers moving across the touchpad to switch applications.
  • I wanted tap gestures.
  • I wanted five finger gestures.
  • I wanted usable gestures for my touchscreen.

So I wrote this.

Features

  • Newly Created [2020-01-17 Fri 07:37:48]
Light dependencies and Driver agnostic
  • Reads raw output from touchpad and touchscreen to construct gestures.
  • This means it has no dependency on libinput, synaptics or touchegg.
  • As a corollary, It can function when any of the touch-pad drivers are active.
Compatibility with both X11 and Wayland
  • works on wayland as well as X11 if using the default keystroke generator. The keystroke generator, evemu_do is based on evemu and injects keyboard events directly into a connected device. In the future, may create an input device solely for this application.
Vanilla gestures
features present in libinput-gestures and fusuma.
Tap gestures
This means that tapping the touchpad with 4 or 5 fingers is recognized as a gesture.
5 finger gestures
This means that you can place 5 fingers on the touchpad and that is recognized as another class of gestures. BTN_TOOL_QUINTTAP must supported. This feature must supported by your driver + touchpad. check by running evtest /dev/input/$(cat /proc/bus/input/devices | grep -iA 5 'touchpad' |grep -oP 'event[0-9]+') | grep BTN_TOOL_QUINTTAP. if it prints a line containing "BTN_TOOL_QUINTTAP", your touchpad+driver support it.
Touchscreen gestures
Extend gestures to touchscreen.
Fluid gestures
Allow the user to do different things without raising their fingers from the touchpad. This takes advantage of the fact that gesture execution is separated into 3 parts, start, update, and end, by doing complementary actions in each.

For example, assume the shortcut to switch windows is “CTRL + ALT” + direction, where direction is “LEFT”, “RIGHT”, “UP”, “DOWN”.

to switch windows fluidly, “CTRL + ALT” are held down programmatically on a start gesture event, then depending on which direction the user is swiping while fingers are still on the touchpad, dynamically generate direction commands. Then when the user raises their fingers, which is an end gesture event, the “CTRL + ALT” are released programmatically.

Orientation adjustment
  • using the script orientation, can figure out the orientation of screen to adjust both touchscreen and touchpad gestures. Script needs to be restarted for updates to take effect.

Dependencies

  • Newly Created [2020-01-17 Fri 04:10:31]
python dependencies (>=3.6)
subprocess, pathlib, simplejson, shlex, threading, queue, time, os, sys, math
*nix dependencies
cat, grep
dependencies
stdbuf, evtest
evtest
will maybe replaced by evemu-record in the future.
daemonize
using & disown should work as well but this is a sure way to detach and run this on a global scale.
xrandr
used by orientation for orientation detection.
default dependencies (if running default configuration)
evemu
need evemu_do (alternative to xdotool that I wrote) in $PATH. evemu_do is currently deprecated. Only recommended on wayland. It doesn’t work when no keyboards are attached to the machine.

Installation/Uninstallation

  • Newly Created [2020-02-04 Tue 21:35:57]
installation
  • git clone
  • run ./install.sh
    • it should handle most things.
    • may need to install daemonize by hand. If on Arch, I recommend daemonize-git from AUR.
    • may want to look at where it places things and if that meets your setup.
    • adds user to the input group.
    • what you truly need from this repo are gestures.config, gestures, getConfig.py. Everything else is just dependencies.
    • asks to replace config file if found. Saves a backup as default to avoid pain.
bash script to install this application
cd ~
git clone git@github.com:natask/gestures.git
cd gestures
./install.sh
    
uninstallation
  • run ./uninstall.sh
  • removes everything except that what was installed by the package manager. To uninstall those, remove evtest and daemonize.
  • removes user from input group.
  • asks before doing removing user from input group and specially deleting config file as it could be costly.

Customization

  • Newly Created [2020-03-02 Mon 04:42:21]
default customization
  • the default customization is my config.
  • uses extensively evemu_do, a script I wrote to replace xdotool. Much less buggy and also works on wayland. It is currently deprecated and only recommended for wayland users. Caveat is that It doesn’t work when no keyboards are attached to the machine.
  • evemu_do works much like xdotool but only for keyboard inputs.
    • evemu_do tab presses tab (also supports evemu_do key tab)
    • evemu_do keydown tab holds down tab
    • evemu_do keyup tab de-presses tab
    • also supports deprecated commands like evemu_do tab down and evemu_do tab up that hold down and de-presse tab respectively.
  • currently works by dumping events in the first keyboard it finds under /proc/bus/input/devices.
    • may look into creating a keyboard device for it to dump all its events on.
  • underneath it uses evemu-event, which is part of the evemu toolkit.
  • needs access to input group.
my setup
touchpad
2 finger
  • 2 finger pinch in and pinch out to zoom in and out (ctrl+plus and ctrl+minus)
3 finger
  • 3 finger horizontal to switch applications (alt + tab + DIRECTION)
  • 3 finger vertical to maximize/unmaximize application (super + i)
  • 3 finger // slanted gesture to change tabs (ctrl + page_up and ctrl + page_down)
  • 3 finger \ slanted gestures to open and close tabs (ctrl+shift+t and ctrl+w)
4 finger
  • 4 finger tap to open workspace view (super + w)
  • 4 finger horizontal and vertical to switch work-spaces (Ctrl + alt + DIRECTION)
  • 4 finger // slanted gestures to go through history (Alt + DIRECTION)
  • 4 finger \ slanted gestures to open and close windows (CTRL+shift+N and script to close application)
5 finger
  • 5 finger tap to open dictionary (goldendict)
  • 5 finger one shot gestures for doing a whole slew of things (a variety of scripts and applications)
touchpscreen
  • same as touchpad except don’t use pinch in and pinch out. just use regular. I also scale the screen so that an equivalent gesture on the touchscreen is much larger (as the screen is larger than the touchpad) than that of the touchpad. This provides consistency and a pleasant user experience.
currently customizable
  • swipe, pinch
  • 3,4,5 finger start and end gestures
  • 3,4,5 finger update gestures. (left, right, up, down) and (left down, left up, right up, right down) are separate update event groups. For example, if left gesture direction has both right and right up update directions specified in configuration, right is interpreted but right up is not.
    • still has limitations in terms of customizability since it is tailored for my workflow.
  • 2 finger fully customize pinch in/out gestures
  • specific gestures for touchpad and touchscreen
example
{'pinch_deadzone_enabled' : 'True',
 'touchpad' :{
     'pinch_deadzone_enabled' : 'False',
     {'swipe': {
         '3': {
             'l' : {'start': ['evemu_do keydown alt', 'evemu_do tab'], 'update': {'l': ["evemu_do Left"], 'r': ["evemu_do Right"], 'u': ["evemu_do Up"], 'd': ["evemu_do Down"], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['evemu_do keyup alt'], 'rep': ''},
         }
     },
      'pinch': {
          '2': {
              'i' : {'start': ['evemu_do keydown control', 'evemu_do equal'], 'update': {'i': ['evemu_do plus'], 'o': ['evemu_do minus']}, 'end': ['evemu_do keyup ctrl'], 'rep': ''},
              'o' : {'start': ['evemu_do keydown control', 'evemu_do minus'], 'update': {'i': ['evemu_do plus'], 'o': ['evemu_do minus']}, 'end': ['evemu_do keyup ctrl'], 'rep': ''}
          }
      }
     }
    
breakdown
pinch_deadzone_enabled
  • enables pinch. It can be set globally for all devices and/or specifically for a device. specific definition overrides global definition.
  • if it is not set, the default is True.
(touchscreen, touchpad)
  • make a set of gestures apply to touchpad or touchscreen
(swipe,pinch)
  • define if the gesture is a swipe or a pinch
(3,4,5)
  • define the number of fingers to activate the gesture
(‘t’, ‘l’, ‘r’,…,’ru’)
define *t*ap and the 8 directions (*l*eft, *r*ight, *l*eft-*r*ight, …) a swipe can be in.
(‘i’, ‘o’)
define pinch in and pinch out.
(start,end)
  • what to do when the gesture starts or ends.
(slated for a future update)
(update)
  • what to do when the gesture is on going. going to start out with just 4 directions as that suffices my needs (and probably most others) but will expand to 8 directional configuration should there be demand.
(rep)
  • how frequently is gesture update run. similar to fusuma. can make this directional as well, but don’t have plans for that yet.
(device level tag)
  • already have gestures apply to touchscreen or touchpad. the extension to specify what device a specific set of gestures apply to.

Debugging

  • Newly Created [2020-05-03 Sun 12:35:16]
Debugging script
running the script with anything after it in the terminal will run it without using demonize (as a child of the terminal).To log out events and errors, run with debug following the script name as follows. e.g
gestures debug 
    

general syntax is

gestures debug type1 type2 ...
    

where types are symbols that are used debug conveniently to print information only of a certain type

gestures debug angle
    

valid types are angle

can also stop the script from executing gestures during debugging using

gestures debug stop
    

can enable both symbols like

gestures debug angle stop
    

order of types doesn’t matter.

Syntactic issues in Config file
  • There are times when the builtin syntax checker for the config file, simplejson, doesn’t point to the correct place where a syntax error occurred within the config file. In such occasions use an online JSON linter. Those tend to work.
  • To use them though, you will need to remove all comments and change "" to '' from the config file. run the following code in a python shell to get a valid version. Then copy paste the results to any JSON linter.
    'Read given configuration file and store internal actions etc'
    import os
    conffile = os.path.expanduser("~/.config/gestures.conf")
    with open(conffile, "r") as fp:
        lines = []
        linenos = []
        for num, line in enumerate(fp, 1):
            if not line or line[0] == '#':
                continue
            lines.append(line.replace("'", "\""))
            linenos.append(num)
        print("".join(lines))
            
the following is a backup config to use or compare to if yours is broken
{'pinch_deadzone_enabled' : 'False',
'touchpad' :{
'pinch_deadzone_enabled' : 'False',
'swipe': {
# t = tap
# l = left
# r = right
# u = up
# d = down
# lu = left_up
# rd = right_down
# ld = left_down
# ru = right_up

# 3 finger swipe
'3': {
    't' : [],
    'l' : {'start': ['xdotool keydown alt', 'xdotool key Tab'], 'update': {'l': ["xdotool key Left"], 'r': ["xdotool key Right"], 'u': ["xdotool key Up"], 'd': ["xdotool key Down"], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool keyup alt'], 'rep': ''},
    'r' : {'start': ['xdotool keydown alt', 'xdotool key Tab'], 'update': {'l': ["xdotool key Left"], 'r': ["xdotool key Right"], 'u': ["xdotool key Up"], 'd': ["xdotool key Down"], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool keyup alt'], 'rep': ''},
    'u' : {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key super+i'], 'rep': ''},
    'd' : {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key super+i'], 'rep': ''},
    'lu': {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key ctrl+shift+t'], 'rep': ''},
    'rd': {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key ctrl+w'], 'rep': ''},
    'ld': {'start': ['xdotool keydown control','xdotool key Page_Up'], 'update': {'l' :[], 'r' :[], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': ["xdotool key Page_Up"], 'ru': ["xdotool key Page_Down"]}, 'end': ['xdotool keyup control'], 'rep': ''},
    'ru': {'start': ['xdotool keydown control','xdotool key Page_Down'],  'update': {'l' :[], 'r' :[], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': ["xdotool key Page_Up"], 'ru': ["xdotool key Page_Down"]}, 'end': ['xdotool keyup control'], 'rep': ''}
 },

# 4 finger swipe
'4': {
    't' : ['xdotool key super+w'],
    'l' : {'start': ['xdotool keydown ctrl+alt', 'xdotool key Left'],'update': {'l': ["xdotool key Left"], 'r': ["xdotool key Right"], 'u': ["xdotool key Up"], 'd': ["xdotool key Down"], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool keyup ctrl+alt'], 'rep': ''},
    'r' : {'start': ['xdotool keydown ctrl+alt', 'xdotool key Right'],'update': {'l': ["xdotool key Left"], 'r': ["xdotool key Right"], 'u': ["xdotool key Up"], 'd': ["xdotool key Down"], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool keyup ctrl+alt'], 'rep': ''},
    'u' : {'start': ['xdotool keydown ctrl+alt', 'xdotool key Up'],'update': {'l': ["xdotool key Left"], 'r': ["xdotool key Right"], 'u': ["xdotool key Up"], 'd': ["xdotool key Down"], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool keyup ctrl+alt'], 'rep': ''},
    'd' : {'start': ['xdotool keydown ctrl+alt', 'xdotool key Down'],'update': {'l': ["xdotool key Left"], 'r': ["xdotool key Right"], 'u': ["xdotool key Up"], 'd': ["xdotool key Down"], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool keyup ctrl+alt'], 'rep': ''},
    'lu': {'start': [], 'update': {'l' :[], 'r' :[], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key ctrl+shift+n'], 'rep': ''},
    'rd': {'start': [], 'update': {'l' :[], 'r' :[], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['save_and_close'], 'rep': ''},
    'ld': {'start': ['xdotool keydown alt','xdotool key Left'], 'update': {'l' :[], 'r' :[], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': ["xdotool key Left"], 'ru': ["xdotool key Right"]}, 'end': ['xdotool keyup Alt'], 'rep': ''},
    'ru': {'start': ['xdotool keydown alt','xdotool key Right'], 'update': {'l' :[], 'r' :[], 'u': [], 'd': [], 'lu': [], 'rd': [],'ld': ["xdotool key Left"], 'ru': ["xdotool key Down"]}, 'end': [], 'rep': ''}
 },
# 5 finger swipe
'5': {
    't' : ['open_dictionary'],
    'l' : {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key super+2'], 'rep': ''},
    'r' : {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key super+1'], 'rep': ''},
    'u' : {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['toggle_global_window_switcher'], 'rep': ''},
    'd' : {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['flip'], 'rep': ''},
    'lu': {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key super+1'], 'rep': ''},
    'rd': {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['restartTouchpadAndPen'], 'rep': ''},
    'ld': {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key super+3'], 'rep': ''},
    'ru': {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['reset_keyboard'], 'rep': ''}
     }
 },


 'pinch': {
# i = in
# o = out

# 2 finger pinch
 '2': {
    'i' : {'start': ['xdotool keydown control', 'xdotool key plus'], 'update': {'i': ['xdotool key plus'], 'o': ['xdotool key minus']}, 'end': ['xdotool keyup ctrl'], 'rep': ''},
    'o' : {'start': ['xdotool keydown control', 'xdotool key minus'], 'update': {'i': ['xdotool key plus'], 'o': ['xdotool key minus']}, 'end': ['xdotool keyup ctrl'], 'rep': ''}
 },
# 3 finger pinch
 '3': {
    'i' : {'start': ['xdotool keydown alt', 'xdotool key Tab'], 'update': {'i': [], 'o': []}, 'end': [], 'rep': ''},
    'o' : {'start': ['xdotool keydown alt', 'xdotool key Tab'], 'update': {'i': [], 'o': []}, 'end': [], 'rep': ''}
 },
# 4 finger pinch
'4': {
    'i' : {'start': ['xdotool keydown alt', 'xdotool key Tab'], 'update': {'i': [], 'o': []}, 'end': [], 'rep': ''},
    'o' : {'start': ['xdotool keydown alt', 'xdotool key Tab'], 'update': {'i': [], 'o': []}, 'end': [], 'rep': ''}
 },
# 5 finger pinch
'5': {
    'i' : {'start': ['xdotool keydown alt', 'xdotool key Tab'], 'update': {'i': [], 'o': []}, 'end': [], 'rep': ''},
    'o' : {'start': ['xdotool keydown alt', 'xdotool key Tab'], 'update': {'i': [], 'o': []}, 'end': [], 'rep': ''}
    }
 }
},


'touchscreen' :{
'swipe': {
# t = tap
# l = left
# r = right
# u = up
# d = down
# lu = left_up
# rd = right_down
# ld = left_down
# ru = right_up

# 3 finger swipe
'3': {
    't' : ['xdotool key shift+Insert'],
    'l' : {'start': ['xdotool keydown alt', 'xdotool key Tab'], 'update': {'l': ["xdotool key Left"], 'r': ["xdotool key Right"], 'u': ["xdotool key Up"], 'd': ["xdotool key Down"], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool keyup alt'], 'rep': ''},
    'r' : {'start': ['xdotool keydown alt', 'xdotool key Tab'], 'update': {'l': ["xdotool key Left"], 'r': ["xdotool key Right"], 'u': ["xdotool key Up"], 'd': ["xdotool key Down"], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool keyup alt'], 'rep': ''},
    'u' : {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key super+i'], 'rep': ''},
    'd' : {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key super+i'], 'rep': ''},
    'lu': {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key ctrl+shift+t'], 'rep': ''},
    'rd': {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key ctrl+w'], 'rep': ''},
    'ld': {'start': ['xdotool keydown control','xdotool key Page_Up'], 'update': {'l' :[], 'r' :[], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': ["xdotool key Page_Up"], 'ru': ["xdotool key Page_Down"]}, 'end': ['xdotool keyup control'], 'rep': ''},
    'ru': {'start': ['xdotool keydown control','xdotool key Page_Down'],  'update': {'l' :[], 'r' :[], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': ["xdotool key Page_Up"], 'ru': ["xdotool key Page_Down"]}, 'end': ['xdotool keyup control'], 'rep': ''}
 },

# 4 finger swipe
'4': {
    't' : ['xdotool key super+w'],
    'l' : {'start': ['xdotool keydown ctrl+alt', 'xdotool key Left'],'update': {'l': ["xdotool key Left"], 'r': ["xdotool key Right"], 'u': ["xdotool key Up"], 'd': ["xdotool key Down"], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool keyup ctrl+alt'], 'rep': ''},
    'r' : {'start': ['xdotool keydown ctrl+alt', 'xdotool key Right'],'update': {'l': ["xdotool key Left"], 'r': ["xdotool key Right"], 'u': ["xdotool key Up"], 'd': ["xdotool key Down"], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool keyup ctrl+alt'], 'rep': ''},
    'u' : {'start': ['xdotool keydown ctrl+alt', 'xdotool key Up'],'update': {'l': ["xdotool key Left"], 'r': ["xdotool key Right"], 'u': ["xdotool key Up"], 'd': ["xdotool key Down"], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool keyup ctrl+alt'], 'rep': ''},
    'd' : {'start': ['xdotool keydown ctrl+alt', 'xdotool key Down'],'update': {'l': ["xdotool key Left"], 'r': ["xdotool key Right"], 'u': ["xdotool key Up"], 'd': ["xdotool key Down"], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool keyup ctrl+alt'], 'rep': ''},
    'lu': {'start': [], 'update': {'l' :[], 'r' :[], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key ctrl+shift+n'], 'rep': ''},
    'rd': {'start': [], 'update': {'l' :[], 'r' :[], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['save_and_close'], 'rep': ''},
    'ld': {'start': ['xdotool keydown alt','xdotool key Left'], 'update': {'l' :[], 'r' :[], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': ["xdotool key Left"], 'ru': ["xdotool key Right"]}, 'end': ['xdotool keyup Alt'], 'rep': ''},
    'ru': {'start': ['xdotool keydown alt','xdotool key Right'], 'update': {'l' :[], 'r' :[], 'u': [], 'd': [], 'lu': [], 'rd': [],'ld': ["xdotool key Left"], 'ru': ["xdotool key Right"]}, 'end': [], 'rep': ''}
 },
# 5 finger swipe
'5': {
    't' : ['open_dictionary'],
    'l' : {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['emacsclient -c -a \"\"'], 'rep': ''},
    'r' : {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key ctrl+alt+t'], 'rep': ''},
    'u' : {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['toggle_global_window_switcher'], 'rep': ''},
    'd' : {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['flip'], 'rep': ''},
    'lu': {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['xdotool key ctrl+t'], 'rep': ''},
    'rd': {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['restartTouchpadAndPen'], 'rep': ''},
    'ld': {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['emacsclient -c -a \"\"'], 'rep': ''},
    'ru': {'start': [], 'update': {'l': [], 'r': [], 'u': [], 'd': [], 'lu': [], 'rd': [], 'ld': [], 'ru': []}, 'end': ['reset_keyboard'], 'rep': ''}
     }
 },

 'pinch': {
# i = in
# o = out

# 2 finger pinch
 '2': {
    'i' : {'start': ['', ''], 'update': {'i': [''], 'o': ['']}, 'end': [''], 'rep': ''},
    'o' : {'start': ['', ''], 'update': {'i': [''], 'o': ['']}, 'end': [''], 'rep': ''}
 },
# 3 finger pinch
 '3': {
    'i' : {'start': ['', ''], 'update': {'i': [], 'o': []}, 'end': [], 'rep': ''},
    'o' : {'start': ['', ''], 'update': {'i': [], 'o': []}, 'end': [], 'rep': ''}
 },
# 4 finger pinch
'4': {
    'i' : {'start': ['', ''], 'update': {'i': [], 'o': []}, 'end': [], 'rep': ''},
    'o' : {'start': ['', ''], 'update': {'i': [], 'o': []}, 'end': [], 'rep': ''}
 },
# 5 finger pinch
'5': {
    'i' : {'start': ['', ''], 'update': {'i': [], 'o': []}, 'end': [], 'rep': ''},
    'o' : {'start': ['', ''], 'update': {'i': [], 'o': []}, 'end': [], 'rep': ''}
    }
}
}
}

Utilities

  • Newly Created [2020-05-10 Sun 13:00:09]
  • utilities are scripts that enhance your default experience. They are placed in /usr/local/bin by the installation script.
open_dictionary
  • script to goldendict on selected text.
  • currently mapped to five finger click.
evemu_do
  • script that generates keyboard events. Much like xdotool.
  • currently deprecated but recommended if using wayland.
orientation
  • script that figures out the orientation of screen.
flip
  • flips screen.
  • mapped to a down fiver finger swipe.
killTouchpad
  • kills the running gesture application.
reset_keyboard
  • resets all held down.
  • uses both evemu_do and xkbmap.
  • mapped to a right+up five finger swipe.
restartTouchpadAndPen
  • restarts the touchpad and pen (surface book stuff) in an intelligent way.
  • uses set_orientation and restartTouchpad.
  • mapped to right+down five finger swipe.
set_orientation
  • sets the orientation of touchpad to match the orientation of screen.
restartTouchpad
  • restarts Touchpad.
save_and_close
  • closes application through Alt+F4, saves a select few applications before closing.
  • mapped to right+down four finger swipe.
toggle_global_window_switcher
  • toogles whether window switcher shows windows from all workspaces or just the current workspace.
  • mapped to up five finger swipe.

Compatibility

  • Newly Created [2020-05-03 Sun 13:06:22]
X11
  • works fine.
  • may need to modify orientation if it is not tracking the screen with a touchscreen/touchpad.
wayland
  • works if using my script evemu_do to generate keystrokes.
  • orientation may not work on wayland since it depends on xrandr although I haven’t tested myself.
  • The default four finger gestures clash with four finger and five finger gestures. They are also not configurable unlike the three finger gestures and can’t be disabled. For reliable use, disabling these in within this script for touchpads is the best option. This and lack of support for sticky keys is the reason I don’t use wayland. hopefully The gnome wayland team will make it optional.

The Code

  • Newly Created [2020-03-05 Thu 03:52:06]
  • may need to adjust the screen size and touchpad calibration. This can be automated by looking at the dimensions as evtest is called.
  • the knobs are as follows
    DEBUG = 0 # variable to enable debug mode
    TYPE = [] # types that are printed. valid ones ["angle", "slot", "gesture"]
    STOP = False # don't execute gestures, useful for debugging
    
    TOUCHPAD_CALIBRATION = 1 # scaling down for touchpad movements
    TOUCHSCREEN_CALIBRATION = 2 # scaling down for touchscreen movements
    
    DECISION = 75 # sufficient movement to make decision on direction, scaled by the number of slots
    PINCH_DECISION = 160 #seems like x_cum and y_cum should got to around 0 if fingers moved symmetrically in or out  #sufficient momvent to make pinch
    
    ANGLE_X = 16 # angle to interpret as horizontal
    ANGLE_Y = 21 # angle to interpret as vertical
    
    DEBOUNCE = 0.02  #sleep for 10 ms(now 40 ms), fastest tap around 25 ms, gotten from new_touch, touchpad data. in practice works well.
    THRESHOLD_SQUARED = 30 # threshold to be considered a move, squared sum of x and y
    PINCH_THRESHOLD = 100
    
    REP_THRES = 0.2 #need to break this TIME before REP engages
    REP_DAG = 250 # REP on diagonal movement
    REP = 150 # REP for horizontal or veritical movement
    PINCH_REP = 40
    
    DEADZONE_SQUARED = 1000 # deadzone where up until this, pinches aren't interpreted
        

Alternatives

  • Newly Created [2020-01-17 Fri 03:16:58]
libinput-gestures
  • what I used to use.
  • Works well, just that the gestures are one-shot, meaning that the command attached to a gesture is executed only once per full swipe.
  • depends on libinput.
fusuma
  • Although it doesn’t have one-shot limitation, it didn’t support commands to run when the gesture begins and ends. This is useful for use-cases like switching applications which require alt-down to be pressed.
  • didn’t support eight-directional gestures.

Thoughts

  • Newly Created [2020-03-09 Mon 02:50:02]
final version
the current implementation suits my use case very well so I am in no hurry to customize. With that said, I would like to implement a fully customizable version of this. A C++ version would be good as well although current performance is more than enough.

something like nested gestures will be interesting where swipes are nested in a hierarchy. for example, swiping left, then right then up is integrated differently than swiping left then right then down. At this point though I think improvements like this only have diminishing marginal returns so I will not pursue them.

Versioning

  • Newly Created [2020-05-08 Fri 22:35:13]
  • this will be based upon Major and Minor completions in TODOS.

[3/9] TODOS

  • State “TODO” from “TODO” [2021-05-08 Sat 17:56:53]
  • State “TODO” from “TODO” [2021-05-08 Sat 17:56:52]
  • State “TODO” from “TODO” [2020-05-25 Mon 11:33:42]
  • State “TODO” from [2020-05-08 Fri 22:41:57]
  • Newly Created [2020-01-17 Fri 03:06:38]

[5/9] enable customization by refactoring code.

commands for gesture start

commands for gesture end

commands for touchscreen

commands for gesture update

rep rate

add multi-finger pinch gestures

detach implementation from personal workflow

more nuanced application of gestures to different attached devices

add debugging notes about fixing config file (use online JSON linter if the interal JSON linter doesn’t lead to debug point)”

ask before doing stuff in installation and uninstallation scripts

[1/3] configuration syntax

implement JSON config file support.

use libinput-gestures config file syntax.

use fusuma config file syntax.

Create a standalone input device for this application

  • Newly Created [2020-05-03 Sun 13:13:14]
evemu_do injects keystroke events in existing connected input device. Attaching it to a standalone input device will be useful.

[1/1] enrich readme

  • Newly Created [2020-01-17 Fri 03:15:49]

update customization readme [/]

  • Newly Created [2020-05-25 Mon 11:32:53]

Write script to fulfill dependencies automatically

  • State “TODO” from [2020-01-17 Fri 04:26:33]
  • Newly Created [2020-01-17 Fri 04:26:25]

Include error handling for mistakes in config file

  • Newly Created [2020-05-03 Sun 13:43:24]
There is already error handling for syntactic issues of the config file. But as noted in this issue, error handling for incorrect proprieties within config is currently nonexistent. More specifically, lines such as
self.gesture_queue.extend(map(lambda x: shlex.split(x), self.gestures["swipe"]['5']['u']['end']));

do no error checking on whether proprieties “swipe”, “5”, “u” or “end” actually exist within the config file.

Implement C++ version

  • State “TODO” from [2020-02-03 Mon 04:26:33]
  • Newly Created [2020-02-03 Mon 04:26:25]

end gesture when adding fingers [/]

  • Newly Created [2020-05-25 Mon 11:34:03]
  • this should be the default behavior. currently it doesn’t run the end gesture sequence and continues on executing the next gesture. Nice for closing alt-tab with super+w but I still don’t think it should be the default behavior. The default behavior should end the gesture before continuing onto the next.