# 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 `# ` 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//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 `#include`d. - **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 `# ` 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.