UWSM(1)

πŸ’– NAME β€” UWSM(1) (General Commands Manual): Universal Wayland Session Manager πŸ’–

okay so bestie, welcome to UWSM(1) β€” yes, the General Commands Manual one β€” and the whole vibe is: UWSM = Universal Wayland Session Manager. That's the name, that's the mission, periodt. πŸ’…


🌈 SYNOPSIS 🌈

uwsm [-h|-v] {subcommand} [options ...]

✨ DESCRIPTION ✨

It launches an arbitrary wayland compositor through a whole little set of systemd user units, and the result is a graphical user session with environment management, XDG autostart support, and a clean shutdown. It also gives you helpers for launching applications as scopes or services. Slay. 🫢


πŸŽ€ SUBCOMMANDS πŸŽ€

  • select β€” Select default compositor Entry.
  • start β€” Start compositor and graphical session.
  • finalize β€” Send compositor-set variables and unit startup notification to systemd user manager.
  • stop β€” Stop graphical session and compositor.
  • app β€” Application unit launcher (with Desktop Entry support).
  • check β€” Perform state checks (for scripting and info).
  • aux β€” Technical functions for use inside units.

See corresponding SUBCOMMANDS subsections below for further info, bestie.

Help for each subcommand is accessible by running uwsm {subcommand} -h.


πŸ¦‹ CONFIGURATION πŸ¦‹

πŸ“ Files πŸ“

In XDG config hierarchy:

  • uwsm/env
  • uwsm/env.d/*
  • uwsm/env-${compositor}
  • uwsm/env-${compositor}.d/* β€” Environment (shell) to be sourced for the graphical session. Sourced from directories of increasing priority, in each directory common file is sourced first, then suffixed files in the order of items listed in XDG_CURRENT_SESSION var (lowercased). Fragments in .d/ subdirs are ignored if suffixed with: .bak, ~, .disabled, .example, .sample, .broken.
  • uwsm/default-id β€” Stores Desktop Entry ID of default compositor.

Fallback is also extended into the system part of XDG data hierarchy, this can be used for distro level defaults.

🌱 Environment vars 🌱

  • UWSM_UNIT_RUNG (run|home) β€” Which rung of systemd/user/ hierarchy to manage generated unit and drop-in files in: $XDG_RUNTIME_DIR or $XDG_CONFIG_HOME.
  • UWSM_TWEAKS (boolean value) β€” Set to False to remove and not generate tweak drop-ins for other software.
  • UWSM_FINALIZE_VARNAMES (whitespace-separated names of env vars) β€” Additional variables for uwsm finalize.
  • UWSM_WAIT_VARNAMES (whitespace-separated names of env vars) β€” Variables to wait for in activation environment before proceeding to graphical session (in addition to WAYLAND_DISPLAY).
  • UWSM_WAIT_VARNAMES_TIMEOUT (int value) β€” Seconds to wait for variables to appear in activation environment. Essentially, startup timeout (default: 10).
  • UWSM_WAIT_VARNAMES_SETTLETIME (float value) β€” Seconds to pause after all expected vars found in activation environment (default: 0.2).
  • UWSM_ALWAYS_EXPORT_VARNAMES, UWSM_NEVER_EXPORT_VARNAMES, UWSM_ALWAYS_CLEANUP_VARNAMES, UWSM_NEVER_CLEANUP_VARNAMES (whitespace-separated names of env vars) β€” Modify sets of variable names UWSM uses to filter export and cleanup. Plain or +-prefixed names are added, --prefixed names are removed. "never" lists always win.
  • UWSM_APP_UNIT_TYPE (scope|service) β€” Default unit type for launching apps (default: scope).
  • UWSM_SILENT_START (int or boolean value) β€” True or 1 to inhibit stdout messages from uwsm start. 2 to also inhibit warnings.
  • UWSM_STOP_JOB_MODE (fail|replace|replace-irreversibly) β€” Set job mode when uwsm stop brings down units, defaults to replace.
  • UWSM_DEBUG, DEBUG (int or boolean value) β€” True or positive number to dump debug info to stderr.

πŸ‘‘ OPERATION OVERVIEW πŸ‘‘

πŸšͺ Login Sequence Integration πŸšͺ

uwsm can be launched by using conditional exec in shell profile to replace login shell (see Shell Profile Integration section).

Alternatively uwsm start ... command can be put into wayland session's Desktop Entry to be launched by a display manager (see Use Inside Desktop Entry section).

🧚 Compositor Selection 🧚

uwsm can run arbitrary compositor command line or a Desktop Entry by ID (specifying Action ID is also supported).

Desktop Entry can also be selected via a whiptail menu (see select subcommand section).

πŸ”₯ Startup πŸ”₯

See start subcommand section for command syntax.

UWSM uses a set of units bound to standard user session targets:

  • wayland-session-pre@.target (bound to graphical-session-pre.target)
    • wayland-wm-env@.service (environment preloader service)
  • wayland-session@.target (bound to graphical-session.target)
    • wayland-wm@.service (service for the selected compositor)
  • wayland-session-xdg-autostart@.target (bound to xdg-desktop-autostart.target)
  • wayland-session-envelope@.target (lives through entire lifecycle)
  • wayland-session-shutdown.target (conflicts with targets above for shutdown)
  • wayland-session-bindpid@.service (PID-tracking session killswitch)
  • wayland-session-waitenv.service (delays graphical session until vars appear)

Compositor ID (Desktop Entry ID or executable name) becomes the specifier for all templated units.

At the stage of graphical-session-pre.target, the environment saved from uwsm start context is loaded (or POSIX shell profile is sourced), uwsm environment files are sourced. The delta is exported to the systemd and D-Bus activation environments by the environment preloader service and is marked for cleanup at shutdown stage. Preloader shell context for convenience has IN_UWSM_ENV_PRELOADER var set to true.

At the stage of graphical-session.target (before it) the main compositor unit wayland-wm@${ID}.service and wayland-session-waitenv.service are started.

Compositor should at least put WAYLAND_DISPLAY variable to systemd activation environment. This will trigger uwsm's automatic finalization logic. Without WAYLAND_DISPLAY in activation environment startup will timeout in 10 seconds.

Manual finalization is possible by running uwsm finalize (see finalize subcommand section), also in combination with tweaking UWSM_WAIT_VARNAMES and UWSM_WAIT_VARNAMES_SETTLETIME vars (see Environment vars section).

Successful activation of compositor unit and existence of WAYLAND_DISPLAY in activation environment will allow graphical-session.target to be declared reached.

Finally, xdg-desktop-autostart.target is activated.

πŸ¦‹ Inside session πŸ¦‹

It is highly recommended to configure the compositor or app launcher to launch apps as scopes or services in special user session slices (app.slice, background.slice, session.slice). uwsm provides custom nested slices for apps to live in and be terminated on session end:

  • app-graphical.slice
  • background-graphical.slice
  • session-graphical.slice

A helper app subcommand is provided to handle all the systemd-run invocations for you (see app subcommand section).

The compositor is launched in session.slice by default (as recommended by systemd.special(7)).

πŸ’€ Shutdown πŸ’€

Can be initiated by either:

  • running uwsm stop
  • stopping wayland-wm@*.service or wayland-session-envelope@*.target
  • starting wayland-session-shutdown.target

Systemd stops all user units in reverse, as it usually does. During deactivation of graphical-session-pre.target, the environment preloader service cleans activation environments by unsetting all variables that were marked for removal during startup and finalization stages.

⚠️ Do not use compositor's native exit mechanism or kill its process directly. fr fr, don't do it. 😭

If complex dependencies result in a destructive transaction, try replace-irreversibly job mode (see Environment vars section and systemctl(1))

🎯 select 🎯

Bestie, this one is literally just a menu. That's the whole bit.

uwsm select

It throws up a whiptail menu so you can pick your default wayland session compositor from the Desktop Entries chillin' in the wayland-sessions XDG data hierarchy. Then it writes your pick to ${XDG_CONFIG_HOME}/uwsm/default-id and dips. That's it β€” nothing else happens. Iconic minimalism.

Returns 1 if you cancel the selection. And yes, you can absolutely use it for scripting a launch condition in your shell profile β€” iykyk. πŸ’…


πŸ§ͺ check πŸ§ͺ

Runs a bunch of tests. Returns 0 on success, 1 on failure. Simple as.

is-active

uwsm check is-active [-h] [-v] [compositor]
  • -v β€” show additional info
  • compositor β€” check for a specific compositor

Checks whether the unit of a specific compositor β€” or graphical-session*.target in general β€” is in active or activating state.

may-start

uwsm check may-start [-h] [-g [S]] [-v|-q] [N ...]
  • N ... β€” allowed VT numbers (default: 1)
  • -g S β€” wait S seconds for graphical.target in queue (default: 60; 0 or less disables the check).
  • -i β€” do not check for login shell
  • -r β€” do not check for local session (allow remote session)
  • -v β€” show all failed tests
  • -q β€” be quiet

Checks whether it's OK to launch a wayland session, vibe-checking these conditions:

  • DBUS_SESSION_BUS_ADDRESS is set
  • Running from login shell
  • System is at graphical.target
  • User graphical-session*.target units are not yet active
  • Foreground VT is among allowed (default: 1)
  • Login session's VT is matching

πŸš€ start πŸš€

Generates units for a given compositor command line or Desktop Entry, then starts them. The main character of the whole operation, honestly.

uwsm start [-h] [-D name[:name...]] [-a|-e] [-N Name] [-C Comment]
[-U {run|home}] [-t] [-o] [-n] -- compositor [args ...]
  • -F β€” Hardcode mode: always write the command line to unit drop-ins and use full paths.
  • -D name[:name...] β€” Names to fill XDG_CURRENT_DESKTOP with (:-separated). Existing var content is a starting point if no active session is running.
  • -a β€” Append desktop names set by -D to other sources (default).
  • -e β€” Use desktop names set by -D exclusively, discard other sources.
  • -N Name β€” Fancy name for compositor (filled from Desktop Entry by default).
  • -C Comment β€” Fancy description for compositor (filled from Desktop Entry by default).
  • -U {run|home} β€” Select rung for generated unit files: run: $XDG_RUNTIME_DIR/systemd/user (default), or home: $XDG_CONFIG_HOME/systemd/user. Permanent destination will save some time by removing need for reloading systemd. Managed files from other rung will be removed. Can be preset with UWSM_UNIT_RUNG environment var.
  • -t β€” Do not generate (and remove) tweak unit files. Can be preset with UWSM_TWEAKS=false environment var.
  • -T β€” Generate tweak unit files for other software. This is default behavior.
  • -g S β€” Wait for S seconds for system graphical.target in queue and warn if timed out or not in queue (default: 60, negative to disable).
  • -G S β€” Wait for S seconds for system graphical.target in queue and abort if timed out or not in queue (overrides -g, default: -1, (disabled)).
  • -o β€” Only generate units, but do not start.
  • -n β€” Dry run, do not write or start anything.

The first argument of the compositor command line acts as an ID, and it should be either one of:

  • Executable name
  • Desktop Entry ID (optionally with ":"-delimited action ID)
  • Special value:
    • select β€” invoke menu to select compositor.
    • default β€” run previously selected compositor (or select if no selection was saved).

If given as path, hardcode mode will be used implicitly. Sneaky but valid.

Always use -- to disambiguate dashed arguments intended for the compositor itself. Fr fr, this one matters. πŸ™ˆ

After units are (re)generated, wayland-session-bindpid@${PID}.service is started, to track the PID of the invoking uwsm, then the uwsm process replaces itself with systemctl execution that starts wayland-wm@${ID}.service and waits for it to finish.

And so, in order to complete the startup sequence, the compositor has to put WAYLAND_DISPLAY into the systemd activation environment. You can do that explicitly by making the compositor run the uwsm finalize command β€” see the next subsection, it's right there. πŸ‘€


✨ finalize ✨

For running by a compositor on startup.

uwsm finalize [-h] [VAR_NAME ...]

Exports WAYLAND_DISPLAY, DISPLAY, and any defined vars mentioned by names in arguments or in the UWSM_FINALIZE_VARNAMES variable (whitespace-separated). Then it sends a startup notification for the unit to the systemd user manager. Slay.

This is required if the compositor itself does not put WAYLAND_DISPLAY into the systemd activation environment β€” otherwise the wayland-session@.service unit, or a dedicated wayland-session-waitenv.service unit, will terminate due to startup timeout. 😭 So like, actually do it.

UWSM_FINALIZE_VARNAMES variable can be prefilled by plugins.

Direct assignment as VAR_NAME=value is also possible, but recommended only for creating flags for the UWSM_WAIT_VARNAMES mechanism. Don't get cute with it otherwise. πŸ’…


πŸ›‘ stop πŸ›‘

Stops the compositor and optionally removes generated units. The goodbye arc.

uwsm stop [-h] [-r [compositor] [-U {run|home}] [-n]
  • -r [compositor] β€” Also remove units (all or only compositor-specific).
  • -U {run|home} β€” Select rung for generated unit files: run: $XDG_RUNTIME_DIR/systemd/user (default), or home: $XDG_CONFIG_HOME/systemd/user. Permanent destination will save some time by removing need for reloading systemd. Managed files from other rung will be removed. Can be preset with UWSM_UNIT_RUNG environment var.
  • -n β€” Dry run, do not stop or remove anything.

πŸ“± app πŸ“±

Application-to-unit launcher, with Desktop Entry support. Obsessed with her.

uwsm app [-h] [-s {a,b,s,custom.slice}] [-t {scope,service}] [-a
app_name] [-u unit_name] [-d unit_description] [-S ] [-T] --
application [args ...]
  • -s {a,b,s,custom.slice} β€” Slice selector (default: a):
    • a - app-graphical.slice
    • b - background-graphical.slice
    • s - session-graphical.slice
    • any slice by full name
  • -t {scope,service} β€” Type of unit to launch (default: scope, can be preset by UWSM_APP_UNIT_TYPE env var).
  • -a app_name β€” Override app name (a substring in unit name).
  • -u unit_name β€” Override the whole autogenerated unit name.
  • -d unit_description β€” Unit Description.
  • -p Property=value β€” Set additional unit property (option is repeatable).
  • -S {out,err,both} β€” Silence stdout, stderr, or both.
  • -T β€” Launch app in a terminal. Allows command to be empty to just launch a terminal.

Application can be provided as a command with optional arguments, or a Desktop Entry ID, optionally suffixed with ":"-delimited Action ID. If a Desktop Entry is being launched, arguments should be compatible with it.

Always use -- to disambiguate dashed arguments intended for the application itself. Same energy as before, different app. 🫢

🧰 aux 🧰

For use in systemd user services. Can only be called by the systemd user manager β€” so like, don't try it yourself, you don't have clearance. πŸ”₯

  • prepare-env β€” Prepares environment (for use in ExecStart in wayland-wm-env@.service bound to wayland-session-pre@.target).
  • cleanup-env β€” Cleans up environment (for use in ExecStop in wayland-wm-env@.service bound to wayland-session-pre@.target).
  • exec β€” Executes a command with arguments or a desktop entry (for use in Exec in wayland-wm@.service bound to wayland-session@.target).
  • app-daemon β€” Daemon for faster app argument generation, used by the uwsm-app client.

πŸ€– APP DAEMON πŸ€–

Provided as wayland-wm-app-daemon.service to be started on-demand.

The daemon receives app arguments from the ${XDG_RUNTIME_DIR}/uwsm-app-daemon-in pipe. Resulting arguments are formatted as shell code and written to the ${XDG_RUNTIME_DIR}/uwsm-app-daemon-out pipe.

Arguments are expected to be \0-delimited, leading \0 are stripped. One command is received per write+close.

The first argument determines the behavior:

  • app β€” the rest is processed the same as in uwsm app
  • ping β€” just pong is returned
  • stop β€” daemon is stopped

Single commands are prepended with exec, iterated commands are assembled with trailing & each, followed by wait.

The purpose of all this is to skip all the expensive Python startup and import routines that slow things down every time uwsm app is called. Instead the daemon does it once and then listens for requests, while a simple shell script may dump arguments to one pipe and run the code received from another via eval β€” which is much faster. Genuinely a glow-up for latency. ✨

The simplest script is:

#!/bin/sh
printf '0%s' app "$@" > "${XDG_RUNTIME_DIR}/uwsm-app-daemon-in"
IFS='' read -r cmd < "${XDG_RUNTIME_DIR}/uwsm-app-daemon-out"
eval "$cmd"

The provided uwsm-app client script is a bit smarter: it can start the daemon, applies timeouts, and supports newlines in returned args. πŸ’œ

🌸 SHELL PROFILE INTEGRATION 🌸

okay so bestie, if you want uwsm to just launch itself when you log in, you gotta add one of these little constructs (or something similar, no cap) to your shell profile. That's it, that's the whole vibe. πŸ’…


the "lemme pick" setup πŸŽ€

This one literally asks you to select a compositor when you're logged in on VT 1 β€” or you can refuse and it'll just continue with your login shell. Respecting your choices, we love to see it:

if uwsm check may-start && uwsm select; then
     exec systemd-cat -t uwsm_start uwsm start default
fi

the "auto-pick per VT" setup πŸ¦‹

And this one just starts a specific compositor depending on which foreground VT you're on. Main character behaviour tbh:

if uwsm check may-start 1; then
     exec systemd-cat -t uwsm_start uwsm start sway.desktop
elif uwsm check may-start 2; then
     exec systemd-cat -t uwsm_start uwsm start labwc.desktop
fi

⚠️ bestie please read this one β€” using "uwsm check may-start" as a condition is essential, and not just to prevent accidental startup attempts where they're not expected. It's also because startup may involve sourcing your shell profile, which might lead to nasty loops. And like... we do not want that for you. 😭

Pop over to the check subcommand section for the info on the may-start checker β€” it's all there, iykyk. πŸ’–

exec is doing the heavy lifting here: it allows uwsm to replace the login shell so it can properly bind to the user session and handle session termination. Iconic, honestly. ✨

"systemd-cat -t uwsm_start" (this part's optional, btw) executes the command given to it (uwsm) with its stdout and stderr connected to the systemd journal, tagged with the identifier "uwsm_start". See systemd-cat(1) for more options. 🫢


πŸ‘‘ USE INSIDE DESKTOP ENTRY πŸ‘‘

So if you want to launch uwsm from a display/login manager, "uwsm start" can be used inside Desktop Entries. Here's an example of /usr/local/share/wayland-sessions/my-compositor.desktop:

[Desktop Entry]
Name=My compositor (with UWSM)
Comment=My cool compositor
Exec=uwsm start -N "My compositor" -D mycompositor -C "My cool compositor" mywm
DesktopNames=mycompositor
Type=Application

things to keep in mind 🧚

  • For consistency, command line arguments should mirror the keys of the entry. (matching fits = elite behaviour πŸ’…)
  • Command in Exec= should start with "uwsm start"
  • It should not point to itself (as a combination of Desktop Entry ID and Action ID)
  • It should not point to a Desktop Entry ID and Action ID that also uses uwsm

Potentially such entries may be found and used by uwsm itself, i.e. in the shell profile integration situation, or when launched manually. Following the principles above ensures uwsm will properly recognize itself and parse requested arguments inside the entry without any side effects. πŸ”₯ Slay. It's giving self-aware. It's giving maturity.


πŸ’œ SEE ALSO πŸ’œ


okay that's the whole page bestie, you ate that. go be a main character in your own session πŸŒˆβ­πŸ“

this one's from the UWSM(1) manual, dated 2026-10-15 β€” mwah, go touch some grass (after you read the docs) πŸ’–πŸŽ€

πŸ“¦ where this page comes from