diff --git a/README.md b/README.md index bb51f9e..030d725 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ Gebaar ========= - WM Independent Touchpad Gesture Daemon for libinput _Gebaar means Gesture in Dutch_ @@ -55,8 +54,20 @@ right_down = "" down = "" left = "" right = "" + +[commands.pinch] +in = "" +out = "" + +[settings] +pinch.distance = "" +swipe.threshold = "" ``` +* `settings.pinch.distance` key sets the distance between fingers where it shold trigger. + Defaults to `0.5` which means fingers should travel exactly half way from their initial position. +* `settings.swipe.threshold` sets the limit when swipe gesture should be executed. Defaults to 100. + ### Repository versions ![](https://img.shields.io/aur/version/gebaar.svg?style=flat) @@ -67,7 +78,7 @@ right = "" _~/.config/gebaar/gebaard.toml_ ```toml -[commands.swipe.three] +[swipe.commands.three] left_up = "" right_up = "" up = "bspc node -f north" @@ -77,7 +88,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,6 +98,19 @@ 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 = true ``` Add `gebaard -b` to `~/.config/bspwm/bspwmrc` @@ -93,9 +118,13 @@ Add `gebaard -b` to `~/.config/bspwm/bspwmrc` ### 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/src/config/config.cpp b/src/config/config.cpp index bafa3c6..0bbd0cc 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -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 . */ @@ -43,23 +43,37 @@ void gebaar::config::Config::load_config() 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"); - - 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"); + + 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; } diff --git a/src/config/config.h b/src/config/config.h index 499eb51..34d7a42 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 . */ @@ -33,16 +33,32 @@ namespace gebaar::config { void load_config(); + + struct settings { + bool pinch_one_shot; + double pinch_threshold; + + bool swipe_one_shot; + double swipe_threshold; + bool swipe_trigger_on_release; + } settings; + + enum pinch {PINCH_IN, PINCH_OUT}; std::string swipe_three_commands[10]; std::string swipe_four_commands[10]; + std::string pinch_commands[10]; private: + bool config_file_exists(); bool find_config_file(); + std::string config_file_path; std::shared_ptr config; + + }; } #endif //GEBAAR_CONFIG_H diff --git a/src/io/input.cpp b/src/io/input.cpp index 943dc96..4c95cf4 100644 --- a/src/io/input.cpp +++ b/src/io/input.cpp @@ -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 . */ @@ -28,6 +28,9 @@ gebaar::io::Input::Input(std::shared_ptr const& config_p { config = config_ptr; gesture_swipe_event = {}; + + gesture_pinch_event = {}; + gesture_pinch_event.scale = DEFAULT_SCALE; } /** @@ -42,6 +45,86 @@ bool gebaar::io::Input::initialize_context() 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); + } + } +} + +/** + * 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. @@ -55,41 +138,12 @@ void gebaar::io::Input::handle_swipe_event_without_coords(libinput_event_gesture if (begin) { gesture_swipe_event.fingers = libinput_event_gesture_get_finger_count(gev); } + // This executed when fingers left the touchpad 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.executed && config->settings.swipe_trigger_on_release) { + trigger_swipe_command(); } - - 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 = {}; + reset_swipe_event(); } } @@ -99,8 +153,57 @@ void gebaar::io::Input::handle_swipe_event_without_coords(libinput_event_gesture */ 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 = config->settings.swipe_threshold * 100; gesture_swipe_event.x += libinput_event_gesture_get_dx(gev); gesture_swipe_event.y += libinput_event_gesture_get_dy(gev); + if (abs(gesture_swipe_event.x) > threshold || abs(gesture_swipe_event.y) > threshold) { + trigger_swipe_command(); + gesture_swipe_event.executed = true; + } +} + + +/** + * 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()); + } + } /** @@ -140,7 +243,8 @@ gebaar::io::Input::~Input() bool gebaar::io::Input::gesture_device_exists() { bool device_found = false; - while ((libinput_event = libinput_get_event(libinput))!=nullptr) { + + 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; @@ -169,6 +273,15 @@ void gebaar::io::Input::handle_event() 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: @@ -209,12 +322,6 @@ void gebaar::io::Input::handle_event() 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; } diff --git a/src/io/input.h b/src/io/input.h index b40d61f..17eacd1 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 . */ @@ -24,11 +24,25 @@ #include #include "../config/config.h" +#define DEFAULT_SCALE 1.0 + namespace gebaar::io { 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 { @@ -47,7 +61,9 @@ namespace gebaar::io { 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(); @@ -56,7 +72,7 @@ namespace gebaar::io { static int open_restricted(const char* path, int flags, void* user_data) { int fd = open(path, flags); - return fd<0 ? -errno : fd; + return fd < 0 ? -errno : fd; } static void close_restricted(int fd, void* user_data) @@ -69,11 +85,38 @@ namespace gebaar::io { .close_restricted = close_restricted, }; + /* + * Decrements step of current trigger. Just to skip 0 + * @param cur current step + */ + inline void dec_step(int &cur) { --cur == 0 ? --cur : cur; } + + /* + * Increase step of current trigger. Just to pass -1 + * @param cur current step + */ + inline void inc_step(int &cur) { ++cur == 0 ? ++cur : cur; } + void handle_event(); + /* Swipe event */ + void reset_swipe_event(); + void handle_swipe_event_without_coords(libinput_event_gesture* gev, bool begin); void handle_swipe_event_with_coords(libinput_event_gesture* gev); + + void trigger_swipe_command(); + + /* Pinch event */ + void reset_pinch_event(); + + void handle_one_shot_pinch(double new_scale); + + void handle_continouos_pinch(double new_scale); + + void handle_pinch_event(libinput_event_gesture* gev, bool begin); + }; } diff --git a/src/main.cpp b/src/main.cpp index 9a61700..982ce3d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -48,6 +48,7 @@ int main(int argc, char* argv[]) } std::shared_ptr config = std::make_shared(); input = new gebaar::io::Input(config); + if (input->initialize()) { input->start_loop(); }