From a9e78f48a49a6f49b30e567fc634b88a86e99f32 Mon Sep 17 00:00:00 2001 From: Rico Tiongson Date: Sat, 30 Sep 2017 00:09:37 +0800 Subject: [PATCH] Unify comfortable-swipe --- README.md | 40 +++-- img/sample.png | Bin 21967 -> 21085 bytes install | 24 ++- src/comfortable-swipe-serve | 2 - src/comfortable-swipe.cpp | 283 +++++++++++++++++++++++++++--------- src/defaults.conf | 69 +++++++++ 6 files changed, 333 insertions(+), 85 deletions(-) delete mode 100644 src/comfortable-swipe-serve create mode 100644 src/defaults.conf diff --git a/README.md b/README.md index 794d00c..cca0745 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,49 @@ -# Ubuntu-Comfortable-3-Finger-Swipe +# Ubuntu Comfortable Swipe 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 1. `sudo apt install libinput-tools libinput-dev libxdo-dev` -2. `git clone https://github.com/Hikari9/Ubuntu-Comfortable-3-Finger-Swipe.git` -3. `cd Ubuntu-Comfortable-3-Finger-Swipe` +2. `git clone https://github.com/Hikari9/comfortable-swipe-ubuntu.git` +3. `cd comfortable-swipe-ubuntu` 4. Tweak `src/comfortable-swipe.cpp` to fit keyboard shortcuts of your gestures 5. `bash install` ## How to Run -1. `comfortable-swipe-serve` +1. `comfortable-swipe start` 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: -1. `sudo gpasswd -a input $USER` +1. `sudo gpasswd -a $USER input` 2. Log out / log back in ## Optional: Add to Startup Applications 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) \ No newline at end of file +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. diff --git a/img/sample.png b/img/sample.png index ae10ca72df61e0afadea251f952f08d534c3cb1a..3439fed464a70bb3781c9376a5aa13eec4486546 100644 GIT binary patch literal 21085 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 literal 21967 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 $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 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 -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" +echo "Successfully installed. You may now run 'comfortable-swipe start'." diff --git a/src/comfortable-swipe-serve b/src/comfortable-swipe-serve deleted file mode 100644 index 9e98f4b..0000000 --- a/src/comfortable-swipe-serve +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -stdbuf -oL -eL libinput-debug-events | comfortable-swipe "$@" diff --git a/src/comfortable-swipe.cpp b/src/comfortable-swipe.cpp index 98a7137..d383f9d 100644 --- a/src/comfortable-swipe.cpp +++ b/src/comfortable-swipe.cpp @@ -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" -#define CMD_THREE_FINGERS_LEFT "ctrl+alt+Right" -#define CMD_THREE_FINGERS_UP "super+w" -#define CMD_THREE_FINGERS_DOWN "super+w" +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. -#define CMD_FOUR_FINGERS_RIGHT "ctrl+shift+alt+Left" -#define CMD_FOUR_FINGERS_LEFT "ctrl+shift+alt+Right" -#define CMD_FOUR_FINGERS_UP "super+d" -#define CMD_FOUR_FINGERS_DOWN "super+d" +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. -#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 - -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 */ -}; +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ #include +#include +#include +#include #include #include #include @@ -35,12 +29,23 @@ const char* commands[] = { #include #include #include +#define cstr const string& +using namespace std; + extern "C" { // sudo apt install libxdo-dev #include } -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 */ @@ -49,6 +54,31 @@ namespace util { string build_gesture_begin(); string build_gesture_update(); string build_gesture_end(); + map 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 { @@ -62,17 +92,50 @@ struct swipe_gesture { ~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 { - int screen_num, ix, iy; + int screen_num, ix, iy, threshold; double x, y; bool gesture_done; - swipe_gesture_impl(): swipe_gesture() {} - ~swipe_gesture_impl() {} + const char** commands; + 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 { xdo_send_keysequence_window(xdo, CURRENTWINDOW, cmd, 0); } void on_begin() override { - // cout << "BEGIN" << endl; xdo_get_mouse_location(xdo, &ix, &iy, &screen_num); gesture_done = false; x = 0; @@ -80,10 +143,9 @@ struct swipe_gesture_impl : swipe_gesture { } void on_update() override { if (gesture_done) return; - // cout << "UPDATE " << x << ' ' << y << " [" << dx << ", " << dy << "]" << endl; x += stod(dx); y += stod(dy); - if (x*x + y*y > THRESHOLD*THRESHOLD) { + if (x*x + y*y > threshold*threshold) { gesture_done = true; int mask = 0; if (fingers == "3") mask |= MSK_THREE_FINGERS; else @@ -97,51 +159,105 @@ struct swipe_gesture_impl : swipe_gesture { if (y < 0) mask |= MSK_NEGATIVE; else mask |= MSK_POSITIVE; } - // cout << "FLICK " << mask << ' ' << commands[mask] << endl; + cout << "SWIPE " << command_map[mask] << endl; key(commands[mask]); } } void on_end() override { - // pass } }; - -/* MAIN DRIVER FUNCTION */ - -int main(int argc, char** args) { - ios::sync_with_stdio(false); - cin.tie(0); - const regex gesture_begin(util::build_gesture_begin()); - const regex gesture_update(util::build_gesture_update()); - const regex gesture_end(util::build_gesture_end()); - string sentence; - swipe_gesture_impl swipe; - 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(); +namespace service { + // parses output from libinput-debug-events + void buffer() { + // check first if $user + ios::sync_with_stdio(false); + cin.tie(0); + const regex gesture_begin(util::build_gesture_begin()); + const regex gesture_update(util::build_gesture_update()); + const regex gesture_end(util::build_gesture_end()); + string sentence; + // read config file + string conf_filename = string(getenv("HOME")) + + "/.config/comfortable-swipe.conf"; + auto config = util::read_config_file(conf_filename.data()); + // initialize gesture handler + swipe_gesture_impl swipe( + config.count("threshold") ? stoi(config["threshold"]) : 20, + 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() + ); + 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]; - 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(); + } + // starts service + void start() { + int x = system("stdbuf -oL -eL libinput-debug-events | comfortable-swipe buffer"); + } + // stops service + void stop() { + // kill all comfortable-swipe, except self + char* buffer = new char[20]; + FILE* pipe = popen("pgrep -f comfortable-swipe", "r"); + if (!pipe) throw std::runtime_error("stop command failed"); + string kill = "kill"; + while (!feof(pipe)) { + if (fgets(buffer, 20, pipe) != NULL) { + 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}; return join("\\s+", arr, 4); } + + map read_config_file(const char* filename) { + map 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; + } } diff --git a/src/defaults.conf b/src/defaults.conf new file mode 100644 index 0000000..8349171 --- /dev/null +++ b/src/defaults.conf @@ -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