Add command line tools for configurations

This commit is contained in:
Rico Tiongson 2020-04-21 03:26:48 +08:00
parent ffe664c7d8
commit de3f88c83c
15 changed files with 541 additions and 268 deletions

215
README.md
View File

@ -3,10 +3,12 @@
[![comfortable-swipe version](https://img.shields.io/github/release/Hikari9/comfortable-swipe.svg?label=comfortable-swipe&color=orange)](https://github.com/Hikari9/comfortable-swipe/releases) [![comfortable-swipe version](https://img.shields.io/github/release/Hikari9/comfortable-swipe.svg?label=comfortable-swipe&color=orange)](https://github.com/Hikari9/comfortable-swipe/releases)
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
> **_New in Version 1.1.0!_** Added mouse gestures, see [#mouse-gestures-experimental](#mouse-gestures-experimental)
Comfortable, seamless, and fast 3-finger (and 4-finger) touchpad swipe gestures for Ubuntu 14.04 LTS and beyond. May work for other Linux distros that support `libinput`. Comfortable, seamless, and fast 3-finger (and 4-finger) touchpad swipe gestures for Ubuntu 14.04 LTS and beyond. May work for other Linux distros that support `libinput`.
> **_New in Version 1.1.0_**: Added mouse gestures, see [#mouse-gestures-experimental](#mouse-gestures-experimental)
> **_New in Version 1.2.0_**: Autostart now switched ON by default
## Installation ## Installation
1. Install git and g++ 1. Install git and g++
@ -18,7 +20,7 @@ Comfortable, seamless, and fast 3-finger (and 4-finger) touchpad swipe gestures
1. Install libinput-tools and C libraries 1. Install libinput-tools and C libraries
```bash ```bash
sudo apt install libinput-tools libconfig-dev libxdo-dev sudo apt install libinput-tools libconfuse-dev libxdo-dev
``` ```
2. Clone this repository 2. Clone this repository
@ -39,44 +41,93 @@ Comfortable, seamless, and fast 3-finger (and 4-finger) touchpad swipe gestures
## How to Run ## How to Run
1. You'll need some group permissions to read touchpad input data. Run 1. You'll need some group permissions to read touchpad input data. Run
```bash ```bash
sudo gpasswd -a $USER $(ls -l /dev/input/event* | awk '{print $4}' | head --line=1) sudo gpasswd -a "$USER" "$(ls -l /dev/input/event* | awk '{print $4}' | head --line=1)"
```
2. **_Important_**: After inputing your `sudo` password, log out then log back in
3. Run
``` ```
1. **_Important_**: After inputing your `sudo` password, log out then log back in
1. Run
```bash
comfortable-swipe start comfortable-swipe start
``` ```
4. _Optional_: Automatically run on startup
``` 1. (Optional) Automatically run on startup
comfortable-swipe autostart
``` ```bash
5. Check the status of your application by running comfortable-swipe autostart on
```
comfortable-swipe status
``` ```
## Swipe Configurations 1. You can check general program status
```basha
$> comfortable-swipe status
Comfortable swipe makes use of keyboard shortcuts for configurations. Edit by running: autostart is ON
comfortable-swipe program is RUNNING
```
gedit $(comfortable-swipe config)
``` ```
| Property | Description | Default Value | Default Behavior | ## List of Commands
| --------- | :------------------------------------------: | -------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| left3 | 3-finger swipe left | ctrl+alt+Right | switch to right workspace | 1. Run / stop the program
| left4 | 4-finger swipe left | ctrl+alt+shift+Right | move window to right workspace | ```
| right3 | 3-finger swipe right | ctrl+alt+Left | switch to left workspace | comfortable-swipe start
| right4 | 4-finger swipe right | ctrl+alt+shift+Left | move window to left workspace | comfortable-swipe stop
| up3 | 3-finger swipe up | ctrl+alt+Down | switch to bottom workspace | comfortable-swipe restart
| up4 | 4-finger swipe up | ctrl+alt+shift+Down | move window to bottom workspace | ```
| down3 | 3-finger swipe down | ctrl+alt+Up | switch to above workspace |
| down4 | 4-finger swipe down | ctrl+alt+shift+Up | move window to above workpace | 1. Show help / version
| threshold | mouse pixels to activate swipe | 0.0 | tweak this if you're having troubles with touchpad sensitivity (higher = less sensitive, values can be as large as 1000.0) | ```
| hold3 | holds a mouse button when 3 fingers are down | (none) | See [Mouse Gestures (Experimental)](#mouse-gestures-experimental) | comfortable-swipe --help
| hold4 | holds a mouse button when 4 fingers are down | (none) | See [Mouse Gestures (Experimental)](#mouse-gestures-experimental) | comfortable-swipe --version
```
1. Show path to configuration file
```bash
comfortable-swipe config
```
1. Toggle autostart
```bash
comfortable-swipe autostart [on|off|toggle|status|path]
```
1. (Advanced) Run program buffer
```bash
comfortable-swipe buffer
```
## Configuring Swipe Gestures
The default configuration file is located at `~/.config/comfortable-swipe.conf`.
Comfortable swipe makes use of keyboard shortcuts to perform swipes through `xdotool`.
An example:
```bash
# File: ~/.config/comfortable-swipe.conf
left3 = ctrl+alt+Right
left4 = ctrl+alt+shift+Right
right3 = ctrl+alt+Left
right4 = ctrl+alt+shift+Left
up3 = ctrl+alt+Down
up4 = ctrl+alt+shift+Down
down3 = ctrl+alt+Up
down4 = ctrl+alt+shift+Up
threshold = 0.0
```
Edit configurations by running:
```
gedit ~/.config/comfortable-swipe.conf
```
After making changes, make sure to restart the program: After making changes, make sure to restart the program:
@ -84,6 +135,28 @@ After making changes, make sure to restart the program:
comfortable-swipe restart comfortable-swipe restart
``` ```
> **Note**: For v1.1.0 below, the configuration file is located at
> `/usr/local/share/comfortable-swipe/comfortable-swipe.conf`
> **Note**: You can locate your configuration by running `comfortable-swipe config`
## Configuration Reference
| Key | Value | Examples |
| --------- | :------------------------------------------: | -------------------- |
| left3 | 3-finger swipe left | ctrl+alt+Right |
| left4 | 4-finger swipe left | ctrl+alt+shift+Right |
| right3 | 3-finger swipe right | ctrl+alt+Left |
| right4 | 4-finger swipe right | ctrl+alt+shift+Left |
| up3 | 3-finger swipe up | ctrl+alt+Down |
| up4 | 4-finger swipe up | ctrl+alt+shift+Down |
| down3 | 3-finger swipe down | ctrl+alt+Up |
| down4 | 4-finger swipe down | ctrl+alt+shift+Up |
| threshold | mouse movement pixels that trigger a swipe (can be as large as 1000.0) | 0.0, 240.0, 1000.0 |
| mouse3 | holds a mouse button when 3 fingers are down | button1 (see [Mouse Gestures](#mouse-gestures-experimental)) | |
| mouse4 | holds a mouse button when 4 fingers are down | button1 (see [Mouse Gestures](#mouse-gestures-experimental) |
Taken from `man xdotool`: Taken from `man xdotool`:
> Type a given keystroke. Examples being "alt+r", "Control_L+J", > Type a given keystroke. Examples being "alt+r", "Control_L+J",
@ -100,42 +173,52 @@ Taken from `man xdotool`:
Refer to https://www.linux.org/threads/xdotool-keyboard.10528/ for a complete list of keycodes you can use. Refer to https://www.linux.org/threads/xdotool-keyboard.10528/ for a complete list of keycodes you can use.
**Keyboard Shortcuts**: ## Keyboard shortcuts
- Unity: https://cheatography.com/sapemeg/cheat-sheets/ubuntu-unity-16-04/ - [Unity Keyboard Shortcuts](https://cheatography.com/sapemeg/cheat-sheets/ubuntu-unity-16-04/)
- GNOME: https://wiki.gnome.org/Design/OS/KeyboardShortcuts - [GNOME Keyboard Shortcuts](https://wiki.gnome.org/Design/OS/KeyboardShortcuts)
- KDE: https://community.linuxmint.com/tutorial/view/47 - [KDE Keyboard Shortcuts](https://community.linuxmint.com/tutorial/view/47)
## Swipe Gesture Templates > **Note**: You can check which desktop you are using with `echo $DESKTOP_SESSION`.
1. Switching workspaces ## Example Configurations
This section includes some example configurations for `~/.config/comfortable-swipe.conf` which you can use for your swipe experience.
1. Switch workspace (horizontal)
```bash ```bash
# Unity, KDE
left3 = ctrl+alt+Right left3 = ctrl+alt+Right
right3 = ctrl+alt+Left right3 = ctrl+alt+Left
```
1. Switch workspace (vertical)
```
up3 = ctrl+alt+Down up3 = ctrl+alt+Down
down3 = ctrl+alt+Up down3 = ctrl+alt+Up
``` ```
```bash ```bash
# GNOME
up3 = super+PgDown up3 = super+PgDown
down3 = super+PgUp down3 = super+PgUp
``` ```
1. Move window to workspace 1. Move window to workspace (horizontal)
```bash ```bash
# Unity, KDE
left4 = ctrl+alt+shift+Right left4 = ctrl+alt+shift+Right
right4 = ctrl+alt+shift+Left right4 = ctrl+alt+shift+Left
```
1. Move window to workspace (vertical)
```bash
up4 = ctrl+alt+shift+Down up4 = ctrl+alt+shift+Down
down4 = ctrl+alt+shift+Up down4 = ctrl+alt+shift+Up
``` ```
```bash ```bash
# GNOME
up4 = super+shift+PgDown up4 = super+shift+PgDown
down4 = super+shift+PgUp down4 = super+shift+PgUp
``` ```
@ -143,7 +226,6 @@ Refer to https://www.linux.org/threads/xdotool-keyboard.10528/ for a complete li
1. Move window to other monitor 1. Move window to other monitor
```bash ```bash
# GNOME
left4 = super+shift+Right left4 = super+shift+Right
right4 = super+shift+Left right4 = super+shift+Left
``` ```
@ -154,20 +236,17 @@ Refer to https://www.linux.org/threads/xdotool-keyboard.10528/ for a complete li
up3 = super+s up3 = super+s
``` ```
1. Show desktop ([setup manually](https://www.itsupportguides.com/knowledge-base/ubuntu/ubuntu-how-to-make-windows-d-show-desktop/)) 1. Show desktop
```bash ```bash
# Ubuntu
down3 = ctrl+super+d
```
```bash
# GNOME
down3 = super+d down3 = super+d
``` ```
```bash ```bash
# KDE down3 = ctrl+super+d
```
```bash
down3 = ctrl+alt+d down3 = ctrl+alt+d
``` ```
@ -199,22 +278,26 @@ Refer to https://www.linux.org/threads/xdotool-keyboard.10528/ for a complete li
## Mouse Gestures (Experimental) ## Mouse Gestures (Experimental)
You can also play around with mouse gestures during swipe.
This enables certain mouse behaviour to trigger along with a 3/4-finger swipe.
Keys:
* mouse3 - for 3-finger mouse gestures
* mouse4 - for 4-finger mosue gestures
* hold3 (deprecated) - old equivalent of mouse3
* hold4 (deprecated) - old equivalent of mouse4
We have included simple mouse gestures on swipe by setting `hold3` and `hold4`. We have included simple mouse gestures on swipe by setting `hold3` and `hold4`.
```bash Possible Values:
Possible Values (hold3, hold4): * button1 - hold left click on finger swipe
* button2 - hold middle click on finger swipe
move # just move the mouse cursor (no mousedown) * button3 - hold right click on finger swipe
button1 # hold left click on finger swipe * button4 - wheel up on finger swipe (experimental)
button2 # hold middle click on finger swipe * button5 - wheel down on finger swipe (experimental)
button3 # hold right click on finger swipe * move - just move the mouse cursor with the fingers
button4 # wheel up on finger swipe (experimental) * scroll - 3/4 finger natural scroll (no acceleration, very experimental)
button5 # wheel down on finger swipe (experimental) * scroll_reverse - 3/4 finger reverse scroll (no acceleration, very experimental)
scroll # naive 3/4 finger natural scroll (no acceleration, very experimental)
scroll_reverse # naive 3/4 finger reverse scroll (no acceleration, very experimental)
```
Any value not mentioned above disables the mouse-hold.
Examples: Examples:

View File

@ -28,7 +28,7 @@ Commands:
restart restart
restarts 3/4-finger gesture service restarts 3/4-finger gesture service
autostart [on|off|toggle|status|path] autostart [<on|off|toggle|status|path>]
toggle to automatically run on startup automatically run on startup (toggleable) toggle to automatically run on startup automatically run on startup (toggleable)
buffer buffer
@ -37,7 +37,7 @@ Commands:
help help
shows the help dialog shows the help dialog
config config [<get|set|delete|list|keys|path>...]
shows the location of the config file shows the location of the config file
debug debug
@ -60,41 +60,20 @@ function start {
# stop running comfortable-swipe commands (except self) # stop running comfortable-swipe commands (except self)
function stop { function stop {
local pids="$(pgrep -f comfortable-swipe | fgrep -v $$)" function stop_name {
local stopped="" pgrep -f "${1:?}" | fgrep -v $$ | xargs kill --
for pid in "$pids"; do }
# stop process tree stop_name "$BASENAME"
if kill -- -$pid 2> /dev/null; then stop_name "$(which comfortable-swipe)"
stopped=" $(echo $pid)" stop_name "$(which comfortable-swipe-buffer)"
fi
done
if [[ ! -z "$stopped" ]]; then
echo "comfortable-swipe stopped"
else
echo "comfortable-swipe is not running"
fi
} }
# restart comfortable swipe # restart comfortable swipe
function restart { function restart {
stop > /dev/null 2>&1 stop > /dev/null 2>&1
start # restart the server in the background
} nohup "$BASENAME" start 0<&- &>/dev/null &
# parse input from a buffer
# internally calls comfortable-swipe-main.cpp, which is
# installed as: comfortable-swipe-buffer
function buffer {
exec comfortable-swipe-buffer "$@"
}
# get location of configuration file
function config {
# TODO: invoke subcommands
echo "$HOME/.config/comfortable-swipe.conf"
} }
@ -110,9 +89,204 @@ function debug {
} }
# parse input from a buffer
# internally calls comfortable-swipe-main.cpp, which is
# installed as: /usr/local/bin/ comfortable-swipe-buffer
function buffer {
exec comfortable-swipe-buffer "$@"
}
# get location of configuration file
function config {
local CONFIG="$HOME/.config/comfortable-swipe.conf"
local KEYS="left3|left4|right3|right4|up3|up4|down3|down4|threshold|mouse3|mouse4"
local DEPRECATED="hold3|hold4"
local USAGE="Usage: $BASENAME config [get|set|delete|list|keys|path]..."
# show path to config
function path {
echo "$CONFIG"
}
# get all keys from config, without comments
# and show only last among duplicates
function list {
# dispatch subcommands
if [[ $# -eq 0 ]]; then
# no options; just show path
tac "$CONFIG" |\
sed -E "s/[#;].*//g" |\
egrep '\s+' |\
tr -d ' ' |\
egrep "^($KEYS|$DEPRECATED)=" |\
awk '!a[$1]++' |\
tac |\
sed 's/=/ = /g'
else
# filter list to match arguments as pattern
local pattern="$1"
for arg in "${@:2}"; do
pattern="$pattern|$arg"
done
list | egrep "($pattern)[^=]* ="
fi
}
# get a list of all valid keys, one per line
function keys {
echo "$KEYS" | sed 's/|/\n/g'
}
# Get a specific key in the config
# Usage: comfortable-swipe config get <key>
function get {
# helper function to print usage
function usage {
echo "Usage: $BASENAME config get <KEY>"
echo -n "Keys: "
echo "$KEYS" | sed 's/|/, /g'
}
# no next argument: show list
if [[ $# -eq 0 ]]; then
list
exit $?
fi
local KEY="$1"
# check if key is valid
if ! [[ "$KEY" =~ ^($KEYS|$DEPRECATED)$ ]]; then
echo "'$KEY' is an invalid key" >&2
echo >&2
usage >&2
echo >&2
echo "If you want to filter keys, you can instead try:" >&2
echo >&2
echo " $BASENAME config list $@"
echo >&2
exit 1
fi
# get key from config file
list | fgrep -m1 "$KEY =" | sed -E "s/^$KEY = //"
}
# Deletes a key in the config
function delete {
# helper function to print usage
function usage {
echo "Usage: $BASENAME config delete [<KEYS>...]"
}
# no next argument: show help
if [[ $# -eq 0 ]]; then
echo "Key is required!" >&2
# no key; show usage
usage >&2
exit 1
fi
local DELETE="$(list "$@")"
# check if there is something to delete
if [[ -z "$DELETE" ]]; then
echo "No config entry to delete" >&2
exit 1
else
echo "Deleted:"
echo "$DELETE"
local RESULT="$(egrep -v "^\\s*($(echo "$DELETE" | awk '{print $1}' | paste -s -d '|'))\\s*=" "$CONFIG")"
echo "$RESULT" > "$CONFIG"
# restart comfortable-swipe
restart
fi
}
# Set a specific key in the config
# Usage: comfortable-swipe config get <key>
function set {
# helper function to print usage
function usage {
echo "Usage: $BASENAME config set <KEY> [=] <VALUE>"
echo -n "Valid keys: "
echo "$KEYS" | sed 's/|/, /g'
}
# no next argument: show help
if [[ $# -eq 0 ]]; then
echo "Key is required!" >&2
echo >&2
# no key; show usage
usage >&2
exit 1
fi
# parse key and value option
local KEYVALUE="${@:1}"
local KEY=
local VALUE=
case "$KEYVALUE" in
*=*)
# key has equal sign, we split it
KEY="${KEYVALUE%%=*}"
VALUE="${KEYVALUE#*=}"
;;
*)
# default: just get from next arguments combined
if [[ $# -eq 1 ]]; then
echo "Value is required!" >&2
usage >&2
echo >&2
echo "If you want to set value to blank, explicitly pass a blank string:" >&2
echo >&2
echo " $BASENAME config set $@ \"\"" >&2
echo >&2
echo "Or delete explicitly:" >&2
echo >&2
echo " $BASENAME config delete $1"
echo >&2
exit 1
fi
KEY="$1"
VALUE="${@:2}"
;;
esac
# trim leading and trailing spaces from key and value
KEY="$(echo "$KEY" | awk '{$1=$1};1')"
VALUE="$(echo "$VALUE" | awk '{$1=$1};1')"
# check if key is valid
if ! [[ "$KEY" =~ ^($KEYS|$DEPRECATED)$ ]]; then
echo "'$KEY' is an invalid key" >&2
echo >&2
usage
exit 1
fi
# if the key is present in the config, perform a replace
# replace the last value from the config with the given key
if list | grep -P "^\Q$KEY =" > /dev/null 2>&1; then
# apply sed to keep our formatting
# \\1 - keep any kind of indentation
# \\2 - keep any comments after the value (with the whitespace instact)
local RESULT="$(tac "$CONFIG" |\
sed -E "s/^(\\s*)$KEY\\s*=\\s*(\?!(\\s*[#;]))*(.*)\$/\\1$KEY = $VALUE\\2/1" |\
tac)"
# make sure we separate piping from outputing
echo "$RESULT" > "$CONFIG"
else
# otherwise, key is not present so we simply append
echo "$KEY = $VALUE" >> "$CONFIG"
fi
# show newly set value
echo "$KEY = $(get "$KEY")"
restart
}
# dispatch subcommands
if [[ $# -eq 0 ]]; then
# no options; just show path
path
elif declare -f "$1" >/dev/null 2>&1; then
# invoke subcommand function, passing arguments through
# TODO: unset all nonlocal functions
"$@" # same as "$1" "$2" "$3" ... for full argument list
else
echo "Error: function $1 not recognized" >&2
echo "$USAGE" >&2
exit 1
fi
}
# enable or disable autostart # enable or disable autostart
# you can also set manually by running: gnome-session-properties # you can also set manually by running: gnome-session-properties
function autostart { function autostart {
# path to autostart files
local AUTOSTART="$HOME/.config/autostart/comfortable-swipe.desktop" local AUTOSTART="$HOME/.config/autostart/comfortable-swipe.desktop"
local ENABLED="X-GNOME-Autostart-enabled" local ENABLED="X-GNOME-Autostart-enabled"
# show autostart file path # show autostart file path
@ -145,15 +319,17 @@ function autostart {
function toggle { function toggle {
[[ $(status) = ON ]] && off || on [[ $(status) = ON ]] && off || on
} }
# dispatch subcommands
if [[ $# -eq 0 ]]; then if [[ $# -eq 0 ]]; then
# default behavior is to toggle # default behavior is to toggle
toggle toggle
elif declare -f "$1" >/dev/null 2>&1; then elif declare -f "$1" >/dev/null 2>&1; then
# invoke subcommand function, passing arguments through # invoke subcommand function, passing arguments through
# TODO: unset all nonlocal functions
"$@" # same as "$1" "$2" "$3" ... for full argument list "$@" # same as "$1" "$2" "$3" ... for full argument list
else else
echo "Function $1 not recognized" >&2 echo "Function $1 not recognized" >&2
echo "Usage: comfortable-swipe autostart [on|off|toggle|status|path]" >&2 echo "Usage: $BASENAME autostart [on|off|toggle|status|path]" >&2
exit 1 exit 1
fi fi
} }
@ -162,8 +338,8 @@ function autostart {
# verbosely show comfortable-swipe status # verbosely show comfortable-swipe status
function status { function status {
# TODO: show configuration status as well # TODO: show configuration status as well
echo "autostart is $(autostart status)" echo "Autostart is $("$BASENAME" autostart status)"
if pgrep -f comfortable-swipe | fgrep -v $$ > /dev/null 2>&1; then if pgrep -f "$BASENAME" | fgrep -v $$ > /dev/null 2>&1; then
echo "comfortable-swipe program is RUNNING" echo "comfortable-swipe program is RUNNING"
else else
echo "comfortable-swipe program is STOPPED" echo "comfortable-swipe program is STOPPED"
@ -193,18 +369,16 @@ case $i in
esac esac
done done
############ # dispatch subcommands
# DISPATCH #
############
if [[ $# -eq 0 ]]; then if [[ $# -eq 0 ]]; then
# no options; just show help # no options; just show help
help help
elif declare -f "$1" >/dev/null 2>&1; then elif declare -f "$1" >/dev/null 2>&1; then
# invoke subcommand function, passing arguments through # invoke subcommand function, passing arguments through
# TODO: unset all nonlocal functions
"$@" # same as "$1" "$2" "$3" ... for full argument list "$@" # same as "$1" "$2" "$3" ... for full argument list
else else
echo "error: function $1 not recognized" >&2 echo "Error: function $1 not recognized" >&2
echo "full usage: comfortable-swipe --help" >&2 echo "Usage: $BASENAME [--help|--version] [start|stop|restart|config|autostart|buffer|debug|status] [<args>]" >&2
exit 1 exit 1
fi fi

View File

@ -1,6 +1,6 @@
#ifndef __comfortable_swipe_defines__ #ifndef __comfortable_swipe_defines__
#define __comfortable_swipe_defines__ #define __comfortable_swipe_defines__
/** All global #define statements go here */ // all global #define statements go here
/* /*
Comfortable Swipe Comfortable Swipe
by Rico Tiongson by Rico Tiongson

View File

@ -54,12 +54,20 @@ public:
virtual void end() override; virtual void end() override;
// override this when keyboard gesture is to be performed // override this when keyboard gesture is to be performed
virtual void do_keyboard_gesture(int mask); virtual void do_keyboard_gesture(int mask);
// public accessors
virtual int get_previous_gesture() const {
return this->previous_gesture;
}
virtual int get_current_mask() const {
return this->current_mask;
}
protected: protected:
// stores square of threshold so we can compute faster // stores square of threshold so we can compute faster
float threshold_squared; float threshold_squared;
// stores previous gesture so we don't repeat action // stores previous gesture so we don't repeat action
int previous_gesture; int previous_gesture;
int current_mask;
public: public:
// static enums we will use for gesture matching // static enums we will use for gesture matching
@ -104,8 +112,12 @@ decltype(
*/ */
gesture_swipe_xdokey::gesture_swipe_xdokey( gesture_swipe_xdokey::gesture_swipe_xdokey(
const decltype(gesture_swipe_xdokey::commands) &commands, float threshold) const decltype(gesture_swipe_xdokey::commands) &commands, float threshold)
: gesture_swipe(), commands(commands), : gesture_swipe(),
threshold_squared(threshold * threshold) {} commands(commands),
threshold_squared(threshold * threshold),
previous_gesture(gesture_swipe_xdokey::FRESH),
current_mask(gesture_swipe_xdokey::FRESH)
{}
/** /**
* Destructs this keyboard swipe gesture. * Destructs this keyboard swipe gesture.
*/ */
@ -116,8 +128,9 @@ gesture_swipe_xdokey::~gesture_swipe_xdokey() {}
void gesture_swipe_xdokey::begin() { void gesture_swipe_xdokey::begin() {
// call superclass method // call superclass method
gesture_swipe::begin(); gesture_swipe::begin();
// assign previous gesture to FRESH // assign gesture to FRESH
this->previous_gesture = gesture_swipe_xdokey::FRESH; current_mask = FRESH;
previous_gesture = FRESH;
} }
/** /**
* Hook on update of swipe gesture. * Hook on update of swipe gesture.
@ -125,60 +138,59 @@ void gesture_swipe_xdokey::begin() {
void gesture_swipe_xdokey::update() { void gesture_swipe_xdokey::update() {
// call superclass method // call superclass method
gesture_swipe::update(); gesture_swipe::update();
// scale threshold to 1/10 when gesture is not fresh // scale threshold to 1/10 when gesture is fresh
float scale = 1; // acts like our static friction coefficient
if (this->previous_gesture == gesture_swipe_xdokey::FRESH) float scale = get_previous_gesture() == FRESH ? 0.01f : 1.0f;
scale = 0.01f; // square root of 1/10
// we are working with floating points which are not exact // we are working with floating points which are not exact
// make sure we compare with a very small value (epsilon) // make sure we compare with a very small value (1e-6f)
static const float EPSILON = 1e-6f;
const float distance_squared = this->x * this->x + this->y * this->y;
const float beyond_threshold = this->threshold_squared * scale;
// if distance goes out of threshold, perform our swipe // if distance goes out of threshold, perform our swipe
if (distance_squared > beyond_threshold + EPSILON) { if (x * x + y * y > threshold_squared * scale + 1e-6f) {
// we parse our mask based on the values obtained from the regex // we parse our mask based on the values obtained from the regex
int mask = 0; int mask = 0;
if (this->fingers == 3) if (fingers == 3)
mask |= gesture_swipe_xdokey::MSK_THREE_FINGERS; mask |= MSK_THREE_FINGERS;
else if (this->fingers == 4) else if (fingers == 4)
mask |= gesture_swipe_xdokey::MSK_FOUR_FINGERS; mask |= MSK_FOUR_FINGERS;
const float absx = x >= 0 ? x : -x; const float absx = x >= 0 ? x : -x;
const float absy = y >= 0 ? y : -y; const float absy = y >= 0 ? y : -y;
if (absx > absy) { if (absx > absy) {
// gesture is horizontal // gesture is horizontal
mask |= gesture_swipe_xdokey::MSK_HORIZONTAL; mask |= MSK_HORIZONTAL;
if (x < 0) if (x < 0)
mask |= gesture_swipe_xdokey::MSK_NEGATIVE; mask |= MSK_NEGATIVE;
else else
mask |= gesture_swipe_xdokey::MSK_POSITIVE; mask |= MSK_POSITIVE;
} else /* std::abs(x) <= std::abs(y) */ } else /* std::abs(x) <= std::abs(y) */
{ {
// gesture is vertical // gesture is vertical
mask |= gesture_swipe_xdokey::MSK_VERTICAL; mask |= MSK_VERTICAL;
if (y < 0) if (y < 0)
mask |= gesture_swipe_xdokey::MSK_NEGATIVE; mask |= MSK_NEGATIVE;
else else
mask |= gesture_swipe_xdokey::MSK_POSITIVE; mask |= MSK_POSITIVE;
} }
// update our mask
current_mask = mask;
// send command on fresh OR opposite gesture // send command on fresh OR opposite gesture
if (this->previous_gesture == gesture_swipe_xdokey::FRESH || if (previous_gesture == FRESH || previous_gesture == (mask ^ MSK_POSITIVE)) {
this->previous_gesture == (mask ^ gesture_swipe_xdokey::MSK_POSITIVE)) { // do keyboard gesture
// finally, perform keyboard gesture do_keyboard_gesture(mask);
this->do_keyboard_gesture(mask); // reset our location variables
x = y = 0;
previous_gesture = mask;
} }
} }
else { // not in threshold, set mask to fresh
current_mask = FRESH;
}
} }
/** /**
* Perform our maske command to xdo. * Perform our maske command to xdo.
*/ */
void gesture_swipe_xdokey::do_keyboard_gesture(int mask) { void gesture_swipe_xdokey::do_keyboard_gesture(int mask) {
// perform our keyboard command with xdo_send_keysequence // perform our keyboard command with xdo_send_keysequence
xdo_send_keysequence_window(this->xdo, CURRENTWINDOW, commands[mask].data(), xdo_send_keysequence_window(xdo, CURRENTWINDOW, commands[mask].data(), 0);
0);
std::cout << "SWIPE " << command_name[mask] << std::endl; std::cout << "SWIPE " << command_name[mask] << std::endl;
// reset our location variables
this->x = this->y = 0;
this->previous_gesture = mask;
} }
/** /**
* Hook on end of swipe gesture. * Hook on end of swipe gesture.

View File

@ -67,8 +67,8 @@ public:
// the button number being clicked // the button number being clicked
int button; int button;
// constructor // constructor
gesture_swipe_xdomouse(const char *hold3, // 3 finger mouse down gesture_swipe_xdomouse(const char *mouse3, // 3 finger mouse gesture
const char *hold4 // 4 finger mouse down const char *mouse4 // 4 finger mouse gesture
); );
// destructor // destructor
virtual ~gesture_swipe_xdomouse(); virtual ~gesture_swipe_xdomouse();
@ -77,39 +77,27 @@ public:
virtual void update() override; virtual void update() override;
virtual void end() override; virtual void end() override;
// provide our own mouse dispatch functions // provide our own mouse dispatch functions
virtual void do_mousedown(const char *); virtual void do_mousedown(int button, int fingers);
virtual void do_mouseup(const char *); virtual void do_mouseup(int button, int fingers);
// utility method to check if mouse is being held
virtual bool is_holding() const;
// utility method to parse mouse input given config characters // utility method to parse mouse input given config characters
static int parse_mouse_button(const char *); static int parse_mouse_button(const char *);
protected: protected:
// command holders // command holders
const char *hold3; const char *mouse3;
const char *hold4; const char *mouse4;
private:
// flag we can use to check if mouse is down
bool flag_is_holding;
}; };
/** /**
* Constructs a new mouse gesture, given "hold3" and "hold4" configurations. * Constructs a new mouse gesture, given "mouse3" and "mouse4" configurations.
*/ */
gesture_swipe_xdomouse::gesture_swipe_xdomouse(const char *hold3, gesture_swipe_xdomouse::gesture_swipe_xdomouse(const char *mouse3,
const char *hold4) const char *mouse4)
: gesture_swipe(), button(MOUSE_NONE), hold3(hold3), hold4(hold4), : gesture_swipe(), button(MOUSE_NONE), mouse3(mouse3), mouse4(mouse4) {}
flag_is_holding(false) {}
/** /**
* Destructs this mouse swipe gesture. * Destructs this mouse swipe gesture.
*/ */
gesture_swipe_xdomouse::~gesture_swipe_xdomouse() {} gesture_swipe_xdomouse::~gesture_swipe_xdomouse() {}
/**
* Determines if some mousehold command is being run.
*/
bool gesture_swipe_xdomouse::is_holding() const {
return this->flag_is_holding;
}
/** /**
* Utility method to parse mouse number from input. * Utility method to parse mouse number from input.
* Returns MOUSE_NONE on failure. * Returns MOUSE_NONE on failure.
@ -132,32 +120,21 @@ int gesture_swipe_xdomouse::parse_mouse_button(const char *input) {
return MOUSE_NONE; return MOUSE_NONE;
} }
/** /**
* Perform mousedown command on hold input. * Run mousedown command on a mouse button input.
*/ */
void gesture_swipe_xdomouse::do_mousedown(const char *input) { void gesture_swipe_xdomouse::do_mousedown(int button, int fingers) {
const int button = this->button = this->parse_mouse_button(input);
if (button != MOUSE_NONE) {
// eg. MOUSE DOWN hold3 mouse1
std::printf("MOUSE DOWN hold%d %s\n", this->fingers, input);
if (MOUSE_LEFT_CLICK <= button && button <= MOUSE_RIGHT_CLICK) { if (MOUSE_LEFT_CLICK <= button && button <= MOUSE_RIGHT_CLICK) {
// send mouse down on associated button // send mouse down on associated button
xdo_mouse_down(this->xdo, CURRENTWINDOW, button); xdo_mouse_down(xdo, CURRENTWINDOW, button);
}
this->flag_is_holding = true;
} }
} }
/** /**
* Run mouseup command on hold output. * Run mouseup command on a mouse button output.
*/ */
void gesture_swipe_xdomouse::do_mouseup(const char *input) { void gesture_swipe_xdomouse::do_mouseup(int button, int fingers) {
const int button = this->button = this->parse_mouse_button(input);
if (button != MOUSE_NONE) {
std::printf("MOUSE UP hold%d %s\n", this->fingers, input);
if (MOUSE_LEFT_CLICK <= button && button <= MOUSE_RIGHT_CLICK) { if (MOUSE_LEFT_CLICK <= button && button <= MOUSE_RIGHT_CLICK) {
// send mouse up on associated button // send mouse up on associated button
xdo_mouse_up(this->xdo, CURRENTWINDOW, button); xdo_mouse_up(xdo, CURRENTWINDOW, button);
}
this->flag_is_holding = false;
} }
} }
/** /**
@ -166,11 +143,16 @@ void gesture_swipe_xdomouse::do_mouseup(const char *input) {
void gesture_swipe_xdomouse::begin() { void gesture_swipe_xdomouse::begin() {
// call superclass method // call superclass method
gesture_swipe::begin(); gesture_swipe::begin();
// dispatch mouse down event // map fingers to gesture command
if (this->fingers == 3) { auto command = fingers == 3 ? mouse3 : (fingers == 4 ? mouse4 : NULL);
this->do_mousedown(this->hold3); if (command != NULL) {
} else if (this->fingers == 4) { // get button int from the command
this->do_mousedown(this->hold4); button = parse_mouse_button(command);
// perform mousedown on the button and print to console
if (button != MOUSE_NONE) {
do_mousedown(button, fingers);
std::printf("MOUSE DOWN mouse%d %s\n", this->fingers, command);
}
} }
} }
/** /**
@ -179,26 +161,25 @@ void gesture_swipe_xdomouse::begin() {
void gesture_swipe_xdomouse::update() { void gesture_swipe_xdomouse::update() {
// call superclass method // call superclass method
gesture_swipe::update(); gesture_swipe::update();
if (this->is_holding()) { if (button != MOUSE_NONE) {
// if MOUSE_MOVE or MOUSE_CLICK* // if MOUSE_MOVE or MOUSE_CLICK*
if (0 <= this->button && this->button <= 3) { if (0 <= button && button <= 3) {
// drag mouse with pointer during update // drag mouse with pointer during update
xdo_move_mouse_relative(this->xdo, this->udx, this->udy); xdo_move_mouse_relative(xdo, udx, udy);
} else if (this->button == MOUSE_SCROLL || } else if (button == MOUSE_SCROLL ||
this->button == MOUSE_SCROLL_REVERSE) { button == MOUSE_SCROLL_REVERSE) {
// perform naive scroll depending on vertical direction // perform naive scroll depending on vertical direction
int wheel = MOUSE_WHEEL_DOWN; int wheel = MOUSE_WHEEL_DOWN;
if ((this->udy > 0) == (this->button == MOUSE_SCROLL)) if ((udy > 0) == (button == MOUSE_SCROLL))
wheel = MOUSE_WHEEL_UP; wheel = MOUSE_WHEEL_UP;
// click wheel on update (note: this is not precise) // click wheel on update (note: this is not precise)
xdo_mouse_down(this->xdo, CURRENTWINDOW, wheel); xdo_mouse_down(xdo, CURRENTWINDOW, wheel);
xdo_mouse_up(this->xdo, CURRENTWINDOW, wheel); xdo_mouse_up(xdo, CURRENTWINDOW, wheel);
} else if (this->button == MOUSE_WHEEL_UP || } else if (button == MOUSE_WHEEL_UP ||
this->button == MOUSE_WHEEL_DOWN) { button == MOUSE_WHEEL_DOWN) {
// click wheel button on 4 or 5 // click wheel button on 4 or 5
xdo_mouse_down(this->xdo, CURRENTWINDOW, this->button); xdo_mouse_down(xdo, CURRENTWINDOW, button);
xdo_mouse_up(this->xdo, CURRENTWINDOW, this->button); xdo_mouse_up(xdo, CURRENTWINDOW, button);
} }
} }
} }
@ -207,13 +188,14 @@ void gesture_swipe_xdomouse::update() {
*/ */
void gesture_swipe_xdomouse::end() { void gesture_swipe_xdomouse::end() {
// optimization: only perform mouseup when flag is set // optimization: only perform mouseup when flag is set
if (this->is_holding()) { if (button != MOUSE_NONE) {
if (this->fingers == 3) { // map fingers to gesture command
this->do_mouseup(this->hold3); // perform mouseup on the button and print to console
} else if (this->fingers == 4) { do_mouseup(button, fingers);
this->do_mouseup(this->hold4); std::printf("MOUSE UP mouse%d %s\n", fingers, fingers == 3 ? mouse3 : mouse4);
}
} }
// reset button
button = MOUSE_NONE;
// call superclass method // call superclass method
gesture_swipe::end(); gesture_swipe::end();
} }

View File

@ -3,7 +3,7 @@
/** /**
* File: comfortable-swipe-gesture-swipe.cpp * File: comfortable-swipe-gesture-swipe.cpp
* *
* The definition of the class comfortable_swipe::gesture_swipe. * This is the base class of all comfortable swipe gestures.
* *
* The class `comfortable_swipe::gesture_swipe` handles dispatching * The class `comfortable_swipe::gesture_swipe` handles dispatching
* of swipe gestures. The method `gesture_swipe::run(const char*)` * of swipe gestures. The method `gesture_swipe::run(const char*)`
@ -41,13 +41,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <iostream> // std::cout, std::endl #include <iostream> // std::cout, std::endl
#include <regex> // std::regex, std::regex_match, std::cmatch #include <regex> // std::regex, std::regex_match, std::cmatch
#include <string> // std::stoi, std::stof #include <string> // std::stoi, std::stof
extern "C" { extern "C" {
#include <xdo.h> // xdo, xdo_new, xdo_free, #include <xdo.h> // xdo, xdo_new, xdo_free,
// xdo_get_mouse_location // xdo_get_mouse_location
// CURRENT_WINDOW // CURRENT_WINDOW
} }
namespace comfortable_swipe { namespace comfortable_swipe {
/** /**
* Handles swipe gesture input from libinput debug-events. * Handles swipe gesture input from libinput debug-events.
@ -70,6 +68,10 @@ public:
virtual void update(); virtual void update();
virtual void end(); virtual void end();
virtual bool run(const char *); virtual bool run(const char *);
// public check if currently swiping
virtual bool is_swiping() const {
return this->flag_swiping;
}
protected: protected:
// xdo container // xdo container

View File

@ -83,20 +83,29 @@ int main() {
// initialize mouse hold gesture handler // initialize mouse hold gesture handler
// for now, this supports 3-finger and 4-finger hold // for now, this supports 3-finger and 4-finger hold
// Examples: // Examples:
// hold3=move move mouse on 3 fingers // mouse3=move move mouse on 3 fingers
// hold3=button1 hold button 1 on 3 fingers // mouse3=button1 hold button 1 on 3 fingers
// hold4=button3 hold button 3 (right click) on 3 fingers // mouse4=button3 hold button 3 (right click) on 3 fingers
// hold3=ignore <do nothing> // warn user that hold3 is deprecated
gesture_swipe_xdomouse mousehold(config["hold3"].data(), if (config.count("hold3"))
config["hold4"].data()); std::cerr << "WARNING: hold3 is deprecated. Use mouse3 instead." << std:: endl;
if (config.count("hold4"))
std::cerr << "WARNING: hold4 is deprecated. Use mouse4 instead." << std::endl;
// get input values
string mouse3 = config.count("mouse3") ? config["mouse3"] : config["hold3"];
string mouse4 = config.count("mouse4") ? config["mouse4"] : config["hold4"];
// create our mouse gesture holder
gesture_swipe_xdomouse mousehold(mouse3.data(), mouse4.data());
// start reading lines from input one by one // start reading lines from input one by one
for (string line; getline(cin, line);) { for (string line; getline(cin, line);) {
// prioritize mouse hold gesture first before keyboard gesture // optimization: if no mouse config is set, just run keyboard
mousehold.run(line.data()); if (mousehold.is_swiping() && mousehold.button == MOUSE_NONE) {
// if mouse hold fails, try keyboard swipe
// attempt to parse keyboard gestures
if (!mousehold.is_holding()) {
keyswipe.run(line.data()); keyswipe.run(line.data());
} else if (mousehold.run(line.data())) {
// only allow keyswipe gestures on mouse move
if (mousehold.button == MOUSE_MOVE) {
keyswipe.run(line.data());
}
} }
} }
return EXIT_SUCCESS; return EXIT_SUCCESS;

12
compile Executable file
View File

@ -0,0 +1,12 @@
#!/bin/bash
# usage: ./compile *.cpp
set -e
DIR="$(dirname $0)"
VERSION="$(cat "$DIR/VERSION" | tr -d '[:space:]')"
CXX='g++'
CXX_FLAGS='-std=c++14 -O2 -Wall -lxdo -linih -lxdo -linih'
g++ "$@" ${CXX_FLAGS} -DCOMFORTABLE_SWIPE_VERSION="\"$VERSION\""

View File

@ -1,9 +0,0 @@
#!/bin/bash
# main compile script
# usage: ./compile.sh *.cpp
set -e
DIR="$(dirname $0)"
VERSION="$(cat "$DIR/VERSION" | tr -d '[:space:]')"
g++ "$@" -std=c++14 -O2 -Wall -lxdo -linih -DCOMFORTABLE_SWIPE_VERSION="\"$VERSION\""

View File

@ -0,0 +1,42 @@
# Comfortable Swipe
#
# Feel free to edit this configuration file if you have different
# keyboard shortcuts that you would like to use. Comments starting
# with a pound (#) or semi-colon (;) are ignored.
#
# Refer to https://www.linux.org/threads/xdotool-keyboard.10528/ for
# a list of keycodes you can use.
left3 = ctrl+alt+Right
left4 = ctrl+alt+shift+Right
right3 = ctrl+alt+Left
right4 = ctrl+alt+shift+Left
up3 = ctrl+alt+Down
up4 = ctrl+alt+shift+Down
down3 = ctrl+alt+Up
down4 = ctrl+alt+shift+Up
# Tweak threshold depending on the sensitivity of your touchpad.
# Make this higher to lessen sensitivity.
# (note: values have no limit, can be as large as 1000.0)
threshold = 0.0
# Uncomment below to enable mouse gestures (3 fingers)
; mouse3 = button1 # drag left click
; mouse3 = button2 # drag middle click
; mouse3 = button3 # drag right click
; mouse3 = button4 # drag wheel up
; mouse3 = button5 # drag wheel down
; mouse3 = move # just move mouse along with the cursor
; mouse3 = scroll # natural scroll (experimental)
; mouse3 = scroll_reverse # reverse scroll (experimental)
# Uncomment below to enable mouse gestures (4 fingers)
; mouse4 = button1 # drag left click
; mouse4 = button2 # drag middle click
; mouse4 = button3 # drag right click
; mouse4 = button4 # drag wheel up
; mouse4 = button5 # drag wheel down
; mouse4 = move # just move mouse along with the cursor
; mouse4 = scroll # natural scroll (experimental)
; mouse4 = scroll_reverse # reverse scroll (experimental)

View File

@ -1,34 +0,0 @@
# Comfortable Swipe converts touchpad swipe gestures to keyboard commands. You
# may edit this configuration file if you have different keyboard shortcuts
# that you would like to use. You can ignore a gesture by commenting out with
# a pound(#) symbol.
#
# Refer to https://www.linux.org/threads/xdotool-keyboard.10528/ for a list of
# keycodes you can use.
left3 = ctrl+alt+Right
left4 = ctrl+alt+shift+Right
right3 = ctrl+alt+Left
right4 = ctrl+alt+shift+Left
up3 = ctrl+alt+Down
up4 = ctrl+alt+shift+Down
down3 = ctrl+alt+Up
down4 = ctrl+alt+shift+Up
# Tweak this value depending on the sensitivity of your mousepad to perform
# gestures. A higher value means less sensitive.
# Note: Sky is the limit! Can be as large as 1000.0
threshold = 0.0
# Uncomment below for mousedown
; hold3 = button1
; hold4 = button1
# Values: button1 (left click)
# button2 (middle click)
# button3 (right click),
# button4 (wheel up)
# button5 (wheel down),
# move (just move with cursor)
# scroll (natural scrolling)
# scroll_reverse (reversed scrolling)

View File

@ -10,17 +10,17 @@ SOURCE="$DIR/comfortable-swipe"
TARGET="/usr/local/bin/comfortable-swipe" TARGET="/usr/local/bin/comfortable-swipe"
# compile targets # compile targets
COMPILE="$DIR/compile.sh" COMPILE="$DIR/compile"
COMPILE_SOURCE="$DIR/comfortable-swipe-main.cpp" COMPILE_SOURCE="$DIR/comfortable-swipe-main.cpp"
COMPILE_TARGET="/usr/local/bin/comfortable-swipe-buffer" COMPILE_TARGET="/usr/local/bin/comfortable-swipe-buffer"
# configurations # configurations
CONF_SOURCE="$DIR/defaults.conf" CONF_SOURCE="$DIR/config/comfortable-swipe.conf"
CONF_TARGET="$HOME/.config/comfortable-swipe.conf" CONF_TARGET="$HOME/.config/comfortable-swipe.conf"
OLD_CONF_TARGET="/usr/local/share/comfortable-swipe/comfortable-swipe.conf" OLD_CONF_TARGET="/usr/local/share/comfortable-swipe/comfortable-swipe.conf"
# autostart # autostart
AUTOSTART_SOURCE="$DIR/comfortable-swipe.desktop" AUTOSTART_SOURCE="$DIR/config/comfortable-swipe.desktop"
AUTOSTART_TARGET="$HOME/.config/autostart/comfortable-swipe.desktop" AUTOSTART_TARGET="$HOME/.config/autostart/comfortable-swipe.desktop"
# stop any running comfortable-swipe if it exists # stop any running comfortable-swipe if it exists

View File

@ -3,7 +3,7 @@
set -e set -e
DIR="$(dirname "$0")" DIR="$(dirname "$0")"
COMPILER="$(dirname "$DIR")/compile.sh" COMPILER="$(dirname "$DIR")/compile"
# just call abort on error # just call abort on error
TEMPOUT="$(mktemp)" TEMPOUT="$(mktemp)"

View File

@ -4,7 +4,7 @@ set -ex
DIR="$(dirname "$0")" DIR="$(dirname "$0")"
ROOT="$(dirname "$DIR")" ROOT="$(dirname "$DIR")"
COMPILER="$ROOT/compile.sh" COMPILER="$ROOT/compile"
# just call abort on error # just call abort on error
TEMPOUT="$(mktemp)" TEMPOUT="$(mktemp)"