xrandrw
Zero-config, self-healing X11 display-layout daemon for dwm/i3 and other bare-X11 window managers. It watches for monitor hotplug/unplug, remembers each display by its EDID identity, and auto-places externals relative to a primary using a persistent attach-order policy — so your monitors end up where you left them, every time.
Pure Python standard library (optional systemd-python for journald logging).
Install
End-user (isolated, lands the xrandrw console-script at ~/.local/bin):
pipx install .
Development (editable):
pip install -e .
Optional journald logging extra:
pip install ".[journald]"
Usage
xrandrw exposes six modes:
| Command | Description |
|---|---|
xrandrw --apply |
Apply the layout once (default when no flag is given). |
xrandrw --watch |
Poll topology and re-apply on change. |
xrandrw --daemon |
Watch for display changes via event-driven RandR notifications and re-apply (systemd entry point). |
xrandrw --print |
Print xrandr --query output and exit. |
xrandrw --set-pref OUTPUT_OR_ID SIDE |
Set a display's preferred side: right-of, left-of, above, below. |
xrandrw --list-state |
Dump the placement state JSON. |
Configuration
Config resolves from (lowest to highest precedence): built-in defaults →
/etc/xdg/xrandrw.conf → ~/.config/xrandrw.conf → process environment. Each
key can be set in a config file (KEY=value lines) or via an environment
variable of the same name.
| Key | Purpose |
|---|---|
USE_XWALLPAPER |
0 = feh/fehbg, 1 = xwallpaper. |
WALL |
Wallpaper image path. |
HIDPI_WIDTH |
Treat internal panel as HiDPI when preferred-mode width ≥ this. |
POLL_INTERVAL |
Watch-loop poll interval in seconds. |
LOG_LEVEL |
none, err, info, notice, debug. |
LOG_FILE |
Optional JSON-lines log file (unset = journald/stderr). |
LOCKFILE |
Apply-lock path. |
PREF_DEFAULT_SIDE |
Default side for new/unknown monitors. |
EXCESS_WINDOW_SEC / EXCESS_THRESHOLD |
Churn-backoff window and threshold. |
See xrandrw.conf.sample for an annotated template —
copy it to ~/.config/xrandrw.conf and edit.
systemd user service
The repo ships a --user unit that runs xrandrw --daemon with Restart=always
and sd_notify integration. Install and enable it via the Makefile:
make install # pipx install . + copy conf sample + unit
make enable # systemctl --user daemon-reload && enable --now
make install uses pipx install --force ., which places the console-script at
~/.local/bin/xrandrw — exactly the ExecStart path the unit expects. Run
systemctl --user daemon-reload after any reinstall.
License
MIT — see LICENSE.
Release files for xrandrw 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| xrandrw-0.1.1.tar.gz | 37.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| xrandrw-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 63.2 kB
Release files / xrandrw-0.1.1.tar.gz
| Download URL | xrandrw-0.1.1.tar.gz |
|---|---|
| Size | 37.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e4383904df26129c5377b837e45753f262b4262a3e8510648655adc6c1614334
|
|
BLAKE2b-256 checksum How to use checksums |
f0a5518ea00ef24354599a9cd5cb542fa3d5f5bd3307d66820aed9d9cabed5ed
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / xrandrw-0.1.1-py3-none-any.whl
| Download URL | xrandrw-0.1.1-py3-none-any.whl |
|---|---|
| Size | 26.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ea2b11f75d13fbcc8ccea1cf2c6131e4017903c68b4efbae3485f2c54eb4e8e2
|
|
BLAKE2b-256 checksum How to use checksums |
f2a230f7eec5e173ef18db1ec1e22211ba3063e3261d5798f3e7978ca2c82253
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|