195 lines
6.0 KiB
Markdown
195 lines
6.0 KiB
Markdown
# pi3-smart-workspace
|
|
|
|
A small CLI helper for the [i3 window manager](https://i3wm.org/). Bound to
|
|
`$mod+<n>` keys, it switches (or moves a container to) the **N-th workspace
|
|
declared for the output your mouse cursor is currently on**, rather than
|
|
i3's default behaviour of jumping to a globally-numbered workspace.
|
|
|
|
This makes multi-monitor i3 setups feel a lot more seamless: pressing
|
|
`$mod+1` always lands you on the first workspace of whichever screen you're
|
|
pointing at.
|
|
|
|
## Usage
|
|
|
|
```
|
|
usage: pi3-smart-workspace [-h] [-d] -i INDEX [-s] [-k]
|
|
|
|
Switch (or shift) to the N-th workspace on the output your cursor is on.
|
|
|
|
options:
|
|
-h, --help show this help message and exit
|
|
-d, --debug Print parsed state and re-raise exceptions.
|
|
|
|
Required:
|
|
-i INDEX, --index INDEX
|
|
The 1-based index into the workspaces declared for
|
|
the cursor's output.
|
|
|
|
Shift:
|
|
manipulate the active window
|
|
|
|
-s, --shift Move the focused container to the indexed workspace.
|
|
-k, --keep-with-it With --shift, also follow the container to the new
|
|
workspace.
|
|
```
|
|
|
|
## Installation
|
|
|
|
Install from PyPI:
|
|
|
|
```sh
|
|
pip install pi3-smart-workspace
|
|
```
|
|
|
|
For local development, clone the repo and install in editable mode with the
|
|
dev extras:
|
|
|
|
```sh
|
|
pip install -e ".[dev]"
|
|
pytest
|
|
```
|
|
|
|
Requires Python 3.14+ and a running i3 session.
|
|
|
|
## i3 configuration
|
|
|
|
`pi3-smart-workspace` parses your live i3 config and looks for three
|
|
specific patterns:
|
|
|
|
1. `set $<var> <output-name>` — output aliases
|
|
2. `set $<var> <digit…>` — workspace-name aliases (quoted or unquoted)
|
|
3. `workspace $<var> output $<var>` — bindings of workspaces to outputs
|
|
|
|
The order in which `workspace … output …` lines appear in your config
|
|
determines the N-th workspace for that output.
|
|
|
|
### Bootstrap with `init` (recommended)
|
|
|
|
Instead of writing the config by hand, run:
|
|
|
|
```sh
|
|
pi3-smart-workspace init
|
|
```
|
|
|
|
This detects every active monitor via i3 and writes:
|
|
|
|
```
|
|
~/.config/i3/pi3-smart-workspace/
|
|
├── bindings.conf # $mod+N, $mod+Shift+N, $mod+Ctrl+N bindings
|
|
├── eDP-1/
|
|
│ └── workspaces.conf # workspaces 1..8 bound to eDP-1
|
|
├── HDMI-A-0/
|
|
│ └── workspaces.conf # workspaces 9..16 bound to HDMI-A-0
|
|
└── DP-1/
|
|
└── workspaces.conf # workspaces 17..24 bound to DP-1
|
|
```
|
|
|
|
Each output gets a contiguous range of global workspace numbers; the
|
|
local index (the number after the colon, e.g. `9:1`) tells you "first
|
|
workspace on this monitor". Then add the include lines `init` prints to
|
|
your main i3 config and reload (`$mod+Shift+r`):
|
|
|
|
```i3config
|
|
include ~/.config/i3/pi3-smart-workspace/bindings.conf
|
|
include ~/.config/i3/pi3-smart-workspace/*/workspaces.conf
|
|
```
|
|
|
|
i3's `include` directive needs i3 ≥ 4.20.
|
|
|
|
Useful flags:
|
|
|
|
| Flag | Default | Effect |
|
|
| --- | --- | --- |
|
|
| `-n N` / `--count N` | 8 | Workspaces per output |
|
|
| `--config-dir PATH` | `~/.config/i3/pi3-smart-workspace` | Where to write |
|
|
| `--dry-run` | off | Print to stdout, don't touch the filesystem |
|
|
| `--force` | off | Overwrite existing files |
|
|
|
|
Re-run `init --force` after plugging or unplugging a monitor to
|
|
regenerate the per-output folders.
|
|
|
|
### Example config (manual)
|
|
|
|
```i3config
|
|
# Displays
|
|
set $primary eDP
|
|
set $top HDMI-A-0
|
|
set $bottom HDMI2
|
|
|
|
# Workspaces
|
|
set $ws1 1:1
|
|
# ... and so on
|
|
set $ws{n} {n}:{n}
|
|
|
|
set $TopWs1 {n+1}:1
|
|
# ... and so on
|
|
set $TopWs{k} {n+1+k}:{k}
|
|
|
|
set $BottomWs1 {k+1}:1
|
|
# ... and so on
|
|
set $BottomWs{q} {k+1+q}:{q}
|
|
|
|
workspace $ws1 output $primary
|
|
# ... and so on
|
|
workspace $ws{n} output $primary
|
|
|
|
workspace $TopWs1 output $top
|
|
# ... and so on
|
|
workspace $TopWs{k} output $top
|
|
|
|
workspace $BottomWs1 output $bottom
|
|
# ... and so on
|
|
workspace $BottomWs{q} output $bottom
|
|
|
|
# Switch to workspace
|
|
bindsym $mod+1 exec --no-startup-id pi3-smart-workspace -i 1
|
|
bindsym $mod+2 exec --no-startup-id pi3-smart-workspace -i 2
|
|
bindsym $mod+3 exec --no-startup-id pi3-smart-workspace -i 3
|
|
bindsym $mod+4 exec --no-startup-id pi3-smart-workspace -i 4
|
|
bindsym $mod+5 exec --no-startup-id pi3-smart-workspace -i 5
|
|
bindsym $mod+6 exec --no-startup-id pi3-smart-workspace -i 6
|
|
bindsym $mod+7 exec --no-startup-id pi3-smart-workspace -i 7
|
|
bindsym $mod+8 exec --no-startup-id pi3-smart-workspace -i 8
|
|
|
|
# Move focused container to workspace
|
|
bindsym $mod+Shift+1 exec --no-startup-id pi3-smart-workspace -i 1 -s
|
|
bindsym $mod+Shift+2 exec --no-startup-id pi3-smart-workspace -i 2 -s
|
|
bindsym $mod+Shift+3 exec --no-startup-id pi3-smart-workspace -i 3 -s
|
|
bindsym $mod+Shift+4 exec --no-startup-id pi3-smart-workspace -i 4 -s
|
|
bindsym $mod+Shift+5 exec --no-startup-id pi3-smart-workspace -i 5 -s
|
|
bindsym $mod+Shift+6 exec --no-startup-id pi3-smart-workspace -i 6 -s
|
|
bindsym $mod+Shift+7 exec --no-startup-id pi3-smart-workspace -i 7 -s
|
|
bindsym $mod+Shift+8 exec --no-startup-id pi3-smart-workspace -i 8 -s
|
|
|
|
# Move to workspace with focused container
|
|
bindsym $mod+Ctrl+1 exec --no-startup-id pi3-smart-workspace -i 1 -sk
|
|
bindsym $mod+Ctrl+2 exec --no-startup-id pi3-smart-workspace -i 2 -sk
|
|
bindsym $mod+Ctrl+3 exec --no-startup-id pi3-smart-workspace -i 3 -sk
|
|
bindsym $mod+Ctrl+4 exec --no-startup-id pi3-smart-workspace -i 4 -sk
|
|
bindsym $mod+Ctrl+5 exec --no-startup-id pi3-smart-workspace -i 5 -sk
|
|
bindsym $mod+Ctrl+6 exec --no-startup-id pi3-smart-workspace -i 6 -sk
|
|
bindsym $mod+Ctrl+7 exec --no-startup-id pi3-smart-workspace -i 7 -sk
|
|
bindsym $mod+Ctrl+8 exec --no-startup-id pi3-smart-workspace -i 8 -sk
|
|
```
|
|
|
|
## Development
|
|
|
|
```sh
|
|
pip install -e ".[dev]"
|
|
pytest # run the unit tests
|
|
python -m build # build sdist + wheel into dist/
|
|
twine check dist/* # validate the distributions
|
|
```
|
|
|
|
The unit tests cover the two pure functions (`parse_workspaces_per_output`
|
|
and `find_output_at_cursor`) and run without an X display, so they work in
|
|
CI. The i3-dispatch path needs a real multi-monitor session and is only
|
|
exercised manually.
|
|
|
|
## Credits
|
|
|
|
Thanks to Michał Wieluński for the inspiration
|
|
([pi3-switch](https://github.com/landmaj/pi3-switch)) and Tony Crisci for
|
|
the easy-to-use i3 Python library
|
|
([i3ipc-python](https://github.com/acrisci/i3ipc-python)).
|