From 66926f7ceaca743c935faf4260cf9c10ca46f146 Mon Sep 17 00:00:00 2001 From: Rico Tiongson Date: Mon, 20 Apr 2020 05:58:18 +0800 Subject: [PATCH] Simplify file checkpoint --- README.md | 10 +- comfortable-swipe | 2 +- comfortable-swipe-buffer.cpp | 89 ++++++++++ comfortable-swipe-config.cpp | 84 +++++++++ comfortable-swipe-defines.cpp | 38 +++++ comfortable-swipe-gesture-mousehold.cpp | 218 ++++++++++++++++++++++++ comfortable-swipe-gesture-swipe.cpp | 156 +++++++++++++++++ comfortable-swipe.desktop | 8 + command_line.cpp | 67 -------- cpp.compile.sh => compile.sh | 8 +- 10 files changed, 608 insertions(+), 72 deletions(-) create mode 100644 comfortable-swipe-buffer.cpp create mode 100644 comfortable-swipe-config.cpp create mode 100644 comfortable-swipe-defines.cpp create mode 100644 comfortable-swipe-gesture-mousehold.cpp create mode 100644 comfortable-swipe-gesture-swipe.cpp create mode 100644 comfortable-swipe.desktop delete mode 100644 command_line.cpp rename cpp.compile.sh => compile.sh (57%) diff --git a/README.md b/README.md index c4cfe5d..0d795ec 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,16 @@ Comfortable, seamless, and fast 3-finger (and 4-finger) touchpad swipe gestures ## Installation -1. Install git, libinput, and g++ +1. Install git and g++ ```bash - sudo apt-get install git libinput-tools libxdo-dev g++ + sudo apt install git g++ + ``` + +1. Install libinput-tools and C libraries + + ```bash + sudo apt install libinput-tools libconfig-dev libxdo-dev ``` 2. Clone this repository diff --git a/comfortable-swipe b/comfortable-swipe index d42c72c..42267f2 100755 --- a/comfortable-swipe +++ b/comfortable-swipe @@ -180,7 +180,7 @@ case $i in exit 0 ;; -v | --version) # eagerly print version - echo "$version" + echo "comfortable-swipe $version" exit 0 ;; *) # unknown option diff --git a/comfortable-swipe-buffer.cpp b/comfortable-swipe-buffer.cpp new file mode 100644 index 0000000..57edac0 --- /dev/null +++ b/comfortable-swipe-buffer.cpp @@ -0,0 +1,89 @@ +#ifndef __comfortable_swipe_buffer__ +#define __comfortable_swipe_buffer__ + +/* +Comfortable Swipe (as of v1.2.0) +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 . +*/ + +#include // fgets_unlocked, stdin +#include // ios, cout, cin +#include // cfg_t + +#include "comfortable-swipe-config.cpp" +#include "comfortable-swipe-defines.cpp" +#include "comfortable-swipe-gesture-mousehold.cpp" +#include "comfortable-swipe-gesture-swipe.cpp" + +/** + * The main driver program. + */ +int main() { + using namespace std; + using namespace comfortable_swipe; + + // unsync with for faster IO + ios::sync_with_stdio(false); + cin.tie(0); + cout.tie(0); + cout.flush(); + + // prepare config file + + cfg_ auto config = + comfortable_swipe::read_config_file(COMFORTABLE_SWIPE_CONFIG); + + // initialize mouse hold gesture handler + // for now, this supports 3-finger and 4-finger hold + // Examples: + // hold3=move move mouse on 3 fingers + // hold3=button1 hold button 1 on 3 fingers + // hold4=button3 hold button 3 (right click) on 3 fingers + // hold3=ignore + gesture_mousehold mouse_hold(config_get_string("hold3"), config_get_string("hold4")); + + // initialize keyboard swipe gesture handler + gesture_swipe = keyboard_swipe( + config_get_float("threshold"), + config_get_string("left3"), + config_get_string("left4"), + config_get_string("right3"), + config_get_string("right4"), + config_get_string("up3"), + config_get_string("up4"), + config_get_string("down3"), + config_get_string("down4"), + ); + + // prepare data containers + array line; + + // start reading lines from input one by one + while (fgets_unlocked(line.data(), line.size(), stdin) != NULL) { + // prioritize mouse hold gesture first + mouse_hold.parse_line(line.data()); + + // if mouse hold fails, try keyboard hold + if (!mouse_hold.is_mousedown()) { + // attempt to parse keyboard gestures + keyboard_swipe.parse_line(line.data()); + } + } + + return 0; +} + +#endif diff --git a/comfortable-swipe-config.cpp b/comfortable-swipe-config.cpp new file mode 100644 index 0000000..d7caf29 --- /dev/null +++ b/comfortable-swipe-config.cpp @@ -0,0 +1,84 @@ +#ifndef __comfortable_swipe_config__ +#define __comfortable_swipe_config__ + +/* +Comfortable Swipe (as of v1.2.0) +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 . +*/ + +#include // std::array +#include // std::cout +#include // std::unique_ptr +extern "C" { +#include // cfg_t +} +#include "comfortable-swipe-defines.cpp" + +namespace comfortable_swipe { +// prepare config file +// Examples: cfg_getstr(get_config(), "left3") +// cfg_getfloat(get_config(), "threshold") +cfg_t *get_config() { + using namespace std; + + // use std::unique_ptr for auto garbage collection + static std::unique_ptr config; + + // try to return existing config + if (config != nullptr) + return config.get(); + + // list down possible options here + // second argument is the default value + // make sure to end with CFG_END() + array cfg_options = { + CFG_STR("left3", "", CFGF_NONE), + CFG_STR("left4", "", CFGF_NONE), + CFG_STR("right3", "", CFGF_NONE), + CFG_STR("right4", "", CFGF_NONE), + CFG_STR("up3", "", CFGF_NONE), + CFG_STR("up4", "", CFGF_NONE), + CFG_STR("down3", "", CFGF_NONE), + CFG_STR("down4", "", CFGF_NONE), + CFG_FLOAT("threshold", 0.0, CFGF_NONE), + CFG_STR("hold3", "", CFGF_NONE), + CFG_STR("hold4", "", CFGF_NONE), + // make sure to end here + CFG_END() + ); + + // initialize + config = std::auto_ptr(cfg_init(cfg_options.data(), CFGF_NONE)): + + if (cfg_parse(config.get(), COMFORTABLE_SWIPE_CONFIG) == CFG_PARSE_ERROR) { + throw runtime_error( + "cannot parse config file at: " COMFORTABLE_SWIPE_CONFIG); + } + + return config.get(); +} + +// utility methods +inline const char *get_config_string(const char *key) { + return cfg_getstr(get_config(), key); +} + +inline double get_config_float(const char *key) { + return cfg_getfloat(get_config(), key); +} +} // namespace comfortable_swipe + +#endif /* __comfortable_swipe_config__ */ diff --git a/comfortable-swipe-defines.cpp b/comfortable-swipe-defines.cpp new file mode 100644 index 0000000..43d2c7f --- /dev/null +++ b/comfortable-swipe-defines.cpp @@ -0,0 +1,38 @@ +#ifndef __comfortable_swipe_defines__ +#define __comfortable_swipe_defines__ +// Place #define statements here + +/* +Comfortable Swipe (as of v1.2.0) +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 . +*/ + +#ifndef COMFORTABLE_SWIPE_CONFIG +#warning Please define COMFORTABLE_SWIPE_CONFIG during compilation!\ + (g++ -DCOMFORTABLE_SWIPE_CONFIG="\"/home/$USER/.config/comfortable-swipe.conf\"") +#endif /* COMFORTABLE_SWIPE_CONFIG */ + +#ifndef COMFORTABLE_SWIPE_AUTOSTART +#warning Please define COMFORTABLE_SWIPE_AUTOSTART during compilation!\ + (g++ -DCOMFORTABLE_SWIPE_AUTOSTART="\"/home/$USER/.config/autostart/comfortable-swipe.desktop\"") +#endif /* COMFORTABLE_SWIPE_AUTOSTART */ + +#ifndef COMFORTABLE_SWIPE_VERSION +#warning COMFORTABLE_SWIPE_VERSION is not defined +#define COMFORTABLE_SWIPE_VERSION "v(UNKNOWN)" +#endif /* COMFORTABLE_SWIPE_VERSION */ + +#endif /* __comfortable_swipe_defines__ */ diff --git a/comfortable-swipe-gesture-mousehold.cpp b/comfortable-swipe-gesture-mousehold.cpp new file mode 100644 index 0000000..5a3d865 --- /dev/null +++ b/comfortable-swipe-gesture-mousehold.cpp @@ -0,0 +1,218 @@ +#ifndef __comfortable_swipe_gesture_mousehold__ +#define __comfortable_swipe_gesture_mousehold__ + +/* +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 . +*/ + +#include // std::sscanf +#include // strncmp +#include // std::cout, std::endl + +extern "C" { +#include // xdo, xdo_new, xdo_free, + // xdo_get_mouse_location + // CURRENT_WINDOW +} + +#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 +}; + +class gesture_mousehold : public gesture_swipe { +public: + // constructor + gesture_mousehold(const char *hold3, // 3 finger mouse down + const char *hold4 // 4 finger mouse down + ); + + // the button being clicked + int button; + + virtual ~gesture_mousehold(); + + // override begin and end for mousedown + virtual void begin() override; + virtual void update() override; + virtual void end() override; + + // provide our own mouse functions + virtual void do_mousedown(const char *); + virtual void do_mouseup(const char *); + virtual bool is_mousedown() const; + + // utility method to parse mouse input given config characters + static int parse_mouse_button(const char *); + +protected: + // command holders + const char *hold3; + const char *hold4; + + // flag we can use to check if mouse is down + bool flag_mousedown; +}; +} // namespace comfortable_swipe + +/** + * Constructs a new mouse gesture, given "hold3" and "hold4" configurations. + */ +gesture_mousehold::gesture_mousehold(const char *hold3, const char *hold4) + : gesture_swipe(), button(MOUSE_NONE), hold3(hold3), hold4(hold4), + flag_mousedown(false) {} + +/** + * Destructs this mouse swipe gesture. + */ +gesture_mousehold::~gesture_mousehold() {} + +/** + * Run mousedown command on hold input. + */ +void gesture_mousehold::do_mousedown(const char *mouseinput) { + const int button = this->button = this->parse_mouse_button(mouseinput); + if (button != MOUSE_NONE) { + // eg. MOUSE DOWN hold3 mouse1 + std::printf("MOUSE DOWN hold%d %s\n", this->fingers, mouseinput); + if (MOUSE_LEFT_CLICK <= button && button <= MOUSE_RIGHT_CLICK) { + // send mouse down on associated button + xdo_mouse_down(this->xdo, CURRENTWINDOW, button); + } + this->flag_mousedown = true; + } +} + +/** + * Run mouseup command on hold output. + */ +void gesture_mousehold::do_mouseup(const char *mouseinput) { + const int button = this->button = this->parse_mouse_button(mouseinput); + if (button != MOUSE_NONE) { + std::printf("MOUSE UP hold%d %s\n", this->fingers, mouseinput); + if (MOUSE_LEFT_CLICK <= button && button <= MOUSE_RIGHT_CLICK) { + // send mouse up on associated button + xdo_mouse_up(this->xdo, CURRENTWINDOW, button); + } + this->flag_mousedown = false; + } +} + +/** + * Utility method to parse mouse number from input. + * Returns -1 on failure. + */ +int gesture_mousehold::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; +} + +/** + * Hook on begin of mouse swipe gesture. + */ +void gesture_mousehold::begin() { + // call superclass method + gesture_swipe::begin(); + // dispatch mouse down event + if (this->fingers == 3) { + this->do_mousedown(this->hold3); + } else if (this->fingers == 4) { + this->do_mousedown(this->hold4); + } +} + +/** + * Hook on end of mouse swipe gesture. + */ +void gesture_mousehold::update() { + // call superclass method + gesture_swipe::update(); + if (this->is_mousedown()) { + if (0 <= this->button && this->button <= 3) { + // drag mouse with pointer during update + xdo_move_mouse_relative(this->xdo, this->udx, this->udy); + } else if (this->button == MOUSE_SCROLL || + this->button == MOUSE_SCROLL_REVERSE) { + // perform naive scroll depending on vertical direction + int wheel = MOUSE_WHEEL_DOWN; + if ((this->udy > 0) == (this->button == MOUSE_SCROLL)) + wheel = MOUSE_WHEEL_UP; + + // click wheel on update (note: this is not precise) + xdo_mouse_down(this->xdo, CURRENTWINDOW, wheel); + xdo_mouse_up(this->xdo, CURRENTWINDOW, wheel); + } else if (this->button == MOUSE_WHEEL_UP || + this->button == MOUSE_WHEEL_DOWN) { + // click wheel button on 4 or 5 + xdo_mouse_down(this->xdo, CURRENTWINDOW, this->button); + xdo_mouse_up(this->xdo, CURRENTWINDOW, this->button); + } + } +} + +/** + * Hook on end of swipe gesture. + */ +void gesture_mousehold::end() { + if (this->is_mousedown()) { + if (this->fingers == 3) { + this->do_mouseup(this->hold3); + } else if (this->fingers == 4) { + this->do_mouseup(this->hold4); + } + } + + // call superclass method + gesture_swipe::end(); +} + +/** + * Utility method to check if mouse is current held. + */ +bool gesture_mousehold::is_mousedown() const { return this->flag_mousedown; } +} + +#endif /* __comfortable_swipe_gesture_mousehold__ */ diff --git a/comfortable-swipe-gesture-swipe.cpp b/comfortable-swipe-gesture-swipe.cpp new file mode 100644 index 0000000..22d8c88 --- /dev/null +++ b/comfortable-swipe-gesture-swipe.cpp @@ -0,0 +1,156 @@ +#ifndef __comfortable_swipe_gesture_swipe__ +#define __comfortable_swipe_gesture_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 . +*/ + +#include // std::cout, std::endl +#include // std::regex, std::regex_match, std::cmatch +#include // std::stoi, std::stof + +namespace comfortable_swipe { +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 parse_line(const char *); + +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: + // regex patterns + static const std::regex *GESTURE_BEGIN_REGEX; + static const std::regex *GESTURE_UPDATE_REGEX; + static const std::regex *GESTURE_END_REGEX; +}; + +/** + * 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() {} + +/** + * 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 gesture_swipe::parse_line(const char *line) { + + // prepare holder for regex matches + static std::cmatch matches; + + if (this->flag_swiping) { + // currently swiping + if (std::regex_match(line, matches, GESTURE_UPDATE_REGEX) != 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_END_REGEX) != 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_BEGIN_REGEX) != 0 || + std::regex_match(line, matches, GESTURE_UPDATE_REGEX) != 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; +} +} // namespace comfortable_swipe + +#endif /* __comfortable_swipe_gesture_swipe__ */ diff --git a/comfortable-swipe.desktop b/comfortable-swipe.desktop new file mode 100644 index 0000000..3b1da8f --- /dev/null +++ b/comfortable-swipe.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=Comfortable Swipe +Comment=Comfortable 3/4-finger touchpad gestures +Exec=comfortable-swipe start +Hidden=false +NoDisplay=false +X-GNOME-Autostart-enabled=true diff --git a/command_line.cpp b/command_line.cpp deleted file mode 100644 index 39e20f3..0000000 --- a/command_line.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* -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 . -*/ - -#include "comfortable_swipe/lib" -#include // std::ios -#include // std::cin, std::cout, std::cerr -#include // std::string - -/* Command-line function. */ - -int main(int argc, char **args) { - // improve buffering by decoupling loggers from stdio - std::ios::sync_with_stdio(false); - std::cin.tie(0); - std::cout.tie(0); - std::cerr.tie(0); - - if (argc > 1) { - std::string arg = args[1]; - - // select based on argument - if (arg == "start") - comfortable_swipe::service::start(); - - else if (arg == "stop") - comfortable_swipe::service::stop(); - - else if (arg == "restart") - comfortable_swipe::service::restart(); - - else if (arg == "buffer") - comfortable_swipe::service::buffer(); - - else if (arg == "autostart") - comfortable_swipe::service::autostart(); - - else if (arg == "config") - comfortable_swipe::service::config(); - - else if (arg == "debug") - comfortable_swipe::service::debug(); - - else if (arg == "status") - comfortable_swipe::service::status(); - - else /* if (arg == "help") */ - comfortable_swipe::service::help(); - } else - comfortable_swipe::service::help(); - - return 0; -} diff --git a/cpp.compile.sh b/compile.sh similarity index 57% rename from cpp.compile.sh rename to compile.sh index 6dcd0ac..86b9b76 100755 --- a/cpp.compile.sh +++ b/compile.sh @@ -2,8 +2,12 @@ dir="$(dirname $0)" version="$(cat "$dir/VERSION" | tr -d '[:space:]')" +libraries=-lxdo -lconfig++ exec g++ $@ \ - -std=c++11 \ - -O2 -lxdo -Wno-unused-result \ + -std=c++14 \ + -O2 \ + -lxdo \ + -lconfig++ \ + -Wno-unused-result \ -DCOMFORTABLE_SWIPE_VERSION="\"$version\""