RediWM
RediWM is a wlroots compositor that draws its whole shell itself: taskbar,
start menu, settings, lock and login screens, notifications, OSDs. One process,
no waybar/greeter/launcher/notification daemon to wire up.
Pages
- Getting Started: build, install, recover a stuck session
- Using RediWM: keybinds, mouse, zoom, tabs, tiling
- Configuration: the config file, window rules, sandbox allowances
- IPC: the socket, the wire format, events, waiting, automation
- IPC Commands: every command, grouped
- rediwm-msg: the CLI, with copy-paste recipes
- Apps and Services: Files, Images, PDF, Editor, desktop, notifications, lock/login
- Protocols and Sandboxing: which Wayland protocols we speak and who gets what
The binaries
| Binary |
What it is |
rediwm |
the compositor |
rediwm-session |
supervises the compositor, restarts it after a crash |
rediwm-msg |
IPC command line client |
rediwm-dm |
optional login manager (only with --dm, never enabled for you) |
rediwm-files |
file manager |
rediwm-images |
image viewer |
rediwm-pdf |
sandboxed PDF viewer |
rediwm-editor |
text editor |
rediwm-desktop |
desktop icons and wallpaper |
rediwm-share-picker |
screen sharing picker |
rediwm-accounts-helper |
root helper for account management, runs under pkexec |
Other docs in the repo
SECURITY.md: security model
TESTING.md: how the tests run
AGENTS.md: implementation notes for contributors
Getting Started
Install
scripts/install.sh installs the prebuilt release for your distro (Arch,
Debian, Ubuntu and Fedora on x86_64, and their derivatives). It brings the exact
wlroots 0.20 RediWM was tested with, installs the runtime libraries through your
package manager, checks the download's checksum and adds a RediWM session
entry. Nothing is compiled. Pass --build to compile from source instead; that
is also the fallback for other distros and CPUs, or if the release's libraries
don't match yours. REDIWM_VERSION=<tag> installs a specific release.
curl -fsSL https://rediwm.redios.org/install.sh | sh -s -- --dm # --dm also installs rediwm-dm
Installing never replaces your login manager. SDDM, GDM and friends keep
working. rediwm-dm is only installed with --dm, and even then it isn't
enabled.
Optional: foot, brightnessctl, gio/xdg-open, git (for Files repo
status), paplay (event sounds). For screen sharing you want
xdg-desktop-portal, xdg-desktop-portal-wlr and xdg-desktop-portal-gtk;
the installer adds all three.
Build and run
Building needs Zig 0.16 and wlroots 0.20 (scripts/install.sh --build builds it
into a private prefix on Debian/Ubuntu; REDIWM_PRIVATE_WLROOTS=1 does the same
on Arch and Fedora), plus dev files for Wayland, wayland-protocols, xkbcommon,
pixman, FreeType, HarfBuzz, Fontconfig, librsvg/GdkPixbuf, PangoCairo, libinput,
PAM, libpulse, libpipewire-0.3, Poppler GLib, libseccomp, libjpeg and libpng.
zig build run -- foot # nested in your current Wayland session
zig build -Doptimize=ReleaseSafe # for daily use and measurements
sh scripts/install-session.sh # install and restart the running session
sh scripts/install-session.sh --no-restart --dm # install only, plus rediwm-dm
From a checkout, scripts/install.sh does deps, build, then
install-session.sh with whatever args you pass.
Installing adds a RediWM entry to /usr/share/wayland-sessions.
Nested sessions
Running inside another Wayland session is handy for hacking, but:
- It's never sharper than the host. Use
REDIWM_SCALE=1 to judge layout.
- The Wayland backend never sets buffer scale, so don't judge sharpness there.
Crashes and recovery
rediwm-session restarts the compositor after a crash, at most 5 times a
minute. If the session was locked when it crashed, it comes back locked. Logs
go to $XDG_STATE_HOME/rediwm/session.log.
Stuck? Switch VTs with Ctrl+Alt+F1..F12 (works while locked), or:
pkill -u "$(id -u)" -x rediwm
Restarting over IPC (restart_shell) disconnects every app.
Using RediWM
Keys
| Keys |
Action |
| Super+Space |
start menu |
| Super+T / E / B |
terminal / Files / browser |
| Super+Q, Super+F, Super+M |
close, fullscreen, maximize |
| Super+Shift+arrows |
tile halves and quarters |
| Super+arrows |
slide to the neighbouring desktop |
| Super+Ctrl+arrows |
focus the window in that direction |
| Alt+Tab |
window switcher |
| Super+1..5, Super+=/-/0 |
window zoom |
| Super+Shift+=/-/0 |
desktop zoom |
| Super+L |
lock |
| Ctrl+Shift+S, Print |
region / full-screen screenshot |
| Super+Z |
undo the last window move or resize |
| Super+Shift+E |
quit |
| Super+Escape |
take shortcuts back from a VM or remote desktop |
Everything lives under [keybinds] in the config. Set a binding to "noop"
to turn it off.
Mouse
- Alt+drag moves a window, Alt+right-drag resizes.
- Super+Alt+mouse or a middle-button drag pans the desktop.
- Super+Alt+scroll zooms the desktop, Alt+scroll over a window zooms
just that window. Scrolling on a titlebar does the same.
- Drag a window to a screen edge to tile or maximize it (
snap_to_edges).
The canvas
The desktop is one big canvas, canvas_columns x canvas_rows screens (3x3 by
default). You pan around it and zoom out to see everything. Desktop zoom scales
all windows together around the pointer, up to 100%, and doesn't change their
world positions. Desktop icons and the taskbar stay fixed on screen by default.
The mini map shows where you are and hides itself after a moment.
The volume/brightness OSD also shows the desktop zoom percentage.
Window switching zoom
Settings > Appearance, or [compositor] focus_zoom. Controls what Alt+Tab,
directional focus and taskbar clicks do to the camera.
| Mode |
What happens |
boost (default) |
the selected floating window goes to 100% on-screen size, then goes back when you switch away. Saved zoom and position are untouched |
keep |
just pans |
camera |
zooms the desktop to the window's depth and fades windows that would grow past full size. Desktop zoom then steps through window depths |
Reveals ease over 320 ms. Animation speed and reduced motion apply. You can
override the pan with [animations.camera_reveal].
Window tabs
Settings > Appearance > Window tabs lets you pick apps that get tabs, using
RediWM chrome (XDG, KDE or X11 decorations). Open the app once and it shows up
in the list.
- The + button opens another window as a tab in that frame. Normal
launches stay separate.
- Each tab has its own close button. The outer close button closes the whole
group and respects app confirmation dialogs.
- Arrow buttons or scrolling over the strip switch tabs. Alt+scroll still
zooms the window.
- The app needs a desktop entry with a usable launch command. Launches that
only activate an existing window don't make a tab.
- Saved in
[compositor] window_tab_apps.
Settings
Start menu cog. It's a normal window covering display modes, scale, night
light, input, audio, appearance, shortcuts and the desktop canvas. Changes are
written to the config and your comments stay intact.
Locking
- The lock screen covers every output. Escape clears the field and never
unlocks.
- Volume and brightness work behind the lock: the hardware keys, or the
sliders at the bottom of the screen (click the speaker to mute). A slider
appears only if there's a sound output or a backlight to control. The login
screen has no sliders.
- It engages before the system sleeps and waits until it's on screen first
(
lock_on_suspend, on by default). loginctl lock-session works too.
- RediWM doesn't lock on idle by itself. Run swayidle from
[[autostart]]:
swayidle -w timeout 300 'swaylock -f'.
- Lock-screen UI can't be driven by virtual input.
Games, VMs, X11
- Games get pointer lock and relative motion.
- Xwayland starts on the first X11 connection. With native scaling, X11 apps
render at
ceil(max output scale) and stay sharp on HiDPI.
- Known limits from wlroots 0.20: X11 to Wayland drag and drop doesn't start,
and X11 pointer input on a second output doesn't arrive.
Apps and Services
Files (rediwm-files)
The file manager. Space opens images, PDFs and text files.
- Git: detects repos, subfolders and linked worktrees. The sidebar shows
the branch and ahead/behind from what's known locally. Badges: M modified,
A added, D deleted, R renamed, U conflict, ? untracked. Click the repo name
to jump to its root. It never fetches or changes anything, and never runs a
repo's own programs. A repo whose config names filter or Git LFS commands
(think archives and USB sticks) is shown as a plain folder.
- Archives: browse ZIP, TAR (gz, bz2, xz, zstd) and 7z like folders. They're
read-only. Opening a member extracts just that file to private temp storage
that's removed on exit. Context menu has Extract and Extract to....
Name clashes ask Skip/Rename/Cancel, nothing is overwritten, and paths
outside the destination are rejected. No passworded archives, links or
special files yet.
- Thumbnails: JPEG and PNG are decoded without GdkPixbuf (it's slow and
goes through sandboxed loaders). Shared freedesktop cache in
$XDG_CACHE_HOME/thumbnails. REDIWM_FILES_THUMBNAILS=0 turns them off.
- Devices: USB sticks and drives show up via UDisks2. Eject means unmount,
the device stays listed, dimmed.
REDIWM_FILES_DEVICES=0 disables it.
- Pins: drop a folder on the PLACES sidebar to pin it. Stored one path per
line in
$XDG_STATE_HOME/rediwm/files-places.
- Drag a file out and the window slides aside (
dodge_file_drags).
Text editor (rediwm-editor [FILE ...])
Tabs, one window per display. UTF-8 files up to 4 GiB (they must fit in free
memory), keeps BOM and line endings, asks before discarding changes. Only the
lines on screen are ever laid out, so opening, scrolling and Find stay fast on
huge files, and a single line of many megabytes is folded onto several rows.
Editing near the start of a very large file moves the rest of the text, which
costs a few milliseconds per 100 MB per keystroke.
| Keys |
Action |
| Ctrl+N / O / S |
new tab / open / save |
| Ctrl+Shift+S |
save as |
| Ctrl+PageUp/Down |
switch tabs |
| Ctrl+W |
close tab |
| Ctrl+F, F3 |
find |
Images (rediwm-images) and PDF (rediwm-pdf FILE)
Files, Images and the editor also run on other Wayland compositors.
The PDF viewer is sandboxed with Landlock (ABI 3 or newer) and needs a
compositor with security-context-v1, which RediWM has. It refuses to parse
a document if it can't isolate itself. Each PDF gets its own viewer. Web links
copy their address instead of opening. Details in docs/pdf-security.md.
Desktop
Icons and wallpaper from ~/Desktop, on by default ([desktop] enabled). A
.desktop launcher only runs if it's installed, owned by root and executable,
or an exact copy of an installed entry. Anything else shows as a plain file
until you pick Allow Launching in its menu. The wallpaper comes from
wallpaper in the config, Settings, or set_wallpaper over IPC.
Notifications
RediWM is the notification daemon. Actions, images, do-not-disturb, history.
Rules go in [[notification_rules]]. If you'd rather use dunst, set
[notifications] daemon = "dunst" and restart. Inspect and drive them over
IPC: notifications, set_dnd, dismiss_notification,
invoke_notification_action.
Lock, login and polkit
- Lock: see Using RediWM. Locks never unlock
because of a crash: after a crash while locked,
rediwm-session locks again
before the backend even starts.
- Greeter /
rediwm-dm: the greeter is a lock that never unlocks, only
rediwm-dm decides. It runs as an unprivileged user on its own VT. To use
it: install with --dm, disable your current display manager,
sudo systemctl enable rediwm-dm, reboot. Options in /etc/rediwm/dm.conf.
It does no Xwayland, autostart, IPC, tray, portal or services.
- Polkit: RediWM is a polkit agent (
[polkit] enable). Capture and screen
sharing are refused during a prompt.
- Accounts: account management goes through
rediwm-accounts-helper under
pkexec. The compositor never runs as root.
Screen capture and sharing
wlr-screencopy and ext-image-copy-capture work (grim, wf-recorder, OBS,
the portal). Any capture lights the taskbar Stop sharing indicator. It's
refused while locked or during a polkit prompt. stop_all_capture over IPC
ends everything. See docs/screen-sharing.md.
App scopes
On a real session each app you launch is moved into its own
app-rediwm-<id>-<pid>.scope so systemd-oomd kills one runaway app and not
your whole session. Off in nested and headless sessions unless
REDIWM_APP_SCOPES=1.
Startup
The desktop renders before autostart runs, and audio and session environment
setup never block frames or input. Autostart entries go in [[autostart]].
Configuration
The file is $XDG_CONFIG_HOME/rediwm/config.toml. REDIWM_CONFIG overrides
the path. It's written with commented defaults on first run and reloads when
you save. A parse error keeps the previous config. You can also reload with
rediwm-msg reload, and check status with rediwm-msg config.
Sections
[theme], [input], [input_method], [compositor], [region], [animations.*],
[idle], [night_light], [notifications], [desktop], [polkit], [ipc],
[keybinds], [[outputs]], [[autostart]], [[window_rules]],
[[notification_rules]], [[sandbox_allow]].
It's not full TOML
The parser reads a line-based subset. Don't run it through a generic TOML
formatter. The rules:
- One
key = value per line. Arrays on one line too.
- Strings use double quotes and are taken verbatim. No escape processing, so
\n, \" and \u mean exactly those characters.
- No single-quoted strings, no multiline strings or arrays, no inline tables,
no arbitrary dotted keys, no dates.
# starts a comment outside double quotes.
app_id = ["firefox", "chromium"] # fine
Theme
Colors, text sizes, corner radii and control styling belong in [theme].
See the complete theme reference for every supported key and its
built-in default. Appearance preserves these overrides when it saves.
Region & Language
Settings → Region & Language lets you choose a personal language, regional
formats and time zone. Empty choices inherit the login environment; system
defaults are unchanged. RediWM's interface currently uses English.
Personal clock and calendar preferences apply immediately and are saved in
[region]:
[region]
clock_24h = false
clock_show_seconds = false
clock_show_day = true
first_day_of_week = "sunday"
clock_24h applies to the taskbar, calendar and built-in lock screen.
clock_show_seconds adds seconds to the taskbar time. clock_show_day shows
the date line beneath it, using the selected regional format (formats, or
the inherited locale). Both lines are centered; hiding the date centers the
time vertically. These two options apply only to the taskbar clock.
first_day_of_week accepts any lowercase weekday name, and changes both the
weekday headings and date positions in the taskbar calendar. Existing configs
keep 12-hour time and Sunday first until changed.
Handy [compositor] keys
| Key |
Default |
Notes |
allow_tearing |
false |
lets focused fullscreen clients request async presentation |
window_tab_apps |
[] |
app IDs that get tabs |
default_file_manager, default_terminal |
"" |
desktop IDs, empty means Files / auto terminal |
taskbar_position |
"bottom" |
top or bottom |
taskbar_items |
battery, network, volume, clock |
prefix a name with - to hide it |
focus_follows_mouse |
false |
|
canvas_columns, canvas_rows |
3, 3 |
1 to 10 |
mini_map_enabled |
true |
|
mini_map_position |
"bottom_right" |
bottom_left, bottom_center, bottom_right |
mini_map_hide_ms |
1500 |
500 to 10000 |
desktop_icons_fixed |
true |
|
desktop_switch_ms |
220 |
Super+arrow slide, 100 to 3000 |
zoom_steps |
[1.0, 0.85, 0.70, 0.55, 0.40] |
|
camera_zoom_min, camera_zoom_max |
0.25, 1.0 |
|
focus_zoom |
"boost" |
keep, boost, camera |
window_gap |
0 |
px between snapped windows |
snap_to_edges |
true |
|
dodge_file_drags |
true |
file manager slides aside while you drag a file out |
switcher_opacity, inactive_opacity |
1.0 |
|
border_radius |
10 |
Legacy frame radius; [theme].radius_lg takes precedence. |
xwayland |
true |
false means native only |
xwayland_native_scaling |
false |
sharp X11 on fractional scales, restart needed |
xwayland_scale |
0 |
0 is automatic, 1.0 to 4.0 explicit, restart needed |
dark_mode |
false |
live, goes through the Settings portal |
placeholder_delay_ms |
0 |
splash only if the app is this slow to show, 0 disables |
wallpaper |
unset |
file in ~/.local/share/rediwm/wallpapers, or a path |
lid_close |
"display_off" |
display_off, lock, suspend, ignore |
lock_on_suspend |
true |
|
sound_theme, sound_enabled, sound_disabled_events |
"freedesktop", true, [] |
event sounds, used by play_sound |
Outputs
[[outputs]]
name = "DP-2"
x = -1920 # negative origin is only possible here
y = 0
scale = "auto" # "auto" or 1 to 3
width = 1920 # optional mode
height = 1080
refresh_mhz = 60000 # omit for the highest at this size
enabled = true
primary = false
transform = "normal" # normal, 90, 180, 270, flipped variants
night_light = false # never tint this monitor
gamma = 1.0 # 0.5 to 2.0
Omit coordinates for automatic left-to-right placement. Scale and placement
apply live. REDIWM_SCALE overrides every output. Changes from Settings,
wlr-randr, kanshi or rediwm-msg output-config are all saved here.
Idle and night light
[idle]
blank_after_seconds = 600 # 0 disables
suspend_after_seconds = 0 # 0 disables
[night_light]
enabled = true
schedule = "always" # "always", "fixed" or "sun"
temperature = 5000 # 1700 to 10000 K
# fixed: day_temperature, start, end, transition_minutes (0 to 180)
# sun: latitude, longitude
Night light is on by default as a soft warm tint all day.
Window rules
Matched by glob. For each property the last matching rule wins.
[[window_rules]]
app_id = ["firefox", "chromium"]
title = "*Picture-in-Picture*"
opacity = 0.9
skip_taskbar = true
focus = false
- Matchers:
app_id, title, tag, x11_class, x11_instance,
backend, dialog, and exclude_* versions.
- Properties:
output, x, y, center, width, height, maximized,
fullscreen, focus, depth, opacity, decorations, skip_taskbar.
To debug a rule, ask the compositor:
rediwm-msg window-rules 12 # what matched this window
rediwm-msg match-window-rules --app-id foo # dry run
tag comes from the client (xdg-toplevel-tag), so it's metadata, not trusted
identity.
[ipc]
[ipc]
automation = true # synthetic input and screen capture. Restart required
Read once at startup. Details on IPC.
[polkit]
[polkit]
enable = true
helper_socket = "/run/polkit/agent-helper.socket"
[[autostart]]
[[autostart]]
cmd = "nm-applet" # one entry per program
[[sandbox_allow]]
See Protocols and Sandboxing.
Theme reference
Put these keys in [theme] in $XDG_CONFIG_HOME/rediwm/config.toml.
REDIWM_CONFIG selects another config file; REDIWM_THEME overlays another
file's [theme] section. Omitted keys use the built-in defaults below.
Appearance saves custom tokens along with its sliders and selectors.
The bundled picker includes RediWM Dark, RediWM Light and Redi Blue. Redi Blue
uses navy surfaces, cyan accents, 98% chrome opacity and a 30% selection tint.
RediWM Light uses 75% window chrome opacity and teal selection accents.
start_button_logo_color optionally tints the bundled R logo; Light uses a
dark tint for contrast. Custom start-button icons retain their own colours.
Appearance also has a Taskbar theme picker under Theme, with the same
presets plus "Same as theme". It gives the taskbar the preset's colours and
leaves every other surface on the theme above. It is saved as a
[taskbar_theme] section holding only these keys: taskbar_bg,
taskbar_surface, taskbar_hover, taskbar_border, border_soft,
surface_hover, window_fg, window_dim, danger, window_close_hover,
start_button_hover, start_button_indicator, start_button_logo_color and
the battery_* colours. Unlisted keys are ignored, so a whole theme file's
body can be pasted under that header; keys it leaves out take the built-in
defaults, not the main theme's. The section's presence selects the taskbar
theme (an empty one is the built-in colours); without it the taskbar follows
[theme]. Sizes, gaps and radii always come from [theme].
Colors accept "#rrggbb", "#rrggbbaa", or "rgba(r,g,b,a)" with RGB
channels from 0 to 255 and alpha from 0 to 1. Numbers must be finite and
nonnegative. Sizes are logical pixels. The tables describe appearance tokens;
window placement, animation timing, cursor themes and sizes, blur, inactive
opacity and frame radius also have settings in the other config sections.
[theme]
shell_accent = "#86bfff"
control_thumb = "#ffffff"
toggle_track = "rgba(120,140,170,0.25)"
slider_track_height = 6
app_text_size = 14
app_heading_size = 13
app_status_size = 12
desktop_selection = "rgba(80,140,220,0.2)"
desktop_selection_border = "#508cdc"
battery_full = "#40dca0"
image_checker_size = 16
pdf_search_match = "rgba(100,180,255,0.4)"
Palettes and reload
Shell surfaces use window_fg and window_dim. Their accent normally follows
start_menu_selected_marker; shell_accent, shell_on_accent and
shell_border_focus override that palette and its control/field focus rings. Settings uses settings_radius for
controls. Text fields, carets and text selection have optional overrides that
otherwise follow the accent of the surface displaying them.
Files' New, Sort and Filter buttons share the secondary-button palette:
surface sets the background, surface_hover the hover fill, and border_soft
the border. Their text and icons use window_fg. Active Filter and view-mode
buttons use shell_accent (or start_menu_selected_marker when unset), tinted
by button_selected_alpha. All of these values can be set in [theme];
Files follows edits while running.
Files' dialog bodies and context menus share app_toolbar as their background
color. Changing it in [theme] updates both.
The compositor reloads the theme when the config changes. Files and Images
follow saved colors and sizes; PDF and Editor load them when starting. The
compositor's frame radius is the numeric [theme].radius_lg. The legacy
[compositor].border_radius is used only when the theme omits radius_lg.
Appearance's Radius presets write all numeric radius tokens: Default restores
the current built-in radii, Sharp caps them at 4 px (the minimap's default),
and Round uses 1.3 times the defaults. Individual theme radii remain editable.
Cursor styling is [input].cursor_theme and [input].cursor_size; animation behavior belongs in [animations].
Set [theme].chrome_round_buttons = true for circular window controls and
pill-shaped window tabs. Tab close, new-tab and overflow controls follow the
same setting. It defaults to false, preserving the existing corner radii,
and reloads live from the main config or the REDIWM_THEME file. Button sizes
and hit targets stay the same; radius_lg still controls the window frame.
The lock's background always paints opaque, even if lock_bg supplies alpha;
lock_wallpaper_opacity controls its wallpaper overlay. Actual image pixels,
PDF paper/content, application-provided icons and fixed layout/hit-test
geometry retain their content or structural dimensions.
Colors
| Key |
Built-in default |
window_bg |
"rgba(7.6,9,11,0.80)" |
window_fg |
"rgba(245,245,245,1)" |
window_dim |
"rgba(214,214,214,1)" |
window_border |
"rgba(255,255,255,0.09)" |
window_border_hover |
"rgba(255,255,255,0.13)" |
window_divider |
"rgba(255,255,255,0.045)" |
window_close_hover |
"rgba(237,27,46,1)" |
text_secondary |
"rgba(214,214,214,1)" |
app_bg |
"rgba(24,28,34,1)" |
app_toolbar |
"rgba(29,34,41,1)" |
app_sidebar |
"rgba(27,33,40,1)" |
app_bar |
"rgba(35,42,51,1)" |
app_item |
"rgba(34,40,46,1)" |
app_item_hover |
"rgba(37,43,50,1)" |
app_item_border |
"rgba(45,51,58,1)" |
app_item_selected |
"rgba(78,37,46,1)" |
app_nav_selected |
"rgba(91,36,46,1)" |
app_divider |
"rgba(55,63,72,1)" |
app_icon |
"rgba(214,214,214,1)" |
scrollbar_thumb |
"rgba(89,98,110,1)" |
scrollbar_thumb_active |
unset |
bg |
"rgba(9,11,24,1)" |
fg |
"rgba(245,245,245,1)" |
dim |
"rgba(214,214,214,1)" |
faint |
"rgba(160,160,160,1)" |
accent |
"rgba(94,234,212,1)" |
surface |
"rgba(255,255,255,0.05)" |
border |
"rgba(255,255,255,0.09)" |
accent_2 |
"rgba(167,139,250,1)" |
accent_3 |
"rgba(244,114,182,1)" |
danger |
"rgba(248,113,113,1)" |
glass |
"rgba(15,19,38,0.56)" |
glass_strong |
"rgba(12,15,30,0.74)" |
taskbar_bg |
"rgba(24,28,34,0.96)" |
border_soft |
"rgba(255,255,255,0.06)" |
border_hover |
"rgba(255,255,255,0.22)" |
surface_hover |
"rgba(255,255,255,0.08)" |
taskbar_surface |
"rgba(255,255,255,0.025)" |
taskbar_hover |
"rgba(255,255,255,0.05)" |
taskbar_border |
"rgba(255,255,255,0.07)" |
on_accent |
"rgba(8,33,28,1)" |
border_focus |
"rgba(94,234,212,0.4)" |
caret |
unset |
selection |
unset |
selection_fg |
unset |
field_bg |
"rgba(21,26,31,1)" |
field_border |
"rgba(63,70,77,1)" |
field_border_focus |
unset |
shadow |
"rgba(0,0,0,0.35)" |
start_menu_bg |
"rgba(29,34,41,0.98)" |
start_menu_border |
"rgba(255,255,255,0.12)" |
start_menu_search_bg |
"rgba(21,26,31,1)" |
start_menu_selected_bg |
"rgba(255,255,255,0.045)" |
start_menu_selected_border |
"rgba(237,27,46,0.35)" |
start_menu_selected_marker |
"rgba(237,27,46,1)" |
toggle_track |
"rgba(255,255,255,0.12)" |
control_thumb |
"rgba(255,255,255,1)" |
slider_track |
"rgba(255,255,255,0.08)" |
pill_bg |
"rgba(255,255,255,0.05)" |
swatch_border |
"rgba(255,255,255,0.1)" |
swatch_border_selected |
"rgba(255,255,255,0.6)" |
settings_card_bg |
"rgba(255,255,255,0.025)" |
lock_bg |
"rgba(4,5,7,1)" |
lock_fg |
"rgba(247,247,250,1)" |
lock_dim |
"rgba(168,173,186,1)" |
lock_divider |
"rgba(76,82,92,0.6)" |
lock_session_fg |
"rgba(219,222,230,1)" |
switcher_bg |
"rgba(9,10,14,1)" |
switcher_border |
"rgba(153,51,66,0.65)" |
switcher_item_bg |
"rgba(23,26,31,1)" |
switcher_item_border |
"rgba(255,255,255,0.13)" |
switcher_fg |
"rgba(240,242,247,1)" |
switcher_dim |
"rgba(153,158,168,1)" |
switcher_icon_bg |
"rgba(26,28,33,1)" |
switcher_selected_border |
"rgba(255,31,51,1)" |
calendar_border |
"rgba(255,255,255,0.16)" |
osd_border |
"rgba(255,255,255,0.12)" |
screenshot_shade |
"rgba(0,0,0,0.36)" |
screenshot_border |
"rgba(255,6,23,1)" |
screenshot_fg |
"rgba(255,255,255,1)" |
screenshot_label_bg |
"rgba(10,13,15,0.95)" |
screenshot_label_border |
"rgba(255,255,255,0.2)" |
battery_bg |
"rgba(5,10,10,0.28)" |
battery_full |
"rgba(32,226,157,1)" |
battery_low |
"rgba(250,204,65,1)" |
battery_critical |
"rgba(245,75,85,1)" |
battery_shimmer |
"rgba(110,255,200,1)" |
desktop_fg |
"rgba(238,241,251,1)" |
desktop_text_shadow |
"rgba(0,0,0,0.22)" |
desktop_hover |
"rgba(255,255,255,0.06)" |
desktop_selected |
"rgba(237,27,46,0.12)" |
desktop_selection |
"rgba(237,27,46,0.15)" |
desktop_selection_border |
"rgba(237,27,46,1)" |
desktop_drop_border |
"rgba(237,27,46,0.8)" |
desktop_dialog_bg |
"rgba(9,11,24,0.98)" |
desktop_notice_bg |
"rgba(31,9,15,0.95)" |
desktop_icon_start |
"rgba(64,166,168,1)" |
desktop_icon_end |
"rgba(89,79,158,1)" |
file_broken |
"rgba(239,68,68,1)" |
file_folder_back |
"rgba(163,171,181,1)" |
file_folder_front |
"rgba(184,191,201,1)" |
file_document |
"rgba(148,163,184,1)" |
file_document_lines |
"rgba(203,213,225,1)" |
file_spinner |
"rgba(166,173,184,1)" |
image_checker_dark |
"rgba(33,36,38,1)" |
image_checker_light |
"rgba(43,46,48,1)" |
image_crop_shade |
"rgba(0,0,0,0.58)" |
image_crop_border |
"rgba(255,255,255,1)" |
image_crop_grid |
"rgba(255,255,255,0.4)" |
image_dialog_backdrop |
"rgba(0,0,0,0.65)" |
pdf_page_shadow |
"rgba(0,0,0,0.35)" |
pdf_page_border |
"rgba(0,0,0,0.15)" |
pdf_loading |
"rgba(153,153,153,1)" |
pdf_search_match |
"rgba(255,230,0,0.4)" |
pdf_search_match_active |
"rgba(255,153,0,0.65)" |
pdf_thumbnail_bg |
"rgba(242,245,250,1)" |
pdf_thumbnail_shadow |
"rgba(0,0,0,0.3)" |
pdf_search_shadow |
"rgba(0,0,0,0.4)" |
pdf_dialog_backdrop |
"rgba(0,0,0,0.6)" |
dialog_backdrop |
"rgba(0,0,0,0.18)" |
shell_accent |
unset |
shell_on_accent |
unset |
shell_border_focus |
unset |
window_close_fg |
"rgba(255,255,255,1)" |
start_button_hover |
"rgba(255,255,255,0.035)" |
start_button_indicator |
"rgba(255,255,255,0.5)" |
polkit_backdrop |
"rgba(0,0,0,0.55)" |
power_menu_backdrop |
"rgba(0,0,0,0.45)" |
accent_2, accent_3, glass_strong and border_focus are compatibility
keys retained by the reader and saver; current painters use the specific
surface and control tokens above. scrollbar_thumb_active now overrides the
hover/drag color; omitted, it follows the surface accent.
Sizes and styling
Numeric tokens accept 0–4096 unless constrained below. Fractions ending in
_alpha or _opacity, and dialog_glyph_lift, accept 0–1.
| Key |
Built-in default |
font_size |
13.0 |
radius |
5.0 |
radius_md |
7.0 |
radius_lg |
10.0 |
title_size |
13.5 |
chrome_height |
46.0 |
chrome_control_gap |
4.0 |
taskbar_title_size |
12.5 |
taskbar_size |
48.0 |
chip_gap |
4.0 |
chip_width |
220.0 |
start_button_gap |
4.0 |
start_button_icon_size |
42.0 |
button_font_size |
12.5 |
caret_width |
1.5 |
scrollbar_width |
8.0 |
selection_alpha |
0.35 |
field_radius |
8.0 |
shadow_size |
24.0 |
shadow_offset_y |
8.0 |
start_menu_width |
560.0 |
start_menu_max_height |
600.0 |
start_menu_radius |
14.0 |
start_menu_left_pad |
2.0 |
start_menu_bottom_pad |
6.0 |
start_menu_icon_left_pad |
2.0 |
start_menu_icon_bottom_pad |
6.0 |
settings_radius |
8 |
settings_sidebar_radius |
10 |
settings_card_radius |
9 |
checkbox_radius |
4 |
toggle_radius |
5 |
toggle_thumb_radius |
4 |
slider_track_height |
5.5 |
slider_settings_track_height |
11 |
slider_thumb_size |
12 |
slider_settings_thumb_height |
20 |
slider_thumb_radius |
4 |
slider_track_grow |
0.25 |
slider_track_tint_alpha |
0.45 |
slider_fill_alpha |
0.6 |
pill_active_alpha |
0.16 |
pill_border_active_alpha |
0.55 |
segmented_selected_alpha |
0.15 |
swatch_radius |
6 |
swatch_glow_alpha |
0.8 |
dialog_badge_radius |
16 |
dialog_badge_alpha |
0.18 |
dialog_glyph_lift |
0.3 |
dialog_title_size |
26 |
dialog_subtitle_size |
15 |
dialog_inset_radius |
12 |
scrollbar_radius |
3 |
scrollbar_rest_width |
0.75 |
scrollbar_active_width |
1.125 |
switcher_radius |
24 |
switcher_item_radius |
12 |
switcher_icon_radius |
10 |
switcher_selection_radius |
15 |
switcher_title_size |
15 |
switcher_app_size |
12 |
calendar_radius |
9 |
calendar_opacity |
0.98 |
osd_radius |
10 |
osd_opacity |
0.97 |
lock_wallpaper_opacity |
0.24 |
image_checker_size |
12 |
start_button_radius |
6 |
mini_map_bg_opacity |
0.91 |
mini_map_grid_alpha |
0.065 |
mini_map_border_alpha |
0.12 |
mini_map_window_alpha |
0.55 |
mini_map_marker_alpha |
0.12 |
mini_map_marker_border_alpha |
0.85 |
mini_map_radius |
4 |
app_text_size |
13 |
app_heading_size |
12 |
app_status_size |
11.5 |
desktop_text_size |
11.5 |
window_close_hover_alpha |
0.85 |
button_disabled_alpha |
0.5 |
button_hover_brightness |
1.08 |
button_selected_alpha |
0.6 |
button_selected_hover_alpha |
0.7 |
| Additional constraint |
Range |
chrome_height |
28–84 |
chrome_control_gap |
0–24 |
chip_width |
64–400 |
scrollbar_width |
4–24 |
scrollbar_rest_width, scrollbar_active_width |
0.25–1.125 times the configured width |
image_checker_size |
1–128 |
taskbar_height is an alias for taskbar_size. Layouts cap some dimensions to
fit their available space. The scrollbar reserves room for its maximum width;
slider_* keys control slider geometry while sharing the scrollbar feedback.
Strings
| Key |
Built-in default |
Meaning |
font |
"Manrope" |
Compositor's preferred proportional font family |
mono_font |
"JetBrains Mono" |
Compositor's preferred monospace font family |
start_button_icon |
"" |
Bundled Redi logo; otherwise an absolute image path |
In Settings → Appearance → Start menu, Choose file… selects a PNG or
SVG for the Start button logo and applies it immediately. Reset to default
restores the bundled R logo. Both choices are saved with the theme.
IPC
RediWM has a JSON IPC over a Unix socket. Use it to script the compositor,
inspect state, or drive it from tests. rediwm-msg is the friendly client,
see rediwm-msg. Every command is in IPC Commands.
The socket
$XDG_RUNTIME_DIR/rediwm-<WAYLAND_DISPLAY>.sock, or whatever REDIWM_SOCKET
says. It lives in a private directory (owned by you, no group/other access) and
is created with umask 0177. If the directory isn't private the server refuses
to start the IPC.
UTF-8 JSON, one request per line, one response per line.
{"version":1,"id":1,"command":"windows"}
{"version":1,"id":2,"command":"move_cursor","params":{"x":100,"y":200}}
| Field |
Rules |
version |
required, the integer 1 |
command |
required, case-sensitive snake_case |
params |
optional object |
id |
optional, a non-negative integer or a string up to 256 bytes. Echoed back once the request parses. Parse errors have no id |
Anything else is rejected: extra envelope fields, duplicate JSON keys, bare
strings, {"query":...} / {"action":...} envelopes, PascalCase names and
CLI aliases. There's no separate "query" vs "action" on the wire, both are just
command.
Responses:
{"id":1,"Ok":{"Windows":[]}}
{"id":1,"Err":"InvalidRequest"}
Response tags are case-sensitive and different from command names. Clients
should ignore unknown response fields. New commands show up in capabilities,
and a breaking change would mean a new protocol version.
Try it with no tools at all:
echo '{"version":1,"command":"version"}' | socat - UNIX-CONNECT:$XDG_RUNTIME_DIR/rediwm-wayland-1.sock
Discovering things
rediwm-msg capabilities # canonical command names
rediwm-msg describe # params, kinds, protocol version
describe_ipc is the source of truth for parameter schemas. The code
reference is src/ipc/protocol.zig and src/ipc/commands.zig.
Queries vs actions
Queries read state. Actions change something. Both use the same envelope.
The split only matters in rediwm-msg (rediwm-msg action focus-window ...
for actions that don't have a top-level subcommand).
Events
event_stream turns the connection into a feed. You get an acknowledgement,
then newline-delimited tagged events until you disconnect.
rediwm-msg event-stream
rediwm-msg event-stream --events window_opened,window_closed
rediwm-msg event-stream --window 12
rediwm-msg event-stream --output DP-1
Raw params: {"window_id"?: n, "output"?: "name", "events"?: ["..."]}.
Events you can filter on:
| Group |
Events |
| Windows |
window_opened, window_closed, window_changed, window_focused, window_moved, window_urgency_changed |
| Outputs |
output_added, output_changed, output_removed |
| Camera |
camera_changed |
| Input |
keyboard_layouts_changed, keyboard_layout_switched |
| Config |
config_loaded |
| Polkit |
polkit_prompt_opened, polkit_prompt_closed |
| Notifications |
notification_shown, notification_closed, notification_action |
| Launching |
launch_started, launch_matched, launch_timeout |
| UI |
widget_changed |
state_snapshot is always delivered, whatever you filter. A window_id
filter only affects window events. Event subscriptions are re-checked against
the lock state, so you get nothing while the session is locked.
Waiting instead of sleeping
If you're scripting, don't sleep. Use the wait commands.
rediwm-msg wait window_mapped --timeout 5000
rediwm-msg wait window_closed --id 12
rediwm-msg wait-frame --output DP-1
wait_for takes a condition and a timeout (default 5000 ms) and returns the
condition, elapsed ms and state. Raw example:
{"version":1,"command":"wait_for","params":{"condition":{"window_mapped":{"app_id":"foot"}},"timeout_ms":5000}}
Conditions:
| Condition |
Params |
menu_opened, menu_closed |
none |
control_center_opened, control_center_closed |
none |
power_menu_opened, power_menu_closed |
none |
window_mapped |
optional id, app_id, title |
window_closed |
id |
window_focused |
optional id |
window_geometry_settled |
id |
output_frame |
optional output |
catalog_published, wallpaper_presented |
none |
notification_count |
optional count, app_name, summary |
notification_closed |
id |
notification_action |
id, optional action_key |
launch_started, launch_matched, launch_timeout |
optional desktop_id (and window_id for matched) |
widget_present, widget_absent |
path |
widget_state |
path, field, equals |
panel_settled |
panel |
wait_for_frame blocks until the next frame commit on an output and returns
the output, frame_seq and elapsed time. The CLI's wait takes a condition
name plus --id/--output/--timeout. For conditions with richer params
(app_id, title, path, ...), use --raw.
Off by default. These commands are refused unless automation is on:
move_cursor, move_cursor_relative, pointer_button, click, scroll,
key, key_press, type_text, drag, screenshot, dump_buffer,
sample_pixels, pinch, swipe, click_widget, hover_widget.
Turn it on:
[ipc]
automation = true
or start with REDIWM_IPC_AUTOMATION=1. It's read once at startup, so you
need a restart. When it's on, the compositor logs a warning, because any
client of the socket can then type and take screenshots.
Security rules
- Everything is refused with
SessionLocked while the session is locked.
- Sandboxed callers (Flatpak and friends, detected via
SO_PEERPIDFD and
.flatpak-info) need an explicit [[sandbox_allow]] with the ipc group.
They fail closed, and the check is redone on config reload.
Synthetic input and capture additionally need the automation group.
- The socket is only reachable by your uid.
- Virtual input never reaches the lock or auth UI.
Writing a client
Python is the easiest. This is basically what tests/ipc_client.py does:
import json, os, socket
path = f"{os.environ['XDG_RUNTIME_DIR']}/rediwm-{os.environ['WAYLAND_DISPLAY']}.sock"
s = socket.socket(socket.AF_UNIX)
s.connect(path)
f = s.makefile("rw")
def call(command, params=None, id=1):
req = {"version": 1, "id": id, "command": command}
if params is not None:
req["params"] = params
f.write(json.dumps(req) + "\n"); f.flush()
resp = json.loads(f.readline())
if "Err" in resp:
raise RuntimeError(resp["Err"])
return resp["Ok"]
print(call("windows"))
call("focus_window", {"id": 3})
For events, open a second connection, send event_stream, then keep calling
readline().
Tips:
- Use
get_state when you need windows, outputs, camera and shell to agree.
Separate queries can race.
- Coordinates are layout coordinates, not per-output pixels.
- Set a socket timeout.
wait_for can take up to its own timeout to answer.
restart_shell drops your connection and every app. That's expected.
IPC Commands
Names are the wire names ("command": "..."). The CLI column is the
rediwm-msg spelling. Run rediwm-msg describe for exact params.
[auto] means it needs automation.
"Action" in the CLI column means rediwm-msg action <name>.
Info
| Command |
CLI |
What it does |
version |
version |
build version |
capabilities |
capabilities |
supported command names |
describe_ipc |
describe |
protocol version, kinds, parameter schemas |
get_runtime_info |
runtime |
compositor, wlroots, backend, renderer |
get_config_status |
config |
effective config and reload status |
reload_config |
reload |
reload the config file |
get_performance_stats |
perf |
commit, paint and error counters |
reset_performance_stats |
perf-reset |
zero them |
get_panel_stats / reset_panel_stats |
Action |
panel paint and allocation counters |
get_animations |
none |
live animations: site, value, velocity, target, settled, curve |
set_anim_time |
Action --ms <N\|null> |
override animation time, null clears it |
Windows
| Command |
CLI |
Params |
windows |
windows |
list toplevels |
focused_window |
focused-window |
|
get_window_debug |
window-debug <id> |
geometry, decoration, serials |
get_window_rules |
window-rules <id> |
matched rules and effective properties |
match_window_rules |
match-window-rules |
dry run: app_id, title, x11_class, x11_instance, backend, dialog |
focus_window |
Action focus-window --id |
|
close_window |
Action close-window [--id] |
no id closes the focused window |
move_window_to |
Action move-window-to --id |
--x --y or --output |
maximize_window |
maximize <id> |
|
minimize_window |
minimize <id> |
|
restore_window |
restore <id> |
|
fullscreen_window |
fullscreen <id> [--output] |
|
set_window_size |
none |
id, width, height, requests a client resize |
set_window_zoom |
set-zoom <id> <pct> |
|
undo |
none |
undo the last move, resize, zoom, pan or focus change |
spawn |
Action spawn [--] CMD ARGS... |
argument vector, no shell |
launch_app |
launch <desktop_id> |
by desktop file ID |
stop_xwayland |
none |
disconnects the compositor-owned Xwayland |
Outputs, camera, workspaces
| Command |
CLI |
What it does |
outputs |
outputs |
list outputs |
set_output_config |
output-config <out> ... |
mode, transform, position, scale, enable. Applied and saved |
workspaces |
workspaces |
there's one, the canvas (workspace_id = 1) |
get_camera |
none |
position, bounds, zoom |
set_camera / reset_camera |
none |
set_camera takes integer x, y |
set_zoom |
none |
camera zoom, percent (a supported step) |
get_state |
state |
atomic snapshot of windows, outputs, camera, shell |
output-config flags: --width --height --refresh-mhz --x --y,
--scale <1..3|auto>, --position auto, --transform <normal|90|180|270|flipped|flipped_90|flipped_180|flipped_270>,
--enabled/--disabled, --primary/--no-primary. auto clears the override.
Inspecting the shell and scene
| Command |
CLI |
What it does |
get_shell_state |
shell-state [--output] |
panels, progress, taskbars |
list_panels |
panels |
registered panels and open state |
get_widget_tree |
widget-tree <panel> |
semantic widget tree of an open panel |
get_input_state |
input-state |
pointer, focus, keys, buttons, grabs |
hit_test |
hit-test <x> <y> [--output] |
what's at those layout coordinates |
get_scene_tree |
scene-tree [--max-depth] |
scene node hierarchy |
get_layer_surfaces |
layers |
layer-shell surfaces |
get_text_input |
text-input |
input-method state and candidate placement, never the text |
get_capture_state |
none |
active screen capture sessions |
get_idle_state |
none |
idle state, deadlines, inhibitors |
Panels and menus
| Command |
CLI |
open_start_menu |
Action open-start-menu |
open_control_center |
Action open-control-center |
open_power_menu |
Action open-power-menu |
open_appearance |
none |
close_panel |
close-panel <panel> |
[auto] click_widget |
none, path, optional button, at |
[auto] hover_widget |
none, path, optional at |
| Command |
CLI |
Notes |
[auto] move_cursor |
Action move-cursor --x --y [--output] |
|
[auto] move_cursor_relative |
Action move-cursor-relative --dx --dy |
|
[auto] pointer_button |
Action pointer-button --button --pressed |
Linux button codes |
[auto] click |
Action click [--button] |
left by default |
[auto] scroll |
Action scroll --dx --dy |
|
[auto] key |
Action key --keycode --pressed |
Linux keycodes |
[auto] key_press |
Action key-press --key |
named key or chord |
[auto] type_text |
Action type-text --text |
|
[auto] drag |
Action drag --from-x --from-y --to-x --to-y [--button] |
|
[auto] pinch |
none |
phase begin/update/end, scale, dx, dy, rotation, fingers, cancelled |
[auto] swipe |
none |
phase, dx, dy, fingers, cancelled |
get_keyboard_layouts |
keyboard-layouts |
zero-based active index |
switch_layout |
switch-layout <next\|prev\|index> |
|
Screenshots and pixels
| Command |
CLI |
Notes |
[auto] screenshot |
Action screenshot |
--output, --window, --mode, --include-cursor, --path, --save. Raw params also take crop and crop_space (logical or device) |
[auto] dump_buffer |
dump-buffer <target> |
targets: titlebar, skirt, start_menu, control_center, power_menu, taskbar. --id, --output |
[auto] sample_pixels |
sample-pixels <x> <y> <w> <h> |
raw framebuffer pixels |
stop_all_capture |
none |
ends every screen capture session |
Prefer screenshot and dump_buffer over host grim when debugging. Host
tools show host scaling and any window covering yours.
Waiting
wait_for (wait) and wait_for_frame (wait-frame). See
IPC for the condition list.
Audio
| Command |
CLI |
get_audio_state |
Action get-audio-state |
set_master_volume |
Action set-master-volume --volume 0..1 |
toggle_mute |
Action toggle-mute |
set_app_volume |
Action set-app-volume --index N --volume 0..1 |
set_app_mute |
Action set-app-mute --index N --muted true\|false |
Volumes are clamped to 0..1.
Notifications
| Command |
What it does |
get_notifications (notifications) |
active toasts, history, DND status |
dismiss_notification |
id, optional reason |
invoke_notification_action |
id, action_key |
set_dnd |
enabled |
clear_notifications |
clears closed history |
Night light and idle
| Command |
What it does |
get_night_light (night-light) |
status and colour curves |
set_night_light |
enabled and/or temperature (1700 to 10000), at least one |
set_night_light_clock |
unix_seconds, override the clock (testing) |
set_idle_config |
enabled, blank_after_seconds, suspend_after_seconds |
advance_idle_time |
seconds, move the idle clock (testing) |
Settings and appearance
These work without opening Settings.
| Command |
Params and behaviour |
get_services |
system services, raw systemd state, activation_us, total boot_us. First calls may say loading/analyzing, unknown times are null |
set_service_mode |
service, mode: on, disabled, on-demand. Changes boot policy only, doesn't start or stop the unit. on-demand needs an existing activation trigger and disables boot enablement. deferred returns UnsupportedServiceMode. Static and masked units are read-only |
get_sounds |
theme and events, with enabled/available flags and resolved files |
play_sound |
event from get_sounds. Plays with /usr/bin/paplay, returns the PID, not "finished" |
get_wallpaper |
selection, resolved and displayed paths, dimensions, loading and fallback status |
set_wallpaper |
path: a wallpaper name or a file, "" restores the default. Decoding is async, poll get_wallpaper |
get_theme |
theme tokens, theme file path (null if inline), dark mode. Colours are straight RGBA, 0 to 1 |
set_accent_color |
color: #RRGGBB or #RRGGBBAA |
get_processes |
window-owning app processes grouped by PID, with window IDs, RSS and CPU sampled for about 200 ms. 100% is one logical CPU. Gone processes have null usage |
set_wallpaper, set_accent_color and set_night_light save to the config
by default. Pass "persist": false for a session-only change. If saving fails
you get the error before anything changes live.
Service changes use systemd's interactive authorization and only return
"accepted". Watch get_services.action_pending, raw state and status for the
result.
Session
| Command |
Notes |
restart_shell |
restarts the compositor in place, disconnects all apps |
Examples
rediwm-msg --raw '{"version":1,"command":"get_theme"}'
rediwm-msg --raw '{"version":1,"command":"set_accent_color","params":{"color":"#e05a47"}}'
rediwm-msg --raw '{"version":1,"command":"set_night_light","params":{"enabled":true,"temperature":4000,"persist":false}}'
rediwm-msg
Command line client for the IPC. It turns friendly subcommands into
v1 JSON, sends them, and pretty prints the reply.
rediwm-msg [options] <command>
| Option |
What it does |
-s, --socket <PATH> |
socket path (default: REDIWM_SOCKET, else $XDG_RUNTIME_DIR/rediwm-$WAYLAND_DISPLAY.sock) |
-t, --timeout <MS> |
request/response timeout |
--raw [JSON] |
send a v1 JSON payload unchanged, from the arg or stdin |
--json |
print the JSON reply instead of the readable version |
-h, --help |
the full command list, always up to date |
rediwm-msg raw [JSON|-] does the same as --raw.
Two kinds of subcommand
Most queries and a few actions are top level: rediwm-msg windows,
rediwm-msg maximize 12. The rest are under action:
rediwm-msg action focus-window --id 12. --help shows which is which.
Wire-name aliases like get-state, get-perf, list-panels also work.
doctor
rediwm-msg doctor
Checks the socket and the environment and tells you what's off. Run this
first when nothing works. It runs even without a compositor to talk to.
Recipes
See what's open
rediwm-msg windows
rediwm-msg focused-window
rediwm-msg state --json | jq '.Ok'
Window management
rediwm-msg action focus-window --id 12
rediwm-msg action move-window-to --id 12 --x 200 --y 100
rediwm-msg action move-window-to --id 12 --output DP-2
rediwm-msg fullscreen 12
rediwm-msg set-zoom 12 80
rediwm-msg action close-window # focused one
Launch things
rediwm-msg launch org.gnome.Nautilus.desktop
rediwm-msg action spawn -- foot -e htop
Displays
rediwm-msg outputs
rediwm-msg output-config DP-1 --scale 1.5
rediwm-msg output-config DP-1 --width 2560 --height 1440 --refresh-mhz 144000
rediwm-msg output-config DP-2 --x -1920 --y 0
rediwm-msg output-config DP-2 --disabled
rediwm-msg output-config DP-1 --scale auto --position auto
Debug a window rule
rediwm-msg window-rules 12
rediwm-msg match-window-rules --app-id firefox --title "Picture-in-Picture"
Watch events
rediwm-msg event-stream --events window_opened,window_focused
Scripted tests (needs automation on)
rediwm-msg action spawn -- foot
rediwm-msg wait window_mapped --timeout 5000
rediwm-msg action key-press --key ctrl+shift+t
rediwm-msg action type-text --text "hello"
rediwm-msg wait-frame
rediwm-msg action screenshot --save /tmp/shot.png
rediwm-msg sample-pixels 100 100 1 1
Poke at the UI
rediwm-msg action open-start-menu
rediwm-msg panels
rediwm-msg widget-tree start_menu
rediwm-msg dump-buffer taskbar
rediwm-msg close-panel start_menu
Audio and keyboard
rediwm-msg action set-master-volume --volume 0.4
rediwm-msg action toggle-mute
rediwm-msg switch-layout next
Anything without a subcommand
rediwm-msg --raw '{"version":1,"command":"get_theme"}'
echo '{"version":1,"command":"set_dnd","params":{"enabled":true}}' | rediwm-msg --raw
Exit behaviour
Bad arguments print an error and exit 1. A compositor Err is printed and
the exit code is nonzero. With no socket it tells you to set REDIWM_SOCKET
or XDG_RUNTIME_DIR and WAYLAND_DISPLAY.
Protocols and Sandboxing
Wayland protocols
On top of xdg-shell, layer-shell and Xwayland:
| Area |
Protocols |
Works with |
| Displays |
wlr-output-management-v1, xdg-output-v1 |
wlr-randr, kanshi, nwg-displays |
| Window lists |
wlr-foreign-toplevel-management-v1, ext-foreign-toplevel-list-v1 |
docks, waybar, rofi/fuzzel, wlrctl |
| Capture |
wlr-screencopy-v1, ext-image-copy-capture-v1 |
grim, wf-recorder, OBS, xdg-desktop-portal-wlr |
| Locking and idle |
ext-session-lock-v1, ext-idle-notify-v1, idle-inhibit-v1 |
swaylock, gtklock, swayidle |
| Remote input |
virtual-pointer-v1, virtual-keyboard-v1 |
wayvnc, KDE Connect, wtype |
| Clipboard |
wlr-data-control-v1, ext-data-control-v1, primary selection |
wl-clipboard |
| Input |
text-input-v3, input-method-v2, relative-pointer-v1, pointer-constraints-v1, keyboard-shortcuts-inhibit-v1, pointer-gestures-v1, cursor-shape-v1 |
fcitx5, IBus, games, virt-manager |
| Workspace |
ext-workspace-v1 |
one canvas workspace shared by all outputs |
| Window metadata |
xdg-toplevel-tag-v1, content-type-v1, xdg-activation-v1 |
window rules, content hints, activation |
| Colour |
color-management-v1, color-representation-v1 |
whatever the renderer supports |
| Presentation |
tearing-control-v1, drm-lease-v1 |
opt-in fullscreen tearing, VR headsets |
| Rendering |
fractional-scale-v1, viewporter, single-pixel-buffer-v1, linux-dmabuf-v1, linux-drm-syncobj-v1, presentation-time |
|
Things worth knowing
- Output changes from clients (wlr-randr, kanshi) are saved just like ones made
in Settings.
- Workspace: the canvas is advertised as one workspace. You can't create,
remove or switch workspaces through the protocol. Window IPC reports
workspace_id = 1, plus tag, description and content_type when the
client sets them. [[window_rules]] can match tag with the same globs as
app_id.
- Content type hints (photo, video, game) don't change idle or focus
policy.
- Colour: follows the renderer. Pixman gives colour feedback without
parametric image creation, GPU renderers advertise what wlroots can convert.
Output stays SDR. No HDR modes or ICC loading.
- Tearing:
[compositor] allow_tearing = true honours async hints from
focused fullscreen clients. If an output can't do it, that output falls back
to synchronized presentation until it's recreated. Suppressed while locked.
- DRM leases: DRM backends only, physical non-desktop outputs only.
Revoked on lock or auth. Private capture outputs are never offered.
- Session lock clients: a client
ext-session-lock is treated as a real
lock. If the client dies the session stays locked.
- Remote input: virtual pointer and keyboard can't touch the lock or auth
UI.
- Pointer lock: active only when the client has both pointer and keyboard
focus.
- Shortcut inhibit (VMs, remote desktops) blocks the switcher and
screenshot shortcuts. Hardware keys, VT switching and open panels still win.
Super+Escape takes shortcuts back.
Sandboxed apps
Clients connecting through wp_security_context_v1 (Flatpak etc.) only see
the safe Wayland globals. Privileged ones are hidden, and guessing the name of
a hidden global is a protocol error. Nested security contexts are fatal.
Grant access per app in the config:
[[sandbox_allow]]
app_id = "com.obsproject.Studio" # exact match on the security-context app id
engine = "org.flatpak" # optional, default matches any engine
allow = ["capture"]
Groups:
| Group |
Unlocks |
capture |
screencopy and image capture |
windows |
foreign toplevel lists, workspace |
clipboard |
data control |
input |
virtual pointer and keyboard |
input_method |
input method |
outputs |
output management, DRM leasing |
lock |
session lock |
layer_shell |
layer shell |
idle |
idle notify |
ipc |
the IPC socket |
automation |
synthetic input and pixel reads over IPC |
On reload, Wayland grants apply to new connections only (already bound
globals stay bound until the app reconnects). IPC grants apply immediately.
The greeter hides all of these.