Skip to content

Gestures

General

Hyprland supports 1:1 trackpad gestures for some operations. The basic syntax looks like this:

hl.gesture({
    action = "...",
    direction = "...",
    fingers = int,
})

You can restrict gestures to a modifier with mods, or scale the animation speed with scale:

hl.gesture({ fingers = 3, direction = "horizontal", action = "workspace" })
hl.gesture({ fingers = 3, direction = "down", mods = "ALT", action = "close" })
hl.gesture({ fingers = 3, direction = "up", mods = "SUPER", scale = 1.5, action = "fullscreen" })
hl.gesture({ fingers = 3, direction = "left", scale = 1.5, action = "float" })

Fields

FieldDescriptionTypeLimits
actionAction to performstring
directionGesture directionstring
fingersNumber of fingersint[2 - 9]
disable_inhibitIf true, allows the gesture to bypass shortcut inhibitorsbool
modsMOD mask, e.g. "SUPER" or "ALT SHIFT"string
scaleGesture delta multiplierfloat[0.1 - 10.0]

Some gestures might have their own additional fields, which are described in the Actions table.

Directions

The following directions are supported:

DirectionDescription
swipeAny swipe
horizontalHorizontal swipe
verticalVertical swipe
leftLeftward swipe
rightRightward swipe
upUpward swipe
downDownward swipe
pinchAny pinch
pinchinInward pinch
pinchoutOutward pinch

Actions

Specifying unset as the action will unset a specific gesture that was previously set. Please note it needs to exactly match everything from the original gesture including direction, mods, fingers and scale.

ActionDescriptionAdditional arguments
lua functionExecutes a named Lua function or Lua lambda function. See belownone
closeCloses the active windownone
cursor_zoomZooms into the cursorzoom_level for a zoom factor, mode of "mult" to use a multiplier or "live" to update continuously during the pinch
floatFloats the active windowmode can be "float" or "tile" to force a direction of floating
fullscreenFullscreens the active windowmode can be "maximize" to do maximize instead of fullscreen
moveMoves the active windownone
resizeResizes the active windownone
scroll_moveScrolls the tape, if the current layout is scrollingnone
specialToggles a special workspaceSpecial workspace name
workspaceWorkspace swipe gesture, for switching workspacesnone

cursor_zoom

Examples:

hl.gesture({ fingers = 2, direction = "pinch", action = "cursor_zoom", zoom_level = 2 })
hl.gesture({ fingers = 2, direction = "pinch", action = "cursor_zoom", zoom_level = 1.2, mode = "mult" })
hl.gesture({ fingers = 2, direction = "pinch", action = "cursor_zoom", zoom_level = 1, mode = "live" })

cursor_zoom toggles by default. mult multiplies the current zoom value. live adjusts the zoom continuously to the pinch scale and keeps the zoom anchored to the cursor position at the start of the gesture. The numeric argument is unused in live mode, so 1 is a reasonable placeholder.

Lua functions

The Lua function can be named or a lambda.

An example with a lambda:

hl.gesture({
    fingers = 3,
    direction = "up",
    action = function()
        hl.notification.create({ text = "I just swiped on my trackpad!", timeout = 5000, icon = "ok" })
    end
})

An example with a named function:

local function swipe()
    hl.notification.create({ text = "I just swiped on my trackpad!", timeout = 5000, icon = "ok" })
end

hl.gesture({
    fingers = 3,
    direction = "up",
    action = swipe
})

Live Lua gestures

For live gestures, i.e. ones that react to the gesture state, pass a table instead of a lambda, which has start, update and finish methods.

The start and update methods are passed a table with the following fields:

FieldDescriptionType
delta.xHorizontal motion relative to the last update. Right motion is positive, left is negativefloat
delta.yVertical motion relative to the last update. Downwards motion is positive, upwards is negativefloat
fingersNumber of fingers (2–9)int
rotationThe change in angle of the finger arrangement, relative to the last update. Clockwise is positive, counterclockwise is negative. Nil if the gesture type is not pinchfloat
scaleThe change in size of the finger arrangement, relative to the start of the gesture. Spread is positive, pinch is negative. Nil if the gesture type is not pinchfloat
time_msThe timestamp at which the even occurred, measured from when the system was bootedint
typeEither swipe or pinchstring

The finish method is passed a table with the following fields:

FieldDescriptionType
cancelledTrue if the gesture was ended abnormally by the backend. False otherwisebool
time_msThe timestamp at which the even occurred, measured from when the system was bootedint
typeEither swipe or pinchstring
Examples
-- Run a Lua lambda function, open a terminal with a 4-finger swipe up:
hl.gesture({ fingers = 4, direction = "up", action = function() hl.exec_cmd("kitty") end })

-- Toggle a special workspace with a 4-finger swipe down, only when holding SUPER, bypassing inhibitors:
hl.gesture({ fingers = 4, direction = "down", mods = "SUPER", action = "special", workspace_name = "scratchpad", disable_inhibit = true })

-- Zoom into the cursor with a pinch, using a multiplier instead of a fixed zoom level:
hl.gesture({ fingers = 2, direction = "pinchin", action = "cursor_zoom", zoom_level = 2.0, mode = "mult" })

-- Output all events as notifications, for testing
hl.gesture({
    fingers = 3,
    direction = "horizontal",
    action = {
        start = function(e) hl.notification.create({ text = "start: type=" .. e.type .. " time_ms=" .. e.time_ms .. " fingers=" .. e.fingers .. " delta=(" .. e.delta.x .. ", " .. e.delta.y .. ")", timeout = 1000, icon = 1}) end,
        update = function(e) hl.notification.create({ text = "update: type=" .. e.type .. " time_ms=" .. e.time_ms .. " fingers=" .. e.fingers .. " delta=(" .. e.delta.x .. ", " .. e.delta.y .. ")", timeout = 1000, icon = 1}) end,
        finish = function(e) hl.notification.create({ text = "finish: type=" .. e.type .. " time_ms=" .. e.time_ms .. " cancelled=" .. tostring(e.cancelled), timeout = 1000, icon = 1}) end
    }
})

-- Output all events as notifications, for testing
hl.gesture({
    fingers = 3,
    direction = "pinch",
    action = {
        start = function(e) hl.notification.create({ text = "start: type=" .. e.type .. " time_ms=" .. e.time_ms .. " fingers=" .. e.fingers .. " delta=(" .. e.delta.x .. ", " .. e.delta.y .. ") scale=" .. e.scale .. " rotation=" .. e.rotation, timeout = 1000, icon = 1}) end,
        update = function(e) hl.notification.create({ text = "update: type=" .. e.type .. " time_ms=" .. e.time_ms .. " fingers=" .. e.fingers .. " delta=(" .. e.delta.x .. ", " .. e.delta.y .. ") scale=" .. e.scale .. " rotation=" .. e.rotation, timeout = 1000, icon = 1}) end,
        finish = function(e) hl.notification.create({ text = "finish: type=" .. e.type .. " time_ms=" .. e.time_ms .. " cancelled=" .. tostring(e.cancelled), timeout = 1000, icon = 1}) end
    }
})

-- Adjust volume
local volume_gesture = function(change) hl.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ " .. math.abs(change) .. "%" .. (change<0 and "-" or "+")) end
hl.gesture({
    fingers = 3,
    direction = "vertical",
    action = {
        start = function(e) volume_gesture(-0.25 * e.delta.y) end,
        update = function(e) volume_gesture(-0.25 * e.delta.y) end
    }
})
Last updated on