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 ae10ca7..3439fed 100644 Binary files a/img/sample.png and b/img/sample.png differ diff --git a/install b/install index 994b96d..a504318 100755 --- a/install +++ b/install @@ -1,11 +1,21 @@ #!/bin/bash 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 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