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.
190 lines
12 KiB
Markdown
190 lines
12 KiB
Markdown
# 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
|
|
`#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 `# <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.
|