Sweets
Configuration

Input Devices

Keyboard, cursor, touch, tablet, and switch-device support.

These blocks configure keyboards and the pointer cursor image. Sweets also routes touchscreen input, offers a tablet fallback, and can run commands for hardware switch events.

sweets.keyboard{ ... }

Keyboard settings apply to physical keyboards and re-apply on reload.

sweets.keyboard({
  numlock = true,
  repeat_rate = 25,
  repeat_delay = 600,
  layout = "us,lv",
  variant = ",apostrophe",
  options = "grp:alt_shift_toggle,caps:escape",
  model = "pc105",
  rules = "evdev",
})
KeyTypeDefaultDescription
numlockbooltrueLock the numpad on at startup
repeat_rateint25Held-key repeats per second (0–1000); 0 disables repeat
repeat_delayint600Milliseconds before repeat begins (0–10000)
layoutstringXKB defaultXKB layout, or comma-separated such as us,lv
variantstringXKB defaultXKB variant; empty entry for no variant, e.g. ,apostrophe
optionsstringXKB defaultComma-separated XKB options; "" clears them
modelstringXKB defaultXKB model, normally pc105
rulesstringXKB defaultXKB rules file, normally evdev

Sweets locks Num Lock on for each keyboard as it connects. Set numlock = false to leave it off.

To make backspace clear faster, raise repeat_rate and lower repeat_delay — for example repeat_rate = 50 and repeat_delay = 250.

layout, variant, options, model, and rules use standard XKB names. Omit a field to keep the system default, normally from XKB_DEFAULT_*. Sweets verifies the keymap compiles before accepting a reload, so an invalid layout keeps the active config. Virtual keyboards keep the keymap from their client.

sweets.cursor{ ... }

Controls the pointer image size and xcursor theme.

sweets.cursor({
  size = 24,
  -- theme = "Adwaita",
})
KeyTypeDefaultDescription
sizeinteger24Cursor size in logical pixels (1–256)
themestringbackendInstalled xcursor theme name

Changes apply on hot reload. An unknown theme falls back to whatever the xcursor library resolves.

How client cursors follow

size and theme set the compositor-drawn cursor. They reach client cursors two ways.

cursor-shape-v1 (preferred). Modern clients — Zed, recent GTK and Qt, SDL3 — ask for a named shape, and Sweets draws it from the configured theme at the right size and per-output scale. These always match, including on fractionally scaled outputs, and update live on reload.

XCURSOR_SIZE / XCURSOR_THEME (fallback). Clients that draw their own xcursor read these env vars. Sweets exports them so spawned programs inherit a matching cursor. They are read at startup, so only newly launched programs pick up a change.

Some GTK/Electron/Firefox builds take the theme from GSettings via the desktop portal. If an app ignores both mechanisms above, set the same values there:

gsettings set org.gnome.desktop.interface cursor-theme 'breeze_cursors'
gsettings set org.gnome.desktop.interface cursor-size 24

Sweets does not write GSettings itself, to avoid mutating global desktop state. Clients drawing their own xcursor on a fractionally scaled output may not multiply XCURSOR_SIZE by the scale, so their cursor can look small. cursor-shape-v1 avoids this.

Touchscreens and tablet tools

Touchscreen contacts are delivered through the Wayland touch protocol. They keep their initial target surface until release, support multiple contacts, and update idle activity. Tapping an XDG window focuses it.

Sweets advertises tablet-v2 for tablet-aware applications. Pen motion, tip and stylus buttons, pressure, distance, tilt, rotation, slider, and wheel axes all go through the native protocol. Tablet pads in the same libinput device group forward buttons, rings, and strips to that application.

On surfaces that do not bind tablet-v2, a pointer fallback applies: pen motion moves the cursor and tip events become pointer events. sweetctl inputs lists a separate tablet_pad entry when the hardware exposes one.

sweets.bindswitch(selector, command)

Hardware switch events do nothing by default. Bind a command to a state change:

sweets.bindswitch("lid:closed", "veila")
sweets.bindswitch("lid:closed", "sweetctl output all power off")
sweets.bindswitch("lid:open", "sweetctl output all power on")
SelectorTrigger
lid:closed / lid:openLaptop lid closes or opens
tablet_mode:on / tablet_mode:offConvertible/tablet-mode switch changes
keypad_slide:on / keypad_slide:offHardware keypad-slide switch changes

on and off work as aliases for every switch state. Multiple commands can match one event and run in config order. Commands are spawned detached, like sweets.bind(..., "spawn", command).

On this page