Compare commits

..

3 Commits

Author SHA1 Message Date
Rico Tiongson
044e215d97
Travis (#62)
* Use a requirements.txt

* Add initial travis configuration

* Attempt fix travis configuration

* Attempt fix g++ error

* Bugfix missing path dir on autostart

* Attempt install build-essential instead of g++

* Add missing psutil from requirements

* Modify README

* Attempt fix build errors in travis

* Update README

* Add simple build badges
2019-03-08 19:57:10 +08:00
Rico Tiongson
adcb3d5e02 Add classifiers 2019-03-06 22:05:30 +08:00
Rico Tiongson
8464084f56
Package comfortable-swipe with Python (#58)
* Add initial python setup script

* Add working initial python porting definition for comfortable-swipe

* Add python extensions to cpp libraries

* Remove boost and prefer vanilla Python.h implementation

* Add main driver function for comfortable_swipe

* Fix compilation for Python 3

* Remove depcreacted cpp scripts in root

* Move script to entrypoint

* Increment version to beta

* Update variable formatting

* Remove deprecated install script

* Move install to post-install command

* Create install script hooks

* Fix bdist_wheel class errors

* Use absolute path for c++ sources

* Unignore index files

* Move autostart to python

* Allow comfortable swipe autostart for possible virtualenv

* Completely port autostart to python

* Move status to python

* Optimize swipe function

* Remove iostream optimization from buffer

* Register pre-install and post-install in a stack

* Remove underscores from value-defined macros

* Compress default config

* Change cpp to __init__.py

* Error message

* Bugfix for python2
2019-03-04 12:45:09 +08:00
56 changed files with 1952 additions and 2502 deletions

121
.gitignore vendored
View File

@ -1,7 +1,126 @@
# C++ generated headers
*.gch
*.o
# IDE-specific
.idea
.vscode
# PYTHON
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
!/lib
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

25
.travis.yml Normal file
View File

@ -0,0 +1,25 @@
dist: xenial
language: python
sudo: required
python:
- 2.7
- 3.5
- 3.6
- 3.7
- 3.8-dev
addons:
apt:
update: true
sources:
- ubuntu-toolchain-r-test
packages:
- libxdo-dev
- libinput-tools
- g++-7
env:
CC: gcc-7
CXX: g++-7
install:
- pip install .
script:
- python setup.py test

View File

@ -1,42 +0,0 @@
v1.2.0 (2020-04-20)
* Simplified source files
* Add `config get|set|list|delete` to command line
* Add specific autostart toggles
* Use clang-format and prettier for formatting
* Add `--bare` and `--attach` flags to buffer
* Remove `restart` and integrated single-instance to `start`
* Provide separate `comfortable-swipe-buffer` program
* Improve README
v1.1.0 (2020-04-18)
* Add mouse hold gestures
* Add experimental `hold3` configuration
* Add experimental `hold4` configuration
* Add linker tests
* Add `driver.cpp`
* Add `mouse_hold_gestures.cpp`
* Bugfix `comfortable-swipe stop`
* Bugfix `comfortable-swipe restart`
* Rename `index.hpp` to `all_headers.hpp`
* Rename source directory `lib` to `comfortable_swipe`
* Separate `swipe_gesture.cpp` from `keyboard_swipe_gesture.cpp`
* Update compiler scripts
* Update configurations
* Update README
* Bump to version 1.1.0
v1.0.4
* Bugfixes
v1.0.3
* Bugfixes
v1.0.2
* Change directory structure
* Bugfixes
v1.0.1
* Bugfixes
v1.0.0
* Initial version

1
MANIFEST.in Normal file
View File

@ -0,0 +1 @@
include comfortable_swipe/res/comfortable-swipe.desktop

745
README.md
View File

@ -1,260 +1,162 @@
# Comfortable Swipe (Ubuntu)
# Comfortable Swipe (Debian/Ubuntu)
[![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)
[![comfortable-swipe: version](https://img.shields.io/github/tag-date/Hikari9/comfortable-swipe.svg?color=informational&label=comfortable-swipe)](https://github.com/Hikari9/comfortable-swipe/releases)
[![build: master](https://img.shields.io/travis/Hikari9/comfortable-swipe/master.svg?label=master)](https://github.com/Hikari9/comfortable-swipe/commits/master)
[![build: develop](https://img.shields.io/travis/Hikari9/comfortable-swipe/develop.svg?label=develop)](https://github.com/Hikari9/comfortable-swipe/commits/develop)
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-orange.svg)](https://www.gnu.org/licenses/gpl-3.0)
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. Tested in Ubuntu 14.04 LTS and beyond, for Unity, GNOME, and KDE desktop environments.
> **_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
> **Note**: May work for other Linux distros that support `libinput` and `libxdo` (untested).
## Installation
1. Install git and g++ ≥ 7.5
1. Update and install essential tools and libraries
```bash
sudo apt install git g++
```
```bash
sudo apt update && sudo apt install git g++-7 python3-pip libinput-tools libxdo-dev
```
1. Install libinput-tools and C libraries
2. Install `comfortable-swipe` for your user
```bash
sudo apt install libinput-tools libinih-dev libxdo-dev
```
```bash
pip3 install --user git+https://github.com/Hikari9/comfortable-swipe
```
1. Clone this repository
3. You can check status with `comfortable-swipe status`
```bash
git clone https://github.com/Hikari9/comfortable-swipe.git --depth 1
cd comfortable-swipe
```
```bash
$> comfortable-swipe status
usr/local/share/comfortable-swipe/comfortable-swipe.conf
threshold = 0.0
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
autostart is ON
comfortable-swipe program is STOPPED
```
1. Install
3. You can list down all commands with `comfortable-swipe` or `comfortable-swipe help`
```bash
bash install
```
```bash
$> comfortable-swipe
comfortable-swipe [start|stop|restart|autostart|buffer|help|config|debug|status]
1. You may delete the downloaded `comfortable-swipe` folder after installation.
start - starts 3/4-finger gesture service
stop - stops 3/4-finger gesture service
restart - stops then starts 3/4-finger gesture service
autostart - automatically run on startup (toggleable)
buffer - parses output of libinput debug-events
help - shows the help dialog
config - locates the config file
debug - logs raw output from input events taken from libinput
status - checks status of program and autostart
```
## How to Run
1. You'll need some group permissions to read touchpad input data. Run
```bash
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. By default, comfortable-swipe should be running. Otherwise, run:
```bash
comfortable-swipe start
```
You can see if gestures work correctly if you see `SWIPE xxx` in the output:
```bash
sudo gpasswd -a "$USER" "$(ls -l /dev/input/event* | awk '{print $4}' | head --line=1)"
```
```bash
$> comfortable-swipe start
SWIPE left3
SWIPE right3
...
```
1. **_Important_**: After inputing your `sudo` password, log out then log back in
## How to Upgrade
1. Start the Program
```
comfortable-swipe start
```
You will see this output:
```
$ comfortable-swipe start
Comfortable swipe is RUNNING in the background
```
1. (Optional) Toggle autostart
```bash
comfortable-swipe autostart on
```
1. (Optional) Stop the Program
```
comfortable-swipe stop
```
1. (Optional) See program status
```bash
comfortable-swipe status
```
Example:
```
$ comfortable-swipe status
Autostart is ON
Program is RUNNING
--------------------
Configuration: /home/user/.config/comfortable-swipe.conf
left3 is VALID (ctrl+super+Right)
left4 is VALID (ctrl+super+shift+Right)
right3 is VALID (ctrl+super+Left)
right4 is VALID (ctrl+super+shift+Left)
up3 is VALID (ctrl+F12)
up4 is VALID (super+d)
down3 is VALID (ctrl+F12)
down4 is VALID (super+d)
threshold is VALID (1.0)
mouse3 is NOTSET
mouse4 is NOTSET
```
1. (Optional) Get config
```
comfortable-swipe <PROPERTY>
```
```bash
comfortable-swipe left3
comfortable-swipe left4
comfortable-swipe right3
comfortable-swipe right4
comfortable-swipe up3
comfortable-swipe up4
comfortable-swipe down3
comfortable-swipe down4
comfortable-swipe threshold
comfortable-swipe mouse3
comfortable-swipe mouse4
```
1. (Optional) Set config
```bash
comfortable-swipe <PROPERTY> [=] <VALUES>
```
```bash
comfortable-swipe left3 = super+Right
comfortable-swipe right3 = super+Left
comfortable-swipe right4 = ctrl+alt+Left
comfortable-swipe down4 = super+d
comfortable-swipe up3 = ctrl+shift+Up
```
<details>
<summary><b>Other Commands</b></summary>
1. All Configuration commands
```bash
comfortable-swipe config list
comfortable-swipe config get <PROPERTY>
comfortable-swipe config set <PROPERTY> [=] <VALUE>
comfortable-swipe config path
comfortable-swipe config keys
```
1. Help and Version
```bash
comfortable-swipe --version
comfortable-swipe --help
```
1. Autostart commands
```bash
comfortable-swipe autostart on
comfortable-swipe autostart off
comfortable-swipe autostart toggle
comfortable-swipe autostart status
comfortable-swipe autostart path
```
1. Show output with `--attach`
Example output of 3-finger left, 4-finger left, 3-finger right, 3-finger up:
```bash
$ comfortable-swipe start --attach
SWIPE left3
SWIPE left4
SWIPE right3
SWIPE up3
...
```
1. Test output with `--bare` to attach without actually swiping
```bash
$ comfortable-swipe start --bare
SWIPE left3
SWIPE left4
SWIPE right3
SWIPE up3
...
```
1. Debug
```bash
$ comfortable-swipe debug
...
-event9 DEVICE_ADDED TouchPad seat0 default group7 cap:pg size 70x50mm tap(dl off) left scroll-nat scroll-2fg-edge click-buttonareas-clickfinger dwt-on
...
event9 GESTURE_SWIPE_BEGIN +2.03s 3
event9 GESTURE_SWIPE_UPDATE +2.03s 3 -9.95/ 2.64 (-26.90/ 7.12 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.03s 3 -10.44/ 3.19 (-28.22/ 8.62 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.04s 3 -9.71/ 2.64 (-26.25/ 7.12 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.05s 3 -8.98/ 2.64 (-24.28/ 7.12 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.06s 3 -7.40/ 2.36 (-20.01/ 6.37 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.06s 3 -6.31/ 2.50 (-17.06/ 6.75 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.07s 3 -5.34/ 1.80 (-14.44/ 4.87 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.08s 3 -4.61/ 2.08 (-12.47/ 5.62 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.09s 3 -4.49/ 1.53 (-12.14/ 4.12 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.09s 3 -4.01/ 1.25 (-10.83/ 3.37 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.10s 3 -4.13/ 0.42 (-11.15/ 1.12 unaccelerated)
event9 GESTURE_SWIPE_END +2.11s 3
...
```
</details>
## Gesture Configurations
The default configuration file is located at `~/.config/comfortable-swipe.conf`.
Comfortable swipe makes use of **keyboard shortcuts** to perform swipes, through `xdotool`.
Set a property directly with:
```
comfortable-swipe <PROPERTY> [=] <VALUE>
```bash
pip3 install -U --user git+https://github.com/Hikari9/comfortable-swipe
```
Or edit the configuration file manually:
## Uninstall
```
gedit ~/.config/comfortable-swipe.conf
```bash
# Uncomment below to COMPLETELY remove configurations (not recommended)
# rm $(comfortable-swipe config)
pip3 uninstall comfortable-swipe
```
After editing, make sure to restart with `comfortable-swipe start`.
## Other Commands
> **Warning**: For v1.1.0 below, the configuration file is located at
> `/usr/local/share/comfortable-swipe/comfortable-swipe.conf`
1. `comfortable-swipe config` - outputs location of configuration file
> **Note**: You can locate the absolute path to your configuration by running: `comfortable-swipe config path`
```bash
$> comfortable-swipe config
/usr/local/share/comfortable-swipe/comfortable-swipe.conf
```
## Configuration Reference
2. `comfortable-swipe autostart` - Toggles autostart
```bash
$> comfortable-swipe autostart
Autostart switched off
```
| Property | 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 | mouses a mouse button when 3 fingers are down | button1 / move / scroll<br> _(see [Mouse Gestures](#mouse-gestures-experimental))_ | |
| mouse4 | mouses a mouse button when 4 fingers are down | button1 / move / scroll <br> _(see [Mouse Gestures](#mouse-gestures-experimental)_ |
3. `comfortable-swipe debug` - Debugs input (this is an unbuffered alias of `libinput debug-events`)
### Keystrokes
```bash
$> comfortable-swipe debug
...
-event9 DEVICE_ADDED TouchPad seat0 default group7 cap:pg size 70x50mm tap(dl off) left scroll-nat scroll-2fg-edge click-buttonareas-clickfinger dwt-on
...
event9 GESTURE_SWIPE_BEGIN +2.03s 3
event9 GESTURE_SWIPE_UPDATE +2.03s 3 -9.95/ 2.64 (-26.90/ 7.12 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.03s 3 -10.44/ 3.19 (-28.22/ 8.62 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.04s 3 -9.71/ 2.64 (-26.25/ 7.12 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.05s 3 -8.98/ 2.64 (-24.28/ 7.12 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.06s 3 -7.40/ 2.36 (-20.01/ 6.37 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.06s 3 -6.31/ 2.50 (-17.06/ 6.75 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.07s 3 -5.34/ 1.80 (-14.44/ 4.87 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.08s 3 -4.61/ 2.08 (-12.47/ 5.62 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.09s 3 -4.49/ 1.53 (-12.14/ 4.12 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.09s 3 -4.01/ 1.25 (-10.83/ 3.37 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.10s 3 -4.13/ 0.42 (-11.15/ 1.12 unaccelerated)
event9 GESTURE_SWIPE_END +2.11s 3
...
```
If you can see `GESTURE_SWIPE_XXX` in your output, that means your touchpad supports multi-touch swipe gestures.
## Configurations
Comfortable swipe makes use of keyboard shortcuts for configurations. Edit by running
```bash
gedit $(comfortable-swipe config)
```
Make sure to restart after making changes:
```bash
comfortable-swipe restart
```
Property | Description | Default Value | Default Behavior
--------- | ----------- | -------------- | -----
threshold | mouse pixels to activate swipe; higher = less sensitive; floating-point (Note: Sky is the limit! Can be as large as 1000.0) | 0.0
left3 | 3-finger swipe left | ctrl+shift+Right | switch to right workspace
left4 | 4-finger swipe left | ctrl+alt+shift+Right | move window to right workspace
right3 | 3-finger swipe right | ctrl+shift+Left | switch to left workspace
right4 | 4-finger swipe right | ctrl+alt+shift+Left | move window to left workspace
up3 | 3-finger swipe up | ctrl+shift+Down | switch to bottom workspace
up4 | 4-finger swipe up | ctrl+alt+shift+Down | move window to bottom workspace
down3 | 3-finger swipe down | ctrl+shift+Down | switch to above workspace
down4 | 4-finger swipe down | ctrl+alt+shift+Up | move window to above workpace
Taken from `man xdotool`:
@ -272,395 +174,6 @@ Taken from `man xdotool`:
Refer to https://www.linux.org/threads/xdotool-keyboard.10528/ for a complete list of keycodes you can use.
- [DEFKEY - All Linux keyboard shortcuts](https://defkey.com/)
- [Unity Keyboard Shortcuts](https://cheatography.com/sapemeg/cheat-sheets/ubuntu-unity-16-04/)
- [GNOME Keyboard Shortcuts](https://wiki.gnome.org/Design/OS/KeyboardShortcuts)
- [KDE Keyboard Shortcuts](https://community.linuxmint.com/tutorial/view/47)
- [PopOS Keyboard Shortcuts](https://support.system76.com/articles/pop-keyboard-shortcuts/)
## Known Issues: Pop!_OS 20.04+
Pop!_OS 20.04+ may be sensitive to capitalization (#76, #82). Make sure to capitalize every first letter:
```bash
# Pop!_OS
comfortable-swipe up3 = Super+Ctrl+Down
comfortable-swipe down3 = Super+Ctrl+Up
```
## Example Configurations
This section includes some example configurations which you can use for your swipe experience.
1. Switch workspace (horizontal)
```bash
# Ubuntu flavors + GNOME
comfortable-swipe left3 = ctrl+alt+Right
comfortable-swipe right3 = ctrl+alt+Left
```
1. Switch workspace (vertical)
```bash
# Ubuntu flavors + GNOME
comfortable-swipe up3 = ctrl+alt+Down
comfortable-swipe down3 = ctrl+alt+Up
```
```bash
# GNOME alt.
comfortable-swipe up3 = super+PgDown
comfortable-swipe down3 = super+PgUp
```
```bash
# Pop OS
comfortable-swipe up3 = Super+Ctrl+Down
comfortable-swipe down3 = Super+Ctrl+Up
```
1. Move window to workspace (horizontal)
```bash
# Ubuntu flavors + GNOME + Kali
comfortable-swipe left4 = ctrl+alt+shift+Right
comfortable-swipe right4 = ctrl+alt+shift+Left
```
```bash
# Elementary OS
comfortable-swipe left4 = super+alt+Right
comfortable-swipe right4 = super+alt+Left
```
1. Move window to workspace (vertical)
```bash
# Ubuntu flavors + GNOME + Kali
comfortable-swipe up4 = ctrl+alt+shift+Down
comfortable-swipe down4 = ctrl+alt+shift+Up
```
```bash
# GNOME alt.
comfortable-swipe up4 = super+shift+PgDown
comfortable-swipe down4 = super+shift+PgUp
```
1. Move window to other monitor
```bash
# Ubuntu flavors + GNOME
comfortable-swipe left4 = super+shift+Right
comfortable-swipe right4 = super+shift+Left
```
1. Toggle workspace overview
```bash
# Ubuntu flavors + Elementary OS
comfortable-swipe up3 = super+s
```
```bash
# Elementary OS (all workspaces)
comfortable-swipe up4 = super+a
```
1. Show desktop
```bash
# Ubuntu flavors
comfortable-swipe down3 = ctrl+super+d
```
```bash
# Linux Mint
comfortable-swipe down3 = super+d
```
```bash
# Kali
comfortable-swipe down3 = ctrl+alt+d
```
```bash
# KDE
comfortable-swipe down3 = ctrl+F12
```
1. Snap windows to the left/right
```bash
comfortable-swipe left3 = super+Left
comfortable-swipe right3 = super+Right
```
1. Toggle maximize
```bash
comfortable-swipe up3 = super+Up
```
1. Toggle minimize
```bash
comfortable-swipe down3 = super+Down
```
## 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
Possible Values:
- button1 - left click
- button2 - middle click
- button3 - right click
- button4 - wheel up (experimental)
- button5 - wheel down (experimental)
- move - just move the mouse cursor while fingers are down
- scroll - 3/4 finger natural scroll (no acceleration, very experimental)
- scroll_reverse - 3/4 finger reverse scroll (no acceleration, very experimental)
> **Tip**: You can clear mouse gestures by setting them blank
>
> ```
> comfortable-swipe mouse3 =
> comfortable-swipe mouse4 =
> ```
Examples:
✔️ swipes OK
⭕ swipes DISABLED
- 3/4-finger drag ⭕
```bash
comfortable-swipe mouse3 = button1
comfortable-swipe mouse4 = button1
```
You can also use `button2` for middle click and `button3` for right click.
- 3/4-finger natural scroll ⭕
```bash
comfortable-swipe mouse3 = scroll
comfortable-swipe mouse4 = scroll
```
- 3/4-finger reverse scroll ⭕
```bash
comfortable-swipe mouse3 = scroll_reverse
comfortable-swipe mouse4 = scroll_reverse
```
- Move 3/4-fingers with the cursor ✔️
```bash
comfortable-swipe mouse3 = move
comfortable-swipe mouse4 = move
```
> **Warning**: Some mouse configuration will **disable up/left/right/down behavior** to avoid gesture conflicts. The logic of this will be improved in the future.
## Debugging
You can check your touchpad driver by running
```bash
comfortable-swipe debug
```
This is an alias of `libinput debug-events`. This logs all gestures you make on your touchpad, along with other input-based events that can be captured by libinput.
A working swipe gesture will show the following:
```bash
$ comfortable-swipe debug
...
-event9 DEVICE_ADDED TouchPad seat0 default group7 cap:pg size 70x50mm tap(dl off) left scroll-nat scroll-2fg-edge click-buttonareas-clickfinger dwt-on
...
event9 GESTURE_SWIPE_BEGIN +2.03s 3
event9 GESTURE_SWIPE_UPDATE +2.03s 3 -9.95/ 2.64 (-26.90/ 7.12 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.03s 3 -10.44/ 3.19 (-28.22/ 8.62 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.04s 3 -9.71/ 2.64 (-26.25/ 7.12 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.05s 3 -8.98/ 2.64 (-24.28/ 7.12 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.06s 3 -7.40/ 2.36 (-20.01/ 6.37 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.06s 3 -6.31/ 2.50 (-17.06/ 6.75 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.07s 3 -5.34/ 1.80 (-14.44/ 4.87 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.08s 3 -4.61/ 2.08 (-12.47/ 5.62 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.09s 3 -4.49/ 1.53 (-12.14/ 4.12 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.09s 3 -4.01/ 1.25 (-10.83/ 3.37 unaccelerated)
event9 GESTURE_SWIPE_UPDATE +2.10s 3 -4.13/ 0.42 (-11.15/ 1.12 unaccelerated)
event9 GESTURE_SWIPE_END +2.11s 3
...
```
If you can see `GESTURE_SWIPE_XXX` in your output, that means your touchpad supports multi-touch swipe gestures.
## FAQ: Can I run a _shell command_ instead of a keystroke?
**Answer 1**: _Unfortunately **NO**..._
For the following reasons:
1. We want prioritize "comfort" over functionality, which we deliver through performance in our negligible-overhead implementation (that's why C++)
1. There are other gesture libraries that already do this properly (eg. [libinput gestures](https://github.com/bulletmark/libinput-gestures), [Fusuma](https://github.com/iberianpig/fusuma)), we don't want to be a clone of them
1. Running a new shell command with unpredictable process time will break our unthreaded optimizations (unlike native keystrokes)
That's why it's not possible... or not?
**Answer 2**: _... but actually **IT'S POSSIBLE**!_
Although we don't provide this out of the box in our config, this can definitely be
done with the default bash tools.
## Hack: Shell command on swipe
Running shell commands our **NOT** part of the core features of comfortable-swipe,
but through the default bash tools you can _mimic_ this functionality via our program output.
<details>
<summary> <b>Use Case</b>: <i>"I want to run <code>gnome-terminal</code> if I swipe up with 3 fingers."</i>
</summary>
1. Attach the program to the shell:
```bash
comfortable-swipe start --attach
```
Verify it outputs when you swipe left, left, up, right, up with 3 fingers:
```bash
$ comfortable-swipe start --attach
SWIPE left3
SWIPE left3
SWIPE up3
SWIPE right3
SWIPE up3
...
```
1. Filter out the wanted gesture with `grep`.
In our case, we want 3-finger swipe up which is "SWIPE up3":
```bash
$ comfortable-swipe start --attach | grep --line-buffered "SWIPE up3"
SWIPE up3
SWIPE up3
...
```
> **Note**: The flag `--line-buffered` ensures the output prints line-by-line.
1. Now we can execute our shell command with `xargs`.
So if we want "SWIPE up3" to open the terminal,
```bash
comfortable-swipe start --attach | grep "SWIPE up3" --line-buffered | xargs -I@ gnome-terminal
```
> **Note**: The flag `-I@` in xargs substitutes the line "SWIPE xxx" to the charatcter "@", which you can use for your program.
1. _Bonus_: Add to autostart
Open our autostart file:
```bash
gedit "$(comfortable-swipe autostart path)"
```
Tweak the `Exec` section:
```ini
[Desktop Entry]
Type=Application
Exec=comfortable-swipe start
Name=Comfortable Swipe
Comment=Comfortable 3/4-finger touchpad gestures
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
```
To:
```ini
[Desktop Entry]
Type=Application
Exec=comfortable-swipe start --attach | grep "SWIPE up3" --line-buffered | xargs -I@ gnome-terminal
Name=Comfortable Swipe
Comment=Comfortable 3/4-finger touchpad gestures
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
```
1. Log out and log back in.
You should now be able to run your custom shell commands on startup.
1. _Bonus_: Use `--bare` instead of `--attach` to NOT run keystrokes while swiping
```bash
comfortable-swipe start --bare | grep "SWIPE up3" --line-buffered | xargs -I@ gnome-terminal
```
1. _Bonus_: You can pipe multiple gestures with `tee`:
```bash
comfortable-swipe start --attach | \
tee >(grep "SWIPE left3" --line-buffered | xargs -I@ <COMMAND>) | \
tee >(grep "SWIPE left4" --line-buffered | xargs -I@ <COMMAND>) | \
tee >(grep "SWIPE right3" --line-buffered | xargs -I@ <COMMAND>) | \
tee >(grep "SWIPE right4" --line-buffered | xargs -I@ <COMMAND>) | \
tee >(grep "SWIPE up3" --line-buffered | xargs -I@ <COMMAND>) | \
tee >(grep "SWIPE up4" --line-buffered | xargs -I@ <COMMAND>) | \
tee >(grep "SWIPE down3" --line-buffered | xargs -I@ <COMMAND>) | \
tee >(grep "SWIPE down4" --line-buffered | xargs -I@ <COMMAND>)
```
Substitute `<COMMAND>` with the shell command of your choice.
</details>
## Uninstall
Run the following script:
```bash
wget -qO - https://raw.githubusercontent.com/Hikari9/comfortable-swipe/master/uninstall | bash
```
## Bug Reports
Search in [Issues](https://github.com/Hikari9/comfortable-swipe/issues?utf8=%E2%9C%93&q=is%3Aissue) if the problem has already been solved.
Otherwise, [create a new issue](https://github.com/Hikari9/comfortable-swipe/issues/new) to report your bug.
Please include the output of the following:
1. `lsb_release -a`
2. `g++ --version`
3. `ls -l /dev/input/event*`
4. `xinput list | grep touchpad -i`
5. `lsmod | grep hid`
6. `comfortable-swipe status`
7. `comfortable-swipe start` (if you can run it)
8. `comfortable-swipe debug` (try swiping if you can see `GESTURE_SWIPE_XXX`)
9. `cat $(comfortable-swipe config)`
Create an issue [here](https://github.com/Hikari9/comfortable-swipe-ubuntu/issues/new) to report a bug.

View File

@ -1 +0,0 @@
v1.2.5

View File

@ -1,539 +0,0 @@
#!/bin/bash
# Comfortable Swipe
# turn on errors per line
set -e
DIR="$(dirname "$0")"
BASENAME="$(basename "$0")"
VERSION="$(cat "$DIR/VERSION" | tr -d '[:space:]')" # note: this will be hardcoded upon install
KEYS="left3|left4|right3|right4|up3|up4|down3|down4|threshold|mouse3|mouse4"
DEPRECATED="hold3|hold4"
#########
# USAGE #
#########
function usage {
cat <<EOF
Usage: $BASENAME [--help|--version] [start|stop|config|autostart|buffer|debug|status] [<args>]
Configuration:
$BASENAME <PROPERTY> gets configuration property
$BASENAME <PROPERTY> [=] <VALUE> sets configuration to a value
EOF
}
# show help
function help {
usage
cat <<EOF
Global Options:
-h, --help show this help text
-v, --version print the program version
Commands:
start [--attach|--bare]
starts 3/4-finger gesture service in a detached process
--attach attach process to console
--bare attach with bare configurations
stop
stops 3/4-finger gesture service
edit
edits the configuration file with gedit
autostart [on|off|toggle|status|path]
toggle to automatically run on startup automatically run on startup (toggleable)
buffer
parses output of libinput debug-events
config [get|set|delete|list|properties|path] [...]
shows the location of the config file
debug
logs raw output from input events taken from libinput
status
checks status of program and autostart
Report bugs to https://github.com/Hikari9/comfortable-swipe/issues/new
EOF
}
####################
# COMMAND OPTIONS #
####################
for i in "$@"; do
case $i in
-h | --help) # eagerly show help
help
exit 0
;;
-v | --version) # eagerly print version
echo "comfortable-swipe $VERSION"
exit 0
;;
--attach | --bare) # for start
if [[ "$1" != start ]]; then
if [[ "$1" != buffer ]]; then
echo "Unknown option: $i" >&2
exit 1
fi
fi
OPTION_ATTACH="true"
;;
*)
if [[ "$i" == -* ]]; then
echo "Unknown option: $i" >&2
exit 1
fi
;;
esac
done
#########
# START #
#########
# start comfortable-swipe
# internally pipes debug text to the buffer
function start {
stop > /dev/null 2>&1
if [[ "$OPTION_ATTACH" == "true" ]]; then
# attach the buffered output
debug | buffer $@
else
# detach buffered output
nohup "$BASENAME" debug </dev/null 2>&1 | "$BASENAME" buffer $@ >/dev/null 2>&1 & disown
echo "Comfortable swipe is RUNNING in the background"
fi
}
########
# STOP #
########
# stop running comfortable-swipe commands (except self)
function stop {
function stop_prop {
pgrep -f "${1:?}" | fgrep -v $$ | xargs -I{} kill -- "{}"
}
stop_prop "$BASENAME"
stop_prop "$(which comfortable-swipe)"
stop_prop "$(which comfortable-swipe-buffer)"
}
#########
# DEBUG #
#########
# show debug text
# internally just calls libinput debug-events
function debug {
if command -v libinput-debug-events > /dev/null 2>&1; then
# compat for old Ubuntu
local COMMAND="libinput-debug-events"
else
local COMMAND="libinput debug-events"
fi
stdbuf -oL -e0 $COMMAND 2> >(fgrep -v 'double tracking')
}
##########
# BUFFER #
##########
# 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 "$@"
}
##########
# STATUS #
##########
function _program_running {
pgrep -f "$BASENAME" | fgrep -v $$ > /dev/null 2>&1
}
# verbosely show comfortable-swipe status
function status {
# show autostart status
echo "Autostart is $("$BASENAME" autostart status)"
# show program status
if _program_running; then
echo "Program is RUNNING"
else
echo "Program is STOPPED"
fi
# show configuration status
echo --------------------
echo "Configuration: $(config path)"
mouse3="$(config get mouse3)"
mouse4="$(config get mouse4)"
for key in $(config keys); do
value="$(config get "$key")"
if [[ -z "$value" ]]; then
vstatus="NOTSET"
else
vstatus="VALID"
if [[ "$key" != mouse* ]]; then
if [[ "$key" == *3 && -n "$mouse3" && "$mouse3" != move ]]; then
vstatus="DISABLED"
elif [[ "$key" == *4 && -n "$mouse4" && "$mouse4" != move ]]; then
vstatus="DISABLED"
fi
fi
if [[ "$key" == threshold ]]; then
if ! [[ "$value" =~ ^[+-]?[0-9]+\.?[0-9]*$ ]]; then
# not a float
vstatus="INVALID"
fi
fi
fi
echo "$key $vstatus" | awk '{printf "%9s is %7s", $1, $2}'
if [[ ! -z "$value" ]]; then
echo " ($value)"
else
echo
fi
done
}
##########
# CONFIG #
##########
# get location of configuration file
function config {
local CONFIG="$HOME/.config/comfortable-swipe.conf"
############
# show usage
function usage {
echo "Usage: $BASENAME config [get|set|delete|list|keys|edit|path] ..."
}
#####################
# show path to config
function path {
echo "$CONFIG"
}
##############################################
# list config, 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
function get {
function usage {
echo "Usage: $BASENAME config get <PROPERTY>"
echo -n "Props: "
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 property" >&2
# echo >&2
usage >&2
echo >&2
echo "If you want to list down configurations, you can run:" >&2
echo >&2
echo " $BASENAME config list" >&2
echo >&2
exit 1
fi
# get key from config file
list | fgrep -m1 "$KEY =" | sed -E "s/^$KEY = //"
}
############################
# delete a key in the config
function delete {
# helper function to print usage
function usage {
echo "Usage: $BASENAME config delete [<PROPERTY>...]"
}
# no next argument: show help
if [[ $# -eq 0 ]]; then
echo "Property name 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 if it is running
if _program_running; then
start > /dev/null
fi
fi
}
##################################
# set a specific key in the config
function set {
# helper function to print usage
function usage {
echo "Usage: $BASENAME config set <PROPERTY> [=] <VALUE>"
echo -n "Valid properties: "
echo "$KEYS" | sed 's/|/, /g'
echo
echo "Example:"
echo
echo " $BASENAME config set left3 = ctrl+super+Right"
echo
}
# no next argument: show help
if [[ $# -eq 0 ]]; then
echo "Property name 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 "Property 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 property name" >&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 comfortable-swipe if it is running
if _program_running; then
start > /dev/null
fi
}
######################
# dispatch subcommands
if [[ $# -eq 0 ]]; then
# no options; just show usage
usage
echo "Try:"
echo
echo " $BASENAME config list"
echo
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
}
#############
# AUTOSTART #
#############
# enable or disable autostart
function autostart {
# path to autostart files
local AUTOSTART="$HOME/.config/autostart/comfortable-swipe.desktop"
local ENABLED="X-GNOME-Autostart-enabled"
##########################
# show autostart file path
function path {
echo "$AUTOSTART"
}
##################################################
# echo autostart status: ON, OFF, MISSING, INVALID
function status {
if [[ ! -f "$AUTOSTART" ]]; then
echo "MISSING"
elif fgrep "$ENABLED=true" < "$AUTOSTART" > /dev/null; then
echo "ON"
elif fgrep "$ENABLED=false" < "$AUTOSTART" > /dev/null; then
echo "OFF"
else
echo "INVALID"
fi
}
##################
# enable autostart
function on {
sed -i "s/$ENABLED=false/$ENABLED=true/" "$AUTOSTART"
echo "Autostart switched on"
}
###################
# disable autostart
function off {
sed -i "s/$ENABLED=true/$ENABLED=false/" "$AUTOSTART"
echo "Autostart switched off"
}
#####################################
# toggle to opposite autostart status
function toggle {
[[ $(status) == ON ]] && off || on
}
######################
# dispatch subcommands
if [[ $# -eq 0 ]]; then
# default behavior is to toggle
toggle
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 "Function $1 not recognized" >&2
echo "Usage: $BASENAME autostart [on|off|toggle|status|path]" >&2
exit 1
fi
}
############
# DISPATCH #
############
# dispatch subcommands
if [[ $# -eq 0 ]]; then
# no options; just show help
help
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
function abort {
# echo "Error: $1 not recognized" >&2
# echo >&2
usage >&2
echo -n "Valid props: " >&2
echo "$KEYS" | sed 's/|/, /g' >&2
}
# try to invoke config set / get depending on number of arguments
if [[ $# -eq 1 ]]; then
# one argument, use shorthand get
config get "$1" || abort
else
# multiple arguments, use shorthand set
config set "$@" 2> /dev/null || abort
fi
fi

View File

@ -1,55 +0,0 @@
#ifndef __comfortable_swipe_defines__
#define __comfortable_swipe_defines__
// all global #define statements go here
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* COMFORTABLE_SWIPE_CONFIG
*
* This definition points to the location of the configuration file.
* A warning will be issued to the compiler if you do not define this at compile
* time.
*/
#ifndef COMFORTABLE_SWIPE_CONFIG
#pragma message "Please define COMFORTABLE_SWIPE_CONFIG during compilation!"
// just point to config from source directory
#define COMFORTABLE_SWIPE_CONFIG "/dev/null"
#endif /* COMFORTABLE_SWIPE_CONFIG */
/**
* COMFORTABLE_SWIPE_AUTOSTART
*
* This definition points to the location of the autostart (.desktop) file.
* A warning will be issued to the compiler if you do not define this at compile
* time.
*/
#ifndef COMFORTABLE_SWIPE_AUTOSTART
#pragma message "Please define COMFORTABLE_SWIPE_AUTOSTART during compilation!"
// just point to autostart from source directory
#define COMFORTABLE_SWIPE_AUTOSTART "/dev/null"
#endif /* COMFORTABLE_SWIPE_AUTOSTART */
/**
* COMFORTABLE_SWIPE_VERSION
*
* This is an optional definition for the version string of comfortable-swipe.
*/
#ifndef COMFORTABLE_SWIPE_VERSION
#pragma message "Please define COMFORTABLE_SWIPE_VERSION during compilation!"
#define COMFORTABLE_SWIPE_VERSION "v(UNKNOWN)"
#endif /* COMFORTABLE_SWIPE_VERSION */
#endif /* __comfortable_swipe_defines__ */

View File

@ -1,197 +0,0 @@
#ifndef __comfortable_swipe_gesture_swipe_xdokey__
#define __comfortable_swipe_gesture_swipe_xdokey__
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <array> // std::array
#include <iostream> // std::cout, std::endl
#include <string> // std::string
extern "C" {
#include <xdo.h> // xdo_send_keysequence_window
// CURRENT_WINDOW
}
#include "comfortable-swipe-gesture-swipe.cpp"
namespace comfortable_swipe {
/**
* Gesture Swipe keyboard-swipe gestures class with xdo.
*
* Performs a swipe in one of the four cardinal directions:
* left, right, up, down. Applicable for 3 or 4 fingers.
*
* When constructing, please mind the order in the
* `gesture_swipe_xdokey::command_name[]` static array.
*/
class gesture_swipe_xdokey : public gesture_swipe {
public:
// stores the 8 command strings for xdo to execute
std::array<std::string, 8> commands;
// constructor
gesture_swipe_xdokey(const decltype(commands) &commands, float threshold = 0);
// destructor
virtual ~gesture_swipe_xdokey();
// hooks that we override
virtual void begin() override;
virtual void update() override;
virtual void end() override;
// override this when keyboard gesture is to be performed
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:
// stores square of threshold so we can compute faster
float threshold_squared;
// stores previous gesture so we don't repeat action
int previous_gesture;
int current_mask;
public:
// static enums we will use for gesture matching
static const int FRESH = -1;
static const int MSK_THREE_FINGERS = 0;
static const int MSK_FOUR_FINGERS = 1;
static const int MSK_NEGATIVE = 0;
static const int MSK_POSITIVE = 2;
static const int MSK_HORIZONTAL = 0;
static const int MSK_VERTICAL = 4;
// mappings to our command string names
static const std::string command_name[];
};
/**
* Our mapped directional command names. These will be
* printed to the console during execution:
*
* SWIPE <command_name>
*
* Refer to the order of this array for the "commands"
* paramter in the constructor.
*/
decltype(
gesture_swipe_xdokey::command_name) gesture_swipe_xdokey::command_name = {
// the order of variables is based on bitmasking
// <VERTICAL?> | <POSITIVE?> | <FOUR FINGERS?>
"left3", // 000
"left4", // 001
"right3", // 010
"right4", // 011
"up3", // 100
"up4", // 101
"down3", // 110
"down4" // 111
};
/**
* Constructs a new keyboard-based swipe gesture, given configurations
* for certain swipe events. Here, we construct our definition based on
* the four directions (left, up, right, down) for 3-finger and 4-finger
* swipes. Note that the direction is based on the Australian natural
* scrolling direction (ie. left3 is natural movement of 3 fingers left).
*/
gesture_swipe_xdokey::gesture_swipe_xdokey(
const decltype(gesture_swipe_xdokey::commands) &commands, float threshold)
: gesture_swipe(), commands(commands),
threshold_squared(threshold * threshold),
previous_gesture(gesture_swipe_xdokey::FRESH),
current_mask(gesture_swipe_xdokey::FRESH) {}
/**
* Destructs this keyboard swipe gesture.
*/
gesture_swipe_xdokey::~gesture_swipe_xdokey() {}
/**
* Hook on begin of swipe gesture.
*/
void gesture_swipe_xdokey::begin() {
// call superclass method
gesture_swipe::begin();
// assign gesture to FRESH
current_mask = FRESH;
previous_gesture = FRESH;
}
/**
* Hook on update of swipe gesture.
*/
void gesture_swipe_xdokey::update() {
// call superclass method
gesture_swipe::update();
// scale threshold to 1/10 when gesture is fresh
// acts like our static friction coefficient
const float scale = get_previous_gesture() == FRESH ? 0.01f : 1.0f;
// we are working with floating points which are not exact
// make sure we compare with a very small value (1e-6f)
// if distance goes out of threshold, perform our swipe
if (x * x + y * y > threshold_squared * scale + 1e-6f) {
// we parse our mask based on the values obtained from the regex
int mask = 0;
if (fingers == 3)
mask |= MSK_THREE_FINGERS;
else if (fingers == 4)
mask |= MSK_FOUR_FINGERS;
const float absx = x >= 0 ? x : -x;
const float absy = y >= 0 ? y : -y;
if (absx > absy) {
// gesture is horizontal
mask |= MSK_HORIZONTAL;
if (x < 0)
mask |= MSK_NEGATIVE;
else
mask |= MSK_POSITIVE;
} else /* std::abs(x) <= std::abs(y) */
{
// gesture is vertical
mask |= MSK_VERTICAL;
if (y < 0)
mask |= MSK_NEGATIVE;
else
mask |= MSK_POSITIVE;
}
// update our mask
current_mask = mask;
// send command on fresh OR opposite gesture
if (previous_gesture == FRESH ||
previous_gesture == (mask ^ MSK_POSITIVE)) {
// do keyboard gesture
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.
*/
void gesture_swipe_xdokey::do_keyboard_gesture(int mask) {
// perform our keyboard command with xdo_send_keysequence
xdo_send_keysequence_window(xdo, CURRENTWINDOW, commands[mask].data(), 0);
std::cout << "SWIPE " << command_name[mask] << std::endl;
}
/**
* Hook on end of swipe gesture.
*/
void gesture_swipe_xdokey::end() {
// just call superclass method
gesture_swipe::end();
}
} // namespace comfortable_swipe
#endif /* __comfortable_swipe_gesture_swipe_xdokey__ */

View File

@ -1,202 +0,0 @@
#ifndef __comfortable_swipe_gesture_swipe_xdomouse__
#define __comfortable_swipe_gesture_swipe_xdomouse__
/**
* File: comfortable-swipe-gesture-swipe-xdomouse.cpp
*
*
*/
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdio> // std::sscanf
#include <cstring> // strncmp
#include <iostream> // std::cout, std::endl
extern "C" {
#include <xdo.h> // xdo_mouse_down
// xdo_mouse_up
// xdo_move_mouse_relative
// CURRENTWINDOW
}
#include "comfortable-swipe-gesture-swipe.cpp"
namespace comfortable_swipe {
// enumerations for mouse hold gesture types
enum {
MOUSE_NONE = -1,
MOUSE_MOVE = 0,
MOUSE_LEFT_CLICK = 1,
MOUSE_MIDDLE_CLICK = 2,
MOUSE_RIGHT_CLICK = 3,
MOUSE_WHEEL_UP = 4,
MOUSE_WHEEL_DOWN = 5,
MOUSE_SCROLL = 6,
MOUSE_SCROLL_REVERSE = 7
};
/**
* Gesture Swipe mouse gestures class with xdo.
*
* If type is MOUSE_*_CLICK, we apply:
* - xdo_mouse_down on begin()
* - xdo_mouse_up on end()
*
* If type is MOUSE_MOVE, we apply:
* - xdo_mouse_move_relative on update().
*
* If type is MOUSE_WHEEL* or MOUSE_SCROLL*, we apply:
* - xdo_mouse_down on update()
* - xdo_mouse_up on update()
*/
class gesture_swipe_xdomouse : public gesture_swipe {
public:
// the button number being clicked
int button;
// constructor
gesture_swipe_xdomouse(const char *mouse3, // 3 finger mouse gesture
const char *mouse4 // 4 finger mouse gesture
);
// destructor
virtual ~gesture_swipe_xdomouse();
// override begin and end for mousedown
virtual void begin() override;
virtual void update() override;
virtual void end() override;
// provide our own mouse dispatch functions
virtual void do_mousedown(int button, int fingers);
virtual void do_mouseup(int button, int fingers);
// utility method to parse mouse input given config characters
static int parse_mouse_button(const char *);
protected:
// command holders
const char *mouse3;
const char *mouse4;
};
/**
* Constructs a new mouse gesture, given "mouse3" and "mouse4" configurations.
*/
gesture_swipe_xdomouse::gesture_swipe_xdomouse(const char *mouse3,
const char *mouse4)
: gesture_swipe(), button(MOUSE_NONE), mouse3(mouse3), mouse4(mouse4) {}
/**
* Destructs this mouse swipe gesture.
*/
gesture_swipe_xdomouse::~gesture_swipe_xdomouse() {}
/**
* Utility method to parse mouse number from input.
* Returns MOUSE_NONE on failure.
*/
int gesture_swipe_xdomouse::parse_mouse_button(const char *input) {
// just move without holding button down
if (std::strcmp(input, "move") == 0)
return MOUSE_MOVE;
if (std::strcmp(input, "scroll") == 0)
return MOUSE_SCROLL;
if (std::strcmp(input, "scroll_reverse") == 0)
return MOUSE_SCROLL_REVERSE;
// get button number
int button;
if (std::sscanf(input, "button%d", &button) == 1) {
if (1 <= button && button <= 6) {
return button;
}
}
return MOUSE_NONE;
}
/**
* Run mousedown command on a mouse button input.
*/
void gesture_swipe_xdomouse::do_mousedown(int button, int fingers) {
if (MOUSE_LEFT_CLICK <= button && button <= MOUSE_RIGHT_CLICK) {
// send mouse down on associated button
xdo_mouse_down(xdo, CURRENTWINDOW, button);
}
}
/**
* Run mouseup command on a mouse button output.
*/
void gesture_swipe_xdomouse::do_mouseup(int button, int fingers) {
if (MOUSE_LEFT_CLICK <= button && button <= MOUSE_RIGHT_CLICK) {
// send mouse up on associated button
xdo_mouse_up(xdo, CURRENTWINDOW, button);
}
}
/**
* Hook on begin of mouse swipe gesture.
*/
void gesture_swipe_xdomouse::begin() {
// call superclass method
gesture_swipe::begin();
// map fingers to gesture command
auto command = fingers == 3 ? mouse3 : (fingers == 4 ? mouse4 : NULL);
if (command != NULL) {
// get button int from the command
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);
}
}
}
/**
* Hook on end of mouse swipe gesture.
*/
void gesture_swipe_xdomouse::update() {
// call superclass method
gesture_swipe::update();
if (button != MOUSE_NONE) {
// if MOUSE_MOVE or MOUSE_CLICK*
if (0 <= button && button <= 3) {
// drag mouse with pointer during update
xdo_move_mouse_relative(xdo, udx, udy);
} else if (button == MOUSE_SCROLL || button == MOUSE_SCROLL_REVERSE) {
// perform naive scroll depending on vertical direction
int wheel = MOUSE_WHEEL_DOWN;
if ((udy > 0) == (button == MOUSE_SCROLL))
wheel = MOUSE_WHEEL_UP;
// click wheel on update (note: this is not precise)
xdo_mouse_down(xdo, CURRENTWINDOW, wheel);
xdo_mouse_up(xdo, CURRENTWINDOW, wheel);
} else if (button == MOUSE_WHEEL_UP || button == MOUSE_WHEEL_DOWN) {
// click wheel button on 4 or 5
xdo_mouse_down(xdo, CURRENTWINDOW, button);
xdo_mouse_up(xdo, CURRENTWINDOW, button);
}
}
}
/**
* Hook on end of swipe gesture.
*/
void gesture_swipe_xdomouse::end() {
// optimization: only perform mouseup when flag is set
if (button != MOUSE_NONE) {
// map fingers to gesture command
// perform mouseup on the button and print to console
do_mouseup(button, fingers);
std::printf("MOUSE UP mouse%d %s\n", fingers,
fingers == 3 ? mouse3 : mouse4);
}
// reset button
button = MOUSE_NONE;
// call superclass method
gesture_swipe::end();
}
} // namespace comfortable_swipe
#endif /* __comfortable_swipe_gesture_swipe_xdomouse__ */

View File

@ -1,229 +0,0 @@
#ifndef __comfortable_swipe_gesture_swipe__
#define __comfortable_swipe_gesture_swipe__
/**
* File: comfortable-swipe-gesture-swipe.cpp
*
* This is the base class of all comfortable swipe gestures.
*
* The class `comfortable_swipe::gesture_swipe` handles dispatching
* of swipe gestures. The method `gesture_swipe::run(const char*)`
* parses a line from libinput debug-events and dispatches a begin(),
* update(), or end() function based on a compiled regex pattern.
*
* We statically compile our swipe gesture regex patterns here.
* This is for parsing `libinput debug-events` console output:
*
* GESTURE_SWIPE_BEGIN - libinput's begin wipe command
* GESTURE_SWIPE_UPDATE - libinput's update swipe command
* GESTURE_SWIPE_END - libinput's end swipe command
*
* We dispatch our events based on the regex patterns above, and
* extract important information, such as number of fingers, in
* an efficient way with Regular Expressions (regex).
*/
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream> // std::cout, std::endl
#include <regex> // std::regex, std::regex_match, std::cmatch
#include <string> // std::stoi, std::stof
extern "C" {
#include <xdo.h> // xdo, xdo_new, xdo_free,
// xdo_get_mouse_location
// CURRENT_WINDOW
}
namespace comfortable_swipe {
/**
* Handles swipe gesture input from libinput debug-events.
* Dispatches keyboard commands on swipe using libxdo.
*/
class gesture_swipe {
public:
// constructor
gesture_swipe();
// destructor
virtual ~gesture_swipe();
// fields for xdo
int fingers;
// normal values (for touchpad mapping)
float x, y, dx, dy;
// unaccelerated values (for screen mapping)
float ux, uy, udx, udy;
// hooks that we can override (mark as virtual)
virtual void begin();
virtual void update();
virtual void end();
virtual bool run(const char *);
// public check if currently swiping
virtual bool is_swiping() const { return this->flag_swiping; }
protected:
// xdo container
xdo_t *xdo;
// location of mouse
int screen_num, ix, iy;
// optimization flag for checking if GESTURE_SWIPE_BEGIN was dispatched
bool flag_swiping;
public:
// defined in: comfortable-swipe-gesture-swipe.regex.cpp
static const std::regex GESTURE_SWIPE_BEGIN;
static const std::regex GESTURE_SWIPE_UPDATE;
static const std::regex GESTURE_SWIPE_END;
};
/**
* Constructs a new fresh swipe gesture container.
*/
gesture_swipe::gesture_swipe() : xdo(xdo_new(NULL)), flag_swiping(false) {
// improve responsiveness of first gesture by pre-empting xdotool
xdo_get_mouse_location(this->xdo, &this->ix, &this->iy, &this->screen_num);
}
/**
* Destructs this swipe gesture.
*/
gesture_swipe::~gesture_swipe() { xdo_free(this->xdo); }
/**
* Hook on begin of swipe gesture (you can override this).
*/
void gesture_swipe::begin() {
// save current screen location for gestured mouse movement
xdo_get_mouse_location(this->xdo, &this->ix, &this->iy, &this->screen_num);
this->x = 0;
this->y = 0;
this->ux = 0;
this->uy = 0;
}
/**
* Hook on update of swipe gesture (you can override this).
*/
void gesture_swipe::update() {
this->x += this->dx;
this->y += this->dy;
this->ux += this->udx;
this->uy += this->udy;
}
/**
* Hook on end of swipe gesture (you can override this).
*/
void gesture_swipe::end() {
// do nothing
}
/**
* Dispatches begin/update/end depending on the regex pattern provided by this
* class. Returns true if a gesture begin/update/end was parsed.
*
* @param line the line from libinput debug-events to parse
* @return true if begin/update/end was dispatched
*/
bool gesture_swipe::run(const char *line) {
// prepare holder for regex matches
static std::cmatch matches;
if (!this->flag_swiping) {
// not swiping, check if swipe will begin
if (std::regex_match(line, matches, GESTURE_SWIPE_BEGIN) != 0) {
// assign necessary variables for swipe begin
this->flag_swiping = true;
this->fingers = std::stoi(matches[1]);
// dispatch begin
this->begin();
return true;
}
} else {
// currently swiping
if (std::regex_match(line, matches, GESTURE_SWIPE_UPDATE) != 0) {
// assign necessary variables for swipe update
this->fingers = std::stoi(matches[1]);
this->dx = std::stof(matches[2]);
this->dy = std::stof(matches[3]);
this->udx = std::stof(matches[4]);
this->udy = std::stof(matches[5]);
// dispatch update
this->update();
return true;
} else if (std::regex_match(line, matches, GESTURE_SWIPE_END) != 0) {
// assign necessary variables for swipe end
this->flag_swiping = false;
this->fingers = std::stoi(matches[1]);
// dispatch end
this->end();
return true;
}
}
return false;
}
/**
* Regex pattern for the libinput entry for start of swipe.
* Extracts one match for the number of fingers used during the swipe.
*
* eg. event15 GESTURE_SWIPE_BEGIN +34.33s 3
* ^
* fingers
*/
const std::regex
gesture_swipe::GESTURE_SWIPE_BEGIN("^" // start of string
"[ -]event\\d+" // event
"\\s+GESTURE_SWIPE_BEGIN" // gesture
"\\s+\\S+" // timestamp
"\\s+(\\d+)" // fingers
"\\s*$" // end of string
);
/**
* Regex pattern for the libinput entry for the end of swipe.
* Extracts one match for the number of fingers used during the swipe.
*
* eg. event15 GESTURE_SWIPE_END +35.03s 3
* ^
* fingers
*/
const std::regex
gesture_swipe::GESTURE_SWIPE_END("^" // start of string
"[ -]event\\d+" // event
"\\s+GESTURE_SWIPE_END" // gesture
"\\s+\\S+" // timestamp
"\\s+(\\d+)" // fingers
"\\s*$" // end of string
);
// matches signed decimal numbers (eg. "6.02" "-1.1")
#define _NUMBER_REGEX "-?\\d+(?:\\.\\d+)"
// matches and extracts a space-prefixed signed fraction (eg. "-3.00/ 5.12")
#define _NUMBER_DIVISION "\\s*(" _NUMBER_REGEX ")/\\s*(" _NUMBER_REGEX ")"
/**
* Regex pattern for the libinput entry for during a swipe.
* Extracts number of fingers used and the speed (normal and accelerated) of the
* swipe.
*
* eg. event15 GESTURE_SWIPE_UPDATE \
* +34.70s 3 -0.12/ 4.99 (-0.33/13.50 unaccelerated)
* ^ ^ ^ ^ ^
* fingers dx dy udx udy
*/
const std::regex gesture_swipe::GESTURE_SWIPE_UPDATE(
"^" // start of string
"[ -]event\\d+" // event
"\\s+GESTURE_SWIPE_UPDATE" // gesture
"\\s+\\S+" // timestamp
"\\s+(\\d+)" // fingers
"\\s+" _NUMBER_DIVISION // speed (dx/dy)
"\\s+\\(" _NUMBER_DIVISION
"\\s+unaccelerated\\)" // unaccelerated speed (udx/udy)
"\\s*$" // end of string
);
// clean up temporary macros
#undef _NUMBER_DIVISION
#undef _NUMBER_REGEX
} // namespace comfortable_swipe
#endif /* __comfortable_swipe_gesture_swipe__ */

View File

@ -1,137 +0,0 @@
#ifndef __comfortable_swipe_main__
#define __comfortable_swipe_main__
/**
* The main driver program for `comfortable-swipe-buffer`
*
* This C++ program parses the output from libinput debug-events
* into a buffer and dispatches C++ libxdo commands upon swipe.
*
* Possible paths of the executable:
*
* /usr/bin/comfortable-swipe-buffer
* /usr/local/bin/comfortable-swipe-buffer
*/
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "comfortable-swipe-defines.cpp"
#include "comfortable-swipe-gesture-swipe-xdokey.cpp"
#include "comfortable-swipe-gesture-swipe-xdomouse.cpp"
#include "comfortable-swipe-gesture-swipe.cpp"
#include <iostream> // std::ios, std::cin, std::getline
#include <map> // std::map
#include <set> // std::set
#include <string> // std::string
extern "C" {
#include <ini.h> // ini_parse
}
/**
* Registers keys and values from the config file to a map.
*/
int parse_config(void *config, const char section[], const char name[],
const char value[]) {
auto &configmap = *(std::map<std::string, std::string> *)config;
configmap[name] = value;
return 0;
}
/**
* The main driver program.
*/
int main(int argc, char *argv[]) {
using namespace std;
using namespace comfortable_swipe;
// unsync stdio for faster IO
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// parse configuration file
map<string, string> config;
if (ini_parse(COMFORTABLE_SWIPE_CONFIG, parse_config, &config) < 0) {
cerr << "error: config " << COMFORTABLE_SWIPE_CONFIG << endl;
return EXIT_FAILURE;
}
// clear config and just use "threshold" if --bare
if (set<string>(argv + 1, argv + argc).count("--bare")) {
if (config.count("threshold")) {
auto threshold = config["threshold"];
config.clear();
config["threshold"] = threshold;
} else {
config.clear();
}
}
// initialize keyboard swipe gesture handler
// commands are: [left|up|right|down][3|4]
// we will fetch our commands from the config in correct order
// Examples:
// left3=ctrl+alt+Right shift to right workspace
// right3=ctrl+alt+Left shift to left workspace
// up3=super+Up maximize
// down3=super+Down minimize
decltype(gesture_swipe_xdokey::commands) commands;
for (size_t i = 0; i < commands.size(); ++i)
commands[i] = config[gesture_swipe_xdokey::command_name[i]];
// correctly parse threshold as float
float threshold = 0.0f;
try {
threshold = stof(config["threshold"]);
} catch (...) {
}
// create swipe handler
gesture_swipe_xdokey keyswipe(commands, threshold);
// initialize mouse hold gesture handler
// for now, this supports 3-finger and 4-finger hold
// Examples:
// mouse3=move move mouse on 3 fingers
// mouse3=button1 hold button 1 on 3 fingers
// mouse4=button3 hold button 3 (right click) on 3 fingers
// warn user that hold3 is deprecated
if (config.count("hold3"))
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"];
bool nomouse = mouse3.empty() || mouse4.empty(); // TODO: check if mouse invalid
// create our mouse gesture holder
gesture_swipe_xdomouse mousehold(mouse3.data(), mouse4.data());
// start reading lines from input one by one
for (string line; getline(cin, line);) {
if (nomouse) {
keyswipe.run(line.data());
} else {
// optimization: if no mouse config is set, just run keyboard
if (mousehold.is_swiping() && mousehold.button == MOUSE_NONE) {
keyswipe.run(line.data());
} else if (mousehold.run(line.data())) {
// only allow keyswipe gestures on mouse move
if (mousehold.button == MOUSE_NONE || mousehold.button == MOUSE_MOVE) {
keyswipe.run(line.data());
}
}
}
}
return EXIT_SUCCESS;
}
#endif

View File

View File

@ -0,0 +1,28 @@
from __future__ import print_function
import sys
from comfortable_swipe.autostart import toggle_status
from comfortable_swipe.constants import CONFIG
from comfortable_swipe.status import print_status
from comfortable_swipe.cpp import service as cpp_service
def main():
if len(sys.argv) <= 1:
cpp_service.help()
else:
dict(
start=cpp_service.start,
stop=cpp_service.stop,
restart=cpp_service.restart,
buffer=cpp_service.buffer,
help=cpp_service.help,
debug=cpp_service.debug,
status=print_status,
autostart=lambda: print('Autostart switched', toggle_status()),
config=lambda: print(CONFIG),
)[sys.argv[1]]()
if __name__ == '__main__':
main()

View File

@ -0,0 +1,66 @@
from __future__ import print_function
import os
import sys
from comfortable_swipe.constants import NAME, RES, exe
# status enums
OFF = 'off'
ON = 'on'
# the target path to the autostart desktop file
def target_path():
return os.path.join(
os.getenv(
'XDG_CONFIG_HOME',
os.path.join(os.getenv('HOME'), '.config')
),
'autostart',
'{}.desktop'.format(NAME)
)
# path to the autostart template file to be copied
def template_path():
return os.path.join(RES, '{}.desktop'.format(NAME))
# parsed contents of the template file
def template(raw=False):
with open(template_path(), 'r') as file:
contents = file.read()
if raw:
return contents
return contents.replace('Exec=' + NAME, 'Exec={} {}'.format(sys.executable, exe()))
# gets the current autostart status
def get_status():
return ON if os.path.exists(target_path()) else OFF
# sets the autostart status
def set_status(status=ON):
if status == ON:
# make sure dir exists
path = target_path()
path_dir = os.path.dirname(path)
if not os.path.exists(path_dir):
os.makedirs(path_dir)
with open(target_path(), 'w') as file:
file.write(template())
elif status == OFF:
if os.path.exists(target_path()):
os.remove(target_path())
else:
raise ValueError('invalid status for autostart')
return status
# toggles autostart status
def toggle_status():
return set_status(OFF if get_status() == ON else ON)

View File

@ -0,0 +1,17 @@
import os
import sys
from distutils.spawn import find_executable
NAME = 'comfortable-swipe'
DESCRIPTION = 'Comfortable 3-finger and 4-finger swipe gestures'
BIN = os.path.dirname(sys.executable)
DIR = os.path.dirname(os.path.abspath(__file__))
PYTHON_NAME = os.path.basename(DIR)
RES = os.path.join(DIR, 'res')
CONFIG = os.path.join(sys.prefix, 'local', 'share', NAME, '{}.conf'.format(NAME))
DEFAULT_CONFIG = os.path.join(RES, 'defaults.conf')
def exe():
return find_executable(NAME)

View File

@ -0,0 +1 @@
# This is a placeholder module that will contain the C++ libraries.

View File

@ -1,8 +1,8 @@
[Desktop Entry]
Type=Application
Exec=comfortable-swipe start
Name=Comfortable Swipe
Comment=Comfortable 3/4-finger touchpad gestures
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
Name=Comfortable Swipe
Comment=3 or 4 finger swipe gestures

View File

@ -0,0 +1,51 @@
# 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.
#################
# MISCELLANEOUS #
#################
# Threshold
# 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)
# Default: threshold = 0.0
threshold = 0.0
#############################
# THREE / FOUR FINGER SWIPE #
#############################
# 3-finger swipe left
# The default shortcut is switching to the right workspace.
# Default: left3 = ctrl+alt+Right
left3 = ctrl+alt+Right
# 3-finger swipe right
# The default shortcut is switching to the left workspace.
# Default: right3 = ctrl+alt+Left
right3 = ctrl+alt+Left
# 4-finger swipe left
# The default shortcut is moving current window to the right workspace.
# Default: left4=ctrl+alt+shift+Right
left4 = ctrl+alt+shift+Right
# 4-finger swipe right
# The default shortcut is moving current window to the left workspace.
# Default: right4=ctrl+alt+shift+Left
right4 = ctrl+alt+shift+Left
# 3-finger swipe up
# The default shortcut is switching to the workspace below.
# Default: up3 = ctrl+alt+Down
up3 = ctrl+alt+Down
# 3-finger swipe down
# The default shortcut is switching to the workspace above.
# Default: down3 = ctrl+alt+Up
down3 = ctrl+alt+Up
# 4-finger swipe up
# The default shortcut is moving current window to the bottom workspace.
# Default: ctrl+alt+shift+Down
up4 = ctrl+alt+shift+Down
# 4-finger swipe down
# The default shortcut is moving current window to the above workspace.
# Default: ctrl+alt+shift+Up
down4 = ctrl+alt+shift+Up

View File

@ -0,0 +1,22 @@
from __future__ import print_function
import os
from comfortable_swipe import autostart
from comfortable_swipe.cpp import service
from comfortable_swipe.constants import NAME, exe
def print_status():
service.status()
print('autostart is', autostart.get_status().upper())
print('{} program is {}'.format(NAME, 'RUNNING' if is_running() else 'STOPPED'))
def is_running():
import psutil
for process in psutil.process_iter():
process_args = [process.name()] + process.cmdline()
for index in range(len(process_args) - 1):
if process_args[index + 1] == 'start' and process_args[index].endswith(NAME):
return True
return False

12
compile
View File

@ -1,12 +0,0 @@
#!/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,42 +0,0 @@
# 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)

8
cpp/_index.cpp Normal file
View File

@ -0,0 +1,8 @@
#ifndef __COMFORTABLE_SWIPE__index_cpp__
#define __COMFORTABLE_SWIPE__index_cpp__
#include "gesture/_index.cpp"
#include "service/_index.cpp"
#include "util/_index.cpp"
#endif /* __COMFORTABLE_SWIPE__index_cpp__ */

8
cpp/_index.hpp Normal file
View File

@ -0,0 +1,8 @@
#ifndef __COMFORTABLE_SWIPE__index_hpp__
#define __COMFORTABLE_SWIPE__index_hpp__
#include "gesture/_index.hpp"
#include "service/_index.hpp"
#include "util/_index.hpp"
#endif /* __COMFORTABLE_SWIPE__index_hpp__ */

12
cpp/_macro.cpp Normal file
View File

@ -0,0 +1,12 @@
#ifndef __COMFORTABLE_SWIPE__macro_hpp__
#define __COMFORTABLE_SWIPE__macro_hpp__
#ifndef COMFORTABLE_SWIPE_CONFIG
#error "COMFORTABLE_SWIPE_CONFIG must be defined."
#endif /* COMFORTABLE_SWIPE_CONFIG */
#ifndef COMFORTABLE_SWIPE_VERSION
#warning "COMFORTABLE_SWIPE_VERSION is not defined."
#endif /* COMFORTABLE_SWIPE_VERSION */
#endif /* __COMFORTABLE_SWIPE__macro_hpp__ */

7
cpp/_python.cpp Normal file
View File

@ -0,0 +1,7 @@
#ifndef __COMFORTABLE_SWIPE__python_cpp__
#define __COMFORTABLE_SWIPE__python_cpp__
#include "service/_python.cpp"
#include "comfortable-swipe.cpp"
#endif /* __COMFORTABLE_SWIPE__python_cpp__ */

1
cpp/comfortable-swipe Normal file
View File

@ -0,0 +1 @@
#include "comfortable-swipe.cpp"

26
cpp/comfortable-swipe.cpp Normal file
View File

@ -0,0 +1,26 @@
#ifndef __COMFORTABLE_SWIPE__
#define __COMFORTABLE_SWIPE__
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "_macro.cpp"
#include "_index.hpp"
#include "_index.cpp"
#endif /* __COMFORTABLE_SWIPE__ */

9
cpp/gesture/_index.cpp Normal file
View File

@ -0,0 +1,9 @@
#ifndef __COMFORTABLE_SWIPE__gesture_index_cpp__
#define __COMFORTABLE_SWIPE__gesture_index_cpp__
#include "_index.hpp"
#include "xdo_gesture.cpp"
#include "swipe_gesture.cpp"
#include "swipe_gesture.regex.cpp"
#endif /* __COMFORTABLE_SWIPE__gesture_index_cpp__ */

7
cpp/gesture/_index.hpp Normal file
View File

@ -0,0 +1,7 @@
#ifndef __COMFORTABLE_SWIPE__gesture_index_hpp__
#define __COMFORTABLE_SWIPE__gesture_index_hpp__
#include "xdo_gesture.h"
#include "swipe_gesture.h"
#endif /* __COMFORTABLE_SWIPE__gesture_index_hpp__ */

View File

@ -0,0 +1,216 @@
#ifndef __COMFORTABLE_SWIPE__gesture_swipe_gesture__
#define __COMFORTABLE_SWIPE__gesture_swipe_gesture__
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream> // std::cout, std::endl
#include <string> // std::stoi, std::stof
#include <regex> // std::regex, std::regex_match, std::cmatch
#include <ctime>
#include "swipe_gesture.h"
extern "C"
{
#include <xdo.h> // xdo, xdo_new, xdo_free,
// xdo_get_mouse_location
// CURRENT_WINDOW
}
namespace comfortable_swipe
{
namespace gesture
{
/**
* Constructs a new swipe gesture, given configurations for certain swipe events.
*/
swipe_gesture::swipe_gesture
(
const float threshold,
const char* left3 /* 000 */,
const char* left4 /* 001 */,
const char* right3 /* 010 */,
const char* right4 /* 011 */,
const char* up3 /* 100 */,
const char* up4 /* 101 */,
const char* down3 /* 110 */,
const char* down4 /* 111 */
):
comfortable_swipe::gesture::xdo_gesture(),
threshold_squared(threshold*threshold),
commands(new const char*[8]{left3, left4, right3, right4, up3, up4, down3, down4}),
flag_swiping(false)
{
// improve responsiveness of first gesture by pre-empting xdotool runtime
xdo_get_mouse_location(this->xdo, &this->ix, &this->iy, &this->screen_num);
}
/**
* Destructs this swipe gesture.
*/
swipe_gesture::~swipe_gesture()
{
delete[] commands;
}
/**
* Hook on begin of swipe gesture.
*/
void swipe_gesture::begin()
{
xdo_get_mouse_location(this->xdo, &this->ix, &this->iy, &this->screen_num);
this->previous_gesture = swipe_gesture::FRESH;
this->x = 0;
this->y = 0;
}
/**
* Hook on update of swipe gesture.
*/
void swipe_gesture::update()
{
this->x += this->dx;
this->y += this->dy;
// scale threshold to 1/10 when gesture is not fresh
float scale = this->previous_gesture == swipe_gesture::FRESH
? 1.00f
: 0.01f; // square root of 1/10
static const float EPSILON = 1e-6f;
if (this->x * this->x + this->y * this->y
> this->threshold_squared * scale + EPSILON)
{
int mask = 0;
if (this->fingers == 3) mask |= swipe_gesture::MSK_THREE_FINGERS;
else if (this->fingers == 4) mask |= swipe_gesture::MSK_FOUR_FINGERS;
const float absx = x >= 0 ? x : -x;
const float absy = y >= 0 ? y : -y;
if (absx > absy)
{ // horizontal
mask |= swipe_gesture::MSK_HORIZONTAL;
if (x < 0)
mask |= swipe_gesture::MSK_NEGATIVE;
else
mask |= swipe_gesture::MSK_POSITIVE;
}
else /* std::abs(x) <= std::abs(y) */
{ // vertical
mask |= swipe_gesture::MSK_VERTICAL;
if (y < 0)
mask |= swipe_gesture::MSK_NEGATIVE;
else
mask |= swipe_gesture::MSK_POSITIVE;
}
// send command on fresh OR opposite gesture
if (this->previous_gesture == swipe_gesture::FRESH
|| this->previous_gesture == (mask ^ swipe_gesture::MSK_POSITIVE))
{
xdo_send_keysequence_window(this->xdo, CURRENTWINDOW, swipe_gesture::commands[mask], 0);
this->x = this->y = 0;
this->previous_gesture = mask;
std::cout << "SWIPE " << swipe_gesture::command_map[mask] << std::endl;
}
}
}
/**
* Hook on end of swipe gesture.
*/
void swipe_gesture::end()
{ }
/**
* Dispatches begin/update/end depending on the regex pattern provided by this class.
*
* @param line the line from libinput debug-events to parse
* @return true if begin/update/end was dispatched
*/
bool swipe_gesture::parse_line(const char * line)
{
static const std::regex gesture_swipe_begin(swipe_gesture::GESTURE_BEGIN_REGEX_PATTERN);
static const std::regex gesture_swipe_update(swipe_gesture::GESTURE_UPDATE_REGEX_PATTERN);
static const std::regex gesture_swipe_end(swipe_gesture::GESTURE_END_REGEX_PATTERN);
// prepare holder for regex matches
static std::cmatch matches;
if (this->flag_swiping)
{
// currently swiping
if (std::regex_match(line, matches, gesture_swipe_update) != 0)
{
// assign necessary variables for swipe update
this->fingers = std::stoi(matches[1]);
this->dx = std::stof(matches[2]);
this->dy = std::stof(matches[3]);
this->udx = std::stof(matches[4]);
this->udy = std::stof(matches[5]);
// dispatch update
this->update();
return true;
}
else if (std::regex_match(line, matches, gesture_swipe_end) != 0)
{
// assign necessary variables for swipe end
this->flag_swiping = false;
this->fingers = std::stoi(matches[1]);
// dispatch end
this->end();
return true;
}
}
else
{
// not swiping, check if swipe will begin
if (std::regex_match(line, matches, gesture_swipe_begin) != 0)
{
// assign necessary variables for swipe begin
this->flag_swiping = true;
this->fingers = std::stoi(matches[1]);
// dispatch begin
this->begin();
return true;
}
}
return false;
}
/* STATICS DEFINITIONS */
const int swipe_gesture::MSK_THREE_FINGERS = 0;
const int swipe_gesture::MSK_FOUR_FINGERS = 1;
const int swipe_gesture::MSK_NEGATIVE = 0;
const int swipe_gesture::MSK_POSITIVE = 2;
const int swipe_gesture::MSK_HORIZONTAL = 0;
const int swipe_gesture::MSK_VERTICAL = 4;
const int swipe_gesture::FRESH = -1;
const char * const swipe_gesture::command_map[8] = {
"left3",
"left4",
"right3",
"right4",
"up3",
"up4",
"down3",
"down4"
};
}
}
#endif /* __COMFORTABLE_SWIPE__gesture_swipe_gesture__ */

View File

@ -0,0 +1,85 @@
#ifndef __COMFORTABLE_SWIPE__gesture_swipe_gesture_h__
#define __COMFORTABLE_SWIPE__gesture_swipe_gesture_h__
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "xdo_gesture.h"
namespace comfortable_swipe
{
namespace gesture
{
class swipe_gesture : protected xdo_gesture
{
public:
// constructor
swipe_gesture(
const float threshold,
const char* left3 /* 000 */,
const char* left4 /* 001 */,
const char* right3 /* 010 */,
const char* right4 /* 011 */,
const char* up3 /* 100 */,
const char* up4 /* 101 */,
const char* down3 /* 110 */,
const char* down4 /* 111 */
);
~swipe_gesture();
// fields for xdo
int fingers;
float dx, dy, udx, udy;
void begin() override;
void update() override;
void end() override;
bool parse_line(const char *) override;
protected:
// location of mouse
int screen_num, ix, iy;
// current location
float x, y, threshold_squared;
int previous_gesture;
const char ** commands;
// optimization flag for checking if GESTURE_SWIPE_BEGIN was dispatched
bool flag_swiping;
public:
// static constants
static const int MSK_THREE_FINGERS;
static const int MSK_FOUR_FINGERS;
static const int MSK_NEGATIVE;
static const int MSK_POSITIVE;
static const int MSK_HORIZONTAL;
static const int MSK_VERTICAL;
static const int FRESH;
static const char * const command_map[8];
// regex patterns
static const char* GESTURE_BEGIN_REGEX_PATTERN;
static const char* GESTURE_UPDATE_REGEX_PATTERN;
static const char* GESTURE_END_REGEX_PATTERN;
};
}
}
#endif /* __COMFORTABLE_SWIPE__gesture_swipe_gesture_h__ */

View File

@ -0,0 +1,94 @@
#ifndef __COMFORTABLE_SWIPE__gesture_swipe_gesture_regex__
#define __COMFORTABLE_SWIPE__gesture_swipe_gesture_regex__
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "swipe_gesture.h"
namespace comfortable_swipe
{
namespace gesture
{
/**
* Regex pattern for the libinput entry for start of swipe.
* Extracts one match for the number of fingers used during the swipe.
*
* eg. event15 GESTURE_SWIPE_BEGIN +34.33s 3
* ^
* fingers
*/
const char* swipe_gesture::GESTURE_BEGIN_REGEX_PATTERN =
"^" // start of string
"[ -]event\\d+" // event
"\\s+GESTURE_SWIPE_BEGIN" // gesture
"\\s+\\S+" // timestamp
"\\s+(\\d+)" // fingers
"\\s*$" // end of string
;
/**
* Regex pattern for the libinput entry for the end of swipe.
* Extracts one match for the number of fingers used during the swipe.
*
* eg. event15 GESTURE_SWIPE_END +35.03s 3
* ^
* fingers
*/
const char* swipe_gesture::GESTURE_END_REGEX_PATTERN =
"^" // start of string
"[ -]event\\d+" // event
"\\s+GESTURE_SWIPE_END" // gesture
"\\s+\\S+" // timestamp
"\\s+(\\d+)" // fingers
"\\s*$" // end of string
;
// matches signed decimal numbers (eg. "6.02" "-1.1")
#define CF_NUMBER_REGEX "-?\\d+(?:\\.\\d+)"
// matches and extracts a space-prefixed signed fraction (eg. "-3.00/ 5.12")
#define CF_NUMBER_DIVISION "\\s*(" CF_NUMBER_REGEX ")/\\s*(" CF_NUMBER_REGEX ")"
/**
* Regex pattern for the libinput entry for during a swipe.
* Extracts number of fingers used and the speed (normal and accelerated) of the swipe.
*
* eg. event15 GESTURE_SWIPE_UPDATE +34.70s 3 -0.12/ 4.99 (-0.33/13.50 unaccelerated)
* ^ ^ ^ ^ ^
* fingers dx dy udx udy
*/
const char* swipe_gesture::GESTURE_UPDATE_REGEX_PATTERN =
"^" // start of string
"[ -]event\\d+" // event
"\\s+GESTURE_SWIPE_UPDATE" // gesture
"\\s+\\S+" // timestamp
"\\s+(\\d+)" // fingers
"\\s+" CF_NUMBER_DIVISION // speed (dx/dy)
"\\s+\\(" CF_NUMBER_DIVISION "\\s+unaccelerated\\)" // unaccelerated speed (udx/udy)
"\\s*$" // end of string
;
// delete macros
#undef CF_NUMBER_DIVISION
#undef CF_NUMBER_EXTRACT
#undef CF_NUMBER_REGEX
}
}
#endif /* __COMFORTABLE_SWIPE__gesture_swipe_gesture_regex__ */

View File

@ -0,0 +1,50 @@
#ifndef __COMFORTABLE_SWIPE__xdo_gesture__
#define __COMFORTABLE_SWIPE__xdo_gesture__
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
extern "C"
{
#include <xdo.h> // xdo, xdo_new
}
#include "xdo_gesture.h"
namespace comfortable_swipe
{
namespace gesture
{
/**
* Constructs a new gesture handler with xdo.
*/
xdo_gesture::xdo_gesture():
xdo(xdo_new(NULL))
{ }
/**
* Constructs a new swipe gesture with xdo.
*/
xdo_gesture::~xdo_gesture()
{
xdo_free(this->xdo);
}
}
}
#endif /* __COMFORTABLE_SWIPE__xdo_gesture__ */

57
cpp/gesture/xdo_gesture.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef __COMFORTABLE_SWIPE__xdo_gesture_h__
#define __COMFORTABLE_SWIPE__xdo_gesture_h__
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
extern "C"
{
#include <xdo.h> // xdo_t
}
#ifdef __cplusplus
extern "C" {
#endif
namespace comfortable_swipe
{
namespace gesture
{
class xdo_gesture
{
protected:
xdo_t * xdo;
public:
xdo_gesture();
~xdo_gesture();
// hooks
virtual void begin() = 0;
virtual void update() = 0;
virtual void end() = 0;
virtual bool parse_line(const char *) = 0;
};
}
}
#ifdef __cplusplus
}
#endif
#endif /* __COMFORTABLE_SWIPE__xdo_gesture_h__ */

13
cpp/service/_index.cpp Normal file
View File

@ -0,0 +1,13 @@
#ifndef __COMFORTABLE_SWIPE__service_index_cpp__
#define __COMFORTABLE_SWIPE__service_index_cpp__
#include "_index.hpp"
#include "buffer.cpp"
#include "debug.cpp"
#include "help.cpp"
#include "restart.cpp"
#include "start.cpp"
#include "status.cpp"
#include "stop.cpp"
#endif /* __COMFORTABLE_SWIPE__service_index_cpp__ */

21
cpp/service/_index.hpp Normal file
View File

@ -0,0 +1,21 @@
#ifndef __COMFORTABLE_SWIPE__service_index_hpp__
#define __COMFORTABLE_SWIPE__service_index_hpp__
#include <map> // std::map
#include <string> // std::string
namespace comfortable_swipe
{
namespace service
{
void buffer();
void debug();
void help();
void restart();
void start();
void stop();
void status();
}
}
#endif /* __COMFORTABLE_SWIPE__service_index_hpp__ */

91
cpp/service/_python.cpp Normal file
View File

@ -0,0 +1,91 @@
#ifndef __COMFORTABLE_SWIPE__service_python__
#define __COMFORTABLE_SWIPE__service_python__
#include "_index.hpp"
extern "C"
{
#include <Python.h>
}
// export as python module
namespace comfortable_swipe
{
namespace service
{
namespace python
{
#define __comfortable_swipe_void_method(method) \
static PyObject * \
method(PyObject * self, PyObject * args) \
{ \
comfortable_swipe::service::method(); \
Py_RETURN_NONE; \
}
// create the python method signatures
__comfortable_swipe_void_method(status);
__comfortable_swipe_void_method(start);
__comfortable_swipe_void_method(stop);
__comfortable_swipe_void_method(restart);
// __comfortable_swipe_void_method(autostart);
__comfortable_swipe_void_method(buffer);
__comfortable_swipe_void_method(help);
// __comfortable_swipe_void_method(config);
__comfortable_swipe_void_method(debug);
#undef __comfortable_swipe_void_method
// create the method list for C++
static PyMethodDef methods[] =
{
{ "status", &status, METH_VARARGS , "checks status of program, autostart, and config" },
{ "start", &start, METH_VARARGS , "starts 3/4-finger gesture service" },
{ "stop", &stop, METH_VARARGS , "stops 3/4-finger gesture service" },
{ "restart", &restart, METH_VARARGS , "stops then starts 3/4-finger gesture service" },
// { "autostart", &autostart, METH_VARARGS , "automatically run on startup (toggleable)" },
{ "buffer", &buffer, METH_VARARGS , "parses output of libinput debug-events" },
{ "help", &help, METH_VARARGS , "shows the help dialog" },
// { "config", &config, METH_VARARGS , "locates the config file " },
{ "debug", &debug, METH_VARARGS , "logs raw output from input events taken from libinput" },
{ NULL, NULL, 0, NULL } // sentinel
};
// create the module configuration
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef module_def =
{
PyModuleDef_HEAD_INIT,
"service",
"Comfortable swipe service",
-1,
methods
};
#endif
PyObject * module;
}
}
}
// initialize module
#if PY_MAJOR_VERSION >= 3
PyMODINIT_FUNC
PyInit_service(void)
{
using comfortable_swipe::service::python::module_def;
using comfortable_swipe::service::python::module;
if (module != NULL) return module;
return module = PyModule_Create(&module_def);
}
#else /* PY_MAJOR_VERSION < 3 */
PyMODINIT_FUNC
initservice(void)
{
using comfortable_swipe::service::python::methods;
using comfortable_swipe::service::python::module;
if (module != NULL) return;
module = Py_InitModule("service", methods);
}
#endif /* PY_MAJOR_VERSION */
#endif /* __COMFORTABLE_SWIPE__service_python__ */

63
cpp/service/buffer.cpp Normal file
View File

@ -0,0 +1,63 @@
#ifndef __COMFORTABLE_SWIPE__service_buffer__
#define __COMFORTABLE_SWIPE__service_buffer__
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../_macro.cpp"
#include "../util/_index.hpp"
#include "../gesture/_index.hpp"
#include <cstdio> // fgets_unlocked, stdin
/**
* Starts the comfortable-swipe service by buffering libinput debug-events.
*/
namespace comfortable_swipe::service
{
void buffer()
{
// read config file
auto config = comfortable_swipe::util::read_config_file(COMFORTABLE_SWIPE_CONFIG);
// initialize swipe gesture handler
comfortable_swipe::gesture::swipe_gesture swipe_gesture_handler
(
config.count("threshold") ? std::stof(config["threshold"]) : 0.0,
config["left3"].c_str(),
config["left4"].c_str(),
config["right3"].c_str(),
config["right4"].c_str(),
config["up3"].c_str(),
config["up4"].c_str(),
config["down3"].c_str(),
config["down4"].c_str()
);
// prepare data containers
std::array<char, 256> line;
// start reading lines from input one by one
while (fgets_unlocked(line.data(), line.size(), stdin) != NULL)
{
// attempt to parse swipe gestures
swipe_gesture_handler.parse_line(line.data());
}
}
}
#endif /* __COMFORTABLE_SWIPE__service_buffer__ */

38
cpp/service/debug.cpp Normal file
View File

@ -0,0 +1,38 @@
#ifndef __COMFORTABLE_SWIPE__service_debug__
#define __COMFORTABLE_SWIPE__service_debug__
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib> // std::system
namespace comfortable_swipe
{
namespace service
{
/**
* Debugs output from `libinput debug-events`.
*/
void debug()
{
(void) std::system("bash -c \"stdbuf -oL -e0 libinput debug-events 2> >(grep -v 'double tracking')\"");
}
}
}
#endif /* __COMFORTABLE_SWIPE__service_debug__ */

48
cpp/service/help.cpp Normal file
View File

@ -0,0 +1,48 @@
#ifndef __COMFORTABLE_SWIPE__service_help__
#define __COMFORTABLE_SWIPE__service_help__
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdio> // std::puts, std::printf
namespace comfortable_swipe
{
namespace service
{
/**
* Shows the help window.
*/
void help()
{
std::puts("comfortable-swipe [start|stop|restart|autostart|buffer|help|config|debug|status]");
std::puts("");
std::puts("start - starts 3/4-finger gesture service");
std::puts("stop - stops 3/4-finger gesture service");
std::puts("restart - stops then starts 3/4-finger gesture service");
std::puts("autostart - automatically run on startup (toggleable)");
std::puts("buffer - parses output of libinput debug-events");
std::puts("help - shows the help dialog");
std::puts("config - locates the config file");
std::puts("debug - logs raw output from input events taken from libinput");
std::puts("status - checks status of program and autostart");
}
}
}
#endif /* __COMFORTABLE_SWIPE__service_help__ */

39
cpp/service/restart.cpp Normal file
View File

@ -0,0 +1,39 @@
#ifndef __COMFORTABLE_SWIPE__service_restart__
#define __COMFORTABLE_SWIPE__service_restart__
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../service/_index.hpp"
namespace comfortable_swipe
{
namespace service
{
/**
* Restarts the comfortable-swipe service.
*/
void restart()
{
comfortable_swipe::service::stop();
comfortable_swipe::service::start();
}
}
}
#endif /* __COMFORTABLE_SWIPE__service_restart__ */

66
cpp/service/start.cpp Normal file
View File

@ -0,0 +1,66 @@
#ifndef __COMFORTABLE_SWIPE__service_start__
#define __COMFORTABLE_SWIPE__service_start__
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "_index.hpp"
#include <iostream> // std::cin, std::cout, std::ios
#include <cstdlib> // std::system
#include <unistd.h> // pipe, fork, perror, exit
namespace comfortable_swipe
{
namespace service
{
/**
* Starts the comfortable-swipe service by buffering libinput debug-events.
* This method is deferred. Please refer to comfortable_swipe::service::buffer()
* for the technical implementation.
*/
void start()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cout.flush();
// redirect stdout to stdin
int fd[2];
pipe(fd); // create the pipes
int child;
if ((child = fork()) == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (child) {
dup2(fd[1], STDOUT_FILENO);
comfortable_swipe::service::debug();
close(fd[0]);
} else {
dup2(fd[0], STDIN_FILENO);
comfortable_swipe::service::buffer();
close(fd[1]);
}
comfortable_swipe::service::stop();
}
}
}
#endif /* __COMFORTABLE_SWIPE__service_start__ */

78
cpp/service/status.cpp Normal file
View File

@ -0,0 +1,78 @@
#ifndef __COMFORTABLE_SWIPE__service_status__
#define __COMFORTABLE_SWIPE__service_status__
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../_macro.cpp"
#include "../util/_index.hpp"
#include "../gesture/_index.hpp"
#include <stdexcept> // std::runtime_error
#include <unistd.h> // popen, pclose, getpid, access, F_OK
#include <memory> // std::unique_ptr
#include <array> // std::array
#include <cstdlib> // std::atoi
#include <cstdio> // FILE, std::feof, std::fgets, std::printf
#include <regex> // std::cmatch, std::regex, std::regex_match
namespace comfortable_swipe
{
namespace service
{
/**
* Prints the status of comfortable-swipe.
*/
void status()
{
// std::printf("autostart is %s\n", autostart_on ? "ON" : "OFF");
// check status of configuration file
try
{
std::puts(COMFORTABLE_SWIPE_CONFIG);
auto config = comfortable_swipe::util::read_config_file(COMFORTABLE_SWIPE_CONFIG);
// print threshold
if (config.count("threshold") > 0)
{
auto & threshold = config["threshold"];
// std::cmatch matches;
// bool ok = (std::regex_match(threshold.data(), matches, std::regex("^\\d+(?:\\.\\d+)??$")) != 0);
// print status of threshold
std::printf(" %9s = %s\n", "threshold", threshold.data()); // ok ? "VALID" : "INVALID"
}
else
std::printf(" %9s is OFF\n", "threshold");
// print swipe commands
for (auto &command : comfortable_swipe::gesture::swipe_gesture::command_map)
{
if (config.count(command) > 0)
std::printf(" %9s = %s\n", command, config[command].data());
else
std::printf(" %9s NOT SET\n", command);
}
}
catch (const std::runtime_error& e)
{
std::printf("config error: %s\n", e.what());
}
}
}
}
#endif /* __COMFORTABLE_SWIPE__service_restart__ */

73
cpp/service/stop.cpp Normal file
View File

@ -0,0 +1,73 @@
#ifndef __COMFORTABLE_SWIPE__service_stop__
#define __COMFORTABLE_SWIPE__service_stop__
#include <cstdio> // std::FILE, std::feof, std::fgets
#include <cstdlib> // std::atoi, std::system
#include <string> // std::string, std::to_string
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdexcept> // std::runtime_error
#include <unistd.h> // popen, pclose, getpid
#include <memory> // std::unique_ptr
#include <array> // std::array
#include <cstdlib> // std::atoi
#include <cstdio> // FILE, std::feof, std::fgets
namespace comfortable_swipe
{
namespace service
{
/**
* Stops all comfortable-swipe instances.
*/
void stop()
{
// read all service names from process (pgrep)
std::array<char, 128> buffer;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen("pgrep -f comfortable-swipe", "r"), pclose);
// make sure pipe exists
if (!pipe)
throw std::runtime_error("stop command failed");
// buffer what to kill
std::string kill = "kill";
// read until end of line
while (!std::feof(pipe.get()))
{
if (std::fgets(buffer.data(), buffer.size(), pipe.get()) != NULL)
{
int pid = std::atoi(buffer.data());
if (pid != getpid())
{
kill += " ";
kill += std::to_string(pid);
}
}
}
// run "kill {pid1} {pid2}..."
(void) std::system(kill.data());
}
}
}
#endif /* __COMFORTABLE_SWIPE__service_stop__ */

7
cpp/util/_index.cpp Normal file
View File

@ -0,0 +1,7 @@
#ifndef __COMFORTABLE_SWIPE__util_index_cpp__
#define __COMFORTABLE_SWIPE__util_index_cpp__
#include "_index.hpp"
#include "read_config_file.cpp"
#endif /* __COMFORTABLE_SWIPE__util_index_cpp__ */

15
cpp/util/_index.hpp Normal file
View File

@ -0,0 +1,15 @@
#ifndef __COMFORTABLE_SWIPE__util_index_hpp__
#define __COMFORTABLE_SWIPE__util_index_hpp__
#include <map> // std::map
#include <string> // std::string
namespace comfortable_swipe
{
namespace util
{
std::map<std::string, std::string> read_config_file(const char*);
}
}
#endif /* __COMFORTABLE_SWIPE__util_index_hpp__ */

View File

@ -0,0 +1,107 @@
#ifndef __COMFORTABLE_SWIPE__util_read_config_file__
#define __COMFORTABLE_SWIPE__util_read_config_file__
/*
Comfortable Swipe
by Rico Tiongson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <map> // std::map
#include <string> // std::string
#include <fstream> // std::ifstream
#include <iostream> // std::endl, std::getline
#include <sstream> // std::ostringstream
#include <cstdlib> // exit
#include <cctype> // std::isspace
#include <stdexcept> // std::runtime_error
namespace comfortable_swipe
{
namespace util
{
/**
* A utility method for reading the config file.
*
* @param filename (const char*) the path of the config file.
*/
std::map<std::string, std::string> read_config_file(const char* filename)
{
std::map<std::string, std::string> conf;
std::ifstream fin(filename, std::ios::in);
if (!fin.is_open())
{
throw std::runtime_error("config file does not exist");
}
static std::string line, token[2];
int line_number = 0;
while (std::getline(fin, line))
{
++line_number;
token[0].clear();
token[1].clear();
int length = line.length();
int equal_flag = 0;
// tokenize comfig config
for (int i = 0; i < length; ++i)
{
if (line[i] == '#') // skip comments
break;
if (line[i] == '=') // flag equal sign
{
if (++equal_flag > 1)
{
std::ostringstream stream;
stream << "error in conf file " << filename << std::endl;
stream << "multiple equal signs in line " << line_number << std::endl;
throw std::runtime_error(stream.str());
}
}
else if (!std::isspace(line[i]))
{
// add to buffer
token[equal_flag].push_back(line[i]);
}
}
// ignore empty lines
if (equal_flag == 0 && token[0].length() == 0)
continue;
// no equal sign found in non-empty line
if (equal_flag == 0)
{
std::ostringstream stream;
stream << "error in conf file: " << filename << std::endl;
stream << "equal sign expected in line " << line_number << std::endl;
throw std::runtime_error(stream.str());
}
// equal sign found, add to tokens
if (token[1].length() > 0)
conf[token[0]] = token[1];
}
return conf;
}
}
}
#endif /* __COMFORTABLE_SWIPE__util_read_config_file__ */

201
install
View File

@ -1,201 +0,0 @@
#!/bin/bash
set -e
if [[ "$USER" == root ]]; then
echo "Please run 'bash install' as non-root user" 2>&1
exit 1
fi
DIR="$(dirname "$0")"
VERSION="$(cat $DIR/VERSION | tr -d '[:space:]')"
# target executable
SOURCE="$DIR/comfortable-swipe"
TARGET="/usr/local/bin/comfortable-swipe"
# compile targets
COMPILE="$DIR/compile"
COMPILE_SOURCE="$DIR/comfortable-swipe-main.cpp"
COMPILE_TARGET="/usr/local/bin/comfortable-swipe-buffer"
# configurations
CONF_SOURCE="$DIR/config/comfortable-swipe.conf"
CONF_TARGET="$HOME/.config/comfortable-swipe.conf"
OLD_CONF_TARGET="/usr/local/share/comfortable-swipe/comfortable-swipe.conf"
# autostart
AUTOSTART_SOURCE="$DIR/config/comfortable-swipe.desktop"
AUTOSTART_TARGET="$HOME/.config/autostart/comfortable-swipe.desktop"
# stop any running comfortable-swipe if it exists
comfortable-swipe stop > /dev/null 2>&1 || true
# shorthand to abort the installation
function abort {
echo "Installation aborted" >&2
exit 1
}
# shorthand to try a command with sudo if it fails
function trysudo {
$@ 2> /dev/null || sudo $@
}
# uninstall existing program
function uninstall_existing {
local PROGRAM="${1:?missing program name}"
# remove existing comfortable-swipe
if [[ -x "$(command -v "$PROGRAM")" ]]; then
echo -n "Removing previous $(which "$PROGRAM") ... "
trysudo rm -f "$(which "$PROGRAM")"
echo "ok"
fi
}
## CONFIGURATION FILE LOADING
# This section handles installation of configuration file.
# We determine first if any old configuration files exists.
# make sure config directory exists
function install_configuration_file {
mkdir -p "$(dirname "$CONF_TARGET")"
# check if configuration already exists
if [[ -f "$CONF_TARGET" ]]; then
# ask user if we overwrite configuration
echo "Old conf file found in $CONF_TARGET" >&2
read -r -p "Keep the old conf file? (default: yes) [Y/n] " response >&2
# If response is empty, consider it as 'yes' (default)
if [[ -z "$response" ]]; then
response="y"
fi
if [[ "${response,,}" =~ ^(no|n)$ ]]; then
# MAKE SURE they really want to overwrite
read -r -p "Conf file will be overwritten! Are you sure? [Y/n] " response >&2
if [[ -z "$response" ]]; then
response="y"
fi
if [[ "${response,,}" =~ ^(no|n)$ ]]; then
abort
else
# They agreed... replace configuration
cat "$CONF_SOURCE" > "$CONF_TARGET"
fi
fi
else
# target does not exist yet
# try copying from older configurations
if [[ -f "$OLD_CONF_TARGET" ]]; then
# move configuration from an older version (< v1.2)
echo "Moving configuration from $OLD_CONF_TARGET to $CONF_TARGET ..."
mv "$OLD_CONF_TARGET" "$CONF_TARGET"
# clean up old directory if possible
rmdir "$(dirname "$OLD_CONF_TARGET")" > /dev/null 2>&1 || true
else
# fresh installation! just copy default configuration
cat "$CONF_SOURCE" > "$CONF_TARGET"
fi
fi
echo "Installed: $CONF_TARGET"
}
## Installation of main program
#
# /usr/local/bin/comfortable-swipe
#
function install_main_program {
# copy source to target with executable permissions
# install to target, with hardcoded version
if [[ -f "$TARGET" ]]; then
trysudo rm "$TARGET"
fi
trysudo cp "$SOURCE" "$TARGET"
trysudo sed -E "s/^VERSION=.*/VERSION=$VERSION/" -i "$TARGET"
# allow execute permissions with group
trysudo chmod +x "$TARGET"
# make sure non-root user is owner
trysudo chown "$USER" "$TARGET"
echo "Installed: $TARGET"
}
## Compilation and installation of C++ buffer program:
#
# /usr/local/bin/comfortable-swipe-buffer
#
function install_cpp_program {
# compile program to temporary file first
TMP_TARGET="$(mktemp)"
$COMPILE "$COMPILE_SOURCE" -o "$TMP_TARGET" -DCOMFORTABLE_SWIPE_VERSION="\"$VERSION\"" -DCOMFORTABLE_SWIPE_CONFIG="\"$CONF_TARGET\"" -DCOMFORTABLE_SWIPE_AUTOSTART="\"$AUTOSTART_TARGET\""
# compilation ok, now try to install with sudo
trysudo mkdir -p "$(dirname "$COMPILE_TARGET")"
# remove existing file for permissions to work
if [[ -f "$COMPILE_TARGET" ]]; then
sudo rm "$COMPILE_TARGET"
fi
trysudo mv "$TMP_TARGET" "$COMPILE_TARGET"
# bugfix: add with group permissions
trysudo chmod go+x "$COMPILE_TARGET"
# make sure non-root user is owner
trysudo chown "$USER" "$COMPILE_TARGET"
echo "Installed: $COMPILE_TARGET"
}
## Installation of Autostart Desktop file
#
# /home/$USER/.config/autostart/comfortable-swipe.desktop
#
function install_autostart {
mkdir -p "$(dirname "$AUTOSTART_TARGET")"
cat "$AUTOSTART_SOURCE" > "$AUTOSTART_TARGET"
echo "Installed: $AUTOSTART_TARGET"
}
# install configuration files first but defer output later
# this is to ensure that the prompt runs first in the script
INSTALL_CONF_OUTPUT="$(install_configuration_file)"
# uninstall existing commands
uninstall_existing comfortable-swipe
uninstall_existing comfortable-swipe-buffer
# install new binaries
echo "Installing binaries ..."
install_cpp_program
install_main_program
echo "$INSTALL_CONF_OUTPUT"
install_autostart
# add permissions to input group (defer)
# GROUP=$(ls -l /dev/input/event* | awk '{print $4}' | head --line=1) || abort
# make sure comfortable-swipe is in path
if ! command -v comfortable-swipe > /dev/null 2>&1; then
cat <<EOF >&2
WARNING: $(dirname "$TARGET") is not in PATH!
Make sure to add the following in your ~/.profile:
export PATH="$PATH:$(dirname "$TARGET")"
EOF
fi
cat <<EOF
Successfully installed comfortable-swipe $VERSION
Autostart switched $("$TARGET" autostart status)
EOF
# start the program
"$TARGET" start

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
psutil==5.6.0

2
setup.cfg Normal file
View File

@ -0,0 +1,2 @@
[build]
compiler = unix

179
setup.py Normal file
View File

@ -0,0 +1,179 @@
from __future__ import print_function
import os
import sys
import setuptools
from shutil import copyfile
from setuptools import setup, find_packages
from setuptools.extension import Extension
from setuptools.command.develop import develop
from setuptools.command.install import install
from setuptools.command.easy_install import easy_install
from wheel.bdist_wheel import bdist_wheel
VERSION = '1.1.0-beta'
__CWD__ = os.getcwd()
__DIR__ = os.path.dirname(os.path.abspath(__file__))
__URL__ = 'https://github.com/Hikari9/comfortable-swipe-ubuntu'
extension_names = ['service']
try:
# make sure working directory is here
os.chdir(__DIR__)
# save README as long_description
with open('README.md', 'r') as README_file:
README = README_file.read()
# match constants with source
from comfortable_swipe.constants import *
# include old conf paths to list from previous versions
conf_paths = [
DEFAULT_CONFIG,
os.path.join(os.getenv('HOME', ''), '.config', 'comfortable-swipe', 'comfortable-swipe.conf'),
os.path.join('/usr/local/share', 'comfortable-swipe', 'comfortable-swipe.conf'),
CONFIG
]
# for C++ library
cpp_macros = dict(
COMFORTABLE_SWIPE_VERSION='"{}"'.format(VERSION),
COMFORTABLE_SWIPE_CONFIG='"{}"'.format(CONFIG)
)
# read C++ libraries for comfortable swipe
extensions = [Extension(
name='{}.cpp.{}'.format(PYTHON_NAME, extension_name),
define_macros=list(cpp_macros.items()),
sources=[os.path.join('cpp', '_python.cpp')],
extra_compile_args=['-O2', '-Wno-unused-result', '-std=c++11', '-lstdc++'],
libraries=['xdo']
) for extension_name in extension_names]
class Command:
outer_installer = None
outer_uninstaller = None
def pre_install(self):
# make sure only outer install script will run post_install
if self.__class__.outer_installer is not None: return
self.__class__.outer_installer = self
print('running pre_install')
def post_install(self):
# make sure post_install will only run when command is outer installer
if self.__class__.outer_installer is not self: return
print('running post_install')
# create program/config directories
if not os.path.exists(os.path.dirname(CONFIG)):
os.makedirs(os.path.dirname(CONFIG))
# copy any of the old config files
conf_files = [path for path in conf_paths if os.path.exists(path) and os.path.isfile(path)]
print('Using configuration file at', conf_files[-1])
if conf_files[-1] != CONFIG:
# new installation or upgrading from old version, copy to new location
copyfile(conf_files[-1], CONFIG)
if conf_files[-1] == DEFAULT_CONFIG:
# new installation - copy default configuration
print('Copying configuration file to', CONFIG)
else:
# upgrading - delete the deprecated config file (failsafe)
print('warning: depcrecated configuration file at', conf_files[-1])
print(' you have to remove this manually')
# enable autostart by default
from comfortable_swipe import autostart
autostart.set_status(autostart.ON)
print('Autostart created at', autostart.target_path())
print('\nInstallation successful\nTry running: {} start'.format(NAME))
def pre_uninstall(self):
# make sure only outer uninstall script will run post_uninstall
if self.__class__.outer_uninstaller is not None: return
self.__class__.outer_uninstaller = self
print('running pre_uninstall')
def post_uninstall(self):
# make sure post_uninstall will only run when command is outer uninstaller
if self.__class__.outer_uninstaller is not self: return
print('running post_uninstall')
from comfortable_swipe import autostart
if autostart.get_status() is autostart.ON:
print('Removing autostart at', autostart.target_path())
autostart.set_status(autostart.OFF)
# add post_install script to install method
def wrap_command(base_cls, uninstall=False):
class InstallCommand(Command, base_cls):
def run(self):
self.pre_uninstall() if uninstall and self.uninstall else self.pre_install()
base_cls.run(self)
self.post_uninstall() if uninstall and self.uninstall else self.post_install()
InstallCommand.__name__ = base_cls.__name__
return InstallCommand
# Override command classes here
cmdclass = dict(
install=wrap_command(install),
easy_install=wrap_command(easy_install),
develop=wrap_command(develop, uninstall=True),
bdist_wheel=wrap_command(bdist_wheel)
)
# install requiremenh open('requirements.txt', 'r') as requirements:
with open('requirements.txt', 'r') as requirements:
install_requires = requirements.read().splitlines()
# classifiers
# https://pypi.org/classifiers/
classifiers = [
'Development Status :: 4 - Beta'
'Intended Audience :: End Users/Desktop',
'Operating System :: POSIX :: Linux',
'Operating System :: Unix',
'Programming Language :: C++',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Environment :: Console'
]
# setup python script
setup_script = setup(
name=NAME,
version=VERSION,
description=DESCRIPTION,
long_description=README,
license='MIT',
author='Rico Tiongson',
author_email='thericotiongson@gmail.com',
url=__URL__,
zip_safe=False,
packages=find_packages(),
include_package_data=True,
entry_points=dict(console_scripts=['{}={}.__main__:main'.format(NAME, PYTHON_NAME)]),
ext_modules=extensions,
cmdclass=cmdclass,
install_requires=install_requires,
classifiers=classifiers
)
finally:
# move working directory back to where it was before
os.chdir(__CWD__)

View File

@ -1,28 +1,6 @@
#!/bin/bash
#!/bin/sh
set -e
DIR="$(dirname "$0")"
COMPILER="$(dirname "$DIR")/compile"
# just call abort on error
TEMPOUT="$(mktemp)"
abort () {
rm "$tempout"
echo "Test aborted"
exit 1
}
# run all shell files in this directory
for SH in "$DIR/test_"*.sh; do
"$SH" || abort
done
# run all cpp files in this directory
for CPP in "$DIR/test_"*.cpp; do
# compile the test
"$COMPILER" "$CPP" -o "$TEMPOUT" || abort
# run the test
chmod +x "$TEMPOUT"
"$TEMPOUT"
done
DIR=$(dirname $0)
g++ -std=c++11 -O2 $DIR/test_regex.cpp -lxdo -o test.out || exec "Test aborted"
./test.out || rm test.out
rm test.out

View File

@ -1,8 +1,9 @@
#include "../comfortable-swipe-gesture-swipe.cpp"
#include <cassert>
#include <iostream>
#include <cassert>
#include <regex>
#include <string>
#include "../lib/gesture/swipe_gesture.h"
#include "../lib/gesture/swipe_gesture.regex.cpp"
/*
Comfortable Swipe
@ -22,67 +23,71 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace test {
void gesture_begin_should_match_regex();
void gesture_update_should_match_regex();
void gesture_end_should_match_regex();
} // namespace test
int main() {
std::cout << "(1) Testing gesture_begin_should_match_regex()" << std::endl;
test::gesture_begin_should_match_regex();
std::cout << "(2) Testing gesture_begin_should_match_regex()" << std::endl;
test::gesture_update_should_match_regex();
std::cout << "(3) Testing gesture_begin_should_match_regex()" << std::endl;
test::gesture_end_should_match_regex();
std::cout << "ALL TEST PASSED" << std::endl;
namespace test
{
void gesture_begin_should_match_regex();
void gesture_update_should_match_regex();
void gesture_end_should_match_regex();
}
namespace test {
void gesture_begin_test(const std::regex &matcher, const char *data,
const char *expected_fingers) {
std::cout << " testing against \"" << data << "\"...";
std::cmatch matches;
int result = std::regex_match(data, matches, matcher);
assert(result != 0);
assert((std::string)matches[1] == expected_fingers);
std::cout << "PASSED" << std::endl;
int main()
{
std::cout << "(1) Testing gesture_begin_should_match_regex()" << std::endl;
test::gesture_begin_should_match_regex();
std::cout << "(2) Testing gesture_begin_should_match_regex()" << std::endl;
test::gesture_update_should_match_regex();
std::cout << "(3) Testing gesture_begin_should_match_regex()" << std::endl;
test::gesture_end_should_match_regex();
std::cout << "ALL TEST PASSED" << std::endl;
}
void gesture_begin_should_match_regex() {
std::regex matcher = comfortable_swipe::gesture_swipe::GESTURE_SWIPE_BEGIN;
test::gesture_begin_test(matcher, " event15 GESTURE_SWIPE_BEGIN +34.33s 3\n",
"3");
test::gesture_begin_test(matcher, "-event4 GESTURE_SWIPE_BEGIN +3.12s 4\n",
"4");
test::gesture_begin_test(matcher, "-event7 GESTURE_SWIPE_BEGIN +4.72s 3\n",
"3");
test::gesture_begin_test(matcher, " event9 GESTURE_SWIPE_BEGIN +45.80s 4\n",
"4");
}
namespace test
{
void gesture_begin_test(
const std::regex& matcher,
const char* data,
const char* expected_fingers)
{
std::cout << " testing against \"" << data << "\"...";
std::cmatch matches;
int result = std::regex_match(data, matches, matcher);
assert(result != 0);
assert((std::string) matches[1] == expected_fingers);
std::cout << "PASSED" << std::endl;
}
void gesture_update_should_match_regex() {
const char *data = " event15 GESTURE_SWIPE_UPDATE +34.70s 3 -0.12/ 4.99 "
"(-0.33/13.50 unaccelerated)\n";
std::regex matcher = comfortable_swipe::gesture_swipe::GESTURE_SWIPE_UPDATE;
std::cmatch matches;
auto result = std::regex_match(data, matches, matcher);
assert(result != 0);
assert((std::string)matches[1] == "3");
assert((std::string)matches[2] == "-0.12");
assert((std::string)matches[3] == "4.99");
assert((std::string)matches[4] == "-0.33");
assert((std::string)matches[5] == "13.50");
}
void gesture_begin_should_match_regex()
{
std::regex matcher(comfortable_swipe::gesture::swipe_gesture::GESTURE_BEGIN_REGEX_PATTERN);
test::gesture_begin_test(matcher, " event15 GESTURE_SWIPE_BEGIN +34.33s 3\n", "3");
test::gesture_begin_test(matcher, "-event4 GESTURE_SWIPE_BEGIN +3.12s 4\n", "4");
test::gesture_begin_test(matcher, "-event7 GESTURE_SWIPE_BEGIN +4.72s 3\n", "3");
test::gesture_begin_test(matcher, " event9 GESTURE_SWIPE_BEGIN +45.80s 4\n", "4");
}
void gesture_end_should_match_regex() {
const char *data = " event15 GESTURE_SWIPE_END +35.03s 3\n";
std::regex matcher = comfortable_swipe::gesture_swipe::GESTURE_SWIPE_END;
std::cmatch matches;
auto result = std::regex_match(data, matches, matcher);
assert(result != 0);
assert((std::string)matches[1] == "3");
void gesture_update_should_match_regex()
{
const char* data = " event15 GESTURE_SWIPE_UPDATE +34.70s 3 -0.12/ 4.99 (-0.33/13.50 unaccelerated)\n";
std::regex matcher(comfortable_swipe::gesture::swipe_gesture::GESTURE_UPDATE_REGEX_PATTERN);
std::cmatch matches;
auto result = std::regex_match(data, matches, matcher);
assert(result != 0);
assert((std::string) matches[1] == "3");
assert((std::string) matches[2] == "-0.12");
assert((std::string) matches[3] == "4.99");
assert((std::string) matches[4] == "-0.33");
assert((std::string) matches[5] == "13.50");
}
void gesture_end_should_match_regex()
{
const char* data = " event15 GESTURE_SWIPE_END +35.03s 3\n";
std::regex matcher(comfortable_swipe::gesture::swipe_gesture::GESTURE_END_REGEX_PATTERN);
std::cmatch matches;
auto result = std::regex_match(data, matches, matcher);
assert(result != 0);
assert((std::string) matches[1] == "3");
}
}
} // namespace test

View File

@ -1,103 +0,0 @@
#!/bin/bash
set -ex
DIR="$(dirname "$0")"
ROOT="$(dirname "$DIR")"
COMPILER="$ROOT/compile"
# just call abort on error
TEMPOUT="$(mktemp)"
abort () {
echo "Test aborted"
exit 1
}
# try to compile main test
# use empty configuration /dev/null
EMPTY_CONFIG="$(mktemp)"
echo "threshold = 0.0" >> "$EMPTY_CONFIG"
"$COMPILER" "$ROOT/comfortable-swipe-main.cpp" -o "$TEMPOUT" \
-DCOMFORTABLE_SWIPE_AUTOSTART="\"$ROOT/comfortable-swipe.desktop\"" \
-DCOMFORTABLE_SWIPE_CONFIG="\"$EMPTY_CONFIG\"" \
|| abort
chmod +x "$TEMPOUT"
set +x
# try to run start, then wait for the user to swipe
echo "Simulating swipe..."
# get output result
OUTPUT="$(mktemp)"
"$TEMPOUT" <<EOF > "$OUTPUT"
event7 GESTURE_SWIPE_BEGIN +1.72s 3
event7 GESTURE_SWIPE_UPDATE +1.72s 3 -12.26/-0.42 (-33.14/-1.12 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.73s 3 -14.08/ 0.00 (-38.06/ 0.00 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.74s 3 -15.78/-0.42 (-42.65/-1.12 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.75s 3 -16.63/-0.14 (-44.95/-0.37 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.75s 3 -16.63/-0.42 (-44.95/-1.12 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.76s 3 -16.39/ 0.14 (-44.29/ 0.37 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.77s 3 -15.66/ 0.28 (-42.32/ 0.75 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.78s 3 -13.96/ 0.55 (-37.73/ 1.50 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.78s 3 -12.38/ 0.28 (-33.46/ 0.75 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.79s 3 -10.56/ 0.55 (-28.54/ 1.50 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.80s 3 -8.86/ 0.69 (-23.95/ 1.87 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.81s 3 -6.92/ 1.53 (-18.70/ 4.12 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.81s 3 -5.10/ 1.53 (-13.78/ 4.12 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.82s 3 -4.13/ 1.39 (-11.15/ 3.75 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.83s 3 -3.40/ 0.97 (-9.19/ 2.62 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.84s 3 -2.43/ 0.97 (-6.56/ 2.62 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.84s 3 -1.82/ 0.69 (-4.92/ 1.87 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.85s 3 -0.97/ 0.69 (-2.62/ 1.87 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.86s 3 -0.36/ 0.69 (-0.98/ 1.87 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.87s 3 -0.24/ 0.42 (-0.66/ 1.12 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.88s 3 0.23/ 0.26 ( 0.66/ 0.75 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.88s 3 0.99/ 0.13 ( 2.95/ 0.37 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.89s 3 2.31/ 0.42 ( 6.23/ 1.12 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.90s 3 3.88/ 0.97 (10.50/ 2.62 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.91s 3 7.65/ 0.55 (20.67/ 1.50 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.91s 3 12.26/ 0.55 (33.14/ 1.50 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.92s 3 17.36/-0.42 (46.92/-1.12 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.93s 3 21.73/-1.25 (58.73/-3.37 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.94s 3 24.64/-2.64 (66.60/-7.12 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.94s 3 26.95/-3.75 (72.83/-10.12 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.95s 3 27.80/-4.72 (75.13/-12.75 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.96s 3 27.07/-4.86 (73.16/-13.12 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.97s 3 25.49/-5.27 (68.90/-14.25 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.97s 3 23.06/-4.72 (62.34/-12.75 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.98s 3 20.15/-4.16 (54.46/-11.25 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +1.99s 3 17.24/-3.33 (46.59/-9.00 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.00s 3 13.84/-2.64 (37.40/-7.12 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.00s 3 10.80/-1.80 (29.20/-4.87 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.01s 3 7.77/-0.83 (21.00/-2.25 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.02s 3 5.46/ 0.14 (14.76/ 0.37 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.03s 3 3.03/ 0.28 ( 8.20/ 0.75 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.04s 3 1.82/ 0.00 ( 4.92/ 0.00 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.04s 3 0.61/-0.28 ( 1.64/-0.75 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.05s 3 0.24/-0.14 ( 0.66/-0.37 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.06s 3 -0.22/ 0.13 (-0.66/ 0.37 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.07s 3 0.00/ 0.06 ( 0.00/ 0.37 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.09s 3 -0.05/ 0.06 (-0.33/ 0.37 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.10s 3 -0.05/ 0.00 (-0.33/ 0.00 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.10s 3 -0.05/ 0.05 (-0.33/ 0.37 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.11s 3 -0.05/ 0.06 (-0.33/ 0.37 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.12s 3 0.05/ 0.00 ( 0.33/ 0.00 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.13s 3 -0.11/ 0.00 (-0.66/ 0.00 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.13s 3 0.00/ 0.07 ( 0.00/ 0.37 unaccelerated)
event7 GESTURE_SWIPE_UPDATE +2.14s 3 -2.41/-0.50 (-7.22/-1.50 unaccelerated)
event7 GESTURE_SWIPE_END +2.19s 3
EOF
EXPECTED_OUTPUT="SWIPE left3 SWIPE right3"
ACTUAL_OUTPUT="$(cat "$OUTPUT" | xargs)"
echo "My output: $ACTUAL_OUTPUT"
echo "Expected: $EXPECTED_OUTPUT"
if [[ "$ACTUAL_OUTPUT" == "$EXPECTED_OUTPUT" ]]; then
echo "PASSED"
else
echo "Did not match expected output:" >&2
echo "$EXPECTED_OUTPUT" >&2
abort
fi

View File

@ -1,37 +0,0 @@
#!/bin/bash
set -e
function trysudo {
$@ 2> /dev/null || sudo $@
}
function uninstall {
TARGET="${1?missing uninstall parameter}"
if [[ -f "$TARGET" ]]; then
trysudo rm -f "$TARGET"
echo "Removed: $TARGET"
elif [[ ! -z "$TARGET" ]]; then
echo "Not found: $TARGET"
fi
}
# stop program first if it is still running
comfortable-swipe stop 2> /dev/null || true
# uninstall binaries
uninstall "$(which comfortable-swipe || echo /usr/share/bin/comfortable-swipe)"
uninstall "$(which comfortable-swipe-buffer || echo /usr/share/bin/comfortable-swipe-buffer)"
# uninstall desktop file
uninstall "$HOME/.config/autostart/comfortable-swipe.desktop"
# tell user we are keeping configuration file
CONF_TARGET="$HOME/.config/comfortable-swipe.conf"
if [ -f "$CONF_TARGET" ]; then
echo "Keeping your configuration file: $CONF_TARGET"
fi
cat <<EOF
Successfully uninstalled comfortable-swipe
EOF