docs(config): document relative output placement and monitor navigation
- commit
- 93b73a79b9dfcbf26b1ef9d7af20000c0cbe2a1f
- author
- brice · · 2026-10-03 14:09 UTC
- parent
- 0e21ae4
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +34 -4
M docs/config.md +32 -2
| @@ -56,6 +56,8 @@ NeferWL reads `$XDG_CONFIG_HOME/neferwl/config` (or `~/.config/neferwl/config`). | |||
| 56 | 56 | | `output.<name>` | preferred | `WxH`, `WxH@Hz`, `preferred` or `off` | | |
| 57 | 57 | | `output.<name>.scale` | `1` | 1 to 4, e.g. `1.5` or `4/3` | | |
| 58 | 58 | | `output.<name>.primary` | `off` | Gets focus and pointer at startup | | |
| 59 | + | | `output.<name>.right-of` / `.left-of` / `.above` / `.below` | automatic | Connector to place the output against, e.g. `DP-1` | | |
| 60 | + | | `output.<name>.offset` | `0` | Logical px along the shared edge: down for `right-of`/`left-of`, right for `above`/`below`. Negative allowed, −65536 to 65536. Ignored without a relation, with a warning | | |
| 59 | 61 | | `output.<name>.hdr` | `off` | Turns on HDR10 on capable outputs; otherwise falls back to SDR (reason in the log) | | |
| 60 | 62 | | `output.<name>.sdr-brightness` | `203` | How bright SDR desktop content appears in HDR, in nits (80–1000) | | |
| 61 | 63 | | `render.direct-scanout` | `on` | Fullscreen buffers straight to the display | | |
| @@ -65,6 +67,32 @@ NeferWL reads `$XDG_CONFIG_HOME/neferwl/config` (or `~/.config/neferwl/config`). | |||
| 65 | 67 | | `log.level` / `log.debug` | `info` / empty | Log level / debug components or `all` | | |
| 66 | 68 | | `bind.<keys>` | see below | Action for a key combo; `none` removes a default | | |
| 67 | 69 | ||
| 70 | + | ## Output placement | |
| 71 | + | ||
| 72 | + | An output with no relation is placed automatically, left to right after the others, at y=0. An output whose reference is not connected is placed automatically too, and returns to its relation when the reference connects. Relation cycles are ignored with a warning. Positions set by an output-management client (kanshi, wdisplays, wlr-randr) take precedence over relations until the next config reload. The pointer crosses any touching edge. | |
| 73 | + | ||
| 74 | + | Two rows of three, with DP-1 as the base: | |
| 75 | + | ||
| 76 | + | ``` | |
| 77 | + | output.DP-1 = 2560x1440 | |
| 78 | + | output.DP-2 = 1920x1080 | |
| 79 | + | output.DP-2.left-of = DP-1 | |
| 80 | + | output.DP-2.offset = 360 | |
| 81 | + | output.DP-3 = 1920x1080 | |
| 82 | + | output.DP-3.right-of = DP-1 | |
| 83 | + | output.DP-3.offset = 360 | |
| 84 | + | output.HDMI-1 = 2560x1440 | |
| 85 | + | output.HDMI-1.above = DP-1 | |
| 86 | + | output.DP-4 = 1920x1200 | |
| 87 | + | output.DP-4.left-of = HDMI-1 | |
| 88 | + | output.DP-4.offset = 240 | |
| 89 | + | output.DP-5 = 1920x1200 | |
| 90 | + | output.DP-5.right-of = HDMI-1 | |
| 91 | + | output.DP-5.offset = 240 | |
| 92 | + | ``` | |
| 93 | + | ||
| 94 | + | The bottom row is DP-2 | DP-1 | DP-3 and the top row DP-4 | HDMI-1 | DP-5. | |
| 95 | + | ||
| 68 | 96 | ## Workspace size | |
| 69 | 97 | ||
| 70 | 98 | A workspace inherits its monitor size unless `workspace.<name>.size` sets a logical `WxH`. Its viewport is centered, and columns, maximization, floating windows and fullscreen use that real layout size—not scaled overview previews. Oversized dimensions are limited to the monitor; reserved layer zones still reduce usable space. Monitor mode and scale stay unchanged. Omit `size` or use `inherit` to restore the default. Workspace transitions involving a smaller viewport switch without the vertical slide, keeping scrolling columns inside their frame. Fullscreen inside a smaller viewport uses composition rather than output-covering direct scanout, so fullscreen-game VRR/tearing eligibility does not apply. | |
| @@ -147,8 +175,8 @@ HDR requires DRM HDR connector properties, suitable KMS planes, and Vulkan fp16 | |||
| 147 | 175 | | `workspace <name>` | Toggle a named workspace | | |
| 148 | 176 | | `move-column-to-workspace <N>` / `-up/-down` | Move the focused column | | |
| 149 | 177 | | `move-window-to-workspace <N>` / `-up/-down` | Move only the focused window | | |
| 150 | - | | `focus-monitor-left/right` | Focus the neighbor monitor | | |
| 151 | - | | `move-workspace-to-monitor-left/right` | Move the workspace; it gets a new home | | |
| 178 | + | | `focus-monitor-left/right` / `focus-monitor-up/down` | Focus the neighbor monitor in that direction | | |
| 179 | + | | `move-workspace-to-monitor-left/right` / `move-workspace-to-monitor-up/down` | Move the workspace to the neighbor monitor in that direction; it gets a new home | | |
| 152 | 180 | | `move-workspace-up/down` | Swap the current numbered workspace with its neighbor; the view follows it. It never passes the empty workspace at the end | | |
| 153 | 181 | | `scale-up` / `scale-down` | Zoom the whole display; one second after the last press, the scale is saved to `output.<name>.scale` and a notification confirms it (`notify-send`) | | |
| 154 | 182 | | `quit` | Exit NeferWL | | |
| @@ -259,6 +287,8 @@ With `keyboard.cmd = alt`, the `cmd+alt+…` binds have the same keys as `cmd+ | |||
| 259 | 287 | | `cmd+shift+code:10` | `move-column-to-workspace 9` | | |
| 260 | 288 | | `cmd+ctrl+left` | `focus-monitor-left` | | |
| 261 | 289 | | `cmd+ctrl+right` | `focus-monitor-right` | | |
| 290 | + | | `cmd+ctrl+up` / `cmd+ctrl+k` | `focus-monitor-up` | | |
| 291 | + | | `cmd+ctrl+down` / `cmd+ctrl+j` | `focus-monitor-down` | | |
| 262 | 292 | | `cmd+ctrl+h` | `focus-monitor-left` | | |
| 263 | 293 | | `cmd+ctrl+l` | `focus-monitor-right` | | |
| 264 | 294 | | `cmd+ctrl+shift+left` | `move-workspace-to-monitor-left` | | |
| @@ -56,6 +56,8 @@ NeferWL reads `$XDG_CONFIG_HOME/neferwl/config` (or `~/.config/neferwl/config`). | |||||
| 56 | | `output.<name>` | preferred | `WxH`, `WxH@Hz`, `preferred` or `off` | | 56 | | `output.<name>` | preferred | `WxH`, `WxH@Hz`, `preferred` or `off` | | ||
| 57 | | `output.<name>.scale` | `1` | 1 to 4, e.g. `1.5` or `4/3` | | 57 | | `output.<name>.scale` | `1` | 1 to 4, e.g. `1.5` or `4/3` | | ||
| 58 | | `output.<name>.primary` | `off` | Gets focus and pointer at startup | | 58 | | `output.<name>.primary` | `off` | Gets focus and pointer at startup | | ||
| 59 | + | | `output.<name>.right-of` / `.left-of` / `.above` / `.below` | automatic | Connector to place the output against, e.g. `DP-1` | | |||
| 60 | + | | `output.<name>.offset` | `0` | Logical px along the shared edge: down for `right-of`/`left-of`, right for `above`/`below`. Negative allowed, −65536 to 65536. Ignored without a relation, with a warning | | |||
| 59 | | `output.<name>.hdr` | `off` | Turns on HDR10 on capable outputs; otherwise falls back to SDR (reason in the log) | | 61 | | `output.<name>.hdr` | `off` | Turns on HDR10 on capable outputs; otherwise falls back to SDR (reason in the log) | | ||
| 60 | | `output.<name>.sdr-brightness` | `203` | How bright SDR desktop content appears in HDR, in nits (80–1000) | | 62 | | `output.<name>.sdr-brightness` | `203` | How bright SDR desktop content appears in HDR, in nits (80–1000) | | ||
| 61 | | `render.direct-scanout` | `on` | Fullscreen buffers straight to the display | | 63 | | `render.direct-scanout` | `on` | Fullscreen buffers straight to the display | | ||
| @@ -65,6 +67,32 @@ NeferWL reads `$XDG_CONFIG_HOME/neferwl/config` (or `~/.config/neferwl/config`). | |||||
| 65 | | `log.level` / `log.debug` | `info` / empty | Log level / debug components or `all` | | 67 | | `log.level` / `log.debug` | `info` / empty | Log level / debug components or `all` | | ||
| 66 | | `bind.<keys>` | see below | Action for a key combo; `none` removes a default | | 68 | | `bind.<keys>` | see below | Action for a key combo; `none` removes a default | | ||
| 67 | 69 | ||||
| 70 | + | ## Output placement | |||
| 71 | + | ||||
| 72 | + | An output with no relation is placed automatically, left to right after the others, at y=0. An output whose reference is not connected is placed automatically too, and returns to its relation when the reference connects. Relation cycles are ignored with a warning. Positions set by an output-management client (kanshi, wdisplays, wlr-randr) take precedence over relations until the next config reload. The pointer crosses any touching edge. | |||
| 73 | + | ||||
| 74 | + | Two rows of three, with DP-1 as the base: | |||
| 75 | + | ||||
| 76 | + | ``` | |||
| 77 | + | output.DP-1 = 2560x1440 | |||
| 78 | + | output.DP-2 = 1920x1080 | |||
| 79 | + | output.DP-2.left-of = DP-1 | |||
| 80 | + | output.DP-2.offset = 360 | |||
| 81 | + | output.DP-3 = 1920x1080 | |||
| 82 | + | output.DP-3.right-of = DP-1 | |||
| 83 | + | output.DP-3.offset = 360 | |||
| 84 | + | output.HDMI-1 = 2560x1440 | |||
| 85 | + | output.HDMI-1.above = DP-1 | |||
| 86 | + | output.DP-4 = 1920x1200 | |||
| 87 | + | output.DP-4.left-of = HDMI-1 | |||
| 88 | + | output.DP-4.offset = 240 | |||
| 89 | + | output.DP-5 = 1920x1200 | |||
| 90 | + | output.DP-5.right-of = HDMI-1 | |||
| 91 | + | output.DP-5.offset = 240 | |||
| 92 | + | ``` | |||
| 93 | + | ||||
| 94 | + | The bottom row is DP-2 | DP-1 | DP-3 and the top row DP-4 | HDMI-1 | DP-5. | |||
| 95 | + | ||||
| 68 | ## Workspace size | 96 | ## Workspace size | ||
| 69 | 97 | ||||
| 70 | A workspace inherits its monitor size unless `workspace.<name>.size` sets a logical `WxH`. Its viewport is centered, and columns, maximization, floating windows and fullscreen use that real layout size—not scaled overview previews. Oversized dimensions are limited to the monitor; reserved layer zones still reduce usable space. Monitor mode and scale stay unchanged. Omit `size` or use `inherit` to restore the default. Workspace transitions involving a smaller viewport switch without the vertical slide, keeping scrolling columns inside their frame. Fullscreen inside a smaller viewport uses composition rather than output-covering direct scanout, so fullscreen-game VRR/tearing eligibility does not apply. | 98 | A workspace inherits its monitor size unless `workspace.<name>.size` sets a logical `WxH`. Its viewport is centered, and columns, maximization, floating windows and fullscreen use that real layout size—not scaled overview previews. Oversized dimensions are limited to the monitor; reserved layer zones still reduce usable space. Monitor mode and scale stay unchanged. Omit `size` or use `inherit` to restore the default. Workspace transitions involving a smaller viewport switch without the vertical slide, keeping scrolling columns inside their frame. Fullscreen inside a smaller viewport uses composition rather than output-covering direct scanout, so fullscreen-game VRR/tearing eligibility does not apply. | ||
| @@ -147,8 +175,8 @@ HDR requires DRM HDR connector properties, suitable KMS planes, and Vulkan fp16 | |||||
| 147 | | `workspace <name>` | Toggle a named workspace | | 175 | | `workspace <name>` | Toggle a named workspace | | ||
| 148 | | `move-column-to-workspace <N>` / `-up/-down` | Move the focused column | | 176 | | `move-column-to-workspace <N>` / `-up/-down` | Move the focused column | | ||
| 149 | | `move-window-to-workspace <N>` / `-up/-down` | Move only the focused window | | 177 | | `move-window-to-workspace <N>` / `-up/-down` | Move only the focused window | | ||
| 150 | - | | `focus-monitor-left/right` | Focus the neighbor monitor | | 178 | + | | `focus-monitor-left/right` / `focus-monitor-up/down` | Focus the neighbor monitor in that direction | |
| 151 | - | | `move-workspace-to-monitor-left/right` | Move the workspace; it gets a new home | | 179 | + | | `move-workspace-to-monitor-left/right` / `move-workspace-to-monitor-up/down` | Move the workspace to the neighbor monitor in that direction; it gets a new home | |
| 152 | | `move-workspace-up/down` | Swap the current numbered workspace with its neighbor; the view follows it. It never passes the empty workspace at the end | | 180 | | `move-workspace-up/down` | Swap the current numbered workspace with its neighbor; the view follows it. It never passes the empty workspace at the end | | ||
| 153 | | `scale-up` / `scale-down` | Zoom the whole display; one second after the last press, the scale is saved to `output.<name>.scale` and a notification confirms it (`notify-send`) | | 181 | | `scale-up` / `scale-down` | Zoom the whole display; one second after the last press, the scale is saved to `output.<name>.scale` and a notification confirms it (`notify-send`) | | ||
| 154 | | `quit` | Exit NeferWL | | 182 | | `quit` | Exit NeferWL | | ||
| @@ -259,6 +287,8 @@ With `keyboard.cmd = alt`, the `cmd+alt+…` binds have the same keys as `cmd+ | |||||
| 259 | | `cmd+shift+code:10` | `move-column-to-workspace 9` | | 287 | | `cmd+shift+code:10` | `move-column-to-workspace 9` | | ||
| 260 | | `cmd+ctrl+left` | `focus-monitor-left` | | 288 | | `cmd+ctrl+left` | `focus-monitor-left` | | ||
| 261 | | `cmd+ctrl+right` | `focus-monitor-right` | | 289 | | `cmd+ctrl+right` | `focus-monitor-right` | | ||
| 290 | + | | `cmd+ctrl+up` / `cmd+ctrl+k` | `focus-monitor-up` | | |||
| 291 | + | | `cmd+ctrl+down` / `cmd+ctrl+j` | `focus-monitor-down` | | |||
| 262 | | `cmd+ctrl+h` | `focus-monitor-left` | | 292 | | `cmd+ctrl+h` | `focus-monitor-left` | | ||
| 263 | | `cmd+ctrl+l` | `focus-monitor-right` | | 293 | | `cmd+ctrl+l` | `focus-monitor-right` | | ||
| 264 | | `cmd+ctrl+shift+left` | `move-workspace-to-monitor-left` | | 294 | | `cmd+ctrl+shift+left` | `move-workspace-to-monitor-left` | | ||
M internal/core/screens.go +1 -1
| @@ -8,7 +8,7 @@ import ( | |||
| 8 | 8 | ||
| 9 | 9 | // screen is one connected output: its monitor tree, scale, layer surfaces and | |
| 10 | 10 | // place in the global layout (ADR 011). Outputs are ordered by config | |
| 11 | - | // (output.<name>.*), then by connection; order places them. | |
| 11 | + | // (output.<name>.*), then by connection; order places them (placeOutputs). | |
| 12 | 12 | type screen struct { | |
| 13 | 13 | info ports.OutputInfo | |
| 14 | 14 | mon *Monitor | |
| @@ -8,7 +8,7 @@ import ( | |||||
| 8 | 8 | ||||
| 9 | // screen is one connected output: its monitor tree, scale, layer surfaces and | 9 | // screen is one connected output: its monitor tree, scale, layer surfaces and | ||
| 10 | // place in the global layout (ADR 011). Outputs are ordered by config | 10 | // place in the global layout (ADR 011). Outputs are ordered by config | ||
| 11 | - | // (output.<name>.*), then by connection; order places them. | 11 | + | // (output.<name>.*), then by connection; order places them (placeOutputs). |
| 12 | type screen struct { | 12 | type screen struct { | ||
| 13 | info ports.OutputInfo | 13 | info ports.OutputInfo | ||
| 14 | mon *Monitor | 14 | mon *Monitor | ||
M internal/ports/client_commands.go +1 -1
| @@ -62,7 +62,7 @@ func (o OutputPlacement) Contains(x, y float64) bool { | |||
| 62 | 62 | return x >= float64(o.X) && x < float64(o.X+o.Width) && y >= float64(o.Y) && y < float64(o.Y+o.Height) | |
| 63 | 63 | } | |
| 64 | 64 | ||
| 65 | - | // Layout is the global arrangement of outputs, left to right. | |
| 65 | + | // Layout is the global arrangement of outputs in logical pixels. | |
| 66 | 66 | type Layout []OutputPlacement | |
| 67 | 67 | ||
| 68 | 68 | // At returns the output under the logical point. | |
| @@ -62,7 +62,7 @@ func (o OutputPlacement) Contains(x, y float64) bool { | |||||
| 62 | return x >= float64(o.X) && x < float64(o.X+o.Width) && y >= float64(o.Y) && y < float64(o.Y+o.Height) | 62 | return x >= float64(o.X) && x < float64(o.X+o.Width) && y >= float64(o.Y) && y < float64(o.Y+o.Height) | ||
| 63 | } | 63 | } | ||
| 64 | 64 | ||||
| 65 | - | // Layout is the global arrangement of outputs, left to right. | 65 | + | // Layout is the global arrangement of outputs in logical pixels. |
| 66 | type Layout []OutputPlacement | 66 | type Layout []OutputPlacement | ||
| 67 | 67 | ||||
| 68 | // At returns the output under the logical point. | 68 | // At returns the output under the logical point. | ||