Files
mesh-catalog/modules/xorg/README.md
T
jochen b2c170acda xorg: the display server holds node-display-server and writes the session's start (hq ADR 0208)
The X server, its start and its tools as one module. It provides x11-display with
the machine's reach, gated by the host's seat capability. It writes a block at the
start of ~/.xinitrc: the account's environment, an explicit import into the user
manager, the mesh's X resources merged without cpp, autorandr, the xinitrc slots,
~/.xinitrc.local, and the session's exec from the last slot.

Its Go tools serve the seat's displays and layout (autorandr profiles keyed by
EDID), and set-mode, primary, dpi, input devices and settings, keyboard, a
screenshot (xwd decoded in Go) and the server's log. internal/desktop is how
every desktop tool finds the operator's session from the runtime, which has none:
from the session's own processes, reading only its words, confirmed with logind.
2026-10-04 13:21:47 +02:00

12 KiB

xorg

The X display server as a module (novox/hq ADR 0208, research 026, to-be 42 phase 2 step 2).

  • Claims node-display-server and serves its verbs displays and layout.
  • Provides x11-display with the machine's reach: i3, xterm, picom and the X lock screen require it, and a requirement for it is met only by this module on the same machine. It is never pulled in for whatever asked.
  • Gated by the capability seat, which the host reports for a machine with a graphics device and a connected display. See Blockers for why not graphical-session.
  • Packages: xorg-server, xorg-xinit, xorg-xrandr, xorg-xset, xorg-xrdb, xorg-xinput, xorg-setxkbmap, xorg-xwd and autorandr. The GPU's driver is not here. It follows the machine, so it belongs to that machine model's hardware module.

What it owns

path class (ADR 0182) what
~/.xinitrc, a block at the start written into (block, at: start) the session's start, below
~/.config/xorg/ owned directory
~/.config/xorg/xresources owned the mesh's X resources: font rendering (Xft.*, DPI 96) and the three xresources slots

The session's start, in ADR 0208 §5's order:

  1. It sources ~/.config/mesh/environment.sh (ADR 0203). If pam did not hand over a session bus, it takes the user manager's socket, never a second bus.
  2. It imports an explicit list of the session's words into the user manager and D-Bus activation, only those that are set. The list is DISPLAY, XAUTHORITY, XDG_SESSION_TYPE, XDG_CURRENT_DESKTOP, XDG_SESSION_DESKTOP, XDG_CONFIG_HOME, XDG_DATA_DIRS, GTK_THEME, GTK2_RC_FILES, QT_QPA_PLATFORMTHEME, QT_STYLE_OVERRIDE, QT_SELECT, XCURSOR_THEME, XCURSOR_SIZE and TERMINAL. It never uses --all.
  3. It merges ~/.config/xorg/xresources with xrdb -nocpp, then ~/.Xresources if you keep one, so yours win.
  4. It runs autorandr --change, which applies the saved profile matching the connected monitors.
  5. It runs the xinitrc slots first and normal, then sources ~/.xinitrc.local if it exists.
  6. It runs the last slot, where the holder of node-display-session starts the session (exec i3).

Who contributes where, among the desktop's modules:

slot module what
first gnome-keyring SSH_AUTH_SOCK
normal adwaita its GSettings keys
normal clipmenu clipmenud &
normal feh the wallpaper
normal i3status-rust the bar watchdog
normal screen-lock the timeouts and the xss-lock loop
last i3 exec i3

Slot last is the session's. A module contributing session lines uses first or normal. Code in last that sorts after the session holder's name would come after its exec and never run.

Why ~/.xinitrc.local. The block ends by starting the session, so no line below it runs. Your own session lines go into ~/.xinitrc.local. Like ~/.zshrc.local, it is yours: found, never written. This is the one step this module adds to ADR 0208 §5. Without it, a line no module carries yet (a DOTNET_ROOT, a wallpaper) would have nowhere to run.

Resources without the preprocessor. xrdb -nocpp needs no C compiler on the machine, and it skips a line starting with #. The controller precedes each contribution with a # <module> line, which is a cpp directive error under plain xrdb -merge but is skipped here.

Tools

tool what
node-display-server.displays r the screen and every output: position, rotation, size, DPI, the monitor's EDID identity (manufacturer, product, serial, name, fingerprint), current and preferred mode, every mode and rate, the autorandr profile in force
node-display-server.layout r/a autorandr profiles: list (each with the monitors it is for, which match now, which is current), save (replace to overwrite), apply
xorg_set_mode a one output's mode, rate, rotation, scale, position (x/y or beside another), off, primary; dry_run
xorg_primary r/a which output is primary; set it
xorg_dpi r/a Xft.dpi beside each monitor's physical DPI; set it for the running session
xorg_input_devices r the input devices, and each pointer's libinput settings
xorg_input_set a tap, tap drag, natural scroll, disable while typing, left handed, middle emulation, enabled, acceleration, by device id or name
xorg_keyboard r/a the XKB map; set layout, variant, model, options
xorg_screenshot d a PNG of the screen, one output or one window, inside the home (default ~/Pictures/Screenshots/)
xorg_x_log r the X server's log for the running session: version, start, errors (and warnings)

All answers are structured. Each tool that changes the running server says how long the change lasts.

How a tool reaches the session (internal/desktop, the same copy in every desktop module). The runtime is a system service running as the operator account. It has no DISPLAY, no XAUTHORITY and no session bus. The tool reads them from the session's own processes:

  1. It looks at the account's processes, preferring the window manager.
  2. It reads only a fixed list of words, never the rest. A session's environment held secrets on these machines.
  3. It asks logind whether that session is active and local.
  4. It checks that the display's socket still exists.

The bus it hands on is the user manager's (unix:path=/run/user/<uid>/bus). With no session, a tool answers {"error": "no-graphical-session", "reason": …, "looked": […]}.

The screenshot needs no screenshot program. xwd dumps the screen, and the module turns the dump into a PNG itself.

What it improves

  • One environment. The session sources the shells' environment file. It no longer exports a hand-kept second copy, and never sources the predecessor's secrets file.
  • No compiler needed for the mesh's resources, and contributions are placed in order rather than #included.
  • Monitor layouts by the monitors' identity (autorandr, research 026/04), not scripts with port names baked in. The package's own udev rule and service apply the matching profile on hotplug.
  • No second bus. The dbus-launch fallback that once ran the whole session on a private bus is gone. That stale session can still be seen today on one workstation: session_bus in a tool's answer shows it.

What it leaves found

~/.xprofile, ~/.Xresources, ~/.Xresources.d/, ~/.screenlayout/, the arandr scripts, every line of yours below the block, and /etc/X11/xinit/xinitrc.d/.

The one-off migration (ADR 0182)

Assign the desktop's modules together (xorg, lemurs, i3, xterm, adwaita and the companions above) and do this migration first. Until i3 is assigned, nothing changes for you. The block's last slot is empty, so the block runs and falls through to your own lines below it, which still end in exec i3. The environment is sourced twice and the import runs twice, which is harmless.

Once i3 is assigned, nothing below the block runs. Before that push, sort today's ~/.xinitrc lines (both workstations hold the same file):

today's line where it goes
the /etc/X11/xinit/xinitrc.d/?*.sh loop delete: the block imports DISPLAY and XAUTHORITY itself, and the login manager's X setup runs that directory too
eval $(gnome-keyring-daemon --start …) and export SSH_AUTH_SOCK delete once gnome-keyring is assigned. PAM starts and unlocks the keyring, and that module names the ssh agent's socket in the xinitrc slot first. Until then, ~/.xinitrc.local
export PATH=… (nine entries) ~/.local/bin, ~/scripts and ~/scripts/bin come from zsh's environment already. Delete ~/scripts/i3-sessions/commands and ~/scripts/mediahuis: neither exists on either workstation. Move ~/.cargo/bin, ~/.config/rofi/scripts, ~/.dotnet and ~/.dotnet/tools to ~/.xinitrc.local until a module carries them
DOTNET_ROOT, DOTNET_CLI_TELEMETRY_OPTOUT ~/.xinitrc.local
XDG_CONFIG_HOME delete: zsh contributes it
XDG_DATA_DIRS with the flatpak directories ~/.xinitrc.local until the flatpak module
QT_QPA_PLATFORMTHEME, GTK_THEME, QT_STYLE_OVERRIDE, GTK2_RC_FILES, QT_SELECT delete once adwaita is assigned (its environment contributions)
XDG_SESSION_DESKTOP, XDG_CURRENT_DESKTOP and their comment delete once i3 is assigned
dbus-update-activation-environment --systemd … and its comment delete: the block's step 2
~/scripts/xdg-appearance || true delete once adwaita is assigned
MY_KV_PATH, MY_LIB_PATH, MY_STREAMING_PATH ~/.xinitrc.local (they are yours)
[ -f "$HOME/.config/hal/env" ] && . … (the secrets file) delete (ADR 0208 §5, research 027 Q2). Whatever in the session needed one of those tokens gets it the way research 027 settles
xset s 1800, xset dpms 1800 1800 3600 delete once screen-lock is assigned (its normal slot line). Until then, ~/.xinitrc.local
~/.fehbg & delete once feh is assigned (its normal slot line). Until then, ~/.xinitrc.local
the xss-lock respawn loop delete once screen-lock is assigned. Until then, ~/.xinitrc.local
exec i3 --shmlog-size=26214400 delete: i3 contributes exec i3 to the last slot

Then delete everything below the block. The old #!/bin/sh line now sits below the block too and means nothing there. The file is run with sh (by the login manager's entry and by startx), never executed by its first line.

~/.xprofile. The login manager's X setup sources it, and it sources ~/.xinitrc. Once i3 and lemurs are assigned, the session entry /etc/lemurs/wms/i3 runs ~/.xinitrc itself, so delete ~/.xprofile. Its bus logic is the block's now. If you keep it, delete its . ~/.xinitrc line, or the session starts from .xprofile and the login manager's entry is never reached. Either way works once, but only one should.

~/.Xresources holds #include ".Xresources.d/xterm", #include ".Xresources.d/xft" and the two Xcursor lines:

  • The xft include and ~/.Xresources.d/xft are deleted now. This module's file carries the same five values.
  • The xterm include and ~/.Xresources.d/xterm are deleted once xterm is assigned. Kept, they win over the module's font and colours.
  • The Xcursor lines are deleted once adwaita is assigned.
  • A file left empty is deleted.

Monitor layouts. For each place you use, arrange the monitors (xorg_set_mode, or arandr once more), then layout save it under a name. Once every place has a profile, these retire: the ~/.screenlayout/ and ~/scripts/.screenlayouts/ scripts, the laptop's hotplug rule, and its i3 bindings. Those bindings belong to that machine's hardware module, not here.

What changes when it is assigned

g14 (laptop) shanks (desktop)
packages autorandr installed; the rest are there already the same
files ~/.config/xorg/xresources new; a block added at the start of ~/.xinitrc the same
running session nothing: everything takes effect at the next login nothing
next login, before i3 is assigned the block runs, then the old file below it. Fonts unchanged (96 DPI); autorandr --change does nothing with no profiles the same. Its session moves to the user manager's bus at the next login, as it should have
next login, after i3 is assigned only the block: what the table above did not move is gone the same

Blockers

  • The capability. ADR 0208 §3 says graphical-session gates the display server. The host reports that capability from its own environment. It is a root daemon with no DISPLAY, so the capability is no on both workstations (node show, 2026-10-04). Gated on it, xorg could never be assigned. This manifest uses seat instead: graphics hardware with a connected display, yes on both. The ADR's wording needs a progressive insight, or the host needs a probe that finds the session the way internal/desktop does.
  • Unassigning removes the packages. The host takes a package away when its declaration goes, and xorg-server is among them. Do not unassign xorg from a workstation you are sitting at without another display server assigned.
  • The # <module> naming lines in the xresources slots are safe only because this module merges with -nocpp. If the controller rendered ! for xresources, that would be the format's own comment.