Sweets
Configuration

General

The sweets.general block — modifier key, background, and workspace behavior.

sweets.general{ ... } sets the global behavior of the compositor: the modifier key bindings expand to, the empty-desktop background, and how workspaces and directional actions behave across monitors.

sweets.general {
  mod_key = "SUPER",
  background = "#12121c",
  workspace_mode = "shared",
  workspace_count = 10,
  workspace_move_follow = false,
  focus_cross_monitor = true,
  move_cross_monitor = true,
  drag_center_cursor = true,
  allow_tearing = false,
  explicit_sync = true,
}

Options

KeyTypeDefaultDescription
mod_keystringSUPERModifier the MOD token expands to in binds
backgroundstring#12121cEmpty-desktop fill color, #RRGGBB[AA]
workspace_modestringsharedHow workspaces relate to monitors: shared or per_monitor
workspace_countint10Number of usable workspaces, 110. Switching to or moving a window past this is ignored
workspace_move_followboolfalseFollow a window to its new workspace after workspace_move
focus_cross_monitorbooltrueDirectional focus crosses into the next monitor when there is no window that way on the current one
move_cross_monitorbooltrueDirectional window movement crosses into the next monitor when the window is at the edge
drag_center_cursorbooltrueA started MOD + drag recenters the window under the cursor
allow_tearingboolfalsePermit tearing page-flips for a fullscreen window that requests async presentation or tags itself a game (lower latency for games)
explicit_syncbooltrueAdvertise linux-drm-syncobj explicit GPU sync when supported; set false to work around flaky driver page-flip timing

mod_key

Accepts SUPER (a.k.a. LOGO / MOD4), ALT (MOD1), CTRL, or SHIFT. This is the modifier the MOD token expands to in your key bindings.

background

The solid color drawn behind everything where no window or wallpaper client covers the screen. It spans the whole output layout and updates on reload.

workspace_mode

Chooses the multi-monitor workspace model:

  • shared (default): one global set of workspaces, each shown on at most one monitor at a time. Switching to a workspace that is already visible on another monitor just moves focus to that monitor. Switching to a hidden workspace that still has windows returns it — and focus — to its home monitor (the one its windows live on); a hidden workspace with no windows appears on the current monitor instead. On startup each monitor opens on a distinct workspace (monitor 1 → 1, monitor 2 → 2, …).
  • per_monitor (dwm-style): every monitor owns an independent set of workspaces, and MOD + N switches the current monitor to its own workspace N.

A monitor pin (see sweets.workspace) acts as a fixed home in either mode. Changing workspace_mode takes effect cleanly on a fresh start; switching it on a running compositor via reload keeps existing windows but only guarantees that each monitor lands on a distinct workspace.

workspace_count

Limits how many of the ten workspaces are usable, from 1 to 10 (default 10). Switching to — or moving a window to — a workspace above the limit is ignored, whether the request comes from a key binding, the IPC workspace command, or a panel. Workspaces above the limit are also marked hidden to ext-workspace clients, so status bars stop showing them.

The count and your key bindings are independent: dropping the count to 5 does not delete the MOD+6MOD+0 binds — they simply become no-ops. For a clean setup, set workspace_count and shorten the bind loop to match:

sweets.general { workspace_count = 5 }

for i = 1, 5 do
  sweets.bind("MOD+" .. i, "workspace", i)
  sweets.bind("MOD+Shift+" .. i, "workspace_move", i)
end

Lowering the count on a running compositor via reload is safe: any windows left on a now-disabled workspace are moved onto the last enabled one, so nothing is stranded out of reach.

workspace_move_follow

Controls what happens after the workspace_move action. By default (false) the focused window moves to the target workspace and you stay where you are. With true, focus follows the window to its new workspace (and, in shared mode, to that workspace's monitor), so the moved window stays focused — like Hyprland's movetoworkspace versus movetoworkspacesilent.

focus_cross_monitor

Controls the directional focus actions (focus_left, focus_right, focus_up, focus_down). With it on (default), pressing a direction when there is no window that way on the current monitor moves focus to the adjacent monitor in that direction and activates it. With it off, directional focus stays within the current monitor.

When focus crosses to another monitor it restores the last-focused window on that monitor's active workspace, unless that window is not a sensible landing for the travel direction — for example, entering a monitor whose master column is on the far side lands on the nearest stacked window (topmost) rather than jumping past it to the master. If the last-focused window was already the nearest one for that direction, it is restored exactly.

move_cross_monitor

Controls the directional move actions (move_left, move_right, move_up, move_down). Moving a tiled window swaps it with its neighbor in that direction. When there is no neighbor that way (the window is at the edge of the layout) and this is on (default), the window crosses to the adjacent monitor in that direction and lands on the edge it entered from — moving right makes it the master/front of the next monitor, moving left appends it to the far end. With it off, a window at the edge stays put. The window keeps focus and the destination monitor becomes active. Floating windows are not moved by these actions.

Focus follows the mouse: the window under the pointer always gains keyboard focus as you move it, including across monitors and workspaces. Hovering empty space or a panel keeps the last window focused, and it never steals the keyboard from a launcher like rofi.

allow_tearing

Master switch for screen tearing, off by default. Tearing lets a frame reach the screen the instant it is ready instead of waiting for the next refresh, which trims input latency for games that run with vsync off — at the cost of a visible horizontal seam when frames change mid-scan.

With it on, Sweets tears for a fullscreen window when either it asks through the tearing-control-v1 protocol (most games expose this as a "tearing" or "immediate / uncapped" option) or it tags itself as a game through content-type-v1 — and only when the driver accepts a tearing page-flip for that frame. Anything else falls back to a normal vsynced flip. It pairs naturally with adaptive_sync: VRR removes tearing inside the refresh window, and tearing covers frames that miss it.

Tearing page-flips only happen on the DRM/KMS backend, so there is nothing to see when running Sweets nested inside another compositor.

explicit_sync

On by default. When the GPU and backend support DRM synchronization timelines, Sweets advertises linux-drm-syncobj-v1 so clients can hand it explicit buffer-ready and buffer-release fences instead of relying on implicit sync. This improves correctness and latency and is strongly recommended on NVIDIA, where implicit sync is prone to flicker.

Set it to false as a workaround on GPU/driver combinations where explicit sync misbehaves. Turning it off makes Sweets fall back to implicit sync, which is solid on Mesa (Intel/AMD). When disabled, the log shows explicit sync disabled by config at startup.

Intel Arc (DG2) on the i915 driver: explicit sync currently triggers a visible glitch — windows flash and stretch while being resized, and the log fills with connector …: … cannot be scanned out. It comes from the direct-scanout path rejecting the client buffer format every frame. If you see this on an Arc GPU, set explicit_sync = false. AMD and NVIDIA are unaffected; this is specific to Arc + i915 + Mesa.

This is decided once at startup, so changing it takes effect on the next session (a hot reload will not create or remove the global).

On this page