i3: Swap Workspaces on Dual Monitors

I’ve hacked an i3 script which realized one of my long time desires – swap workspaces on dual monitors. For example, I have Firefox on my laptop monitor and Emacs on the external monitor. After pressing some keyboard shortcut, Emacs is placed on the laptop monitor and Firefox is moved to the external monitor.

1 Installation and Configuration

  • Make sure you have i3 and python2.7 installed
  • Learn the basics of i3: http://i3wm.org/docs/userguide.html
  • Install i3-py: sudo pip install i3-py
  • Download my script: swap-workspace.py
  • Give executable permission to swap-workspace.py : chmod a+x swap-workspace
  • Add the following lines to the configuration file of i3 (home/yourname.i3/config):
    # swap workspaces
    bindsym $mod+Shift+s exec /path/to/i3-scripts/swap-workspace.py
  • Reload i3: Mod+Shift+r (You can configure what key you want to use as Mod,I use the Windows key)
  • Press Mod+Shift+s to swap the workspaces


Reading Chen Bin’s answer to a Zhihu question makes me try i3 – a famous windows manager on Linux. Roughly going through the tutorial on the official website (basically the first section, aka, Default keybindings ) enables me to use i3, nice and easy.

After using it for a day, the idea of implementing “swapping workspaces on two monitors” suddenly come to my mind (it’s actually an old idea, but I didn’t know how to do it). Googling leads me to an article on the i3 website: http://i3wm.org/docs/user-contributed/swapping-workspaces.html

Following the article, put the following code in swap-workspace.py:


import i3
outputs = i3.get_outputs()

# set current workspace to output 0

# ..and move it to the other output.
# outputs wrap, so the right of the right is left ;)
i3.command('move', 'workspace to output right')

# rinse and repeat
i3.command('move', 'workspace to output right')

But it doesn’t work as expected and returns the following error message:

Traceback (most recent call last):
  File "swap-workspace.py", line 14, in <module>
  File "/usr/local/lib/python2.7/dist-packages/i3.py", line 404, in function
    msg_full = ' '.join([message] + list(args)  + list(args2))
TypeError: sequence item 1: expected string, NoneType found

Then I read the python script carefully. With some experiments in python‘s REPL, I find there are 4 (rather than 2) outputs on my computer:

>>> import i3
>>> outputs = i3.get_outputs()
>>> outputs
[{u'active': True, u'current_workspace': u'4', u'name': u'LVDS1', u'rect': {u'y': 0, u'x': 0, u'height': 768, u'width': 1360}, u'primary': True}, {u'active': False, u'current_workspace': None, u'name': u'VGA1', u'rect': {u'y': 0, u'x': 0, u'height': 0, u'width': 0}, u'primary': False}, {u'active': True, u'current_workspace': u'2', u'name': u'HDMI1', u'rect': {u'y': 0, u'x': 1360, u'height': 1080, u'width': 1920}, u'primary': False}, {u'active': False, u'current_workspace': None, u'name': u'DP1', u'rect': {u'y': 0, u'x': 0, u'height': 0, u'width': 0}, u'primary': False}]
>>> len(outputs)

According to this information, the script is modified as below:

for output in i3.get_outputs():
    if output['active']:
        i3.command('move', 'workspace to output right')

It works very well!

But this would raise some exceptions if there were three or more monitors, so I need to change the script so it only handles dual monitors:

# Swap workspaces on two monitors
import i3

# collect active outputs
to_be_swapped = [output for output in i3.get_outputs() if output['active']]
# only swap when there are two active outputs
if len(to_be_swapped) == 2:
    for output in to_be_swapped:
        i3.command('move', 'workspace to output right')

I have the feeling that I’m going to write more i3 scripts, so I created a Github repository: https://github.com/RenWenshan/i3-scripts