diff --git a/.gitignore b/.gitignore index fda7434..a747486 100644 --- a/.gitignore +++ b/.gitignore @@ -195,3 +195,7 @@ fabric.properties # Editor-based Rest Client .idea/httpRequests +# LSP stuff +.ccls* +.clangd + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a35a27f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ +language: c +compiler: gcc +os: linux +dist: xenial +before_install: +- docker build -t builder -f build.Dockerfile . +script: +- docker run --rm --device /dev/input -v /run/udev/data:/run/udev/data -v $(pwd):/workdir + -w /workdir -it builder cmake . +- docker run --rm --device /dev/input -v /run/udev/data:/run/udev/data -v $(pwd):/workdir + -w /workdir -it builder make +deploy: + provider: releases + file: "gebaard" + skip_cleanup: true + on: + repo: Coffee2CodeNL/gebaar-libinput + tags: true + api_key: + secure: cocUFpTCvgEjAZzj5EC9iglQ4j1zx71a+U4aPLVUgNQNsFCZusSU//4ZEEwh1uYkJnnoxfhooRhAdgyMaWJs9Uvj7lgxk3dmHkc5f/p6iTFVH1lhPz06BT1tZkWzMkxLFilX8o53xl18qIiGBhgWGvqc5jFCFPrMALphql3qNrI0L/ZZRgU6FP7sXMU4LIomcIUsXKhDUZdRnrDZ3TIYaCrHSZEvLHPPlbYEVHncdmWFoZBjLMQ/M+i0cOgTHybxA6vlyLzA7zoMSGBgM7jDPDE6UN275PJenHT04gn6b9E3Ye6KLqhO/NZWseWfMZkQAnR1HJ6SswpF45nqLoMpjhEjQ5stJKe2dEIbsoeTEjfJgY5usOzh9UBawp+5SyuXYTMGHyaYpJM5w55KQv4bkecQm0yLVg4GBD852lEN2YuWkoteRH+3NYRR2l+Y/xsGKLAxJS4LoO+Jocf6ZUDjhrAyec2bxztQE7VGko2ndwC7nWPO6W8YfWfvgc08lqux5cYJjPE6Jj4fwrxMFhQ03eREcK8iYGHXs4kRTEv8Fj+Q0APFWIXVm6D2s9vPKPmzY84Q/Hq6B1gZKWwbsMOLUerRzsm6LtAxgg+vUQ3EIlknL2ciOkjHtWItkM6kl3txfalrf8TDl+mSTKnLpj2ZO5NxfdXC0aC7AZFGNhn+zSM= diff --git a/README.md b/README.md index bb51f9e..79e6332 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Click to join: [![Discord](https://img.shields.io/discord/548978799136473106.svg 14. Reboot and see the magic ```toml -[commands.swipe.three] +[swipe.commands.three] left_up = "" right_up = "" up = "" @@ -46,7 +46,7 @@ down = "" left = "" right = "" -[commands.swipe.four] +[swipe.commands.four] left_up = "" right_up = "" up = "" @@ -55,8 +55,26 @@ right_down = "" down = "" left = "" right = "" + +[pinch.commands] +in = "" +out = "" + +[pinch.settings] +threshold = 0.25 +one_shot = false + + +[swipe.settings] +threshold = 0.5 +one_shot = true +trigger_on_release = false ``` +* `pinch.settings.threshold` key sets the distance between fingers where it shold trigger. + Defaults to `0.25` which means fingers should travel exactly 25% distance from their initial position. +* `swipe.settings.threshold` sets the limit when swipe gesture should be executed. Defaults to 0.5. + ### Repository versions ![](https://img.shields.io/aur/version/gebaar.svg?style=flat) @@ -67,7 +85,7 @@ right = "" _~/.config/gebaar/gebaard.toml_ ```toml -[commands.swipe.three] +[swipe.commands.three] left_up = "" right_up = "" up = "bspc node -f north" @@ -77,7 +95,8 @@ down = "bspc node -f south" left = "bspc node -f west" right = "bspc node -f east" -[commands.swipe.four] + +[swipe.commands.four] left_up = "" right_up = "" up = "rofi -show combi" @@ -86,16 +105,57 @@ right_down = "" down = "" left = "bspc desktop -f prev" right = "bspc desktop -f next" + +[pinch.commands.two] +in = "xdotool key Control_L+equal" +out = "xdotool key Control_L+minus" + +[pinch.settings] +threshold=0.25 +one_shot=false + +[swipe.settings] +threshold = 0.5 +one_shot = true +trigger_on_release = false ``` Add `gebaard -b` to `~/.config/bspwm/bspwmrc` +**KDE Plasma 5** + +Any multiword value (like in some qdbus commands) must have escaped double quotes to work (`\"`). + +Additionally, in some distros (like openSUSE), the `qdbus` command might be named `qdbus-qt5`. +Some navigation-related qdbus commands are provided directly by KWin and can be seen via `qdbus org.kde.KWin /KWin`, +but most shortcuts can be checked with `qdbus org.kde.kglobalaccel /component/kwin org.kde.kglobalaccel.Component.shortcutNames`. + +For Wayland users, a tool similar to `xdotool` in functionality is [ydotool](https://github.com/ReimuNotMoe/ydotool). + +```toml +[swipe.commands.three] +up = "qdbus org.kde.kglobalaccel /component/kwin invokeShortcut \"Switch One Desktop Up\"" +down = "qdbus org.kde.kglobalaccel /component/kwin invokeShortcut \"Switch One Desktop Down\"" +left = "qdbus org.kde.kglobalaccel /component/kwin invokeShortcut \"Switch One Desktop to the Left\"" +right = "qdbus org.kde.kglobalaccel /component/kwin invokeShortcut \"Switch One Desktop to the Right\"" + +[swipe.commands.four] +up = "qdbus org.kde.kglobalaccel /component/kwin invokeShortcut \"Window Maximize\"" +down = "qdbus org.kde.kglobalaccel /component/kwin invokeShortcut \"Window Minimize\"" +left = "xdotool key alt+Left" +right = "xdotool key alt+Right" +``` + ### State of the project - [x] Receiving swipe events from libinput -- [ ] Receiving pinch/zoom events from libinput +- [x] Swipe gesture have trigger treshold +- [x] Receiving pinch/zoom events from libinput +- [x] Support continous pinch +- [ ] Support pinch-and-rotate gestures - [ ] Receiving rotation events from libinput - [x] Converting libinput events to motions - [x] Running commands based on motions - [x] Refactor code to be up to Release standards, instead of testing-hell + diff --git a/assets/PKGBUILD b/assets/PKGBUILD new file mode 100644 index 0000000..4d40549 --- /dev/null +++ b/assets/PKGBUILD @@ -0,0 +1,25 @@ +# Maintainer: Alex Zaslavsky + +pkgname=gebaar-libinput +pkgver=0.1.4 +pkgrel=1 +pkgdesc='A Super Simple WM Independent Touchpad Gesture Daemon for libinput.' +arch=('x86_64') +url="https://github.com/Coffee2CodeNL/$pkgname" +license=('GPL3') +depends=('libinput') +conflicts=('gebaar') +provides=('gebaar') +source=('https://github.com/Coffee2CodeNL/gebaar-libinput/releases/download/v0.1.4/gebaard' + 'https://raw.githubusercontent.com/Coffee2CodeNL/gebaar-libinput/master/LICENSE' + 'https://raw.githubusercontent.com/Coffee2CodeNL/gebaar-libinput/master/README.md') +md5sums=('74efb11da5bb1714e114e6305d999186' + 'SKIP' + 'SKIP') + + +package() { + install -Dm755 gebaard "$pkgdir/usr/bin/gebaard" + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md" +} diff --git a/assets/PKGBUILD.git b/assets/PKGBUILD.git new file mode 100644 index 0000000..1136d70 --- /dev/null +++ b/assets/PKGBUILD.git @@ -0,0 +1,42 @@ +# Maintainer: Alex Zaslavsky + +pkgname=gebaar-libinput-git +pkgver=r47.07b0ca8 +pkgrel=1 +pkgdesc='A Super Simple WM Independent Touchpad Gesture Daemon for libinput.' +arch=('x86_64') +url="https://github.com/Coffee2CodeNL/gebaar-libinput" +license=('GPL3') +depends=('libinput') +makedepends=('cmake' 'git') +conflicts=('gebaar') +provides=('gebaar') +source=('git+https://github.com/Coffee2CodeNL/gebaar-libinput') +md5sums=('SKIP') + + +pkgver() { + cd "$srcdir/gebaar-libinput" + printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" +} + +prepare() { + cd $srcdir/gebaar-libinput + git submodule init + git submodule update + mkdir -p build && cd build + cmake -DCMAKE_INSTALL_PREFIX="/usr" .. +} + +build() { + cd $srcdir/gebaar-libinput/build + make -j$(nproc) +} + +package() { + cd $srcdir/gebaar-libinput/build + make DESTDIR="$pkgdir" install/strip + cd .. + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md" +} diff --git a/gebaar-libinput.desktop b/assets/gebaar-libinput.desktop similarity index 100% rename from gebaar-libinput.desktop rename to assets/gebaar-libinput.desktop diff --git a/assets/gebaard.service b/assets/gebaard.service new file mode 100644 index 0000000..b953f29 --- /dev/null +++ b/assets/gebaard.service @@ -0,0 +1,10 @@ +[Unit] +Description=Gebaar Daemon +Documentation=https://github.com/Coffee2CodeNL/gebaar-libinput + +[Service] +ExecStart=/usr/local/bin/gebaard +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/build.Dockerfile b/build.Dockerfile new file mode 100644 index 0000000..84a1a27 --- /dev/null +++ b/build.Dockerfile @@ -0,0 +1,7 @@ +FROM debian:stretch + +RUN set -ex; \ + echo 'deb http://http.us.debian.org/debian/ testing non-free contrib main' > /etc/apt/sources.list.d/debian-testing.list; \ + apt-get update -q; \ + apt-get install -y git gcc-8 curl make cmake build-essential libinput-dev zlib1g-dev libinput-tools libsystemd-dev + diff --git a/src/config/config.cpp b/src/config/config.cpp index bafa3c6..09ddbef 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -1,101 +1,137 @@ /* gebaar Copyright (C) 2019 coffee2code - + 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 #include "config.h" #include "../util.h" +#include /** * Check if config file exists at current path */ -bool gebaar::config::Config::config_file_exists() -{ - auto true_path = std::filesystem::path(config_file_path); - return std::filesystem::exists(true_path); +bool gebaar::config::Config::config_file_exists() { + auto true_path = std::filesystem::path(config_file_path); + return std::filesystem::exists(true_path); } /** * Load Configuration from TOML file */ -void gebaar::config::Config::load_config() -{ - if (find_config_file()) { - if (config_file_exists()) { - try { - config = cpptoml::parse_file(std::filesystem::path(config_file_path)); - } catch (const cpptoml::parse_exception& e) { - std::cerr << e.what() << std::endl; - exit(EXIT_FAILURE); - } - swipe_three_commands[1] = *config->get_qualified_as("commands.swipe.three.left_up"); - swipe_three_commands[2] = *config->get_qualified_as("commands.swipe.three.up"); - swipe_three_commands[3] = *config->get_qualified_as("commands.swipe.three.right_up"); - swipe_three_commands[4] = *config->get_qualified_as("commands.swipe.three.left"); - swipe_three_commands[6] = *config->get_qualified_as("commands.swipe.three.right"); - swipe_three_commands[7] = *config->get_qualified_as("commands.swipe.three.left_down"); - swipe_three_commands[8] = *config->get_qualified_as("commands.swipe.three.down"); - swipe_three_commands[9] = *config->get_qualified_as("commands.swipe.three.right_down"); +void gebaar::config::Config::load_config() { + if (find_config_file()) { + if (config_file_exists()) { + try { + config = cpptoml::parse_file(std::filesystem::path(config_file_path)); + } catch (const cpptoml::parse_exception &e) { + std::cerr << e.what() << std::endl; + exit(EXIT_FAILURE); + } - swipe_four_commands[1] = *config->get_qualified_as("commands.swipe.four.left_up"); - swipe_four_commands[2] = *config->get_qualified_as("commands.swipe.four.up"); - swipe_four_commands[3] = *config->get_qualified_as("commands.swipe.four.right_up"); - swipe_four_commands[4] = *config->get_qualified_as("commands.swipe.four.left"); - swipe_four_commands[6] = *config->get_qualified_as("commands.swipe.four.right"); - swipe_four_commands[7] = *config->get_qualified_as("commands.swipe.four.left_down"); - swipe_four_commands[8] = *config->get_qualified_as("commands.swipe.four.down"); - swipe_four_commands[9] = *config->get_qualified_as("commands.swipe.four.right_down"); + /* Swipe Settings */ + swipe_three_commands[1] = *config->get_qualified_as( + "swipe.commands.three.left_up"); + swipe_three_commands[2] = + *config->get_qualified_as("swipe.commands.three.up"); + swipe_three_commands[3] = *config->get_qualified_as( + "swipe.commands.three.right_up"); + swipe_three_commands[4] = + *config->get_qualified_as("swipe.commands.three.left"); + swipe_three_commands[6] = + *config->get_qualified_as("swipe.commands.three.right"); + swipe_three_commands[7] = *config->get_qualified_as( + "swipe.commands.three.left_down"); + swipe_three_commands[8] = + *config->get_qualified_as("swipe.commands.three.down"); + swipe_three_commands[9] = *config->get_qualified_as( + "swipe.commands.three.right_down"); - loaded = true; - } - } + swipe_four_commands[1] = + *config->get_qualified_as("swipe.commands.four.left_up"); + swipe_four_commands[2] = + *config->get_qualified_as("swipe.commands.four.up"); + swipe_four_commands[3] = *config->get_qualified_as( + "swipe.commands.four.right_up"); + swipe_four_commands[4] = + *config->get_qualified_as("swipe.commands.four.left"); + swipe_four_commands[6] = + *config->get_qualified_as("swipe.commands.four.right"); + swipe_four_commands[7] = *config->get_qualified_as( + "swipe.commands.four.left_down"); + swipe_four_commands[8] = + *config->get_qualified_as("swipe.commands.four.down"); + swipe_four_commands[9] = *config->get_qualified_as( + "swipe.commands.four.right_down"); + + settings.swipe_threshold = + config->get_qualified_as("swipe.settings.threshold") + .value_or(0.5); + settings.swipe_one_shot = + config->get_qualified_as("swipe.settings.one_shot") + .value_or(true); + settings.swipe_trigger_on_release = + config->get_qualified_as("swipe.settings.trigger_on_release") + .value_or(true); + + /* Pinch settings */ + pinch_commands[PINCH_IN] = + *config->get_qualified_as("pinch.commands.two.out"); + pinch_commands[PINCH_OUT] = + *config->get_qualified_as("pinch.commands.two.in"); + settings.pinch_threshold = + config->get_qualified_as("pinch.settings.threshold") + .value_or(0.25); + settings.pinch_one_shot = + config->get_qualified_as("pinch.settings.one_shot") + .value_or(false); + + loaded = true; + } + } } /** * Find the configuration file according to XDG spec * @return bool */ -bool gebaar::config::Config::find_config_file() -{ - std::string temp_path = gebaar::util::stringFromCharArray(getenv("XDG_CONFIG_HOME")); +bool gebaar::config::Config::find_config_file() { + std::string temp_path = + gebaar::util::stringFromCharArray(getenv("XDG_CONFIG_HOME")); + if (temp_path.empty()) { + // first get the path to HOME + temp_path = gebaar::util::stringFromCharArray(getenv("HOME")); if (temp_path.empty()) { - // first get the path to HOME - temp_path = gebaar::util::stringFromCharArray(getenv("HOME")); - if (temp_path.empty()) { - temp_path = getpwuid(getuid())->pw_dir; - } - // then append .config - if (!temp_path.empty()) { - temp_path.append("/.config"); - } + temp_path = getpwuid(getuid())->pw_dir; } + // then append .config if (!temp_path.empty()) { - config_file_path = temp_path; - config_file_path.append("/gebaar/gebaard.toml"); - return true; + temp_path.append("/.config"); } - return false; + } + if (!temp_path.empty()) { + config_file_path = temp_path; + config_file_path.append("/gebaar/gebaard.toml"); + return true; + } + return false; } -gebaar::config::Config::Config() -{ - if (!loaded) { - load_config(); - } +gebaar::config::Config::Config() { + if (!loaded) { + load_config(); + } } diff --git a/src/config/config.h b/src/config/config.h index 499eb51..cde96c9 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -1,17 +1,17 @@ /* gebaar Copyright (C) 2019 coffee2code - + 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 . */ @@ -21,28 +21,40 @@ #include #include -#include #include +#include namespace gebaar::config { - class Config { - public: - Config(); +class Config { +public: + Config(); + + bool loaded = false; + + void load_config(); + + struct settings { + bool pinch_one_shot; + double pinch_threshold; - bool loaded = false; + bool swipe_one_shot; + double swipe_threshold; + bool swipe_trigger_on_release; + } settings; - void load_config(); + enum pinch { PINCH_IN, PINCH_OUT }; + std::string swipe_three_commands[10]; + std::string swipe_four_commands[10]; + std::string pinch_commands[10]; - std::string swipe_three_commands[10]; - std::string swipe_four_commands[10]; +private: + bool config_file_exists(); - private: - bool config_file_exists(); + bool find_config_file(); - bool find_config_file(); + std::string config_file_path; + std::shared_ptr config; +}; +} // namespace gebaar::config - std::string config_file_path; - std::shared_ptr config; - }; -} -#endif //GEBAAR_CONFIG_H +#endif // GEBAAR_CONFIG_H diff --git a/src/daemonizer.cpp b/src/daemonizer.cpp index 3c0e383..639c818 100644 --- a/src/daemonizer.cpp +++ b/src/daemonizer.cpp @@ -1,22 +1,21 @@ /* gebaar Copyright (C) 2019 coffee2code - + 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 "daemonizer.h" /** @@ -24,40 +23,45 @@ * * @return bool that denotes fork success */ -bool gebaar::daemonizer::Daemonizer::daemonize() -{ - pid_t pid = 0; - pid = fork(); - if (pid<0) { - exit(EXIT_FAILURE); - } - if (pid>0) { - exit(EXIT_SUCCESS); - } - if (setsid()<0) { - // Boo. - } - signal(SIGCHLD, SIG_IGN); - signal(SIGTRAP, SIG_IGN); - pid = fork(); - if (pid<0) { - exit(EXIT_FAILURE); - } - if (pid>0) { - exit(EXIT_SUCCESS); - } - umask(0); - if ((chdir("/"))<0) { - return false; - } - close(STDOUT_FILENO); - close(STDIN_FILENO); - close(STDERR_FILENO); - if (getpid()!=getsid(getpid())) { - // - } - return true; +bool gebaar::daemonizer::Daemonizer::daemonize() { + pid_t pid = 0; + pid = fork(); + if (pid < 0) { + exit(EXIT_FAILURE); + } + + if (pid > 0) { + exit(EXIT_SUCCESS); + } + + if (setsid() < 0) { + // Boo. + } + + signal(SIGCHLD, SIG_IGN); + signal(SIGTRAP, SIG_IGN); + + pid = fork(); + if (pid < 0) { + exit(EXIT_FAILURE); + } + + if (pid > 0) { + exit(EXIT_SUCCESS); + } + umask(0); + if ((chdir("/")) < 0) { + return false; + } + + close(STDOUT_FILENO); + close(STDIN_FILENO); + close(STDERR_FILENO); + if (getpid() != getsid(getpid())) { + // + } + return true; } gebaar::daemonizer::Daemonizer::Daemonizer() = default; diff --git a/src/daemonizer.h b/src/daemonizer.h index ca60a15..debce01 100644 --- a/src/daemonizer.h +++ b/src/daemonizer.h @@ -1,33 +1,36 @@ /* gebaar Copyright (C) 2019 coffee2code - + 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 GEBAAR_DAEMONIZER_H +#define GEBAAR_DAEMONIZER_H + #include -#include #include #include +#include -#ifndef GEBAAR_DAEMONIZER_H -#define GEBAAR_DAEMONIZER_H namespace gebaar::daemonizer { - class Daemonizer { - public: - Daemonizer(); - ~Daemonizer(); - bool daemonize(); - }; -} -#endif //GEBAAR_DAEMONIZER_H +class Daemonizer { +public: + Daemonizer(); + ~Daemonizer(); + bool daemonize(); +}; +} // namespace gebaar::daemonizer + +#endif // GEBAAR_DAEMONIZER_H diff --git a/src/io/input.cpp b/src/io/input.cpp index 943dc96..415fdab 100644 --- a/src/io/input.cpp +++ b/src/io/input.cpp @@ -1,33 +1,37 @@ /* gebaar Copyright (C) 2019 coffee2code - + 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 #include "input.h" +#include /** - * Input system constructor, we pass our Configuration object via a shared pointer + * Input system constructor, we pass our Configuration object via a shared + * pointer * * @param config_ptr shared pointer to configuration object */ -gebaar::io::Input::Input(std::shared_ptr const& config_ptr) -{ - config = config_ptr; - gesture_swipe_event = {}; +gebaar::io::Input::Input( + std::shared_ptr const &config_ptr) { + config = config_ptr; + gesture_swipe_event = {}; + + gesture_pinch_event = {}; + gesture_pinch_event.scale = DEFAULT_SCALE; } /** @@ -35,193 +39,299 @@ gebaar::io::Input::Input(std::shared_ptr const& config_p * * @return bool */ -bool gebaar::io::Input::initialize_context() -{ - udev = udev_new(); - libinput = libinput_udev_create_context(&libinput_interface, nullptr, udev); - return libinput_udev_assign_seat(libinput, "seat0")==0; +bool gebaar::io::Input::initialize_context() { + udev = udev_new(); + libinput = libinput_udev_create_context(&libinput_interface, nullptr, udev); + return libinput_udev_assign_seat(libinput, "seat0") == 0; +} + +/** + * Reset swipe event struct to defaults + */ +void gebaar::io::Input::reset_swipe_event() { + gesture_swipe_event = {}; + gesture_swipe_event.executed = false; +} + +/** + * Reset pinch event struct to defaults + */ +void gebaar::io::Input::reset_pinch_event() { + gesture_pinch_event = {}; + gesture_pinch_event.scale = DEFAULT_SCALE; + gesture_pinch_event.executed = false; +} + +/** + * Pinch one_shot gesture handle + * @param new_scale last reported scale between the fingers + */ +void gebaar::io::Input::handle_one_shot_pinch(double new_scale) { + if (new_scale > gesture_pinch_event.scale) { // Scale up + // Add 1 to required distance to get 2 > x > 1 + if (new_scale > 1 + config->settings.pinch_threshold) { + std::system(config->pinch_commands[config->PINCH_IN].c_str()); + gesture_pinch_event.executed = true; + } + } else { // Scale Down + // Substract from 1 to have inverted value for pinch in gesture + if (gesture_pinch_event.scale < 1 - config->settings.pinch_threshold) { + std::system(config->pinch_commands[config->PINCH_OUT].c_str()); + gesture_pinch_event.executed = true; + } + } +} + +/** + * Pinch continous gesture handle + * Calculates the trigger value according to current step + * @param new_scale last reported scale between the fingers + */ +void gebaar::io::Input::handle_continouos_pinch(double new_scale) { + int step = gesture_pinch_event.step == 0 ? gesture_pinch_event.step + 1 + : gesture_pinch_event.step; + double trigger = 1 + (config->settings.pinch_threshold * step); + + if (new_scale > gesture_pinch_event.scale) { // Scale up + if (new_scale >= trigger) { + std::system(config->pinch_commands[config->PINCH_IN].c_str()); + inc_step(gesture_pinch_event.step); + } + } else { // Scale down + if (new_scale <= trigger) { + std::system(config->pinch_commands[config->PINCH_OUT].c_str()); + dec_step(gesture_pinch_event.step); + } + } } /** - * This event has no coordinates, so it's an event that gives us a begin or end signal. - * If it begins, we get the amount of fingers used. - * If it ends, we check what kind of gesture we received. + * Pinch Gesture + * Currently supporting only "one shot" pinch-in and pinch-out gestures. + * @param gev Gesture Event + * @param begin Boolean to denote begin or continuation of gesture. + **/ +void gebaar::io::Input::handle_pinch_event(libinput_event_gesture *gev, + bool begin) { + if (begin) { + reset_pinch_event(); + gesture_pinch_event.fingers = libinput_event_gesture_get_finger_count(gev); + } else { + double new_scale = libinput_event_gesture_get_scale(gev); + if (config->settings.pinch_one_shot && !gesture_pinch_event.executed) + handle_one_shot_pinch(new_scale); + if (!config->settings.pinch_one_shot) + handle_continouos_pinch(new_scale); + gesture_pinch_event.scale = new_scale; + } +} + +/** + * This event has no coordinates, so it's an event that gives us a begin or end + * signal. If it begins, we get the amount of fingers used. If it ends, we check + * what kind of gesture we received. * * @param gev Gesture Event * @param begin Boolean to denote begin or end of gesture */ -void gebaar::io::Input::handle_swipe_event_without_coords(libinput_event_gesture* gev, bool begin) -{ - if (begin) { - gesture_swipe_event.fingers = libinput_event_gesture_get_finger_count(gev); - } - else { - double x = gesture_swipe_event.x; - double y = gesture_swipe_event.y; - int swipe_type = 5; // middle = no swipe - // 1 = left_up, 2 = up, 3 = right_up... - // 1 2 3 - // 4 5 6 - // 7 8 9 - const double OBLIQUE_RATIO = 0.414; // =~ tan(22.5); - - if (abs(x) > abs(y)) { - // left or right swipe - swipe_type += x < 0 ? -1 : 1; - - // check for oblique swipe - if (abs(y) / abs(x) > OBLIQUE_RATIO) { - swipe_type += y < 0 ? -3 : 3; - } - } else { - // up of down swipe - swipe_type += y < 0 ? -3 : 3; - - // check for oblique swipe - if (abs(x) / abs(y) > OBLIQUE_RATIO) { - swipe_type += x < 0 ? -1 : 1; - } - } - - if (gesture_swipe_event.fingers == 3) { - std::system(config->swipe_three_commands[swipe_type].c_str()); - } else if (gesture_swipe_event.fingers == 4) { - std::system(config->swipe_four_commands[swipe_type].c_str()); - } - - gesture_swipe_event = {}; +void gebaar::io::Input::handle_swipe_event_without_coords( + libinput_event_gesture *gev, bool begin) { + if (begin) { + gesture_swipe_event.fingers = libinput_event_gesture_get_finger_count(gev); + } + // This executed when fingers left the touchpad + else { + if (!gesture_swipe_event.executed && + config->settings.swipe_trigger_on_release) { + trigger_swipe_command(); } + reset_swipe_event(); + } } /** * Swipe events with coordinates, add it to the current tally * @param gev Gesture Event */ -void gebaar::io::Input::handle_swipe_event_with_coords(libinput_event_gesture* gev) -{ - gesture_swipe_event.x += libinput_event_gesture_get_dx(gev); - gesture_swipe_event.y += libinput_event_gesture_get_dy(gev); +void gebaar::io::Input::handle_swipe_event_with_coords( + libinput_event_gesture *gev) { + if (config->settings.swipe_one_shot && gesture_swipe_event.executed) + return; + + // Since swipe gesture counts in dpi we have to convert + int threshold_x = config->settings.swipe_threshold * SWIPE_X_THRESHOLD * + gesture_swipe_event.step; + int threshold_y = config->settings.swipe_threshold * SWIPE_Y_THRESHOLD * + gesture_swipe_event.step; + gesture_swipe_event.x += libinput_event_gesture_get_dx_unaccelerated(gev); + gesture_swipe_event.y += libinput_event_gesture_get_dy_unaccelerated(gev); + if (abs(gesture_swipe_event.x) > threshold_x || + abs(gesture_swipe_event.y) > threshold_y) { + trigger_swipe_command(); + gesture_swipe_event.executed = true; + inc_step(gesture_swipe_event.step); + } +} + +/** + * Making calculation for swipe direction and triggering + * command accordingly + */ +void gebaar::io::Input::trigger_swipe_command() { + double x = gesture_swipe_event.x; + double y = gesture_swipe_event.y; + int swipe_type = 5; // middle = no swipe + // 1 = left_up, 2 = up, 3 = right_up... + // 1 2 3 + // 4 5 6 + // 7 8 9 + const double OBLIQUE_RATIO = 0.414; // =~ tan(22.5); + + if (abs(x) > abs(y)) { + // left or right swipe + swipe_type += x < 0 ? -1 : 1; + + // check for oblique swipe + if (abs(y) / abs(x) > OBLIQUE_RATIO) { + swipe_type += y < 0 ? -3 : 3; + } + } else { + // up of down swipe + swipe_type += y < 0 ? -3 : 3; + + // check for oblique swipe + if (abs(x) / abs(y) > OBLIQUE_RATIO) { + swipe_type += x < 0 ? -1 : 1; + } + } + + if (gesture_swipe_event.fingers == 3) { + std::system(config->swipe_three_commands[swipe_type].c_str()); + } else if (gesture_swipe_event.fingers == 4) { + std::system(config->swipe_four_commands[swipe_type].c_str()); + } } /** * Initialize the input system * @return bool */ -bool gebaar::io::Input::initialize() -{ - initialize_context(); - return gesture_device_exists(); +bool gebaar::io::Input::initialize() { + initialize_context(); + return gesture_device_exists(); } /** * Run a poll loop on the file descriptor that libinput gives us */ -void gebaar::io::Input::start_loop() -{ - struct pollfd fds{}; - fds.fd = libinput_get_fd(libinput); - fds.events = POLLIN; - fds.revents = 0; - - while (poll(&fds, 1, -1)>-1) { - handle_event(); - } -} +void gebaar::io::Input::start_loop() { + struct pollfd fds {}; + fds.fd = libinput_get_fd(libinput); + fds.events = POLLIN; + fds.revents = 0; -gebaar::io::Input::~Input() -{ - libinput_unref(libinput); + while (poll(&fds, 1, -1) > -1) { + handle_event(); + } } +gebaar::io::Input::~Input() { libinput_unref(libinput); } + /** * Check if there's a device that supports gestures on this system * @return */ -bool gebaar::io::Input::gesture_device_exists() -{ - bool device_found = false; - while ((libinput_event = libinput_get_event(libinput))!=nullptr) { - auto device = libinput_event_get_device(libinput_event); - if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_GESTURE)) { - device_found = true; - } - - libinput_event_destroy(libinput_event); - libinput_dispatch(libinput); +bool gebaar::io::Input::gesture_device_exists() { + bool device_found = false; + + while ((libinput_event = libinput_get_event(libinput)) != nullptr) { + auto device = libinput_event_get_device(libinput_event); + if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_GESTURE)) { + device_found = true; } - return device_found; + + libinput_event_destroy(libinput_event); + libinput_dispatch(libinput); + } + return device_found; } /** * Handle an event from libinput and run the appropriate action per event type */ -void gebaar::io::Input::handle_event() -{ - libinput_dispatch(libinput); - while ((libinput_event = libinput_get_event(libinput))) { - switch (libinput_event_get_type(libinput_event)) { - case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: - handle_swipe_event_without_coords(libinput_event_get_gesture_event(libinput_event), true); - break; - case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: - handle_swipe_event_with_coords(libinput_event_get_gesture_event(libinput_event)); - break; - case LIBINPUT_EVENT_GESTURE_SWIPE_END: - handle_swipe_event_without_coords(libinput_event_get_gesture_event(libinput_event), false); - break; - case LIBINPUT_EVENT_NONE: - break; - case LIBINPUT_EVENT_DEVICE_ADDED: - break; - case LIBINPUT_EVENT_DEVICE_REMOVED: - break; - case LIBINPUT_EVENT_KEYBOARD_KEY: - break; - case LIBINPUT_EVENT_POINTER_MOTION: - break; - case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: - break; - case LIBINPUT_EVENT_POINTER_BUTTON: - break; - case LIBINPUT_EVENT_POINTER_AXIS: - break; - case LIBINPUT_EVENT_TOUCH_DOWN: - break; - case LIBINPUT_EVENT_TOUCH_UP: - break; - case LIBINPUT_EVENT_TOUCH_MOTION: - break; - case LIBINPUT_EVENT_TOUCH_CANCEL: - break; - case LIBINPUT_EVENT_TOUCH_FRAME: - break; - case LIBINPUT_EVENT_TABLET_TOOL_AXIS: - break; - case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: - break; - case LIBINPUT_EVENT_TABLET_TOOL_TIP: - break; - case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: - break; - case LIBINPUT_EVENT_TABLET_PAD_BUTTON: - break; - case LIBINPUT_EVENT_TABLET_PAD_RING: - break; - case LIBINPUT_EVENT_TABLET_PAD_STRIP: - break; - case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: - break; - case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: - break; - case LIBINPUT_EVENT_GESTURE_PINCH_END: - break; - case LIBINPUT_EVENT_SWITCH_TOGGLE: - break; - } - - libinput_event_destroy(libinput_event); - libinput_dispatch(libinput); +void gebaar::io::Input::handle_event() { + libinput_dispatch(libinput); + while ((libinput_event = libinput_get_event(libinput))) { + switch (libinput_event_get_type(libinput_event)) { + case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: + handle_swipe_event_without_coords( + libinput_event_get_gesture_event(libinput_event), true); + break; + case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: + handle_swipe_event_with_coords( + libinput_event_get_gesture_event(libinput_event)); + break; + case LIBINPUT_EVENT_GESTURE_SWIPE_END: + handle_swipe_event_without_coords( + libinput_event_get_gesture_event(libinput_event), false); + break; + case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: + handle_pinch_event(libinput_event_get_gesture_event(libinput_event), + true); + break; + case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: + handle_pinch_event(libinput_event_get_gesture_event(libinput_event), + false); + break; + case LIBINPUT_EVENT_GESTURE_PINCH_END: + handle_pinch_event(libinput_event_get_gesture_event(libinput_event), + false); + break; + case LIBINPUT_EVENT_NONE: + break; + case LIBINPUT_EVENT_DEVICE_ADDED: + break; + case LIBINPUT_EVENT_DEVICE_REMOVED: + break; + case LIBINPUT_EVENT_KEYBOARD_KEY: + break; + case LIBINPUT_EVENT_POINTER_MOTION: + break; + case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: + break; + case LIBINPUT_EVENT_POINTER_BUTTON: + break; + case LIBINPUT_EVENT_POINTER_AXIS: + break; + case LIBINPUT_EVENT_TOUCH_DOWN: + break; + case LIBINPUT_EVENT_TOUCH_UP: + break; + case LIBINPUT_EVENT_TOUCH_MOTION: + break; + case LIBINPUT_EVENT_TOUCH_CANCEL: + break; + case LIBINPUT_EVENT_TOUCH_FRAME: + break; + case LIBINPUT_EVENT_TABLET_TOOL_AXIS: + break; + case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: + break; + case LIBINPUT_EVENT_TABLET_TOOL_TIP: + break; + case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: + break; + case LIBINPUT_EVENT_TABLET_PAD_BUTTON: + break; + case LIBINPUT_EVENT_TABLET_PAD_RING: + break; + case LIBINPUT_EVENT_TABLET_PAD_STRIP: + break; + case LIBINPUT_EVENT_SWITCH_TOGGLE: + break; } -} - + libinput_event_destroy(libinput_event); + libinput_dispatch(libinput); + } +} diff --git a/src/io/input.h b/src/io/input.h index b40d61f..6c29ba3 100644 --- a/src/io/input.h +++ b/src/io/input.h @@ -1,17 +1,17 @@ /* gebaar Copyright (C) 2019 coffee2code - + 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 . */ @@ -19,62 +19,103 @@ #ifndef GEBAAR_INPUT_HPP #define GEBAAR_INPUT_HPP -#include +#include "../config/config.h" #include +#include #include -#include "../config/config.h" + +#define DEFAULT_SCALE 1.0 +#define SWIPE_X_THRESHOLD 1000 +#define SWIPE_Y_THRESHOLD 500 namespace gebaar::io { - struct gesture_swipe_event { - int fingers; - double x; - double y; - }; +struct gesture_swipe_event { + int fingers; + double x; + double y; + + bool executed; + int step; +}; + +struct gesture_pinch_event { + int fingers; + double scale; + double angle; + + bool executed; + int step; +}; + +class Input { +public: + Input(std::shared_ptr const &config_ptr); + + ~Input(); + + bool initialize(); + + void start_loop(); + +private: + std::shared_ptr config; + + struct libinput *libinput; + struct libinput_event *libinput_event; + struct udev *udev; + + struct gesture_swipe_event gesture_swipe_event; + struct gesture_pinch_event gesture_pinch_event; + + bool initialize_context(); + + bool gesture_device_exists(); - class Input { - public: - Input(std::shared_ptr const& config_ptr); + static int open_restricted(const char *path, int flags, void *user_data) { + int fd = open(path, flags); + return fd < 0 ? -errno : fd; + } - ~Input(); + static void close_restricted(int fd, void *user_data) { close(fd); } - bool initialize(); + constexpr static struct libinput_interface libinput_interface = { + .open_restricted = open_restricted, + .close_restricted = close_restricted, + }; - void start_loop(); + /* + * Decrements step of current trigger. Just to skip 0 + * @param cur current step + */ + inline void dec_step(int &cur) { --cur == 0 ? --cur : cur; } - private: - std::shared_ptr config; + /* + * Increase step of current trigger. Just to pass -1 + * @param cur current step + */ + inline void inc_step(int &cur) { ++cur == 0 ? ++cur : cur; } - struct libinput* libinput; - struct libinput_event* libinput_event; - struct udev* udev; - struct gesture_swipe_event gesture_swipe_event; + void handle_event(); - bool initialize_context(); + /* Swipe event */ + void reset_swipe_event(); - bool gesture_device_exists(); + void handle_swipe_event_without_coords(libinput_event_gesture *gev, + bool begin); - static int open_restricted(const char* path, int flags, void* user_data) - { - int fd = open(path, flags); - return fd<0 ? -errno : fd; - } + void handle_swipe_event_with_coords(libinput_event_gesture *gev); - static void close_restricted(int fd, void* user_data) - { - close(fd); - } + void trigger_swipe_command(); - constexpr static struct libinput_interface libinput_interface = { - .open_restricted = open_restricted, - .close_restricted = close_restricted, - }; + /* Pinch event */ + void reset_pinch_event(); - void handle_event(); + void handle_one_shot_pinch(double new_scale); - void handle_swipe_event_without_coords(libinput_event_gesture* gev, bool begin); + void handle_continouos_pinch(double new_scale); - void handle_swipe_event_with_coords(libinput_event_gesture* gev); - }; -} + void handle_pinch_event(libinput_event_gesture *gev, bool begin); +}; +} // namespace gebaar::io -#endif //GEBAAR_INPUT_HPP +#endif // GEBAAR_INPUT_HPP diff --git a/src/main.cpp b/src/main.cpp index 9a61700..e43a41d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,41 +16,43 @@ along with this program. If not, see . */ - -#include -#include #include "config/config.h" -#include "io/input.h" #include "daemonizer.h" +#include "io/input.h" +#include +#include + +gebaar::io::Input *input; + +int main(int argc, char *argv[]) { + cxxopts::Options options(argv[0], "Gebaard Gestures Daemon"); + + bool should_daemonize = false; -gebaar::io::Input* input; + options.add_options() + ("b,background", "Daemonize", cxxopts::value(should_daemonize)) + ("h,help", "Prints this help text"); -int main(int argc, char* argv[]) -{ - cxxopts::Options options(argv[0], "Gebaard Gestures Daemon"); + auto result = options.parse(argc, argv); - bool should_daemonize = false; + if (result.count("help")) { + std::cout << options.help() << std::endl; + exit(EXIT_SUCCESS); + } - options.add_options() - ("b,background", "Daemonize", cxxopts::value(should_daemonize)) - ("h,help", "Prints this help text"); + if (should_daemonize) { + auto *daemonizer = new gebaar::daemonizer::Daemonizer(); + daemonizer->daemonize(); + } - auto result = options.parse(argc, argv); + std::shared_ptr config = + std::make_shared(); - if (result.count("help")) { - std::cout << options.help() << std::endl; - exit(EXIT_SUCCESS); - } + input = new gebaar::io::Input(config); - if (should_daemonize) { - auto *daemonizer = new gebaar::daemonizer::Daemonizer(); - daemonizer->daemonize(); - } - std::shared_ptr config = std::make_shared(); - input = new gebaar::io::Input(config); - if (input->initialize()) { - input->start_loop(); - } + if (input->initialize()) { + input->start_loop(); + } - return 0; + return 0; } diff --git a/src/util.cpp b/src/util.cpp index a56dfe6..947a4a6 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -23,7 +23,6 @@ * @param charArr The char array to convert * @return charArr or an empty string, if charArr is a nullptr */ -std::string gebaar::util::stringFromCharArray(char* charArr) -{ - return charArr == nullptr ? "" : charArr; +std::string gebaar::util::stringFromCharArray(char *charArr) { + return charArr == nullptr ? "" : charArr; } diff --git a/src/util.h b/src/util.h index eafec82..5c0791e 100644 --- a/src/util.h +++ b/src/util.h @@ -22,7 +22,7 @@ #include namespace gebaar::util { - std::string stringFromCharArray(char* charArr); +std::string stringFromCharArray(char *charArr); } #endif // UTIL_H