diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b3c3529e..37308dfd 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -6,7 +6,7 @@ on: [push, pull_request] jobs: lint: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout code uses: actions/checkout@v3 diff --git a/.github/workflows/release-ppa.yml b/.github/workflows/release-ppa.yml index 4384cc12..d8b32ea7 100644 --- a/.github/workflows/release-ppa.yml +++ b/.github/workflows/release-ppa.yml @@ -13,7 +13,7 @@ on: jobs: update-ppa-branch: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout code uses: actions/checkout@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0c822a64..aa87f22a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,13 +11,14 @@ on: jobs: deb-package: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 env: target_docker_image: debian:bookworm-backports target_distribution: bookworm strategy: matrix: - target_arch: ["amd64", "armhf", "arm64"] + target_arch: ["amd64"] + # target_arch: ["amd64", "armhf", "arm64"] steps: - name: GitHub Environment Variables Action uses: FranzDiebold/github-env-vars-action@v1.2.1 @@ -50,13 +51,13 @@ jobs: dpkg_buildpackage_opts: "--no-sign --no-check-builddeps --post-clean" - name: Upload Debian package artifacts - uses: "actions/upload-artifact@v2" + uses: "actions/upload-artifact@v4" with: name: "${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_${{matrix.target_arch}}.deb" path: "${{ github.workspace }}/artifacts/${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_${{matrix.target_arch}}.deb" rpm-package-rhel: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 container: image: almalinux:8 steps: @@ -91,20 +92,20 @@ jobs: rpmbuild -ba ${{ env.GITHUB_REPOSITORY_NAME }}/rpm/${{ env.GITHUB_REPOSITORY_NAME }}.spec - name: Upload RPM package - uses: "actions/upload-artifact@v2" + uses: "actions/upload-artifact@v4" with: name: "rhel8_${{ env.GITHUB_REPOSITORY_NAME }}-${{ env.GITHUB_REF_NAME }}-1.x86_64.rpm" path: "~/rpmbuild/RPMS/x86_64/${{ env.GITHUB_REPOSITORY_NAME }}-${{ env.GITHUB_REF_NAME }}-1.x86_64.rpm" rpm-package-fedora: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 container: - image: fedora:38 + image: fedora:42 steps: - name: Install dependencies run: | dnf upgrade -y - dnf groupinstall -y "Development Tools" + dnf group install -y "development-tools" dnf install -y gcc gcc-c++ cmake rpm-build libudev-devel libinput-devel pugixml-devel cairo-devel libX11-devel libXtst-devel libXrandr-devel libXi-devel glib2-devel gtk3-devel - name: GitHub Environment Variables Action @@ -128,20 +129,20 @@ jobs: rpmbuild -ba ${{ env.GITHUB_REPOSITORY_NAME }}/rpm/${{ env.GITHUB_REPOSITORY_NAME }}.spec - name: Upload RPM package - uses: "actions/upload-artifact@v2" + uses: "actions/upload-artifact@v4" with: name: "fedora34_${{ env.GITHUB_REPOSITORY_NAME }}-${{ env.GITHUB_REF_NAME }}-1.x86_64.rpm" path: "~/rpmbuild/RPMS/x86_64/${{ env.GITHUB_REPOSITORY_NAME }}-${{ env.GITHUB_REF_NAME }}-1.x86_64.rpm" create-release: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: [ deb-package, rpm-package-rhel, rpm-package-fedora ] steps: - name: GitHub Environment Variables Action uses: FranzDiebold/github-env-vars-action@v1.2.1 - name: Download packages - uses: "actions/download-artifact@v2" + uses: "actions/download-artifact@v4" with: path: ./ @@ -166,25 +167,25 @@ jobs: asset_name: "${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_amd64.deb" asset_content_type: application/vnd.debian.binary-package - - name: Upload Debian package (armhf) - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: "${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_armhf.deb/${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_armhf.deb" - asset_name: "${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_armhf.deb" - asset_content_type: application/vnd.debian.binary-package - - - name: Upload Debian package (arm64) - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: "${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_arm64.deb/${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_arm64.deb" - asset_name: "${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_arm64.deb" - asset_content_type: application/vnd.debian.binary-package + # - name: Upload Debian package (armhf) + # uses: actions/upload-release-asset@v1 + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # with: + # upload_url: ${{ steps.create_release.outputs.upload_url }} + # asset_path: "${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_armhf.deb/${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_armhf.deb" + # asset_name: "${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_armhf.deb" + # asset_content_type: application/vnd.debian.binary-package + + # - name: Upload Debian package (arm64) + # uses: actions/upload-release-asset@v1 + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # with: + # upload_url: ${{ steps.create_release.outputs.upload_url }} + # asset_path: "${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_arm64.deb/${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_arm64.deb" + # asset_name: "${{ env.GITHUB_REPOSITORY_NAME }}_${{ env.GITHUB_REF_NAME }}_arm64.deb" + # asset_content_type: application/vnd.debian.binary-package - name: Upload RPM package (RHEL) uses: actions/upload-release-asset@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index c501b367..7e79ade6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 2.0.18 - 2025-05-24 + + - Allow to execute actions on gesture begin and end + + Until now, it was only possible to execute the MOUSE_CLICK, SEND_KEYS an + RUN_COMMAND actions when the gesture started or when the gesture ended. + + Include a third option, begin-and-end allowing to run the actions + both when the gesture starts and ends. + + When this new setting is used, MOUSE_CLICK starts presses the mouse button + when the gesture starts and releases it when the gesture ends. + + In the case of RUN_COMMAND, a environment variable is set + (TOUCHEGG_GESTURE_ON) allowing the executed script to take different actions + at the beginning and the end of the gesture. + + https://github.com/JoseExposito/touchegg/pull/670 + ## 2.0.17 - 2023-08-13 This version does not include any new features, but the deb and rpm packages diff --git a/CMakeLists.txt b/CMakeLists.txt index 3df99d6f..c131bf36 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.0.0) project(touchegg) set(MAJOR_VERSION "2") set(MINOR_VERSION "0") -set(PATCH_VERSION "17") +set(PATCH_VERSION "18") add_definitions(-D_VERSION="v${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}") set(CMAKE_CXX_STANDARD 17) diff --git a/HACKING.md b/HACKING.md index d62194ce..5d3f3499 100755 --- a/HACKING.md +++ b/HACKING.md @@ -9,7 +9,7 @@ $ sudo apt-get install git build-essential gdb cmake debhelper \ libgtk-3-dev # GTK is optional, see "Compilation flags" # Red Hat, Fedora, CentOS and derivatives: -$ sudo dnf groupinstall "Development Tools" +$ sudo dnf group install "development-tools" $ sudo dnf install git gcc gcc-c++ gdb cmake rpm-build \ libudev-devel libinput-devel pugixml-devel cairo-devel libX11-devel libXtst-devel libXrandr-devel libXi-devel glib2-devel \ gtk3-devel # GTK is optional, see "Compilation flags" diff --git a/README.md b/README.md index 4c25990f..2e984ed8 100644 --- a/README.md +++ b/README.md @@ -544,7 +544,7 @@ Options: | repeat | `true`/`false` | Whether to execute the keyboard shortcut multiple times (default: `false`). This is useful to perform actions like pinch to zoom. | | modifiers | Keysym | Typical values are: `Shift_L`, `Control_L`, `Alt_L`, `Alt_R`, `Meta_L`, `Super_L`, `Hyper_L`. You can use multiple keysyms: `Control_L+Alt_L`.See "Keysyms" below for more information. | | keys | Keysym | Shortcut keys. You can use multiple keysyms: `A+B+C`. See "Keysyms" below for more information. | -| on | `begin`/`end` | Only used when `repeat` is `false`. Whether to execute the shortcut at the beginning or at the end of the gesture. | +| on | `begin`/`end`/`begin-and-end` | Only used when `repeat` is `false`. Whether to execute the shortcut at the beginning and/or at the end of the gesture. | | decreaseKeys | Keysym | Only used when `repeat` is `true`. Keys to press when you change the gesture direction to the opposite. You can use multiple keysyms: `A+B+C`. This is useful to perform actions like pinch to zoom, check `Example 2` below. | | times | 2...15 | Only used when `repeat` is `true`. Number of times to repeat the action. | | animate | `true`/`false` | Set it to `true` to display the animation set in `animation`. `false` otherwise. | @@ -640,7 +640,7 @@ Options: | - | - | - | | repeat | `true`/`false` | `true` if the command should be executed multiple times. `false` otherwise. | | command | Command | The command to execute. | -| on | `begin`/`end` | Only used when `repeat` is `false`. If the command should be executed on the beginning or on the end of the gesture. | +| on | `begin`/`end`/`begin-and-end` | Only used when `repeat` is `false`. If the command should be executed and/on the beginning or on the end of the gesture. | | decreaseCommand | Command | Only used when `repeat` is `true`. Command to run when you change the gesture direction to the opposite. Check `Example 2` below. | | times | 2...15 | Only used when `repeat` is `true`. Number of times to repeat the action. | | animate | `true`/`false` | Set it to `true` to display the animation set in `animation`. `false` otherwise. | @@ -654,7 +654,7 @@ Example 1: false - notify-send 'Hello World' "Swipe down, DEVICE_TYPE=$TOUCHEGG_DEVICE_TYPE" + notify-send 'Hello World' "Swipe down, DEVICE_TYPE=$TOUCHEGG_DEVICE_TYPE TOUCHEGG_GESTURE_ON=$TOUCHEGG_GESTURE_ON" begin @@ -681,7 +681,9 @@ Options: | Option | Value | Description | | - | - | - | | button | `1`/`2`/`3`/`8`/`9` | Left click (1), middle click (2), right click (3), back button (8) or forward button (9) | -| on | `begin`/`end` | If the command should be executed on the beginning or on the end of the gesture. | +| on | `begin`/`end`/`begin-and-end` | If the mouse click should be executed on the beginning and/or on the end of the gesture. | + +When the `begin-and-end` option is used, the mouse button is down when the gesture begins and up when the gesture ends. Example: diff --git a/debian/changelog b/debian/changelog index 6188717b..6f7fc26a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +touchegg (2.0.18) focal; urgency=medium + + * Allow to execute actions on gesture begin and end + + -- José Expósito Sat, 24 May 2025 12:00:00 +0100 + touchegg (2.0.17) focal; urgency=medium * Upgrade the target Debian version on CI diff --git a/rpm/touchegg.spec b/rpm/touchegg.spec index 12f2d77e..7ae42de0 100644 --- a/rpm/touchegg.spec +++ b/rpm/touchegg.spec @@ -1,7 +1,7 @@ Name: touchegg Summary: Multi-touch gesture recognizer Url: https://github.com/JoseExposito/touchegg -Version: 2.0.17 +Version: 2.0.18 Release: 1 License: GPLv3+ Group: Applications/Productivity @@ -84,8 +84,10 @@ fi %changelog +* Sat May 24 2025 José Expósito - 2.0.18-1 +- Allow to execute actions on gesture begin and end -* Sun Aug 13 2023 José Expósito - 2.0.16-1 +* Sun Aug 13 2023 José Expósito - 2.0.17-1 - Upgrade the target Fedora and RHEL version on CI * Mon Feb 06 2023 José Expósito - 2.0.16-1 diff --git a/src/actions/execute-action-on.h b/src/actions/execute-action-on.h new file mode 100644 index 00000000..d9aa8332 --- /dev/null +++ b/src/actions/execute-action-on.h @@ -0,0 +1,58 @@ +/** + * Copyright 2011 - 2024 José Expósito + * + * This file is part of Touchégg. + * + * Touchégg 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. + * + * Touchégg 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 + * Touchégg. If not, see . + */ +#ifndef ACTIONS_EXECUTE_ACTION_ON_H_ +#define ACTIONS_EXECUTE_ACTION_ON_H_ + +#include + +enum class ExecuteActionOn { + NOT_SUPPORTED, + BEGIN, + END, + BEGIN_AND_END, + // Adding a new value? Don't forget to add it in executeActionOnFromStr and + // shouldExecuteAction +}; + +inline ExecuteActionOn executeActionOnFromStr(const std::string &str) { + if (str == "begin") { + return ExecuteActionOn::BEGIN; + } + if (str == "end") { + return ExecuteActionOn::END; + } + if (str == "begin-and-end") { + return ExecuteActionOn::BEGIN_AND_END; + } + return ExecuteActionOn::NOT_SUPPORTED; +} + +inline bool shouldExecuteAction(ExecuteActionOn phase, ExecuteActionOn config) { + switch (config) { + case ExecuteActionOn::BEGIN: + return phase == ExecuteActionOn::BEGIN; + case ExecuteActionOn::END: + return phase == ExecuteActionOn::END; + case ExecuteActionOn::BEGIN_AND_END: + return true; + default: + return false; + } +} + +#endif // ACTIONS_EXECUTE_ACTION_ON_H_ diff --git a/src/actions/mouse-click.cpp b/src/actions/mouse-click.cpp index 72731017..cbf2a19c 100644 --- a/src/actions/mouse-click.cpp +++ b/src/actions/mouse-click.cpp @@ -23,18 +23,26 @@ void MouseClick::onGestureBegin(const Gesture& /*gesture*/) { } if (this->settings.count("on") == 1) { - this->onBegin = (this->settings.at("on") == "begin"); + this->executeActionOn = executeActionOnFromStr(this->settings.at("on")); } - if (this->onBegin) { - this->windowSystem.sendMouseClick(this->button); + if (shouldExecuteAction(ExecuteActionOn::BEGIN, this->executeActionOn)) { + this->windowSystem.sendMouseDown(this->button); + + if (this->executeActionOn != ExecuteActionOn::BEGIN_AND_END) { + this->windowSystem.sendMouseUp(this->button); + } } } void MouseClick::onGestureUpdate(const Gesture& /*gesture*/) {} void MouseClick::onGestureEnd(const Gesture& /*gesture*/) { - if (!this->onBegin) { - this->windowSystem.sendMouseClick(this->button); + if (shouldExecuteAction(ExecuteActionOn::END, this->executeActionOn)) { + if (this->executeActionOn != ExecuteActionOn::BEGIN_AND_END) { + this->windowSystem.sendMouseDown(this->button); + } + + this->windowSystem.sendMouseUp(this->button); } } diff --git a/src/actions/mouse-click.h b/src/actions/mouse-click.h index afdc1492..830878c6 100644 --- a/src/actions/mouse-click.h +++ b/src/actions/mouse-click.h @@ -21,6 +21,7 @@ #include #include "actions/action.h" +#include "actions/execute-action-on.h" /** * Action to emulate a shortcut. @@ -35,7 +36,7 @@ class MouseClick : public Action { private: int button = 1; - bool onBegin = true; + ExecuteActionOn executeActionOn = ExecuteActionOn::BEGIN; }; #endif // ACTIONS_MOUSE_CLICK_H_ diff --git a/src/actions/repeated-action.cpp b/src/actions/repeated-action.cpp index e200d4ec..43e10aa4 100644 --- a/src/actions/repeated-action.cpp +++ b/src/actions/repeated-action.cpp @@ -34,14 +34,15 @@ void RepeatedAction::onGestureBegin(const Gesture &gesture) { } if (this->settings.count("on") == 1) { - this->onBegin = (this->settings.at("on") == "begin"); + this->executeActionOn = executeActionOnFromStr(this->settings.at("on")); } // execute supplied prelude this->executePrelude(); // run action on begin - if (!this->repeat && this->onBegin) { + if (!this->repeat && + shouldExecuteAction(ExecuteActionOn::BEGIN, this->executeActionOn)) { this->executeAction(gesture); } } @@ -70,8 +71,12 @@ void RepeatedAction::onGestureUpdate(const Gesture &gesture) { } void RepeatedAction::onGestureEnd(const Gesture &gesture) { - if (!this->repeat && !this->onBegin) { - if (gesture.percentage() >= this->threshold) { + if (!this->repeat && + shouldExecuteAction(ExecuteActionOn::END, this->executeActionOn)) { + // Do not take into account the threshold is the action is executed on begin + // and end. Otherwise, we could miss could execute only the on begin part. + if (this->executeActionOn == ExecuteActionOn::BEGIN_AND_END || + gesture.percentage() >= this->threshold) { this->executeAction(gesture); } } diff --git a/src/actions/repeated-action.h b/src/actions/repeated-action.h index 693c7f3a..9a6f32d5 100644 --- a/src/actions/repeated-action.h +++ b/src/actions/repeated-action.h @@ -19,6 +19,7 @@ #define ACTIONS_REPEATED_ACTION_H_ #include "actions/animated-action.h" +#include "actions/execute-action-on.h" class RepeatedAction : public AnimatedAction { public: @@ -84,7 +85,7 @@ class RepeatedAction : public AnimatedAction { /** * Whether the action should be executed on gesture begin or end. */ - bool onBegin = true; + ExecuteActionOn executeActionOn = ExecuteActionOn::BEGIN; }; #endif // ACTIONS_REPEATED_ACTION_H_ diff --git a/src/actions/run-command.cpp b/src/actions/run-command.cpp index 86c4f8de..af9cd0cb 100644 --- a/src/actions/run-command.cpp +++ b/src/actions/run-command.cpp @@ -22,6 +22,7 @@ #include "animations/animation-factory.h" void RunCommand::onGestureBegin(const Gesture &gesture) { + setenv("TOUCHEGG_GESTURE_ON", "begin", 1); RepeatedAction::onGestureBegin(gesture); if (!this->animate) { @@ -38,6 +39,16 @@ void RunCommand::onGestureBegin(const Gesture &gesture) { } } +void RunCommand::onGestureUpdate(const Gesture &gesture) { + setenv("TOUCHEGG_GESTURE_ON", "update", 1); + RepeatedAction::onGestureUpdate(gesture); +} + +void RunCommand::onGestureEnd(const Gesture &gesture) { + setenv("TOUCHEGG_GESTURE_ON", "end", 1); + RepeatedAction::onGestureEnd(gesture); +} + void RunCommand::executePrelude() { if (this->settings.count("command") == 1) { this->command = this->settings.at("command"); diff --git a/src/actions/run-command.h b/src/actions/run-command.h index 7c24035c..fded290d 100644 --- a/src/actions/run-command.h +++ b/src/actions/run-command.h @@ -29,6 +29,8 @@ class RunCommand : public RepeatedAction { public: using RepeatedAction::RepeatedAction; void onGestureBegin(const Gesture &gesture) override; + void onGestureUpdate(const Gesture &gesture) override; + void onGestureEnd(const Gesture &gesture) override; bool runOnSystemWindows() override { return true; } void executePrelude() override; void executeAction(const Gesture &gesture) override; diff --git a/src/animations/animation.h b/src/animations/animation.h index 9da70d6f..14f6bdbe 100644 --- a/src/animations/animation.h +++ b/src/animations/animation.h @@ -20,6 +20,7 @@ #include +#include #include #include "window-system/cairo-surface.h" diff --git a/src/config/config.cpp b/src/config/config.cpp old mode 100755 new mode 100644 index 666aaaa7..75b9a5b7 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -46,27 +46,43 @@ std::string Config::getGlobalSetting(const std::string &name) const { void Config::saveGestureConfig( const std::string &application, GestureType gestureType, const std::string &numFingers, GestureDirection gestureDirection, - ActionType actionType, + GestureAxis gestureAxis, ActionType actionType, const std::unordered_map &actionSettings) { std::string key = Config::getConfigKey(application, gestureType, numFingers, - gestureDirection); + gestureDirection, gestureAxis); this->config[key] = std::make_pair(actionType, actionSettings); } bool Config::hasGestureConfig(const std::string &application, GestureType gestureType, int numFingers, - GestureDirection gestureDirection) const { - std::string key = Config::getConfigKey( - application, gestureType, std::to_string(numFingers), gestureDirection); + GestureDirection gestureDirection, + GestureAxis gestureAxis) const { + std::string key = Config::getConfigKey(application, + gestureType, std::to_string(numFingers), + gestureDirection, gestureAxis); + if (this->config.count(key) == 1) return true; + + // Check for definitions with no axis specified + key = Config::getConfigKey(application, + gestureType, std::to_string(numFingers), + gestureDirection, GestureAxis::UNKNOWN); return (this->config.count(key) == 1); } std::pair> Config::getGestureConfig(const std::string &application, GestureType gestureType, int numFingers, - GestureDirection gestureDirection) const { - std::string key = Config::getConfigKey( - application, gestureType, std::to_string(numFingers), gestureDirection); + GestureDirection gestureDirection, + GestureAxis gestureAxis) const { + std::string key = Config::getConfigKey(application, + gestureType, std::to_string(numFingers), + gestureDirection, gestureAxis); + auto match = this->config.find(key); + if (match != this->config.end()) return match->second; + + key = Config::getConfigKey(application, + gestureType, std::to_string(numFingers), + gestureDirection, GestureAxis::UNKNOWN); return this->config.at(key); } @@ -78,9 +94,12 @@ void Config::loadDefaultGlobalSettings() { std::string Config::getConfigKey(const std::string &application, GestureType gestureType, const std::string &numFingers, - GestureDirection gestureDirection) { + GestureDirection gestureDirection, + GestureAxis gestureAxis) { auto gestureTypeInt = static_cast(gestureType); auto gestureDirectionInt = static_cast(gestureDirection); + auto gestureAxisInt = static_cast(gestureAxis); return toLower(application) + "_" + std::to_string(gestureTypeInt) + "_" + - numFingers + "_" + std::to_string(gestureDirectionInt); + numFingers + "_" + std::to_string(gestureDirectionInt) + "_" + + std::to_string(gestureAxisInt); } diff --git a/src/config/config.h b/src/config/config.h old mode 100755 new mode 100644 index 0165d4e4..e9bc91d6 --- a/src/config/config.h +++ b/src/config/config.h @@ -23,6 +23,7 @@ #include #include "actions/action-type.h" +#include "gesture/gesture-axis.h" #include "gesture/gesture-direction.h" #include "gesture/gesture-type.h" @@ -66,22 +67,23 @@ class Config { void saveGestureConfig( const std::string &application, GestureType gestureType, const std::string &numFingers, GestureDirection gestureDirection, - ActionType actionType, + GestureAxis gestureAxis, ActionType actionType, const std::unordered_map &actionSettings); /** * @returns If there is an action configured for the gesture. */ bool hasGestureConfig(const std::string &application, GestureType gestureType, - int numFingers, - GestureDirection gestureDirection) const; + int numFingers, GestureDirection gestureDirection, + GestureAxis gestureAxis) const; /** * @returns The action configured for the gesture. */ std::pair> getGestureConfig(const std::string &application, GestureType gestureType, - int numFingers, GestureDirection gestureDirection) const; + int numFingers, GestureDirection gestureDirection, + GestureAxis gestureAxis) const; private: /** @@ -113,7 +115,8 @@ class Config { static std::string getConfigKey(const std::string &application, GestureType gestureType, const std::string &numFingers, - GestureDirection gestureDirection); + GestureDirection gestureDirection, + GestureAxis gestureAxis); }; #endif // CONFIG_CONFIG_H_ diff --git a/src/config/xml-config-loader.cpp b/src/config/xml-config-loader.cpp index b708d267..b5aecaf1 100644 --- a/src/config/xml-config-loader.cpp +++ b/src/config/xml-config-loader.cpp @@ -101,6 +101,7 @@ void XmlConfigLoader::parseApplicationXmlNodes(const pugi::xml_node &rootNode) { const std::string gestureType = gestureNode.attribute("type").value(); const std::string fingers = gestureNode.attribute("fingers").value(); const std::string direction = gestureNode.attribute("direction").value(); + const std::string axis = gestureNode.attribute("axis").value(); pugi::xml_node actionNode = gestureNode.child("action"); const std::string actionType = actionNode.attribute("type").value(); @@ -116,8 +117,8 @@ void XmlConfigLoader::parseApplicationXmlNodes(const pugi::xml_node &rootNode) { for (const std::string &application : applications) { this->config->saveGestureConfig( trim(application), gestureTypeFromStr(gestureType), fingers, - gestureDirectionFromStr(direction), actionTypeFromStr(actionType), - actionSettings); + gestureDirectionFromStr(direction), gestureAxisFromStr(axis), + actionTypeFromStr(actionType), actionSettings); } } } diff --git a/src/daemon/daemon-client.cpp b/src/daemon/daemon-client.cpp index 2f8d25a9..cab975d3 100644 --- a/src/daemon/daemon-client.cpp +++ b/src/daemon/daemon-client.cpp @@ -56,10 +56,12 @@ void DaemonClient::connect() { std::this_thread::sleep_for(std::chrono::seconds(5)); } else { tlg::info << "Connection with Touchégg established" << std::endl; + auto interfaceVersion = (sizeof(DBUS_INTERFACE_NAMES)/sizeof(DBUS_INTERFACE_NAMES[0])) - 1; + auto objectPathVersion = (sizeof(DBUS_OBJECT_PATHS)/sizeof(DBUS_OBJECT_PATHS[0])) - 1; g_dbus_connection_signal_subscribe( - connection, nullptr, DBUS_INTERFACE_NAME, nullptr, DBUS_OBJECT_PATH, - nullptr, G_DBUS_SIGNAL_FLAGS_NONE, DaemonClient::onNewMessage, this, - nullptr); + connection, nullptr, DBUS_INTERFACE_NAMES[interfaceVersion], nullptr, + DBUS_OBJECT_PATHS[objectPathVersion], nullptr, G_DBUS_SIGNAL_FLAGS_NONE, + DaemonClient::onNewMessage, this, nullptr); g_signal_connect( connection, "closed", @@ -122,15 +124,16 @@ std::unique_ptr DaemonClient::makeGestureFromSignalParams( GVariant *signalParameters) { GestureType type = GestureType::NOT_SUPPORTED; GestureDirection direction = GestureDirection::UNKNOWN; + GestureAxis axis = GestureAxis::UNKNOWN; double percentage = -1; int fingers = -1; DeviceType deviceType = DeviceType::UNKNOWN; uint64_t elapsedTime = -1; g_variant_get(signalParameters, // NOLINT - "(uudiut)", &type, &direction, &percentage, &fingers, + "(uuudiut)", &type, &direction, &axis, &percentage, &fingers, &deviceType, &elapsedTime); - return std::make_unique(type, direction, percentage, fingers, + return std::make_unique(type, direction, axis, percentage, fingers, deviceType, elapsedTime); } diff --git a/src/daemon/daemon-server.cpp b/src/daemon/daemon-server.cpp index 0fbb25cb..7a5d0439 100644 --- a/src/daemon/daemon-server.cpp +++ b/src/daemon/daemon-server.cpp @@ -82,10 +82,14 @@ gboolean DaemonServer::onNewConnection(GDBusServer * /*server*/, tlg::info << "New client connection request" << std::endl; GDBusInterfaceVTable interfaceVTable{nullptr, nullptr, nullptr}; - int id = g_dbus_connection_register_object( - connection, DBUS_OBJECT_PATH, - self->introspectionData->interfaces[0], // NOLINT - &interfaceVTable, nullptr, nullptr, nullptr); + int id = 0; + for (auto i = 0; i < sizeof(DBUS_OBJECT_PATHS)/sizeof(DBUS_OBJECT_PATHS[0]); i++) { + id = g_dbus_connection_register_object( + connection, DBUS_OBJECT_PATHS[i], + self->introspectionData->interfaces[0], // NOLINT + &interfaceVTable, nullptr, nullptr, nullptr); + if (id != 0) break; + } if (id == 0) { tlg::error << "Error connecting client" << std::endl; @@ -114,34 +118,51 @@ void DaemonServer::send(const std::string &signalName, std::unique_ptr gesture) { std::vector closedConnections{}; + GVariant* signalParams[sizeof(DBUS_INTERFACE_NAMES)/sizeof(DBUS_INTERFACE_NAMES[0])] = {0}; + auto i = 0; // Copy every gesture field into the signal parameters for serialization - GVariant *signalParams = + // Offer signal versions in order from oldest to newest + signalParams[i] = { g_variant_new("(uudiut)", // NOLINT - static_cast(gesture->type()), // u - static_cast(gesture->direction()), // u - gesture->percentage(), // d - gesture->fingers(), // i - static_cast(gesture->performedOnDeviceType()), // u - gesture->elapsedTime()); // t - g_variant_ref_sink(signalParams); + static_cast(gesture->type()), // u + static_cast(gesture->direction()), // u + gesture->percentage(), // d + gesture->fingers(), // i + static_cast(gesture->performedOnDeviceType()), // u + gesture->elapsedTime())}; // t + g_variant_ref_sink(signalParams[i++]); + signalParams[i] = { + g_variant_new("(uuudiut)", // NOLINT + static_cast(gesture->type()), // u + static_cast(gesture->direction()), // u + static_cast(gesture->axis()), // u + gesture->percentage(), // d + gesture->fingers(), // i + static_cast(gesture->performedOnDeviceType()), // u + gesture->elapsedTime())}; // t + g_variant_ref_sink(signalParams[i++]); // Send the message to every client - for (auto *connection : this->connections) { - if (g_dbus_connection_is_closed(connection) == TRUE) { - closedConnections.push_back(connection); - } else { - GError *error = nullptr; - gboolean sent = g_dbus_connection_emit_signal( - connection, nullptr, DBUS_OBJECT_PATH, DBUS_INTERFACE_NAME, - signalName.c_str(), signalParams, &error); - if (sent == FALSE) { - tlg::error << "Error sending message: " << error->message << std::endl; + while (--i >= 0) { + for (auto *connection : this->connections) { + if (g_dbus_connection_is_closed(connection) == TRUE) { closedConnections.push_back(connection); + } else { + GError *error = nullptr; + gboolean sent = g_dbus_connection_emit_signal( + connection, nullptr, DBUS_OBJECT_PATHS[i], DBUS_INTERFACE_NAMES[i], + signalName.c_str(), signalParams[i], &error); + if (sent == FALSE) { + if (error != nullptr) { + tlg::error << "Error sending message: " << error->message << std::endl; + } + closedConnections.push_back(connection); + } } } - } - g_variant_unref(signalParams); + g_variant_unref(signalParams[i]); + } // Remove dead clients for (auto *connection : closedConnections) { diff --git a/src/daemon/dbus.h b/src/daemon/dbus.h index 9e9f1924..021842ea 100644 --- a/src/daemon/dbus.h +++ b/src/daemon/dbus.h @@ -24,14 +24,27 @@ constexpr auto DBUS_ADDRESS = "unix:path=/tmp/touchegg#0"; constexpr auto DBUS_ADDRESS = "unix:abstract=touchegg"; #endif +// Singular is legacy constexpr auto DBUS_OBJECT_PATH = "/io/github/joseexposito/Touchegg"; +// New versions should be appended to the bottom of the list +constexpr const char* DBUS_OBJECT_PATHS[] = { + "/io/github/joseexposito/Touchegg", // 0 for legacy + "/io/github/joseexposito/Touchegg1" +}; +// Singular is legacy constexpr auto DBUS_INTERFACE_NAME = "io.github.joseexposito.Touchegg"; +// New versions should be appended to the bottom of the list +constexpr const char* DBUS_INTERFACE_NAMES[] = { + "io.github.joseexposito.Touchegg", // 0 for legacy + "io.github.joseexposito.Touchegg1" +}; constexpr auto DBUS_ON_GESTURE_BEGIN = "OnGestureBegin"; constexpr auto DBUS_ON_GESTURE_UPDATE = "OnGestureUpdate"; constexpr auto DBUS_ON_GESTURE_END = "OnGestureEnd"; +// Versions should be in order from oldest to newest constexpr auto DBUS_INTROSPECTION_XML = "" " " @@ -60,6 +73,35 @@ constexpr auto DBUS_INTROSPECTION_XML = " " " " " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " ""; #endif // DAEMON_DBUS_H_ diff --git a/src/gesture-controller/gesture-controller.cpp b/src/gesture-controller/gesture-controller.cpp old mode 100755 new mode 100644 index 5801c963..b76903a7 --- a/src/gesture-controller/gesture-controller.cpp +++ b/src/gesture-controller/gesture-controller.cpp @@ -25,6 +25,7 @@ #include "actions/action-type.h" #include "actions/action.h" #include "config/config.h" +#include "gesture/gesture-axis.h" #include "gesture/gesture-direction.h" #include "gesture/gesture-type.h" #include "gesture/gesture.h" @@ -36,12 +37,18 @@ GestureController::GestureController(const Config &config, : config(config), windowSystem(windowSystem) {} void GestureController::onGestureBegin(std::unique_ptr gesture) { + // Deduce axis from direction when possible + if (gesture->axis() == GestureAxis::UNKNOWN) { + gesture->setAxis(gestureAxisFromDirection(gesture->direction())); + } + tlg::debug << "Gesture begin detected" << std::endl; tlg::debug << "\tGesture information:" << std::endl; tlg::debug << "\t\tFingers: " << gesture->fingers() << std::endl; tlg::debug << "\t\tType: " << gestureTypeToStr(gesture->type()) << std::endl; tlg::debug << "\t\tDirection: " << gestureDirectionToStr(gesture->direction()) << std::endl; + tlg::debug << "\t\tAxis: " << gestureAxisToStr(gesture->axis()) << std::endl; this->rotatedDirection = this->windowSystem.calculateRotation( gesture->type(), gesture->performedOnDeviceType(), gesture->direction()); @@ -49,6 +56,7 @@ void GestureController::onGestureBegin(std::unique_ptr gesture) { tlg::debug << "\t\tDirection after rotation: " << gestureDirectionToStr(this->rotatedDirection) << std::endl; gesture->setDirection(this->rotatedDirection); + gesture->setAxis(gestureAxisFromDirection(this->rotatedDirection)); } this->window = this->windowSystem.getWindowUnderCursor(); @@ -102,18 +110,19 @@ std::unique_ptr GestureController::getActionForGesture( const GestureType gestureType = gesture.type(); int fingers = gesture.fingers(); const GestureDirection direction = gesture.direction(); + const GestureAxis axis = gesture.axis(); // First check if there is an specific application gesture application = this->windowSystem.getWindowClassName(window); tlg::debug << "\tGesture performed on app: " << application << std::endl; hasAction = this->config.hasGestureConfig(application, gestureType, fingers, - direction); + direction, axis); // If no gesture was configured, check the global gestures if (!hasAction) { application = "All"; hasAction = this->config.hasGestureConfig(application, gestureType, fingers, - direction); + direction, axis); } if (!hasAction) { @@ -124,7 +133,7 @@ std::unique_ptr GestureController::getActionForGesture( tlg::debug << "\tAction configured for this gesture" << std::endl; auto pair = this->config.getGestureConfig(application, gestureType, fingers, - direction); + direction, axis); ActionType actionType = pair.first; std::unordered_map actionSettings = pair.second; diff --git a/src/gesture-controller/gesture-controller.h b/src/gesture-controller/gesture-controller.h old mode 100755 new mode 100644 index e9dc6bdc..1af51d86 --- a/src/gesture-controller/gesture-controller.h +++ b/src/gesture-controller/gesture-controller.h @@ -60,10 +60,11 @@ class GestureController : public GestureControllerDelegate { bool executeAction = false; /** - * If the screen is rotated, this values is set on gesture begin to match the - * screen rotation. + * If the screen is rotated, these values are set on gesture begin to match + * the screen rotation. */ GestureDirection rotatedDirection = GestureDirection::UNKNOWN; + GestureAxis rotatedAxis = GestureAxis::UNKNOWN; /** * @returns The action associated to a gesture or nullptr. diff --git a/src/gesture-gatherer/libinput-handler.cpp b/src/gesture-gatherer/libinput-handler.cpp index f09a22b8..d6623b58 100644 --- a/src/gesture-gatherer/libinput-handler.cpp +++ b/src/gesture-gatherer/libinput-handler.cpp @@ -58,6 +58,12 @@ GestureDirection LininputHandler::calculateSwipeDirection(double deltaX, return (deltaY > 0) ? GestureDirection::DOWN : GestureDirection::UP; } +GestureAxis LininputHandler::calculatePinchAxis(double deltaX, + double deltaY) { + return (std::abs(deltaX) > std::abs(deltaY)) ? + GestureAxis::HORIZONTAL : GestureAxis::VERTICAL; +} + double LininputHandler::calculateSwipeAnimationPercentage( const LibinputDeviceInfo &info, GestureDirection direction, double deltaX, double deltaY) { diff --git a/src/gesture-gatherer/libinput-handler.h b/src/gesture-gatherer/libinput-handler.h index bf7ac02a..0d82d600 100644 --- a/src/gesture-gatherer/libinput-handler.h +++ b/src/gesture-gatherer/libinput-handler.h @@ -54,6 +54,11 @@ class LininputHandler { */ static GestureDirection calculateSwipeDirection(double deltaX, double deltaY); + /** + * @returns The primary axis of a pinch gesture. + */ + static GestureAxis calculatePinchAxis(double deltaX, double deltaY); + /** * @returns The percentage (between 0 and 100) of the gesture animation. */ diff --git a/src/gesture-gatherer/libinput-pinch-handler.cpp b/src/gesture-gatherer/libinput-pinch-handler.cpp index 189fd8d6..bdbbc01d 100644 --- a/src/gesture-gatherer/libinput-pinch-handler.cpp +++ b/src/gesture-gatherer/libinput-pinch-handler.cpp @@ -22,8 +22,19 @@ #include "gesture/device-type.h" -void LininputPinchHandler::handlePinchBegin(struct libinput_event * /*event*/) { +void LininputPinchHandler::handlePinchBegin(struct libinput_event *event) { this->state.reset(); + + struct libinput_event_gesture *gestureEvent = + libinput_event_get_gesture_event(event); + if ( + std::abs(libinput_event_gesture_get_dx(gestureEvent)) + > std::abs(libinput_event_gesture_get_dy(gestureEvent)) + ) { + this->state.axis = GestureAxis::HORIZONTAL; + } else { + this->state.axis = GestureAxis::VERTICAL; + } } void LininputPinchHandler::handlePinchUpdate(struct libinput_event *event) { @@ -36,14 +47,19 @@ void LininputPinchHandler::handlePinchUpdate(struct libinput_event *event) { this->state.startTimestamp = LininputHandler::getTimestamp(); this->state.direction = (this->state.delta > 1) ? GestureDirection::OUT : GestureDirection::IN; + this->state.axis = (std::abs(libinput_event_gesture_get_dx(gestureEvent)) > + std::abs(libinput_event_gesture_get_dy(gestureEvent))) + ? GestureAxis::HORIZONTAL + : GestureAxis::VERTICAL; this->state.percentage = LininputHandler::calculatePinchAnimationPercentage( this->state.direction, this->state.delta); this->state.fingers = libinput_event_gesture_get_finger_count(gestureEvent); uint64_t elapsedTime = 0; auto gesture = std::make_unique( - GestureType::PINCH, this->state.direction, this->state.percentage, - this->state.fingers, DeviceType::TOUCHPAD, elapsedTime); + GestureType::PINCH, this->state.direction, this->state.axis, + this->state.percentage, this->state.fingers, DeviceType::TOUCHPAD, + elapsedTime); this->gestureController->onGestureBegin(std::move(gesture)); } else { this->state.percentage = LininputHandler::calculatePinchAnimationPercentage( @@ -52,8 +68,9 @@ void LininputPinchHandler::handlePinchUpdate(struct libinput_event *event) { LininputHandler::calculateElapsedTime(this->state.startTimestamp); auto gesture = std::make_unique( - GestureType::PINCH, this->state.direction, this->state.percentage, - this->state.fingers, DeviceType::TOUCHPAD, elapsedTime); + GestureType::PINCH, this->state.direction, this->state.axis, + this->state.percentage, this->state.fingers, DeviceType::TOUCHPAD, + elapsedTime); this->gestureController->onGestureUpdate(std::move(gesture)); } } @@ -69,8 +86,9 @@ void LininputPinchHandler::handlePinchEnd(struct libinput_event *event) { LininputHandler::calculateElapsedTime(this->state.startTimestamp); auto gesture = std::make_unique( - GestureType::PINCH, this->state.direction, this->state.percentage, - this->state.fingers, DeviceType::TOUCHPAD, elapsedTime); + GestureType::PINCH, this->state.direction, this->state.axis, + this->state.percentage, this->state.fingers, DeviceType::TOUCHPAD, + elapsedTime); this->gestureController->onGestureEnd(std::move(gesture)); } diff --git a/src/gesture-gatherer/libinput-pinch-handler.h b/src/gesture-gatherer/libinput-pinch-handler.h index 78a7a428..16a1a986 100644 --- a/src/gesture-gatherer/libinput-pinch-handler.h +++ b/src/gesture-gatherer/libinput-pinch-handler.h @@ -32,6 +32,7 @@ struct LibinputPinchState { uint64_t startTimestamp = 0; double delta = 1; GestureDirection direction = GestureDirection::UNKNOWN; + GestureAxis axis = GestureAxis::UNKNOWN; double percentage = 0; int fingers = 0; @@ -40,6 +41,7 @@ struct LibinputPinchState { startTimestamp = 0; delta = 1; direction = GestureDirection::UNKNOWN; + axis = GestureAxis::UNKNOWN; percentage = 0; fingers = 0; } diff --git a/src/gesture-gatherer/libinput-touch-handler.cpp b/src/gesture-gatherer/libinput-touch-handler.cpp index 2f8b3837..d35fae41 100644 --- a/src/gesture-gatherer/libinput-touch-handler.cpp +++ b/src/gesture-gatherer/libinput-touch-handler.cpp @@ -124,10 +124,12 @@ void LibinputTouchHandler::handleTouchMotion(struct libinput_event *event) { this->state.startFingers = this->state.currentFingers; this->state.startTimestamp = LininputHandler::getTimestamp(); this->state.type = this->getGestureType(); - this->state.direction = - (this->state.type == GestureType::SWIPE) - ? LininputHandler::calculateSwipeDirection(deltaX, deltaY) - : this->calculatePinchDirection(); + if (this->state.type == GestureType::SWIPE) { + this->state.direction = LininputHandler::calculateSwipeDirection(deltaX, deltaY); + } else { + this->state.direction = this->calculatePinchDirection(); + this->state.axis = this->calculatePinchAxis(deltaX, deltaY); + } // Once the direction is calculated, save the currentX/Y as startX/Y so // the startThreshold is not included in the percentage calculations @@ -136,8 +138,9 @@ void LibinputTouchHandler::handleTouchMotion(struct libinput_event *event) { this->state.startY = this->state.currentY; auto gesture = std::make_unique( - this->state.type, this->state.direction, percentage, - this->state.startFingers, DeviceType::TOUCHSCREEN, 0); + this->state.type, this->state.direction, this->state.axis, + percentage, this->state.startFingers, DeviceType::TOUCHSCREEN, + 0); this->gestureController->onGestureBegin(std::move(gesture)); } } else { diff --git a/src/gesture-gatherer/libinput-touch-handler.h b/src/gesture-gatherer/libinput-touch-handler.h index ccd5333f..daf1be38 100644 --- a/src/gesture-gatherer/libinput-touch-handler.h +++ b/src/gesture-gatherer/libinput-touch-handler.h @@ -22,6 +22,7 @@ #include #include "gesture-gatherer/libinput-handler.h" +#include "gesture/gesture-axis.h" #include "gesture/gesture-direction.h" #include "gesture/gesture-type.h" @@ -32,6 +33,7 @@ struct LibinputTouchState { bool started = false; GestureType type = GestureType::NOT_SUPPORTED; GestureDirection direction = GestureDirection::UNKNOWN; + GestureAxis axis = GestureAxis::UNKNOWN; uint64_t startTimestamp = 0; int startFingers = 0; std::unordered_map startX; @@ -45,6 +47,7 @@ struct LibinputTouchState { started = false; type = GestureType::NOT_SUPPORTED; direction = GestureDirection::UNKNOWN; + axis = GestureAxis::UNKNOWN; startTimestamp = 0; startFingers = 0; tapFingers = 0; diff --git a/src/gesture/gesture-axis.h b/src/gesture/gesture-axis.h new file mode 100644 index 00000000..f7b84579 --- /dev/null +++ b/src/gesture/gesture-axis.h @@ -0,0 +1,77 @@ +/** + * Copyright 2011 - 2022 José Expósito + * + * This file is part of Touchégg. + * + * Touchégg 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. + * + * Touchégg 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 + * Touchégg. If not, see . + */ +#ifndef GESTURE_GESTURE_AXIS_H_ +#define GESTURE_GESTURE_AXIS_H_ + +#include +#include "gesture-direction.h" + +enum class GestureAxis { + // A gesture may have an unknown axis until we have more information + UNKNOWN = 0, + + HORIZONTAL = 1, + VERTICAL = 2 + // DISTAL = 3 + + // Adding a new GestureDirection? Don't forget to add it in + // gestureAxisToStr and gestureAxisFromStr as well +}; + +inline std::string gestureAxisToStr(GestureAxis gestureAxis) { + switch (gestureAxis) { + case GestureAxis::HORIZONTAL: + return "HORIZONTAL"; + case GestureAxis::VERTICAL: + return "VERTICAL"; + // case GestureAxis::DISTAL: + // return "DISTAL"; + default: + return "UNKNOWN"; + } +} + +inline GestureAxis gestureAxisFromStr(const std::string &str) { + if (str == "HORIZONTAL") { + return GestureAxis::HORIZONTAL; + } + if (str == "VERTICAL") { + return GestureAxis::VERTICAL; + } + // if (str == "DISTAL") { + // return GestureAxis::DISTAL; + // } + return GestureAxis::UNKNOWN; +} + +inline GestureAxis gestureAxisFromDirection(GestureDirection gestureDirection) { + switch (gestureDirection) { + case GestureDirection::UP: + return GestureAxis::VERTICAL; + case GestureDirection::DOWN: + return GestureAxis::VERTICAL; + case GestureDirection::LEFT: + return GestureAxis::HORIZONTAL; + case GestureDirection::RIGHT: + return GestureAxis::HORIZONTAL; + default: + return GestureAxis::UNKNOWN; + } +} + +#endif // GESTURE_GESTURE_AXIS_H_ diff --git a/src/gesture/gesture.h b/src/gesture/gesture.h index 8a7e320d..57446405 100644 --- a/src/gesture/gesture.h +++ b/src/gesture/gesture.h @@ -21,6 +21,7 @@ #include #include "gesture/device-type.h" +#include "gesture/gesture-axis.h" #include "gesture/gesture-direction.h" #include "gesture/gesture-type.h" @@ -34,6 +35,17 @@ class Gesture { int fingers, DeviceType performedOnDeviceType, uint64_t elapsedTime) : gestureType(type), gestureDirection(direction), + gestureAxis(gestureAxisFromDirection(direction)), + gesturePercentage(percentage), + gestureFingers(fingers), + deviceType(performedOnDeviceType), + gestureElapsedTime(elapsedTime) {} + Gesture(GestureType type, GestureDirection direction, GestureAxis axis, + double percentage, int fingers, DeviceType performedOnDeviceType, + uint64_t elapsedTime) + : gestureType(type), + gestureDirection(direction), + gestureAxis(axis), gesturePercentage(percentage), gestureFingers(fingers), deviceType(performedOnDeviceType), @@ -51,6 +63,12 @@ class Gesture { */ GestureDirection direction() const { return this->gestureDirection; } + /** + * @returns The gesture axis. + * @see GestureAxis + */ + GestureAxis axis() const { return this->gestureAxis; } + /** * Percentage of the gesture performed, used for animations. * @return Value between 0 and 100. @@ -81,9 +99,16 @@ class Gesture { this->gestureDirection = direction; } + /** + * Set the gesture axis. + * @see GestureAxis + */ + void setAxis(GestureAxis axis) { this->gestureAxis = axis; } + protected: GestureType gestureType = GestureType::NOT_SUPPORTED; GestureDirection gestureDirection = GestureDirection::UNKNOWN; + GestureAxis gestureAxis = GestureAxis::UNKNOWN; double gesturePercentage = -1; int gestureFingers = -1; DeviceType deviceType = DeviceType::UNKNOWN; diff --git a/src/window-system/window-system.h b/src/window-system/window-system.h index aa77ae29..0c9fd75c 100644 --- a/src/window-system/window-system.h +++ b/src/window-system/window-system.h @@ -133,13 +133,22 @@ class WindowSystem { bool isPress) const = 0; /** - * Simulates a mouse click. + * Simulates a mouse down event. * @param button One of this values: * 1 – Left button click * 2 – Middle button click * 3 – Right button click */ - virtual void sendMouseClick(int button) const = 0; + virtual void sendMouseDown(int button) const = 0; + + /** + * Simulates a mouse up event. + * @param button One of this values: + * 1 – Left button click + * 2 – Middle button click + * 3 – Right button click + */ + virtual void sendMouseUp(int button) const = 0; /** * @returns The size of the desktop workarea, ie, the area of the desktop not diff --git a/src/window-system/x11.cpp b/src/window-system/x11.cpp index 3a587af4..3001ec32 100644 --- a/src/window-system/x11.cpp +++ b/src/window-system/x11.cpp @@ -466,8 +466,12 @@ void X11::sendKeys(const std::vector &keycodes, XFlush(this->display); } -void X11::sendMouseClick(int button) const { +void X11::sendMouseDown(int button) const { XTestFakeButtonEvent(this->display, button, True, 0); + XFlush(this->display); +} + +void X11::sendMouseUp(int button) const { XTestFakeButtonEvent(this->display, button, False, 0); XFlush(this->display); } diff --git a/src/window-system/x11.h b/src/window-system/x11.h index fcb1df78..a2713ee0 100644 --- a/src/window-system/x11.h +++ b/src/window-system/x11.h @@ -66,7 +66,8 @@ class X11 : public WindowSystem { void sendKeys(const std::vector &keycodes, bool isPress) const override; - void sendMouseClick(int button) const override; + void sendMouseDown(int button) const override; + void sendMouseUp(int button) const override; Rectangle getDesktopWorkarea() const override; void changeDesktop(ActionDirection direction, bool cyclic) const override;