Skip to content

Commit

Permalink
add ability to create more complex gestures
Browse files Browse the repository at this point in the history
  • Loading branch information
taj-ny committed Dec 24, 2024
1 parent b7dad0a commit f3b90a2
Show file tree
Hide file tree
Showing 25 changed files with 400 additions and 215 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# KWin Gestures
Custom touchpad gestures for KDE Plasma 6.
0# KWin Gestures
Highly customizable custom touchpad gestures for KDE Plasma 6.

Tested on 6.1.5 and 6.2. X11 is not supported.
Gestures are handled by **libgestures**, which is included in the project. It doesn't depend on KWin, so it could be used for other window managers or as a standalone application for X11.

X11 is currently not supported.

# Features
- Override built-in gestures
- Override built-in KWin gestures
- Application-specific gestures

# Installation
Expand Down
97 changes: 60 additions & 37 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,78 @@
# Configuration
There is currently no configuration UI and there probably won't be one. The configuration file is located at ``~/.config/kwingesturesrc``. The format may change at any time until 1.0.0 is released. Any changes will be mentioned in the release description.
> [!NOTE]
> The configuration format may change at any time until v1.0.0 is released. You will have to make manual changes to your configuration file. All changes will be mentioned in the release description.
Run ``qdbus org.kde.KWin /Effects org.kde.kwin.Effects.reconfigureEffect kwin_gestures`` or toggle the effect in system settings after updating the file.
There is currently no configuration UI and I have no intention of making one.

The configuration file is located at ``~/.config/kwingesturesrc`` and **is not created automatically after installation**. Run ``qdbus org.kde.KWin /Effects org.kde.kwin.Effects.reconfigureEffect kwin_gestures`` or toggle the effect in system settings after updating it.

See [example_gestures.md](example_gestures.md) for examples.

### Configuration file structure
Feel free to open an issue if something is unclear for you.

### Configuration file structure
- **[Gestures]**
- **[$Device]** (enum) - Which device to add this gesture for (``Touchpad``).<br>
- **[$GestureId]** (int) - Unique ID for the gesture.
- **Type** (enum) - ``Hold``, ``Pinch``, ``Swipe``
- **Fingers** (int) - Number of fingers required to trigger this gesture.<br>Sets **MinimumFingers** and **MaximumFingers**.<br>Minimum value: **2** for hold and pinch gestures, **3** for swipe.<br>Default: **none**
- **MinimumFingers** (int) - Minimum number of fingers required to trigger this gesture.<br>See **Fingers** for accepted values.<br>Default: **none**
- **MaximumFingers** (int) - Maximum number of fingers required to trigger this gesture.<br>See **Fingers** for accepted values.<br>Default: **none**
- **TriggerWhenThresholdReached** (bool) - Whether to trigger the gesture immediately after the specified threshold is reached.<br>Default: **false**&nbsp;
- **[Hold]** - Configuration for hold gestures.
- **Threshold** (int) - In milliseconds.<br>Default: **0**
- **[Pinch]** - Configuration for pinch gestures.
- **Direction** (enum)
- ``Contracting``
- ``Expanding``
- ``Any`` - Either contracting or expanding, intended for repeating actions.
- **Threshold** (float) - Should be >= 1.0 for expanding gestures and =< 1.0 for contracting gestures.<br>Default: **1.0**
- **[Swipe]** - Configuration for swipe gestures.
- **Direction** (enum)
- **[$Device]** (enum) - Which device to add this gesture for (currently only ``Touchpad`` is supported).<br>
- **[GestureRecognizer]** - Configuration for the gesture recognizer.
- **[Speed]** - Configuration for the determination of gesture speed. If the fast threshold is reached, the gesture will be treated as a fast gesture, otherwise as a slow one.
- **SampledEvents** (int) - How many input events to sample in order to determine the gesture speed. The deltas of each event are summed and compared against the specified thresholds.<br>Default: **5**<br><br>
- **SwipeThreshold** (int)<br>Default: **300**<br><br>
- **PinchContractingThreshold** (float)<br>Default: **0.4**
- **PinchExpandingThreshold** (float)<br>Default: **2.0**<br><br>
- **[$GestureId]** (int) - Unique ID for the gesture. Gestures are handled in ascending order by their ID.
- **Type** (enum, **required**) - ``Hold``, ``Pinch``, ``Swipe``<br>Default: **none**
- **Direction** (enum, **required**)
- For pinch gestures:
- ``Contracting`` - Pinch in.
- ``Expanding`` - Pinch out.
- ``Any`` - Either contracting or expanding.
- For swipe gestures:
- ``Left``
- ``Right``
- ``Up``
- ``Down``
- ``LeftRight`` - Either left or right, intended for repeating actions.
- ``UpDown`` - Either up or down, intended for repeating actions.
- **Threshold** (int) - Threshold in pixels for the X axis if **Direction** is ``Left``, ``Right`` or ``LeftRight``, and Y axis if **Direction** is ``Up``, ``Down`` or ``UpDown``.<br>Must be positive.<br>Default: **0**<br>&nbsp;
- ``LeftRight`` - Either left or right.
- ``UpDown`` - Either up or down.<br>
- **Speed** (enum) - ``Slow``, ``Fast``, ``Any``<br>If a value other than ``Any`` is specified, this gesture will cause all other gestures of the same device and type to be delayed by **[Gestures][\$Device][GestureRecognizer][Speed] SampledEvents** input events until the speed is determined.<br>Default: **Any**
- **Fingers** (int) - Number of fingers required to trigger this gesture.<br>Sets **MinimumFingers** and **MaximumFingers**.<br>Minimum value: **1** for hold gestures, **2** for pinch gestures, **3** for swipe.<br>Maximum value: Depends on how many fingers the device can detect.<br>Default: **none**
- **MinimumFingers** (int) - Minimum number of fingers required to trigger this gesture.<br>See **Fingers**.<br>Default: **none**
- **MaximumFingers** (int) - Maximum number of fingers required to trigger this gesture.<br>See **Fingers**.<br>Default: **none**
- **MinimumThreshold** - TODO
- **MaximumThreshold** - TODO<br><br>

- **[Conditions]** - At least one condition (or 0 if none specified) must be satisfied in order for this gesture to be triggered.
- **[$ConditionId]** (int) - Unique ID for this condition.
- **Negate** (bool) - If true, this condition will be satisfied only when none of its specified properties are.<br>Default: **false**
- **[$ConditionId]** (int) - Unique ID for this condition. Conditions are handled in ascending order by their ID.
- **Negate** (bool) - If true, this condition will be satisfied only when none of its specified subconditions are.<br>Default: **false**
- **WindowClassRegex** (string) - A regular expression executed on the currently focused window's resource class and resource name. If a match is not found for either, the condition will not be satisfied.<br>Default: **none**
- **WindowState** (enum) - ``Fullscreen``, ``Maximized``. Values can be combined using the | separator, For example, ``Fullscreen|Maximized`` will match either fullscreen or maximized windows.<br>Default: **none**<br>&nbsp;
- **[Actions]** - What do to when the gesture is triggered. Actions are executed in order as they appear in the configuration file.
- **[$ActionId]** (int) - Unique ID for this action.
- **WindowState** (enum) - ``Fullscreen``, ``Maximized``. Values can be combined using the | separator, For example, ``Fullscreen|Maximized`` will match either fullscreen or maximized windows.<br>Default: **none**<br><br>

- **[Actions]** - What do to when the gesture is triggered.
- **[$ActionId]** (int) - Unique ID for this action. Actions are handled in ascending order by their ID.
- **Type** (enum)
- ``Command`` - Run a command.
- ``GlobalShortcut`` - Invoke a global shortcut.
- ``KeySequence`` - Send keystrokes.
- **RepeatInterval** (int/float) - Whether and how often this action should repeat.<br>Can be negative for all gestures except hold.<br>Default: **0 (no repeating)**
- **BlockOtherActions** (bool) - Whether this action should block other actions if triggered.<br>Default: **false**<br>&nbsp;
- ``GlobalShortcut`` - Invoke a global shortcut (KDE).
- ``KeySequence`` - Send keystrokes.<br>
**Required**
- **When** (enum) - When the action should be triggered.<br>
For actions that should trigger after the fingers have been lifted, use ``Ended`` and optionally specify **MinimumThreshold** and **MaximumThreshold**.<br>
For actions that should trigger immediately when the specified **MinimumThreshold** is reached, use ``Updated``. <br>
- ``Started`` - When the gesture has started.
- ``Ended`` - When the gesture has ended.
- ``Cancelled`` - When the gesture has been cancelled. A gesture can be cancelled for example when a finger is lifted or the direction changes.
- ``Updated`` - When the gesture has been updated. Can only be executed once, unless **RepeatInterval** is specified.
- ``EndedOrCancelled`` - Both ``Ended`` and ``Cancelled``.
- **RepeatInterval** (int/float) - Whether and how often an ``Update`` action should repeat.<br>Can be negative for all gestures except hold.<br>Default: **0 (no repeating)**
- **BlockOtherActions** (bool) - Whether this action should block other actions if triggered.<br>Default: **false**
- **MinimumThreshold** - Same as **[Gestures][\$Device][$GestureId] MinimumThreshold**, but only applied to this action.<br>
- **MinimumThreshold** - Same as **[Gestures][\$Device][$GestureId] MaximumThreshold**, but only applied to this action.<br><br>

- **[Command]** - Configuration for the GlobalShortcut action.
- **Command** (string) - The command to run.<br>Default: **none**
- **Command** (string, **required**) - The command to run.<br>Default: **none**<br>**Required**
- **[GlobalShortcut]** - Configuration for the GlobalShortcut action.
- **Component** (string) - Run ``qdbus org.kde.kglobalaccel | grep /component`` for the list of components. Don't put the ``/component/`` prefix here.<br>Default: **none**
- **Shortcut** (string) - Run ``qdbus org.kde.kglobalaccel /component/$component org.kde.kglobalaccel.Component.shortcutNames`` for the list of shortcuts.<br>Default: **none**
- **Component** (string, **required**) - Run ``qdbus org.kde.kglobalaccel | grep /component`` for the list of components. Don't put the ``/component/`` prefix here.<br>Default: **none**
- **Shortcut** (string, **required**) - Run ``qdbus org.kde.kglobalaccel /component/$component org.kde.kglobalaccel.Component.shortcutNames`` for the list of shortcuts.<br>Default: **none**
- **[KeySequence]** - Configuration for the KeySequence action.
- **Sequence** (string) - The key sequence to run. Case-sensitive. Invalid format will cause a crash (for now). For the full list of keys see [src/gestures/actions/keysequence.h](src/gestures/actions/keysequence.h).<br>Example: ``press LEFTCTRL,keyboardPressKey T,release LEFTCTRL,keyboardReleaseKey T``.<br>Default: **none**<br>&nbsp;
- **[Conditions]** - Same as **[Gestures][\$Device][$GestureId][Conditions]**, but only applied to this action.
- **Sequence** (string, **required**) - The key sequence to run. Keys can be pressed in one action and released in another.<br>For the full list of keys see [src/gestures/actions/keysequence.h](src/gestures/actions/keysequence.h).<br>Example: ``press LEFTCTRL,press T,release LEFTCTRL,release T``.<br>Default: **none**<br><br>
- **[Conditions]** - Same as **[Gestures][\$Device][$GestureId][Conditions]**, but only applied to this action.
14 changes: 7 additions & 7 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
description = "Custom touchpad and touchscreen shortcuts";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/1925c603f17fc89f4c8f6bf6f631a802ad85d784";
nixpkgs.url = "github:NixOS/nixpkgs/970e93b9f82e2a0f3675757eb0bfc73297cc6370";
utils.url = "github:numtide/flake-utils";
};

Expand Down
39 changes: 32 additions & 7 deletions src/kwin/config/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,13 @@ void Config::read(std::shared_ptr<GestureInputEventFilter> filter, std::shared_p
{
minimumFingers = maximumFingers = fingers;
}
const bool triggerWhenThresholdReached = gestureGroup.readEntry("TriggerWhenThresholdReached", false);

std::shared_ptr<libgestures::Gesture> gesture;
if (gestureType == "Hold")
{
const auto holdGestureGroup = gestureGroup.group("Hold");

const auto holdGesture = std::make_shared<libgestures::HoldGesture>();
holdGesture->setThreshold(holdGestureGroup.readEntry("Threshold", 0));
filter->registerTouchpadGesture(holdGesture);
gesture = holdGesture;
}
Expand All @@ -50,14 +48,13 @@ void Config::read(std::shared_ptr<GestureInputEventFilter> filter, std::shared_p
const auto pinchGestureGroup = gestureGroup.group("Pinch");

const auto pinchGesture = std::make_shared<libgestures::PinchGesture>();
const auto directionStr = pinchGestureGroup.readEntry("Direction", "");
const auto directionStr = gestureGroup.readEntry("Direction", "");
libgestures::PinchDirection direction = libgestures::PinchDirection::Any;
if (directionStr == "Contracting")
direction = libgestures::PinchDirection::Contracting;
else if (directionStr == "Expanding")
direction = libgestures::PinchDirection::Expanding;
pinchGesture->setDirection(direction);
pinchGesture->setThreshold(pinchGestureGroup.readEntry("Threshold", 1.0));

filter->registerTouchpadGesture(pinchGesture);
gesture = pinchGesture;
Expand All @@ -67,7 +64,7 @@ void Config::read(std::shared_ptr<GestureInputEventFilter> filter, std::shared_p
const auto swipeGestureGroup = gestureGroup.group("Swipe");

const auto swipeGesture = std::make_shared<libgestures::SwipeGesture>();
const auto directionString = swipeGestureGroup.readEntry("Direction", "Left");
const auto directionString = gestureGroup.readEntry("Direction", "Left");
auto direction = libgestures::SwipeDirection::Left;
if (directionString == "Right")
direction = libgestures::SwipeDirection::Right;
Expand All @@ -80,14 +77,22 @@ void Config::read(std::shared_ptr<GestureInputEventFilter> filter, std::shared_p
else if (directionString == "UpDown")
direction = libgestures::SwipeDirection::UpDown;
swipeGesture->setDirection(direction);
swipeGesture->setThreshold(swipeGestureGroup.readEntry("Threshold", 0.0));

filter->registerTouchpadGesture(swipeGesture);
gesture = swipeGesture;
}

const auto speedStr = gestureGroup.readEntry("Speed", "Any");
libgestures::GestureSpeed speed = libgestures::GestureSpeed::Any;
if (speedStr == "Slow")
speed = libgestures::GestureSpeed::Slow;
else if (speedStr == "Fast")
speed = libgestures::GestureSpeed::Fast;
gesture->setSpeed(speed);

gesture->setFingers(minimumFingers, maximumFingers);
gesture->setTriggerWhenThresholdReached(triggerWhenThresholdReached);
gesture->setThresholds(gestureGroup.readEntry("MinimumThreshold", -1.0),
gestureGroup.readEntry("MaximumThreshold", -1.0));

if (!gesture)
continue;
Expand Down Expand Up @@ -125,6 +130,26 @@ void Config::read(std::shared_ptr<GestureInputEventFilter> filter, std::shared_p
if (!action)
continue;

libgestures::When when;
auto whenStr = actionGroup.readEntry("When", "");
if (whenStr == "Started")
when = libgestures::When::Started;
else if (whenStr == "Ended")
when = libgestures::When::Ended;
else if (whenStr == "Updated")
when = libgestures::When::Updated;
else if (whenStr == "Cancelled")
when = libgestures::When::Cancelled;
else if (whenStr == "EndedOrCancelled")
when = libgestures::When::EndedOrCancelled;
action->setWhen(when);

action->setThresholds(actionGroup.readEntry("MinimumThreshold", -1.0),
actionGroup.readEntry("MaximumThreshold", -1.0));

// action->setTriggerWhenThresholdReached(actionGroup.readEntry("TriggerWhenThresholdReached", false));


action->setRepeatInterval(actionGroup.readEntry("RepeatInterval", 0.0));
action->setBlockOtherActions(actionGroup.readEntry("BlockOtherActions", false));

Expand Down
Loading

0 comments on commit f3b90a2

Please sign in to comment.