Draft: Stroke gesture infrastructure basics

See #216.

Marking as draft because there's no point in reviewing this in detail until the dependencies have landed.

An attempt to port my stroke recognition KWin effect prototype to fit within KWin proper. Now depends on the gesture customization branches from !9062, kglobalacceld!97, and frameworks/kglobalaccel!137.

It's basically working at this point, may need a little more polish (importantly, tests) and code review. But you now get stroke recognition, a stroke-painting visualization effect, and assignable actions via KGlobalAccelD (specified via kglobalshortcutsrc config file). Happy about all feedback.

Note that this MR talks about "stroke gestures" a lot. I plan to rename this to "line shape gestures", given that "stroke" was seen to carry a negative connotation to a serious medical condition. Expect another rebase to edit names across the board.


In its initial form, this MR introduces two sets of files:

  • stroke_gestures.{h,cpp} contains a minimally modified fork of wstroke's continuation of EasyStroke's stroke matching code which pretty much every C++ mouse gesture tool is derived from.
    • Plus a tiny C++ wrapper to keep C-isms local to that file.
  • stroke_input_filter.{h,cpp} contains a stroke recognition input filter class, powered by a gesture collection that can be owned by someone else, as well as StrokeGesture objects in the style of Gesture or the proposed ScreenEdgeGesture.

These files contain the core stroke recognition logic. Everything else is just plumbing.

Stroke gesture recognition differs in three significant ways from multi-touch or screen edge gesture recognition:

  1. We can't commit to an individual gesture object having started(). "Started" is a state in the recognizer, but an individual gesture object is only really useful once triggered(). Consequently, no per-gesture cancelled() either. Theoretically we could call started() and cancelled() on all the gestures in our list, but what's the point.
  2. There's no use for a split between input filter and iterative gesture recognizer. The split is between input filter and one-shot stroke matching. All of the iterative recognition code is just button grab management and collecting stroke points for later.
  3. The input filter / gesture recognizer must be able to emulate a delayed button click if a pointer button was grabbed, but didn't travel the required distance.

Nonetheless, I tried plumbing it in a way that's similar to existing pinch and swipe gestures. This includes:

  • StrokeInputFilter owned by InputRedirection.
  • New StrokeShortcut type for GlobalShortcutManager, which owns the gesture-to-QAction map.
    • GlobalShortcutManager also hooks up stroke shortcuts with KGlobalAccel for configurable global actions.
  • Gesture registration:
    • EffectsHandler::registerStrokeShortcut(modifiers, points, action) which calls
    • InputRedirection::registerStrokeShortcut(modifiers, points, action) which calls
    • GlobalShortcutsManager::registerStroke(modifiers, points, action) which calls
    • StrokeGestures::add(StrokeGesture*) (input filter / gesture recognizer knows this StrokeGestures* for matching strokes).
  • Gesture events for Effect, mainly to visualize the stroke:
    • strokeGestureBegin(), strokeGestureUpdate(), strokeGestureEnd() and strokeGestureCancelled().

Based on this, a StrokeEffect which visualizes the stroke path as it's being drawn. It hooks into the new Effect stroke events, getting enough data to draw the path without needing to know about the action by itself.

I feel like this is a fairly good match for KWin's existing architecture, but let me know if or where I'm wrong.

Thanks in advance for your feedback!


In follow-up MRs, we should expose a way for KWin to capture gestures, allowing a "Record Gesture..." button in System Settings' "Shortcuts" (kcm_keys) module. Based on discussions with @zamundaaa, this could be done with a second instance of the input filter, pre-empting the first one and capturing line shapes instead of recognizing from a set of given ones. This and the corresponding "record gesture" D-Bus API will not be tackled in this MR.

Manual testing

To test, build the branches and assign an activation button in your .config/kcminputrc for each mouse that you want gestures for. E.g. 2 for Qt::MouseButton::RightButton (remember to replace my mouse device names with yours):

[LineShapeGestures]
DefaultActivationButton=2

[LineShapeGestures][Device][Razer ProClickM]
ActivationButton=8

Then start drawing on screen with that button. At the current time, the branch contains an extra commit that adds default up/down gestures invoking Overview / Grid View. If we don't want this, we can throw them out again.

Edited by Jakob Petsovits

Merge request reports

Loading