Skip to content

Gestures

GGEZUS edited this page Sep 23, 2026 · 4 revisions

Reference for every gesture the patchset adds, how recognition works, and which fallback applies when something is left unbound. For the config syntax itself, see Configuration.

The multi-finger swipes run through the same spring-physics pipeline as niri's touchpad gestures: rotation-proof logical coordinates, live follow, inertia. Touchpad behavior is untouched; everything on this page is touchscreen-only.

The two animated drags (multi-finger)

With fingers fingers (3 or 4; default 3) down and dragging:

  • Vertical drag runs the workspace carousel, the same continuous animated switch as the touchpad swipe.
  • Horizontal drag moves the view across columns, or, with horizontal-swipe "resize-column", resizes the focused window instead (next section).

While a drag is in progress the view tracks your fingers 1:1; on lift it settles with inertia.

Live width resize (resize-column)

touchscreen-swipe {
    horizontal-swipe "resize-column"
}

Instead of scrolling the view, the horizontal drag widens or narrows the focused window, tracking your fingers 1:1: drag right to widen, left to narrow; the width stops when you stop and stays where you lift.

It rides niri's own interactive-resize machinery, so the resize is smooth, respects window min/max sizes, and works on floating windows too. On a tiled layout it resizes the focused column; with a floating window focused, that window. With nothing focused, the drag falls back to moving the view.

The default remains "move-view" (the animated scroll), and that is also the compiled default: with no gestures {} config at all, only the two animated drags and the 3-finger tap do anything.

Taps and flicks

The discrete gestures fire once, on finger lift. fingers sets the base count (3 or 4; default 3) and the *-more nodes sit at one finger more than that:

  • tap - fingers fingers down and up within ~250 ms, without the gesture's centroid crossing the 16 px swipe-recognition threshold (the same figure GNOME Shell uses). Compiled default: maximize-column.
  • tap-more - taps with one finger more than fingers (4 by default); overrides tap for those.
  • swipe-more-down / swipe-more-up - a quick vertical flick with one finger more than fingers (4 by default), direction as the name says.
  • swipe-more-left / swipe-more-right - the horizontal counterparts, added after v26.04.18. Unbound (the shipped default), such flicks keep the horizontal drag's behavior (move-view or resize-column).

Renamed after v26.04.18: tap-4/swipe-4-up/swipe-4-down became tap-more/swipe-more-up/swipe-more-down and now track fingers instead of a fixed four. EasySetup reads the old names and writes the new ones on its next save.

A "quick" flick is decided by how long your fingers lingered below the movement threshold before moving; see hold-swipes below for the other side of that decision.

Fallbacks

Anything left unbound falls back to the animated gesture (or, for taps with more fingers than fingers, to the regular tap action). Concretely: with tap-more unset, a 4-finger tap runs tap's action; with swipe-more-up unset, a 4-finger flick drives the workspace carousel; with swipe-more-left or swipe-more-right unset, the flick resizes (or scrolls) like the horizontal drag. So "my 4-finger gestures act like 3-finger ones" means "the 4-finger binds are missing from the config", not a bug: see Troubleshooting.

Hold-swipes (move windows)

Rest three (or more) fingers still for a moment (~400ms), then swipe: instead of the animated view scroll, the dominant direction runs a discrete action. Quick swipes never trigger it; how long your fingers lingered below the movement threshold decides.

Each direction binds on its own (left, right, up, down); a direction left unset falls back to the animated swipe. Remove the hold block (or a single direction) and held swipes behave like quick ones again.

Gesture ownership

From the moment touchscreen-swipe.fingers fingers are down, the whole touch sequence belongs to the compositor: apps never see it (touches they already received are cancelled), and normal delivery resumes when the last finger lifts. So a 3-finger swipe doesn't pinch-zoom a browser or drag-select terminal text mid-gesture.

Single- and two-finger input, including app pinch-zoom, is untouched. If an app genuinely needs 3-finger touches (some design tools use them), raise fingers to 4.

Edge swipes (one finger)

A single finger swiping inward from a screen edge:

  • the touch must start within ~48 logical pixels of an edge, and
  • swipe inward past ~40 pixels, with the inward direction dominant.

Then the action bound to that edge runs. bottom means swiping up, top swiping down, left swiping right, right swiping left.

Behavior details that matter in practice:

  • Touches in an edge strip deliver to applications normally until the trigger distance is crossed: plain taps and short drags on shell bars and app content keep working. The compositor cancels the touch (and swallows the rest of the stroke) only when the gesture actually fires.
  • A second finger landing cancels the gesture.
  • The zones follow the output the touch lands on, including its transform, so edges stay in the visual orientation on rotated tablets and per-output on multi-monitor setups.
  • Zones left unbound are inert.
  • The trigger path doesn't check whether the overview is open (only the screenshot and window-MRU UIs are gated), so an edge swipe can fire with the overview up: a bound bottom edge toggles the overview closed mid-drag. Kept deliberately.
  • Don't bind an edge covered by a swipe-driven shell surface (for example a pull-down notification panel along the top): the bound gesture would fire mid-drag.

Corner swipes (one finger)

Single-finger diagonal swipes out of a screen corner. The corner zones are the ~48 logical pixel squares where two edge strips meet, and the swipe must move inward by ~20 pixels on both axes: a genuinely diagonal stroke. A mostly-straight swipe starting in a corner zone deliberately triggers nothing, so the adjacent edges stay usable right up to the corner.

Corners win over edges when the touch starts within range of both. Unbound corners are inert; the shipped config leaves all four unbound on purpose.

Touch point visualization

show-touch-points renders translucent dots that follow your fingers, drawn by the compositor itself: any screen recording captures them and input is never touched. Off by default; meant for demo videos and for debugging gestures.

gestures {
    show-touch-points "gestures"   // dots only while a multi-finger gesture is down
    // show-touch-points "all"     // every finger (debugging)
}

In gestures mode dots appear only while enough fingers are down concurrently for a gesture (touchscreen-swipe.fingers), so ordinary taps and scrolling stay clean.

Clone this wiki locally