Monitors
Configure monitor modes, refresh rates, adaptive sync, scale, position, transform, and enablement.
sweets.monitor(name, { ... }) configures one output by its connector name.
Rules are optional. Unlisted outputs use their preferred mode, scale 1, a
normal transform, and automatic placement.
sweets.monitor("DP-1", {
position = { 0, 0 },
mode = { 2560, 1440 },
refresh_hz = 144,
scale = 1.5,
transform = "normal",
enabled = true,
adaptive_sync = "on_demand",
})
sweets.monitor("eDP-1", {
enabled = false,
})Options
| Field | Type | Accepted value | Omitted behavior |
|---|---|---|---|
position | table | Two signed logical coordinates: { x, y } | Automatic placement |
mode | table | Physical size { width, height }; each value is 1 through 16384 | Preferred backend mode |
refresh_hz | number | Finite value from 1 through 1000; requires mode | Preferred or highest refresh for the selected size |
scale | number | 0.1 through 16 in exact increments of 1/120 | 1 |
transform | string | "normal", "90", "180", "270", "flipped", "flipped-90", "flipped-180", or "flipped-270" | "normal" |
enabled | boolean | true or false | true |
adaptive_sync | boolean or string | false/"disabled", true/"always", or "on_demand" | Fixed refresh (false) |
Every rule must set at least one field. You can declare up to 32 monitor rules. Each connector may appear once per configuration layer.
A user rule for the same connector replaces the installed rule as a whole. It does not merge individual fields. Repeat any installed fields you want to keep.
HDR, tearing, and custom modes are not monitor configuration fields. Adding one of those fields makes the configuration invalid.
Finding connector names
Inspect connected outputs with:
sweets msg outputsThe response includes each connector name, description, supported modes, current mode, position, scale, transform, enablement, power state, and adaptive-sync capability, configured mode, and effective state.
Use JSON output in scripts:
sweets msg --json outputsConnector matching is exact and case-sensitive. Common names include DP-1,
HDMI-A-1, and eDP-1.
Mode and refresh rate
mode uses physical pixels. The requested size must match a mode advertised by
the monitor and graphics driver.
sweets.monitor("DP-1", {
mode = { 3840, 2160 },
})When refresh_hz is omitted, Sweets selects the preferred matching mode. If no
matching mode is marked preferred, it selects the highest refresh rate.
Set refresh_hz when you need a specific advertised refresh:
sweets.monitor("DP-1", {
mode = { 2560, 1440 },
refresh_hz = 144,
})Sweets selects the closest advertised refresh within 0.5 Hz. A request for
60 may therefore select 59.94 Hz. If two rates are equally close, the
preferred mode wins.
An unavailable size or refresh rate rejects the live configuration change. Sweets keeps the previous working output state.
Adaptive sync (VRR)
Set adaptive_sync = "on_demand" to keep ordinary desktop use at fixed
refresh and enable variable refresh only for explicitly selected fullscreen
applications:
sweets.monitor("DP-1", {
mode = { 2560, 1440 },
refresh_hz = 144,
adaptive_sync = "on_demand",
})
sweets.window_rule({
app_id = "steam",
adaptive_sync = true,
})The window rule is eligibility, not a fullscreen request. VRR activates only when the opted-in view is mapped, visible, fullscreen, and actually rendered on that output. Sweets renders the qualifying fullscreen frame first and changes the DRM state for the following frame. Leaving fullscreen, switching away, unmapping, moving the view, session lock, the special-workspace overlay, or compositor drag-and-drop returns an on-demand output to fixed refresh.
Use adaptive_sync = true or "always" for the original always-on behavior.
Because always-on VRR lets desktop damage move a panel across its refresh
range, some displays show slight luminance flicker; on-demand mode avoids that
behavior during ordinary desktop use. false and "disabled" select fixed
refresh. Boolean values remain accepted for compatibility.
Sweets enables VRR only when the kernel marks the connector vrr_capable and
the DRM state passes Smithay's test. An explicit reload or output-management
request that enables VRR on unsupported hardware is rejected without changing
the working output configuration. During startup, resume, or hotplug recovery,
an unavailable configured VRR state degrades that connector to fixed refresh
so the desktop remains usable. Nested sessions always use fixed refresh.
Use sweets msg outputs to inspect capability, configured mode, and effective
state. In on-demand mode, adaptive_sync_mode remains "on_demand" while
adaptive_sync changes as eligible fullscreen content appears or disappears.
EDID range text alone is not proof that the kernel can enable VRR.
Scale
scale controls the relationship between physical pixels and logical desktop
coordinates. Larger values make applications and interface elements appear
larger.
sweets.monitor("eDP-1", {
scale = 1.5,
})Sweets supports fractional scale values that can be represented in 1/120
steps. Common values such as 1.25, 1.5, and 2 are valid. A value such as
1.31 is rejected because it is not an exact 1/120 step.
Logical output dimensions are the physical mode divided by scale and rounded
to the nearest logical pixel. A 3840 × 2160 mode at scale 1.5 becomes a
2560 × 1440 logical output.
Transform
transform rotates or mirrors the output before it enters the logical layout.
sweets.monitor("DP-1", {
transform = "90",
})The 90, 270, flipped-90, and flipped-270 transforms swap the logical
width and height. Position calculations use the transformed logical size.
Position
position sets the logical top-left corner of an output. Coordinates may be
negative.
sweets.monitor("DP-1", {
position = { 0, 0 },
mode = { 3840, 2160 },
scale = 1.5,
})
sweets.monitor("DP-2", {
position = { 2560, 0 },
mode = { 2560, 1440 },
scale = 1,
})Here, DP-2 begins after the 2560-pixel logical width of DP-1. Monitor
rectangles with explicit positions must not overlap.
Outputs without a position are sorted by connector name and placed left to
right at y = 0. Automatic placement begins after the furthest right edge of
all explicitly placed outputs.
Disabled outputs do not participate in overlap checks or automatic placement.
Enabling and disabling outputs
Set enabled = false to remove a connector from the active desktop topology:
sweets.monitor("eDP-1", {
enabled = false,
})Sweets migrates workspaces, focus, and pointer state to the remaining outputs. A configuration that disables every connected usable output is rejected.
Rules for disconnected or disabled monitors are retained. They apply when the connector becomes active again.
Temporary output power
Output power is separate from the configured enabled field. Turn a connected
output off temporarily with:
sweets msg output-power DP-1 offTurn it back on with:
sweets msg output-power DP-1 onPower commands do not edit sweets.lua or change the configured monitor rule.
The powered field from sweets msg outputs reports the current state.
Automatic placement example
You can configure only the details you care about:
sweets.monitor("DP-1", {
mode = { 2560, 1440 },
refresh_hz = 144,
})
sweets.monitor("DP-2", {
scale = 1.5,
})Both outputs are placed automatically. Their other fields use the omitted behaviors shown in the options table.
Live reload
A valid reload prepares the complete desired output topology before applying it. Sweets rejects unsupported modes, refresh rates, adaptive-sync requests, overlapping positions, invalid logical sizes, and unavailable hardware assignments.
If applying a candidate fails, Sweets keeps or restores the previous working topology. An unchanged configuration leaves the live output state untouched.
Removing a field restores its omitted behavior on the next valid reload. For
example, removing scale restores scale 1.
sweets --check-config
sweets msg reloadsweets --check-config validates Lua syntax, field types, ranges, and basic
coordinate bounds. It cannot verify connected hardware modes, output overlap,
or graphics-device assignment without the running compositor.
Output-management clients
Sweets supports the wlr-output-management-unstable-v1 protocol. Compatible
tools can change modes, scale, transform, position, enablement, and adaptive
sync at runtime. Custom modes remain unsupported; adaptive-sync enable requests
are rejected when the connector does not report the capability.
The protocol exposes adaptive sync as a boolean: an explicit enable selects always-on VRR and an explicit disable selects fixed refresh. It cannot select on-demand mode. Changing another property preserves an untouched head's configured on-demand policy.
A successful client apply creates a temporary runtime override. It does not write either Lua configuration file. A client test validates its candidate without changing the desktop.
The next valid Lua reload clears the complete runtime override and reapplies the resolved monitor rules. This happens even when the Lua files have not changed. A rejected reload leaves the runtime override active.
Check whether a runtime override is active with:
sweets msg statusThe response reports it as runtime_output_override under backend.
Workspace monitor homes
Use sweets.workspace to give a numbered workspace a preferred connector:
sweets.workspace(1, { monitor = "eDP-1" })
sweets.workspace(4, { monitor = "DP-1" })Workspace pins survive monitor disconnects and reclaim their monitor after it reconnects. See Workspaces for shared and per-monitor behavior.