# pi3-smart-workspace A small CLI helper for the [i3 window manager](https://i3wm.org/). Bound to `$mod+` 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 $ ` — output aliases 2. `set $ ` — workspace-name aliases (quoted or unquoted) 3. `workspace $ output $` — 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)).