Unify comfortable-swipe

This commit is contained in:
Rico Tiongson 2017-09-30 00:09:37 +08:00
parent bb275a1c94
commit 7ce9d0cb3d
6 changed files with 333 additions and 85 deletions

View File

@ -1,27 +1,49 @@
# Ubuntu-Comfortable-3-Finger-Swipe # Ubuntu Comfortable Swipe
Author: Rico Tiongson Author: Rico Tiongson
Comfortable 3-finger and 4-finger swipe gestures. Uses Xdotool in native C++. For Ubuntu 14.04 LTS and beyond. Comfortable 3-finger (and 4-finger) swipe gestures for Ubuntu 14.04 LTS+.
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
## Installation ## Installation
1. `sudo apt install libinput-tools libinput-dev libxdo-dev` 1. `sudo apt install libinput-tools libinput-dev libxdo-dev`
2. `git clone https://github.com/Hikari9/Ubuntu-Comfortable-3-Finger-Swipe.git` 2. `git clone https://github.com/Hikari9/comfortable-swipe-ubuntu.git`
3. `cd Ubuntu-Comfortable-3-Finger-Swipe` 3. `cd comfortable-swipe-ubuntu`
4. Tweak `src/comfortable-swipe.cpp` to fit keyboard shortcuts of your gestures 4. Tweak `src/comfortable-swipe.cpp` to fit keyboard shortcuts of your gestures
5. `bash install` 5. `bash install`
## How to Run ## How to Run
1. `comfortable-swipe-serve` 1. `comfortable-swipe start`
2. Flick away! 2. Flick away!
### Input Permissions ### Permissions
Sometimes, you'll need some permissions to read touchpad input data. Perform these steps to solve the permission issue: Sometimes, you'll need some permissions to read touchpad input data. Perform these steps to solve the permission issue:
1. `sudo gpasswd -a input $USER` 1. `sudo gpasswd -a $USER input`
2. Log out / log back in 2. Log out / log back in
## Optional: Add to Startup Applications ## Optional: Add to Startup Applications
1. `gnome-session-properties` 1. `gnome-session-properties`
2. Click `Add`, then enter `comfortable-swipe-serve` 2. Add, then enter:
![Add to Startup Applications](img/sample.png)
![Comfortable Swipe](img/sample.png) 3. Save
## Configurations
The configuration file is located at `~/.config/comfortable-swipe.conf`.
Make sure to run `comfortable-swipe restart` after making changes.
Property | Description | Default Value | Default Behavior
--------- | ----------- | -------------- | -----
left3 | 3-finger swipe left | ctrl+shift+Right | switch to right workspace
right3 | 3-finger swipe right | ctrl+shift+Left | switch to left workspace
up3 | 3-finger swipe up | super+w | window spread
down3 | 3-finger swipe down | super+w | window spread
left4 | 4-finger swipe left | ctrl+alt+shift+Right | move window to right workspace
right4 | 4-finger swipe right | ctrl+alt+shift+Left | move window to left workspace
up4 | 4-finger swipe up | super+d | show desktop
down4 | 4-finger swipe down | super+d | show desktop
threshold | mouse pixels to activate swipe; higher = less sensitive; integers only | 20
## Bug Reports
Please create an issue to report a bug.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 21 KiB

24
install
View File

@ -1,11 +1,21 @@
#!/bin/bash #!/bin/bash
DIR=$(dirname $0) DIR=$(dirname $0)
echo "Compiling..." #copy config file
mkdir -p ~/.config
DCONF_PATH=$DIR/src/defaults.conf
CONF_PATH=~/.config/comfortable-swipe.conf
if [ ! -f $CONF_PATH ]; then
cat $DCONF_PATH > $CONF_PATH
else
# config file found, ask user if overwrite
echo "Previous conf file found in $CONF_PATH"
read -r -p "Do you want to overwrite? [Y/n] " response
response=${response,,} # tolower
if [[ "$response" =~ ^(yes|y)$ ]]; then
cat $DCONF_PATH > $CONF_PATH
fi
fi
echo "Installing..."
mkdir -p ~/.local/bin mkdir -p ~/.local/bin
g++ -std=c++11 -O2 $DIR/src/comfortable-swipe.cpp -lxdo -o ~/.local/bin/comfortable-swipe || exec echo "Installation aborted" g++ -std=c++11 -O2 $DIR/src/comfortable-swipe.cpp -lxdo -o ~/.local/bin/comfortable-swipe || exec echo "Installation aborted"
cat $DIR/src/comfortable-swipe-serve > ~/.local/bin/comfortable-swipe-serve echo "Successfully installed. You may now run 'comfortable-swipe start'."
chmod +x ~/.local/bin/comfortable-swipe-serve
echo "Successfully installed. You may now try running 'comfortable-swipe-serve'."
echo "--------------------------------------------------------------------------"
echo "If you want 'comfortable-swipe-serve' to automatically load on login, add it to your Startup Applications."
echo "See https://help.ubuntu.com/stable/ubuntu-help/startup-applications.html"

View File

@ -1,2 +0,0 @@
#!/bin/sh
stdbuf -oL -eL libinput-debug-events | comfortable-swipe "$@"

View File

@ -1,31 +1,25 @@
// you may tweak these before calling `build` /*
#define THRESHOLD 20 Comfortable Swipe
by Rico Tiongson
#define CMD_THREE_FINGERS_RIGHT "ctrl+alt+Left" This program is free software: you can redistribute it and/or modify
#define CMD_THREE_FINGERS_LEFT "ctrl+alt+Right" it under the terms of the GNU General Public License as published by
#define CMD_THREE_FINGERS_UP "super+w" the Free Software Foundation, either version 3 of the License, or
#define CMD_THREE_FINGERS_DOWN "super+w" (at your option) any later version.
#define CMD_FOUR_FINGERS_RIGHT "ctrl+shift+alt+Left" This program is distributed in the hope that it will be useful,
#define CMD_FOUR_FINGERS_LEFT "ctrl+shift+alt+Right" but WITHOUT ANY WARRANTY; without even the implied warranty of
#define CMD_FOUR_FINGERS_UP "super+d" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#define CMD_FOUR_FINGERS_DOWN "super+d" GNU General Public License for more details.
#define MSK_THREE_FINGERS 0 You should have received a copy of the GNU General Public License
#define MSK_FOUR_FINGERS 1 along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MSK_NEGATIVE 0 */
#define MSK_POSITIVE 2
#define MSK_HORIZONTAL 0
#define MSK_VERTICAL 4
const char* commands[] = {
CMD_THREE_FINGERS_LEFT /* 000 */, CMD_FOUR_FINGERS_LEFT /* 001 */,
CMD_THREE_FINGERS_RIGHT /* 010 */ , CMD_FOUR_FINGERS_RIGHT /* 011 */,
CMD_THREE_FINGERS_UP /* 100 */ , CMD_FOUR_FINGERS_UP /* 101 */,
CMD_THREE_FINGERS_DOWN /* 110 */, CMD_FOUR_FINGERS_DOWN /* 111 */
};
#include <iostream> #include <iostream>
#include <fstream>
#include <sstream>
#include <map>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <cstdlib> #include <cstdlib>
@ -35,12 +29,23 @@ const char* commands[] = {
#include <chrono> #include <chrono>
#include <ctime> #include <ctime>
#include <unistd.h> #include <unistd.h>
#define cstr const string&
using namespace std;
extern "C" { extern "C" {
// sudo apt install libxdo-dev // sudo apt install libxdo-dev
#include <xdo.h> #include <xdo.h>
} }
using namespace std;
#define cstr const string& /* MASKS FOR GESTURES */
#define MSK_THREE_FINGERS 0
#define MSK_FOUR_FINGERS 1
#define MSK_NEGATIVE 0
#define MSK_POSITIVE 2
#define MSK_HORIZONTAL 0
#define MSK_VERTICAL 4
/* FORWARD DECLARATIONS */ /* FORWARD DECLARATIONS */
@ -49,6 +54,31 @@ namespace util {
string build_gesture_begin(); string build_gesture_begin();
string build_gesture_update(); string build_gesture_update();
string build_gesture_end(); string build_gesture_end();
map<string, string> read_config_file(const char*);
}
namespace service {
void buffer();
void start();
void stop();
void restart();
void help();
}
/* MAIN DRIVER FUNCTION */
int main(int argc, char** args) {
if (argc > 1) {
string arg = args[1];
// select based on argument
if (arg == "start") service::start();
else if (arg == "stop") service::stop();
else if (arg == "restart") service::restart();
else if (arg == "buffer") service::buffer();
else service::help();
} else {
service::help();
}
} }
struct swipe_gesture { struct swipe_gesture {
@ -62,17 +92,50 @@ struct swipe_gesture {
~swipe_gesture() {xdo_free(xdo);} ~swipe_gesture() {xdo_free(xdo);}
}; };
const char* const command_map[] = {
"left 3",
"left 4",
"right 3",
"right 4",
"up 3",
"up 4",
"down 3",
"down 4"
};
struct swipe_gesture_impl : swipe_gesture { struct swipe_gesture_impl : swipe_gesture {
int screen_num, ix, iy; int screen_num, ix, iy, threshold;
double x, y; double x, y;
bool gesture_done; bool gesture_done;
swipe_gesture_impl(): swipe_gesture() {} const char** commands;
~swipe_gesture_impl() {} swipe_gesture_impl(
const int 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(), threshold(threshold) {
commands = new const char*[8];
commands[0] = left3;
commands[1] = left4;
commands[2] = right3;
commands[3] = right4;
commands[4] = up3;
commands[5] = up4;
commands[6] = down3;
commands[7] = down4;
}
~swipe_gesture_impl() {
delete[] commands;
}
void key(const char* cmd) const { void key(const char* cmd) const {
xdo_send_keysequence_window(xdo, CURRENTWINDOW, cmd, 0); xdo_send_keysequence_window(xdo, CURRENTWINDOW, cmd, 0);
} }
void on_begin() override { void on_begin() override {
// cout << "BEGIN" << endl;
xdo_get_mouse_location(xdo, &ix, &iy, &screen_num); xdo_get_mouse_location(xdo, &ix, &iy, &screen_num);
gesture_done = false; gesture_done = false;
x = 0; x = 0;
@ -80,10 +143,9 @@ struct swipe_gesture_impl : swipe_gesture {
} }
void on_update() override { void on_update() override {
if (gesture_done) return; if (gesture_done) return;
// cout << "UPDATE " << x << ' ' << y << " [" << dx << ", " << dy << "]" << endl;
x += stod(dx); x += stod(dx);
y += stod(dy); y += stod(dy);
if (x*x + y*y > THRESHOLD*THRESHOLD) { if (x*x + y*y > threshold*threshold) {
gesture_done = true; gesture_done = true;
int mask = 0; int mask = 0;
if (fingers == "3") mask |= MSK_THREE_FINGERS; else if (fingers == "3") mask |= MSK_THREE_FINGERS; else
@ -97,51 +159,105 @@ struct swipe_gesture_impl : swipe_gesture {
if (y < 0) mask |= MSK_NEGATIVE; if (y < 0) mask |= MSK_NEGATIVE;
else mask |= MSK_POSITIVE; else mask |= MSK_POSITIVE;
} }
// cout << "FLICK " << mask << ' ' << commands[mask] << endl; cout << "SWIPE " << command_map[mask] << endl;
key(commands[mask]); key(commands[mask]);
} }
} }
void on_end() override { void on_end() override {
// pass
} }
}; };
namespace service {
/* MAIN DRIVER FUNCTION */ // parses output from libinput-debug-events
void buffer() {
int main(int argc, char** args) { // check first if $user
ios::sync_with_stdio(false); ios::sync_with_stdio(false);
cin.tie(0); cin.tie(0);
const regex gesture_begin(util::build_gesture_begin()); const regex gesture_begin(util::build_gesture_begin());
const regex gesture_update(util::build_gesture_update()); const regex gesture_update(util::build_gesture_update());
const regex gesture_end(util::build_gesture_end()); const regex gesture_end(util::build_gesture_end());
string sentence; string sentence;
swipe_gesture_impl swipe; // read config file
while (getline(cin, sentence)) { string conf_filename = string(getenv("HOME"))
auto data = sentence.data(); + "/.config/comfortable-swipe.conf";
cmatch matches; auto config = util::read_config_file(conf_filename.data());
if (regex_match(data, matches, gesture_begin)) { // initialize gesture handler
swipe.device = matches[1]; swipe_gesture_impl swipe(
swipe.stamp = matches[2]; config.count("threshold") ? stoi(config["threshold"]) : 20,
swipe.fingers = matches[3]; config["left3"].c_str(),
swipe.on_begin(); 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()
);
while (getline(cin, sentence)) {
auto data = sentence.data();
cmatch matches;
if (regex_match(data, matches, gesture_begin)) {
swipe.device = matches[1];
swipe.stamp = matches[2];
swipe.fingers = matches[3];
swipe.on_begin();
}
else if (regex_match(data, matches, gesture_end)) {
swipe.device = matches[1];
swipe.stamp = matches[2];
swipe.fingers = matches[3];
swipe.on_end();
}
else if (regex_match(data, matches, gesture_update)) {
swipe.device = matches[1];
swipe.stamp = matches[2];
swipe.fingers = matches[3];
swipe.dx = matches[4];
swipe.dy = matches[5];
swipe.udx = matches[6];
swipe.udy = matches[7];
swipe.on_update();
}
} }
else if (regex_match(data, matches, gesture_end)) { }
swipe.device = matches[1]; // starts service
swipe.stamp = matches[2]; void start() {
swipe.fingers = matches[3]; int x = system("stdbuf -oL -eL libinput-debug-events | comfortable-swipe buffer");
swipe.on_end(); }
} // stops service
else if (regex_match(data, matches, gesture_update)) { void stop() {
swipe.device = matches[1]; // kill all comfortable-swipe, except self
swipe.stamp = matches[2]; char* buffer = new char[20];
swipe.fingers = matches[3]; FILE* pipe = popen("pgrep -f comfortable-swipe", "r");
swipe.dx = matches[4]; if (!pipe) throw std::runtime_error("stop command failed");
swipe.dy = matches[5]; string kill = "kill";
swipe.udx = matches[6]; while (!feof(pipe)) {
swipe.udy = matches[7]; if (fgets(buffer, 20, pipe) != NULL) {
swipe.on_update(); int pid = atoi(buffer);
if (pid != getpid()) {
kill += " " + to_string(pid);
}
}
} }
int result = system(kill.data());
delete[] buffer;
pclose(pipe);
}
// stops then starts service
void restart() {
service::stop();
service::start();
}
// shows help
void help() {
puts("comfortable-swipe [start|stop|restart|buffer|help]");
puts("start - starts 3/4-finger gesture service");
puts("stop - stops 3/4-finger gesture service");
puts("restart - stops then starts 3/4-finger gesture service");
puts("buffer - parses output of libinput-debug-events");
puts("help - shows the help dialog");
puts("");
puts("Configuration file can be found in ~/.config/comfortable-swipe.conf");
} }
} }
@ -192,5 +308,38 @@ namespace util {
string arr[] = {device, gesture, seconds, fingers}; string arr[] = {device, gesture, seconds, fingers};
return join("\\s+", arr, 4); return join("\\s+", arr, 4);
} }
map<string, string> read_config_file(const char* filename) {
map<string, string> conf;
ifstream fin(filename);
if (!fin.is_open()) {
cerr << "file \"" << filename << "\" does not exist!" << endl;
exit(1);
}
string line, key, token, buffer, value;
int line_number = 0;
while (getline(fin, line)) {
++line_number;
istringstream is(line);
buffer.clear();
while (is >> token) {
if (token[0] == '#')
break;
buffer += token;
}
if (buffer.empty())
continue;
auto id = buffer.find('=');
if (id == string::npos) {
cerr << "error in conf file: " << filename << endl;
cerr << "equal sign expected in line " << line_number << endl;
exit(1);
}
key = buffer.substr(0, id);
value = buffer.substr(id + 1);
conf[key] = value;
}
return conf;
}
} }

69
src/defaults.conf Normal file
View File

@ -0,0 +1,69 @@
# 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.
# THREE 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
# 3-finger swipe up
# The default shortcut is window spread.
# Default: up3=super+w
up3=super+w
# 3-finger swipe down
# The default shortcut is window spread.
# Default: down3=super+w
down3=super+w
# FOUR FINGER SWIPE
# 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
# 4-finger swipe up
# The default shortcut is show desktop.
# Default: up4=super+d
up4=super+d
# 4-finger swipe down
# The default shortcut is show desktop.
# Default: down4=super+d
down4=super+d
# MISCELLANEOUS
# Threshold
# Tweak this value depending on the sensitivity of your mousepad to perform
# gestures. A higher value means less sensitive.
# Default: threshold=20
threshold=20