Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
66797ea
feat: Add touch gesture support (mostly hardcoded)
Atan-D-RP4 Nov 30, 2025
7d4bd2f
fix: prevent leaks of touch events when gestures are in progress
Atan-D-RP4 Feb 1, 2026
760b769
fix: use output on absolute touch position instead of cursor position
Atan-D-RP4 Feb 1, 2026
ae60b69
fix: allow clients to detect 2f gestures by changing `>=` to `>`
Atan-D-RP4 Feb 4, 2026
6357631
feat: configurable touch gestures
Mar 23, 2026
6460877
refactor: rename touch to touchscreen, add configurable touchpad gest…
Apr 6, 2026
4c912db
fix: remove per-gesture natural-scroll, clean up unused imports
Apr 7, 2026
152e3f6
fix: finger count not checked during gesture direction detection
Apr 7, 2026
8161c8e
feat: discrete touchpad swipe gesture binds (Mod+TouchpadSwipe3/4/5)
Apr 7, 2026
b9d920c
feat: touchscreen edge swipe gestures + gesture lock
Apr 7, 2026
73b555d
feat: dynamic touch-binds system for touchscreen gestures
Apr 8, 2026
fa8de56
feat: move touchscreen binds into unified binds {} block
Apr 9, 2026
b9190c7
feat: move touchpad gestures into unified binds {} block
Apr 9, 2026
d8de732
feat: IPC gesture events with tags for external tool integration
Apr 9, 2026
ab221dd
feat: add Noop continuous gesture kind with edge swipe progress accum…
Apr 9, 2026
3bce301
feat: configurable gesture-progress-distance, touchpad progress, code…
Apr 9, 2026
4862044
docs: match touch gesture docs to current binds-based model
Apr 10, 2026
52cf0ea
refactor: rename Touch{N}Swipe/Pinch triggers to TouchSwipe{N}/TouchP…
Apr 10, 2026
c6095b4
feat: continuous pinch gestures with smooth overview animation
Apr 10, 2026
f23340e
feat: split each touchscreen edge into three zone triggers
Apr 10, 2026
e1dcec5
fix: don't toggle window floating on second touch finger during MoveGrab
Apr 10, 2026
b1c58ed
feat: touchscreen-gesture-passthrough window rule
Apr 10, 2026
410b8e6
docs: add Design:-Touchscreen-Gestures design rationale
Apr 10, 2026
e229be7
feat: 3/4/5-finger rotation gestures (proof of concept)
Apr 11, 2026
55ee3fe
refactor: parameterize Touch*/Touchpad* triggers with KDL properties
Apr 11, 2026
c64d0f2
fix: multi-finger rotation recognition hit rate (47% -> 75%)
Apr 11, 2026
88c9f48
feat: gesture recognizer telemetry IPC + classifier fixes
Apr 11, 2026
e4da83a
feat: N-finger tap detection (TouchTap trigger)
Apr 14, 2026
b9ba0c3
perf: batch touchscreen gesture processing per frame
Apr 14, 2026
f7df867
feat: touchpad tap-hold and tap-hold-drag triggers (TouchpadTapHold, …
Apr 14, 2026
90b256f
feat: touchscreen tap-hold-drag trigger (TouchTapHoldDrag)
Apr 14, 2026
5a22061
tune: adjust touchscreen gesture defaults based on real-world testing
Apr 14, 2026
dc79491
docs: add Design:-Gesture-IPC-Refactor RFC + admonition fix
Apr 15, 2026
32ab2c9
chore: align with niri logging convention — TOUCH-DBG FRAME → trace!
Apr 15, 2026
2995891
fix: send up+cancel for pre-gesture touches to clear client phantom s…
Apr 17, 2026
30ed8b5
chore: apply cargo fmt across touch-gesture modules
Apr 17, 2026
038a280
feat: touchpad pinch trigger (TouchpadPinch)
Apr 18, 2026
9695817
chore: cargo fmt (trailing-comma + import grouping in foreign_topleve…
May 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 110 additions & 5 deletions docs/wiki/Configuration:-Input.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

In this section you can configure input devices like keyboard and mouse, and some input-related options.

There's a section for each device type: `keyboard`, `touchpad`, `mouse`, `trackpoint`, `trackball`, `tablet`, `touch`.
There's a section for each device type: `keyboard`, `touchpad`, `mouse`, `trackpoint`, `trackball`, `tablet`, `touchscreen`.
Settings in those sections will apply to every device of that type.
Currently, there's no way to configure specific devices individually (but that is planned).

Expand Down Expand Up @@ -46,6 +46,14 @@ input {
// left-handed
// disabled-on-external-mouse
// middle-emulation

// Touchpad gesture binds live in the main binds {} block using
// the `TouchpadSwipe` trigger with `fingers=N direction="..."`
// properties. This subblock only contains tuning parameters.
// gestures {
// swipe-trigger-distance 16.0
// swipe-progress-distance 40.0
// }
}

mouse {
Expand Down Expand Up @@ -95,10 +103,29 @@ input {
// calibration-matrix 1.0 0.0 0.0 0.0 1.0 0.0
}

touch {
touchscreen {
// off
map-to-output "eDP-1"
// natural-scroll
// calibration-matrix 1.0 0.0 0.0 0.0 1.0 0.0

// Touchscreen gesture binds live in the main binds {} block using
// parameterized triggers like TouchSwipe fingers=3 direction="up",
// TouchPinch fingers=4 direction="in", or TouchEdge edge="left".
// This subblock only contains tuning parameters.
gestures {
// swipe-trigger-distance 100.0 // px of centroid motion before swipe latches
// edge-start-distance 30.0 // px-wide edge start zone
// pinch-trigger-distance 100.0 // px of spread change before pinch latches
// pinch-dominance-ratio 1.0 // spread must beat swipe × this (higher = stricter pinch)
// pinch-sensitivity 1.0
// pinch-progress-distance 100.0 // px of spread = IPC progress ±1.0 (signed)
// swipe-multi-finger-scale 1.2 // scales swipe-trigger-distance for 4+ fingers (1.0 = off)
// swipe-progress-distance 200.0 // px of swipe = IPC progress 1.0
// rotation-trigger-angle 20.0 // ° before rotation can latch
// rotation-dominance-ratio 0.5 // arc must beat swipe × this (higher = stricter rotation)
// rotation-progress-angle 90.0 // ° that map to IPC progress ±1.0
}
}

// disable-power-key-handling
Expand Down Expand Up @@ -259,11 +286,89 @@ Settings specific to `touchpad` and `mouse`:

<sup>Since: 25.08</sup> You can also override horizontal and vertical scroll factor separately like so: `scroll-factor horizontal=2.0 vertical=-1.0`

Settings specific to `tablet` and `touch`:
Settings specific to `tablet` and `touchscreen`:

- `calibration-matrix`: set to six floating point numbers to change the calibration matrix. See the [`LIBINPUT_CALIBRATION_MATRIX` documentation](https://wayland.freedesktop.org/libinput/doc/latest/device-configuration-via-udev.html) for examples.
- <sup>Since: 25.02</sup> for `tablet`
- <sup>Since: 25.11</sup> for `touch`
- <sup>Since: 25.11</sup> for `touchscreen`

Settings specific to `touchscreen`:

- `natural-scroll`: <sup>Since: next</sup> if set, inverts the scrolling direction for touchscreen swipe gestures.
- `gestures {}`: <sup>Since: next</sup> tuning parameters for touchscreen gesture recognition.

> [!NOTE]
>
> Touchscreen gesture **binds** are configured in the main `binds {}` block using parameterized triggers like `TouchSwipe fingers=3 direction="up"`, `TouchPinch fingers=4 direction="in"`, or `TouchEdge edge="left"`. The `touchscreen { gestures { } }` subblock below only contains tuning parameters that affect *how* gestures are recognized, not *which* ones fire. See the [Gestures](./Gestures.md) wiki page for the full list of touchscreen gesture triggers.

The `touchscreen { gestures { } }` tuning parameters are:

All knobs are grouped as: **trigger** (classifier commit gates), **dominance** (3-way race tuning), **progress** (IPC output scaling), and **misc**.

**Swipe:**

- `swipe-trigger-distance <float>`: pixels of centroid motion before a swipe gesture commits. Lower values feel more responsive but risk triggering on incidental finger drift. Default: `100.0`.
- `swipe-multi-finger-scale <float>`: scaling applied to `swipe-trigger-distance` for gestures with more than 3 fingers. The formula is `base * (1 + (fingers − 3) * (scale − 1))`, so with a base of 100 and scale 1.2 a 4-finger swipe needs 120 px and a 5-finger swipe needs 140 px. Default `1.2` — gives a small pinch-priority bias at high finger counts so ambiguous 4/5-finger motions resolve as pinch rather than swipe. Set `1.0` to disable the bias entirely.
- `swipe-progress-distance <float>`: pixels of swipe distance that map to IPC `GestureProgress = 1.0`. IPC-output knob — doesn't affect classification. Tune this for tagged external-app gestures (sidebar drawers, scrubbers, etc.). Default: `200.0`.

**Pinch:**

- `pinch-trigger-distance <float>`: pixels of `|spread_change|` before a pinch gesture commits. Default: `100.0`.
- `pinch-dominance-ratio <float>`: `|spread_change|` must exceed `swipe_distance × this` for pinch to win the race against swipe. Higher = stricter pinch. Default: `1.0`.
- `pinch-sensitivity <float>`: multiplier mapping finger spread change to continuous pinch animation delta (e.g. overview open/close progress). At `1.0`, one pixel of spread change contributes one pixel to the gesture accumulator. Applies to **all** pinch-bound continuous actions — the bind's own `sensitivity=` property is ignored for pinch because raw spread-delta pixels need different scaling from linear swipe distances. Default: `1.0`.
- `pinch-progress-distance <float>`: pixels of spread change that map to IPC `GestureProgress = ±1.0`. Signed: positive for pinch-out, negative for pinch-in. Default: `100.0`.

**Rotation:**

- `rotation-trigger-angle <float>`: cumulative rotation in **degrees** before a rotation gesture commits. Default: `20.0`. Rotation detection is an early proof of concept — see the warning in the [Rotation Gestures](./Gestures.md#rotation-gestures) section.
- `rotation-dominance-ratio <float>`: rotation arc length (`|cumulative_rotation| × cluster_radius`) must exceed both `swipe_distance × this` and `|spread_change| × this` for rotation to win the race. Higher = stricter rotation. Default: `0.5` (deliberately lenient — rotation almost always includes incidental translation). Matches `pinch-dominance-ratio` semantics (higher = stricter for both).
- `rotation-progress-angle <float>`: degrees of cumulative rotation that map to IPC `GestureProgress = ±1.0`. Signed: positive = counter-clockwise, negative = clockwise. Default: `90.0`.

**Edge:**

- `edge-start-distance <float>`: width in pixels of the screen-edge start zone. A touch must *begin* within this distance from an edge to count as a `TouchEdge` gesture; touches starting farther in are treated as regular swipes. Default: `30.0`.

Example:

```kdl
input {
touchscreen {
gestures {
swipe-trigger-distance 26.0
edge-start-distance 30.0
pinch-sensitivity 1.0
swipe-progress-distance 200.0
pinch-progress-distance 100.0
rotation-trigger-angle 15.0
rotation-dominance-ratio 0.5
}
}
}
```

### Touchpad Gesture Tuning

<sup>Since: next</sup>

The `touchpad { gestures { } }` subblock contains tuning parameters for touchpad gesture recognition. Like touchscreen, the actual gesture binds (`TouchpadSwipe fingers=N direction="..."`, `TouchpadPinch fingers=N direction="..."`) live in the main `binds {}` block.

- `swipe-trigger-distance <float>`: libinput delta units of centroid motion before a swipe gesture commits. These units are acceleration-adjusted and not directly comparable to touchscreen pixels. Default: `16.0`.
- `swipe-progress-distance <float>`: libinput delta units of swipe motion that map to IPC `GestureProgress = 1.0`. Because libinput acceleration curves are nonlinear, the same physical swipe can produce different delta magnitudes depending on speed — this value is **not** directly comparable to the touchscreen `swipe-progress-distance`. Default: `40.0`.
- `pinch-trigger-scale <float>`: `|scale - 1.0|` required before a `TouchpadPinch` bind fires. libinput normalizes pinch scale (1.0 = no change, 1.5 = 50% spread out, 0.5 = 50% spread in), so this is a unitless ratio and **not** directly comparable to the touchscreen `pinch-trigger-distance` (which is in pixels). Fires once per gesture when the threshold is crossed; direction is picked from the sign of the scale change. Default: `0.15`.

Example:

```kdl
input {
touchpad {
gestures {
swipe-trigger-distance 16.0
swipe-progress-distance 40.0
pinch-trigger-scale 0.15
}
}
}
```

Tablets and touchscreens are absolute pointing devices that can be mapped to a specific output like so:

Expand All @@ -273,7 +378,7 @@ input {
map-to-output "eDP-1"
}

touch {
touchscreen {
map-to-output "eDP-1"
}
}
Expand Down
36 changes: 36 additions & 0 deletions docs/wiki/Configuration:-Window-Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ window-rule {
clip-to-geometry true
tiled-state true
baba-is-float true
touchscreen-gesture-passthrough true

background-effect {
xray true
Expand Down Expand Up @@ -1017,6 +1018,41 @@ For example, GTK 4 pop-ups with pointing arrows (`has-arrow=true` property) are

These pop-ups with custom shapes will need the app to implement the [ext-background-effect protocol](https://wayland.app/protocols/ext-background-effect-v1) to work properly.

#### `touchscreen-gesture-passthrough`

Forward touchscreen multi-finger gestures to matching windows instead of letting niri's gesture recognizer consume them.

By default, niri claims 3+ finger touchscreen swipes and pinches for compositor actions like workspace switching and overview toggle.
This rule opts specific windows out of that behavior so apps that implement their own touch gestures (browsers, drawing apps, mapping tools) receive the raw touch events.

Escape hatches that still work on passthrough windows:

- **Mod+touch gestures** still trigger compositor binds — holding the mod key always bypasses passthrough so you can invoke niri actions even on a passthrough window.
- **Edge swipes** still belong to niri — a swipe that starts in a screen-edge zone runs the edge gesture even if the window under it has passthrough enabled.
- **2-finger touches** are unaffected — they already forward to clients by default regardless of this rule.

This rule is touchscreen-only. Touchpad gestures are not affected.

To discover which app-id to match, run niri with `RUST_LOG=niri=debug` and watch for lines like `touch: captured 3-finger gesture over app-id="org.mozilla.firefox"` after performing a gesture on the target window.

```kdl
// Let Firefox handle touch gestures itself (page navigation, pinch-zoom).
window-rule {
match app-id="firefox"
match app-id="org.mozilla.firefox"

touchscreen-gesture-passthrough true
}

// Same for a drawing app and Blender.
window-rule {
match app-id="org.kde.krita"
match app-id="org.blender.Blender"

touchscreen-gesture-passthrough true
}
```

#### Size Overrides

You can amend the window's minimum and maximum size in logical pixels.
Expand Down
Loading