Input

September 8, 2026 · View on GitHub

Configure keyboard, pointer, touchpad, tablet, cursor, and focus behavior in one place.

Settings

[input]
middle_click_paste = false
window_drag_toggle = "none"

middle_click_paste controls the primary-selection clipboard. It defaults to true. Set it to false to disable pasting selected text with a middle click from either a mouse or touchpad. This also disables other primary-selection paste methods such as Shift+Insert, while the regular clipboard used by Ctrl+C and Ctrl+V remains available.

When disabled, Umbriel clears the current primary selection and rejects new primary selections from connected clients. Applications started while it is disabled are not offered the primary-selection protocol. The setting applies immediately on config reload, but protocol visibility is fixed when an application connects. Applications started while it was disabled must be restarted after re-enabling it.

window_drag_toggle retargets a window drag in progress: while a window is held with one mouse button, a press of the other main button changes where the drag will put it. It defaults to none, which leaves the drag alone. floating switches between the tiled layout and floating. pinned switches pinning on and off; turning it off puts the window back in the tiled layout when that is where it was pinned from, and leaves it floating otherwise.

The window's state changes when the drag drops, not when the button is pressed, so a drag can be retargeted as often as needed and toggling back and forth returns the window to the column width it was dragged out of. A drop that floats the window restores its remembered floating size, and the pointer keeps its grip on the same part of the window across that resize. A window that cannot take the target state (a fullscreen window cannot be pinned) drops where it was.

Keyboard

[input.keyboard]
layout = ""       # XKB layout, empty = system default
variant = ""      # XKB variant
options = ""      # XKB options, comma-separated
repeat_rate = 25  # 0-1000 Hz, 0 disables
repeat_delay = 600 # 0-10000 ms
numlock_toggle = true # true enables NumLock when a keyboard connects; false leaves it off
track_layout = "global" # "global", or "window" to track the layout per surface

These settings apply to physical keyboards. Virtual keyboard clients provide their own XKB keymaps, and Umbriel attaches each device to the seat only after its first usable keymap is ready. Applications therefore never receive the temporary empty keymap from a virtual keyboard that is still initializing.

layout takes a comma-separated list to load several layouts at once (layout = "us,de", optionally with a matching variant = ",nodeadkeys"). The first entry is active at startup. Switch between them with the keyboard-layout-next keybind or umbriel msg keyboard-layout-next, or put a toggle in options:

[input.keyboard]
layout = "us,de"
options = "grp:alt_shift_toggle"

Physical keyboards that share a named layout stay on that layout together, whether the change comes from an XKB toggle or keyboard-layout-next. Device overrides may list layouts in a different order: Umbriel matches them by XKB name, and leaves a keyboard unchanged when it does not provide the selected layout. IPC reports the layout vocabulary of the keyboard that most recently changed groups. Connecting another keyboard adopts that selected layout when available and does not reset the existing keyboards.

Run umbriel keyboard-layouts to list the layouts reported by that keyboard. The active layout is prefixed with *. Use umbriel keyboard-layouts --json for the same names and a zero-based current_index in structured output.

options is passed to XKB verbatim, so anything xkbcli list reports under options works (grp:win_space_toggle, caps:escape, compose:ralt, …). An options value XKB does not recognize is ignored silently, the same as with setxkbmap; a layout or variant that fails to compile is reported in the log and the whole keyboard block falls back to the system default.

Layout switching

keyboard-layout-next advances one physical keyboard to its next configured layout and synchronizes that named layout to physical keyboards that also provide it. The action wraps at the source keyboard's final layout, is inert when no physical keyboard provides multiple layouts, and never changes a virtual keyboard's client-owned keymap.

[input.keyboard]
layout = "us,de"

[keybinds]
"Mod+Shift+K" = "keyboard-layout-next"

umbriel msg keyboard-layout-next does the same from a script or panel. An XKB toggle such as options = "grp:alt_shift_toggle" is an alternative that lives in the keymap itself, and the two can coexist.

Tracking the layout per window

With several layouts loaded, track_layout decides how far a layout change reaches.

ValueBehavior
"global"A layout change applies to the whole session. This is the default.
"window"Each surface keeps its own layout.

Under "window", the named layout in use when a surface loses focus is stored against that surface and restored when it regains focus. Matching by XKB name keeps device overrides with differently ordered layout lists consistent. A surface that has not been focused before starts from the canonical keyboard's first layout, so it does not inherit the layout of the previous surface.

The unit here is the surface, not the window, so layer-shell clients are covered too. Opening a launcher while a window using the second layout is focused gives the new launcher the first layout. Closing it returns focus, and its remembered layout, to the window. Reloading input configuration rebuilds the physical keymaps and clears remembered surface layouts.

This matters most when the layouts share nothing. Two Latin layouts differ by a few keys, but a Latin and a non-Latin layout share no characters at all, so writing in one application and typing commands in another means switching on every single change of focus unless the compositor remembers.

Touchpad

[input.touchpad]
tap = true
natural_scroll = true
# accel_profile = "adaptive"  # "flat", "adaptive", or a custom curve
# sensitivity = 0.5           # -1.0 to 1.0
# scroll_factor = 1.5         # touchpad scroll speed, 0.1 to 10.0
# disable_while_typing = true
# disable_on_external_mouse = true
# click_method = "clickfinger"  # "button_areas" or "clickfinger"

Tap-to-click is enabled by default. Set tap = false to disable it globally, or use a per-device override below. natural_scroll and disable_while_typing remain unset by default, which preserves each device's corresponding libinput default. Set disable_while_typing = false to keep the touchpad active while typing. Removing either optional setting on reload restores the device default. Options are applied only when supported by the device; an explicitly configured unsupported option is reported in the log.

The effective natural_scroll value also controls Umbriel's three-finger gestures: workspace switching along the output's workspace axis, strip scrolling across it, and workspace selection while the overview is open. A per-device override or preserved libinput default applies to gestures from that device. The four-finger overview open and close gesture keeps its fixed direction.

accel_profile and sensitivity work like their [input.mouse] counterparts, including custom curves. Both remain unset by default, which uses each touchpad's libinput default profile and speed. Removing either setting on reload restores the corresponding default. sensitivity alone adjusts pointer speed under the device's default profile.

click_method decides how a physical press becomes a button. button_areas splits the bottom of the pad into left, middle, and right zones, while clickfinger reads the finger count instead: one finger is a left click, two a right click, three a middle click, anywhere on the pad. It is unset by default, which keeps each device's libinput default, and removing it on reload restores that default. Clickpads that only expose software buttons support just button_areas; asking for clickfinger there is reported in the log and leaves the device alone.

scroll_factor multiplies the smooth two-finger scroll a touchpad sends to the focused window, so 2.0 scrolls twice as fast and 0.5 half as fast. It remains unset by default (identity, 1.0) and takes the next scroll event on reload. It applies only to the continuous scroll delta: discrete notches, overview wheel stepping, and three-finger-swipe strip travel keep their own counting semantics.

Set disable_on_external_mouse = true to disable the touchpad while an external mouse is connected. Libinput re-enables it automatically once the mouse is unplugged. Detection is handled by libinput itself, so this only works in a native session because a nested session has no libinput devices to configure. Unlike tap and disable_while_typing, this option has no [[input.device]] per-device override. If a device doesn't support the mode, an explicitly configured value is ignored and a warning is logged; removing the key on reload restores the device's default.

Mouse

[input.mouse]
natural_scroll = false
# accel_profile = "flat"  # "flat", "adaptive", or a custom curve
sensitivity = 0.0        # -1.0 to 1.0
scroll_wheel_step = 60  # 1-1000, pixels per step for layout-scroll-left/right
# scroll_button = "MouseBack"       # Hold this button and move the mouse to scroll
# scroll_button_lock = false        # One press latches scrolling instead of holding

Omitting accel_profile preserves each device's libinput default, which is usually adaptive for a mouse. Set accel_profile = "flat" to disable speed-dependent acceleration, or set it to adaptive explicitly to override a different device default. sensitivity controls pointer speed independently of the selected profile. A custom curve can be supplied with this syntax:

accel_profile = "custom 0.2 0.0 0.5 1.0 2.0"

The first number is the positive input-speed step, followed by at least two non-negative output-speed points. Libinput interpolates between them. sensitivity has no effect when a custom profile is selected. Omit natural_scroll or accel_profile to preserve each device's corresponding libinput default. layout-scroll-left and layout-scroll-right clamp to the strip bounds, so the columns never park past either edge. Wheel-triggered scrolling uses twice scroll_wheel_step during an active tiled window drag.

scroll_button hands one button to libinput as a scroll modifier: while it is held, pointer motion scrolls the surface under the cursor instead of moving the cursor, and that motion consumes the button rather than clicking. Pressing and releasing it without moving the pointer still clicks, so the button keeps working for plain clicks and a bind on it still fires in that case, which is what the load-time log line means. It takes the same names as a mouse keybind, MouseLeft, MouseRight, MouseMiddle, MouseBack, or MouseForward. scroll_button_lock = true latches instead: one press starts scrolling, the next stops it, which suits a side button that is awkward to hold. Both keys are unset by default and removing them restores the device's libinput default. A device that cannot do button scrolling, or that has no such button, is reported in the log and left alone.

[input.mouse] reaches every pointer that is not a touchpad. A touchpad takes a scroll button only from its own [[input.device]] rule, because a touchpad can run one scroll method at a time and button scrolling would cost it two-finger scrolling.

Per-device overrides

Use [[input.device]] to override settings for devices whose name exactly matches name. Matching is case-sensitive. The name is the Device value reported by libinput list-devices.

[[input.device]]
name = "Acme Split Keyboard"
layout = "us"
variant = "colemak_dh"
repeat_rate = 40
repeat_delay = 250

[[input.device]]
name = "Acme Precision Touchpad"
tap = true
natural_scroll = false
accel_profile = "flat"
sensitivity = 0.0
disable_while_typing = false
click_method = "clickfinger"

[[input.device]]
name = "Acme Gaming Mouse"
accel_profile = "flat"
sensitivity = 0.0
scroll_button = "MouseBack"
scroll_button_lock = false

Each rule inherits the matching class settings and overrides only the keys it contains. layout, variant, options, repeat_rate, and repeat_delay apply to keyboards. tap, disable_while_typing, and click_method apply to touchpads. natural_scroll applies to touchpads and mice. scroll_button and scroll_button_lock apply to any pointer, including a touchpad, which reads them nowhere else. accel_profile and sensitivity apply to mice and touchpads; for a touchpad the rule overrides [input.touchpad] rather than [input.mouse]. Unsupported libinput settings are reported in the log.

Rules match every attached device with the exact name. Device overrides also apply when a device is connected after startup and when the configuration is reloaded. Rules from included files are collected alongside the ones in the file that includes them, and duplicate rules for the same name are rejected.

scroll_wheel_step, cursor settings, tablet settings, and focus settings remain compositor-wide because they are not properties of one physical input device.

Tablet

[input.tablet]
enabled = true                 # false disables the tablet and its pads
map_to_output = "DP-1"         # connector or monitor Config name
map_to_focused_output = false
map_to_focused_window = false  # pen area = focused window
left_handed = false
calibration_matrix = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]  # libinput calibration, 6 numbers

Stylus and pad input is delivered over the tablet-v2 protocol to clients that support it (pressure, tilt, eraser as a distinct tool, pad buttons, rings, and strips). Every other client receives pointer emulation instead: the tip acts as the left button, BTN_STYLUS as the right button, and BTN_STYLUS2 as the middle button.

KeyTypeDefaultDescription
enabledbooltrueSilences the tablet and its pads at the libinput level. Has no effect on devices libinput cannot disable.
map_to_outputstr(none)Confines the tablet area to the named output, using the same names as [output.NAME].
map_to_focused_outputboolfalsePen area follows the output holding keyboard focus.
map_to_focused_windowboolfalsePen area tracks the focused window.
left_handedboolfalseFlips the tablet orientation via libinput.
calibration_matrixarray(none)Six finite numbers passed to libinput; omitting the key restores the device default.

The mapping options form a cascade. map_to_focused_window wins while a window is focused; otherwise map_to_focused_output applies while an output holds keyboard focus; otherwise map_to_output applies while that output is connected; otherwise the pen covers the full output layout. Each level falls through to the next when its target is unavailable, so combining options is harmless. The tablet area is stretched to the target box without aspect-ratio correction. enabled, left_handed, and calibration_matrix changes apply on config reload, as do the mapping options for the next pen event.

Cursor

[input.cursor]
theme = ""   # empty = environment/default Xcursor theme
size = 24    # 1-512
hardware_cursor = true
follows_focus = false
hide_when_typing = false
hide_timeout_ms = 0  # 0-3600000, 0 disables hiding

Set hardware_cursor = false to composite the cursor in the output render pass. This can work around cursor flicker or disappearance caused by hardware cursor planes. Cursor settings apply on config reload. Output scale changes also reload the cursor image at the matching scale without requiring a restart. Set hide_when_typing = true to hide the cursor after a non-modifier key press. Modifier-only presses leave it visible. Typing while a pointer button is held also leaves it visible so active clicks, drags, and game actions are not interrupted. Set hide_timeout_ms to a value from 1 to 3600000 to hide the cursor after that many milliseconds without pointer activity. Motion, clicks, scrolling, and tablet input reveal the cursor and restart the timeout. The two hiding options can be enabled together.

While any pointer button is held, pointer focus stays with the window that received the press, and the compositor's own cursor changes, such as the Mod-held move and resize affordance, do not move it. That keeps clicks, drags, and held game actions intact: the window always receives the matching release.

Set follows_focus = true to warp the cursor to the visible center of a window selected by directional window focus, next-window focus, floating-state focus, or first/last-column focus navigation. It also follows the focused window after an in-workspace window-move-or-output-* move, an output focus action, a window or column move to another workspace or output, and a foreign-toplevel activation request from a dock or taskbar. This applies whether the matching action comes from a keybind, wheel bind, or IPC. Pointer-driven focus, automatic focus after a window closes, gestures, and overview selection do not warp the cursor. window-focus:<id> remains focus-only; use window-focus-warp:<id> when an individual id-based request must always move the cursor.

Focus

[input.focus]
follows_mouse = false
follows_mouse_max_scroll = 0.5  # optional, measured in viewport widths
KeyTypeDefaultDescription
follows_mouseboolfalseFocus the pointer target on enter, after compositor focus changes reveal another target, and after client drag completion.
follows_mouse_max_scrollfloat(no limit)Do not change focus when revealing the window would scroll farther than this many viewport widths. 0.0 allows only windows that are already fully visible. Omit for no limit.

Mapping windows and switching workspaces can change which window is under a stationary pointer. The existing focus remains until the next pointer motion, which selects the window under the pointer without requiring a border crossing. Finishing a client data drag performs the same refresh at the unchanged cursor position, so dropping over another window selects it immediately.

For example, a window three screens away requires a limit of at least 3.0. Values outside 0.0 to 100.0 are clamped and reported.