Asger Weirsøe 90cb90349e Obfuscate the maintainer address, and allow PKGBUILD-only releases
The Maintainer line uses the usual AUR form (asger at weircon dot dk)
instead of a raw address that spam crawlers harvest. The AUR workflow
can now also be started by hand with a version and pkgrel, so a fix to
the PKGBUILD alone can be published without a new upstream release.

Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG
2026-09-30 21:23:34 +02:00
2020-09-05 11:59:07 +02:00
2020-09-04 20:30:20 +02:00

pi3-smart-workspace

A small CLI helper for the i3 window manager. 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:

pip install pi3-smart-workspace

For local development, clone the repo and install in editable mode with the dev extras:

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.

Instead of writing the config by hand, run:

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):

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
--sync off Only write configs for outputs that don't have one yet
--force off Regenerate every file from scratch (renumbers workspaces)
--prune off Delete generated configs for outputs that are no longer active

Plugging in a new monitor

Run:

pi3-smart-workspace init --sync

--sync writes a workspaces.conf only for the outputs that don't have one yet, and gives the newcomer the lowest block of global workspace numbers that isn't already taken. The monitors you already use keep the numbers they have, so your muscle memory survives — which a plain --force regeneration would not guarantee, since that allocates numbers by i3's current output order and can shuffle everything.

Configs for monitors that are merely switched off are left in place and their numbers stay reserved. Add --prune to delete them and free those numbers for a new screen:

pi3-smart-workspace init --sync --prune

Files that don't carry the # Auto-generated by pi3-smart-workspace init header are never overwritten or deleted, not even with --force — so anything you hand-tune in that directory is safe.

Example config (manual)

# 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

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) and Tony Crisci for the easy-to-use i3 Python library (i3ipc-python).

S
Description
Awsome-like controls for i3-vm
Readme Apache-2.0
101 KiB
Languages
Python 98.3%
Shell 1.7%