Skip to content

Instantly share code, notes, and snippets.

@fbrinker
Last active March 6, 2024 11:09
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save fbrinker/df9cfbc84511d807f45041737ff3ea02 to your computer and use it in GitHub Desktop.
Save fbrinker/df9cfbc84511d807f45041737ff3ea02 to your computer and use it in GitHub Desktop.
Swap i3 displays / workspaces between displays
#!/usr/bin/env bash
# requires jq
DISPLAY_CONFIG=($(i3-msg -t get_outputs | jq -r '.[]|"\(.name):\(.current_workspace)"'))
for ROW in "${DISPLAY_CONFIG[@]}"
do
IFS=':'
read -ra CONFIG <<< "${ROW}"
if [ "${CONFIG[0]}" != "null" ] && [ "${CONFIG[1]}" != "null" ]; then
echo "moving ${CONFIG[1]} right..."
i3-msg -- workspace --no-auto-back-and-forth "${CONFIG[1]}"
i3-msg -- move workspace to output right
fi
done
@mark2185
Copy link

i3 v4.20.1 throws an error for this:

i3-msg -- output primary
ERROR: Your command: output primary
ERROR:               ^^^^^^^^^^^^^^
ERROR: Expected one of these tokens: <end>, '[', 'move', 'exec', 'exit', 'restart', 'reload', 'shmlog', 'debuglog', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'sticky', 'split', 'floating', 'mark', 'unmark', 'resize', 'rename', 'nop', 'scratchpad', 'swap', 'title_format', 'title_window_icon', 'mode', 'bar', 'gaps'
[{"success":false,"parse_error":true,"error":"Expected one of these tokens: <end>, '[', 'move', 'exec', 'exit', 'restart', 'reload', 'shmlog', 'debuglog', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'sticky', 'split', 'floating', 'mark', 'unmark', 'resize', 'rename', 'nop', 'scratchpad', 'swap', 'title_format', 'title_window_icon', 'mode', 'bar', 'gaps'","input":"output primary","errorposition":"^^^^^^^^^^^^^^"}]

And I've found it's not needed at all, the i3-msg -- workspace "${ACTIVE_WS}" will switch to the output anyway.

But it also needs --no-auto-back-and-forth in order to work as expected.

@olorton
Copy link

olorton commented Mar 6, 2024

I have taken a stab at re-writing this based on all the examples above. The behaviour I expect is:

  • Works with two displays, not more
  • Swap the currently active workspaces on each display
  • Maintain focus on the display that had focus before the swap
#!/usr/bin/env bash

if ! command -v jq &>/dev/null; then
    echo "jq could not be found"
    exit 1
fi

# Swaps workspaces between two displays, focus stays on the current active display

DISPLAY_CONFIG="$(i3-msg -t get_outputs | jq -r '.[]|select(.active == true) |"\(.current_workspace)"')"
ACTIVE_DISPLAY="$(i3-msg -t get_workspaces | jq -r '.[]|select(.focused == true) | "\(.output)"')"
IFS=$'\n'
for ROW in ${DISPLAY_CONFIG}; do
    i3-msg -- workspace --no-auto-back-and-forth "$ROW"
    i3-msg -- move workspace to output next
done
sleep 0.15
i3-msg -- focus output "${ACTIVE_DISPLAY}"

Notable changes I've made:

  • The bash globbing was causing all sorts of problems with workspace names that had spaces in them. So I've removed that in favour of string splitting on new lines.
  • Fixed the move workspace command to use the next token which works with horizontally and vertically arranged monitors.
  • I do not like having a sleep in there, but the final msg is useless without it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment