It requires x11-display and contributes exec i3 to xinitrc's last slot, and XDG_CURRENT_DESKTOP/XDG_SESSION_DESKTOP to the environment. It owns ~/.config/i3/config, ending with the config.d include where other modules drop their files, and /etc/lemurs/wms/i3, which runs the session's start. Over today's identical config it drops the dead lxpolkit, the D-Bus-activated portal, the xrdb merge xorg now does, and the execs that XDG autostart already started. It sets JetBrainsMono Nerd Font, runs i3-sensible-terminal, and declares dex, which the desktop lacked. The tools speak i3's IPC: the seat's reload, workspaces and windows, and focus, move, layout save/restore, exec, kill, bindings, config check, marks and the scratchpad. A watcher in the bundle (ADR 0198) replaces the predecessor's inotify script and user unit. It reloads only a configuration i3 -C accepts, and at its start whatever i3 has not loaded.
153 lines
9.6 KiB
Markdown
153 lines
9.6 KiB
Markdown
# i3
|
|
|
|
The window manager as a module (novox/hq ADR 0208, research 026, to-be 42 phase 2, step 4).
|
|
|
|
- **Claims `node-display-session`** and serves its verbs `reload`, `workspaces` and `windows`.
|
|
- **Requires `x11-display`**, which only `xorg` on the same machine provides.
|
|
- **Packages:** `i3-wm`, and `dex`, which the configuration has always run for XDG autostart. `dex`
|
|
was missing on the desktop, so its autostart entries never started there.
|
|
- **Environment** (ADR 0203): `XDG_CURRENT_DESKTOP=i3` and `XDG_SESSION_DESKTOP=i3`. They reach
|
|
shells, the session (through `xorg`'s block) and the user manager (`environment.d`). The portal
|
|
keys off the first.
|
|
- **Session code** (ADR 0208 §4): `exec i3 --shmlog-size=26214400` in the `xinitrc` slot `last`.
|
|
That is the end of `xorg`'s block in `~/.xinitrc`.
|
|
|
|
## What it owns
|
|
|
|
| path | class (ADR 0182) | what |
|
|
|---|---|---|
|
|
| `~/.config/i3/` | owned directory | its contents other than `config` are found and kept |
|
|
| `~/.config/i3/config.d/` | owned directory | other modules' drop-ins, and yours |
|
|
| `~/.config/i3/config` | owned (the found file kept once) | the main configuration, from [`config/config`](config/config) |
|
|
| `/etc/lemurs/wms/i3` | owned | the login manager's session entry: `exec /bin/sh "$HOME/.xinitrc"` |
|
|
|
|
**Drop-ins.** Another module adds to i3 with its own `~/.config/i3/config.d/<NN>-<module>.conf`. The
|
|
`include` is the last line of the main file, so every variable set there (`$mod`, `$ws1`…`$ws10`) is
|
|
in scope. Files are read in name order. A file of yours there is yours.
|
|
|
|
## The reload watcher
|
|
|
|
The bundle runs the watcher for as long as the runtime runs it (ADR 0198). It replaces the
|
|
predecessor's `i3-reload-watcher` script and its user unit, so this needs **no user-scoped unit**:
|
|
|
|
- every 2 s it looks at `config` and `config.d/*.conf` (size and time);
|
|
- at its start, if the files differ from what the running i3 loaded, that counts as a change;
|
|
- after a change, once the files have been still for one more interval, it checks the result with
|
|
`i3 -C`;
|
|
- it **reloads only a configuration without errors**. Otherwise it keeps the running one and records
|
|
the errors.
|
|
|
|
What it has done is in the answers of `reload` and `i3_config_check`. It polls rather than using
|
|
inotify, so a file replaced by rename and a directory created later are seen without a fresh watch.
|
|
It reloads i3 only. Re-running the bar, the compositor and the notifier is each of those modules'
|
|
own business.
|
|
|
|
## Tools
|
|
|
|
| tool | | what |
|
|
|---|---|---|
|
|
| `node-display-session.reload` | a | checks, then reloads, keeping windows. Refused with the errors when the check fails; `force` reloads anyway |
|
|
| `node-display-session.workspaces` | r | number, name, output, visible, focused, urgent |
|
|
| `node-display-session.windows` | r | container id, X id, class, instance, role, title, workspace, output, focused, urgent, floating, fullscreen, scratchpad, marks; narrowed to one workspace |
|
|
| `i3_focus` | d | a window by criteria, or a workspace |
|
|
| `i3_move` | d | windows to a workspace or output; a workspace to an output |
|
|
| `i3_layout_save` | d | a workspace's arrangement as a named layout of placeholders (class, instance, role), in `~/.local/state/mesh/i3/layouts/` |
|
|
| `i3_layout_restore` | d | lays a saved layout back; `name: list` lists them |
|
|
| `i3_exec` | d | starts a program as i3's child, optionally on a workspace |
|
|
| `i3_kill` | d | closes the matching windows, or the focused one when asked; with no arguments it closes nothing |
|
|
| `i3_bindings` | r | every binding by mode, with its command and file, from what i3 loaded |
|
|
| `i3_config_check` | r | `i3 -C` on the files on disk (errors with file and line), the files i3 loaded, the watcher's record |
|
|
| `i3_marks` | r | each mark and its window |
|
|
| `i3_scratchpad` | r/d | list, show or move into the scratchpad |
|
|
|
|
The tools speak i3's IPC on its socket in the account's runtime directory, and need no display. Every
|
|
value a caller gives is quoted before it reaches an i3 command. With no i3 running, the answer is
|
|
`{"error": "no-graphical-session", …}`. The bundle's binary is `i3-tools`, so nothing that looks for
|
|
`i3` by name finds it.
|
|
|
|
## What it improves over today's configuration
|
|
|
|
Both workstations ran the same configuration. These changes are against it:
|
|
|
|
- **dead:** `exec lxpolkit` (installed on neither machine), the unused `$refresh_i3status`, and the
|
|
predecessor's `rice_set` comment;
|
|
- **duplicates:**
|
|
- `exec xdg-desktop-portal` (it is D-Bus activated);
|
|
- `exec xrdb -merge ~/.Xresources` (`xorg`'s session start merges it);
|
|
- the `picom`, `nm-applet`, `blueman-applet` and `nextcloud` execs. Each also has an XDG autostart
|
|
entry that `dex` starts, and both machines carry those entries;
|
|
- **font:** `JetBrainsMono Nerd Font 11` for titles, replacing Hack (research 026/04);
|
|
- **terminal:** `$mod+Return` runs `i3-sensible-terminal`, which starts whatever `$TERMINAL` names
|
|
(the terminal module's contribution), instead of naming xterm;
|
|
- **reloads:** the watcher never reloads into a broken configuration.
|
|
|
|
**Moved to their own modules' drop-ins** (the companion modules of research 026). These leave this
|
|
file because each module carries them now:
|
|
|
|
| lines | now in |
|
|
|---|---|
|
|
| the launcher bindings (`$mod+d`, `$mod+t`, `$mod+Shift+t`) and the power menu (`$mod+Escape`) | `rofi`'s `50-rofi.conf`, which also adds `$mod+Shift+w` |
|
|
| the greenclip daemon and its menu (`$mod+period`) | `clipmenu`'s `50-clipmenu.conf` and its session line |
|
|
| the wallpaper key (`$mod+Shift+b`) | `feh`'s `50-feh.conf` |
|
|
| both bars | `i3status-rust`'s `60-i3status-rust.conf` |
|
|
| the keyring prompt (`unlock-keyring.sh`) | gone: `gnome-keyring` unlocks the keyring through PAM at login |
|
|
|
|
The theme picker (`$mod+Shift+d`) goes too. It was the predecessor's tool for its theme variables,
|
|
and settings take its place once issue 168 closes. `$mod+Delete` (`loginctl lock-session`) stays here,
|
|
because `screen-lock` relies on it. The test `TestTheMainFileAndEveryModulesDropInLoadTogether`
|
|
loads this file with every catalogue module's drop-in through `i3 -C`, so no two of them bind one
|
|
key.
|
|
|
|
**Kept until their owners exist.** A marked section holds the peripherals' tray applet and the
|
|
operator's own scripts: volume, games volume, the sessions launcher and the screenshot binding. Each
|
|
leaves when the module that owns it is written.
|
|
|
|
## What it leaves found
|
|
|
|
`~/.config/i3/scripts/`, `~/.config/i3/unlock-keyring.sh`, every file in `config.d/`, the
|
|
`/usr/share/xsessions` entries (the package's, no longer offered by `lemurs`), and i3's restart
|
|
state and logs.
|
|
|
|
## The one-off migration (ADR 0182)
|
|
|
|
1. **Before the push that assigns `i3`:** do `xorg`'s migration (its README). From that push on, your
|
|
lines below `xorg`'s block in `~/.xinitrc` no longer run.
|
|
2. **Disable the predecessor's watcher:** `systemctl --user disable --now i3-reload-watcher.service`.
|
|
Then remove `~/.config/systemd/user/i3-reload-watcher.service` and `~/scripts/i3-reload-watcher`.
|
|
One thing reloads i3 now. Kept running, it would also `i3-msg restart` on every change, without
|
|
checking first. **Keep `i3-bar-watchdog.service` as it is**: the bar is `i3status-rust`'s, and that
|
|
module decides.
|
|
3. **Delete `/etc/lemurs/wms/i3wm`** (see `lemurs`).
|
|
4. **`config.d/` fragments:**
|
|
- `10-asus.conf` and `20-g14.conf` (the laptop) belong to that machine model's hardware module.
|
|
Keep them until it exists. The desktop no longer carries them.
|
|
- `50-slack.conf` (both) is yours, or a future `slack` module's. Keep it.
|
|
- `99-tmp-wine-focus.conf` (the desktop), a test of a predecessor change by its own comment:
|
|
**delete** it, or keep it as yours.
|
|
5. **The main file's predecessor copy** is kept once by the host and written over. Nothing to do.
|
|
|
|
## What changes when it is assigned
|
|
|
|
| | g14 | shanks |
|
|
|---|---|---|
|
|
| packages | none (`i3-wm` and `dex` present) | `dex` installed |
|
|
| `~/.config/i3/config` | written: the improved configuration | the same |
|
|
| running i3 | **the watcher reloads it once the file changes**. i3 keeps every window, and the title font becomes JetBrains Mono. **Assigned without `rofi`, `clipmenu`, `feh` and `i3status-rust`, the reload takes away the bars and those keys** until they are assigned too, so assign them in the same push. The dropped duplicate execs end nothing that runs | the same |
|
|
| `/etc/lemurs/wms/i3` | new: offered as `i3` at the next boot | the same |
|
|
| `~/.xinitrc` | `xorg`'s block now ends in `exec i3`: the lines below it stop running at the next login | the same |
|
|
| `~/.config/environment.d/50-mesh.conf`, `environment.sh` | gain `XDG_CURRENT_DESKTOP=i3`, `XDG_SESSION_DESKTOP=i3` | the same. Its user manager lacked them, and gets them at the next login |
|
|
| next login | XDG autostart as before | **XDG autostart runs for the first time**: Slack, JetBrains Toolbox, Nextcloud, the FortiClient tray, the print applet, the geoclue demo agent and snap's user daemon start from their entries |
|
|
|
|
**One reload on assignment is the one live change.** To avoid it, assign during a session you are
|
|
about to end, or accept it: a reload keeps every window and workspace.
|
|
|
|
## Blockers
|
|
|
|
- **`fonts` first** (to-be 42 orders it first). Without `ttf-jetbrains-mono-nerd`, i3's titles and
|
|
`i3status-rust`'s bars fall back to pango's default face. On 2026-10-04 the laptop had the package, and the desktop
|
|
only a hand-copied file of the face.
|
|
- **None for the watcher.** The runtime restarts with every push that changes it, after the push has
|
|
written the files. So at its start the watcher compares the files on disk with what the running i3
|
|
loaded (`GET_CONFIG`), and reloads when they differ. The push that assigns `i3`, or changes its
|
|
configuration, is therefore reloaded although it also restarted the watcher.
|