Multiple configurations for different monitor setups in i3wm
General

I use i3 window manager (i3wm, https://i3wm.org/) on my ThinkPad X2xx series Ubuntu Linux work laptop. At work I have a docking bay for the laptop and an external screen connected to it, at home I have just a HDMI cable that I plug in. I really like how i3 makes everything quick and easy, it just takes effort to learn how to set things up, like most Linux things.
The problem
In i3, to set up workspaces on specific monitors, you have to have `workspace X output OUTPUT` in your i3 configuration file (usually at ~/.config/i3/config, but changes with version), where X is workspace number 1 through 10, and OUTPUT is the name of the display output. This means that the workspace output is defined in the i3 configuration file, when i3 is loaded! Put in another way, the variable OUTPUT_E as you see in Figure 1 above, is hard coded in the i3 config file at start. You might want to define this variable different depending on your situation. You can use some suggested solutions to this, see e.g. answers to these questions:
- https://askubuntu.com/questions/912431/i3-for-dual-monitor-on-laptop
- https://faq.i3wm.org/question/2332/flexible-monitor-setup.1.html
Both of these solutions are less than ideal, as they don’t change the i3 configuration file to assign workspaces.
To get a list of your outputs, you can run
xrandr | grep connect | cut -d’ ‘ -f1
in a terminal, assuming you have grep, coreutils, xrandr.
For me this list is
eDP1, DP1, DP2, HDMI1, HDMI2, VIRTUAL1.
The solution
I have set up a rather bulky, but working solution which is a script that checks which external monitor is connects, and if any, open the i3 configuration file and change the definition of the variable OUTPUT_E. This is less then ideal, but OK. This means that when I start my computer, or if I move it and plug in an external monitor, I first run my sensing script which finds any connected extra monitor and redefines the OUTPUT_E to that (or my internal one if none is connected), and then I can choose my self to reload i3 ($MOD+SHIFT+R). I also have a separate i3 mode to configure the monitor layout, see Figure 2.
Figure 2: My monitor setup/mode section of i3 configuration file. A bit messy at the moment, but does what I want.
The sensing and changing script
So first I have a (Python 3) script to do the sensing and editing of the configuration file:
#!/usr/bin/python3 import subprocess import fileinput import sys from shutil import copy2 import os cwd = os.getcwd() # Path to the i3 config file CONFIG_FILE = 'config' INTERNAL_DISPLAY = 'eDP1' # FIRST get the connected displays # alternative 1 outputs = subprocess.Popen('xrandr | grep connected | grep -v disconnected | cut -d' ' -f1',shell=True, stdout=subprocess.PIPE).communicate()[0].split() # "eDP1" is the internal monitor outputs = [i.decode('utf-8') for i in outputs if INTERNAL_DISPLAY not in i.decode('utf-8')] #print(outputs) def change_external(line): # function that changes one thing in a file # returns each line and returns the change when # a match is passed to it. if line.startswith('set $OUTPUT_E'): if not outputs: # if outputs is empty, only internal display is there return '{0} {1}n'.format(' '.join(line.split()[:-1]), INTERNAL_DISPLAY) elif outputs: return '{0} {1}n'.format(' '.join(line.split()[:-1]),outputs[0]) else: # if it doesn't contain it return line # copy the file copy2(os.path.join(cwd,CONFIG_FILE), os.path.join(cwd,'{0}.bak'.format(CONFIG_FILE)) ) # open inplace for editing. Grabs the stout and writes for line in fileinput.input(CONFIG_FILE, inplace=True): sys.stdout.write(change_external(line))
This script just goes through the i3wm config file line by line until it finds on that starts with set $OUTPUT_E and writes the external monitor connected, or the default internal one if none is found. This of course assumes you have a similar section in your configuration file as shown in Figure 1 above.
File locations
As you can see from the second half, this script assumes that it is run IN THE FOLDER where the i3 configuration file (config) is. This means that this script can be anywhere on your machine, and then in your /usr/bin you have a runable script (let’s call it monitor_setup) with the following contents:
#!/bin/bash (cd $HOME/.config/i3 && python /PATH/TO/SCRIPT.py)
Where you of course change /PATH/TO/SCRIPT.py to whatever is applicable for you. This only works for the user that has access to the script. So it might be better to just have the script in the /usr/bin folder directly and do some Python scripting to run it from the $HOME/.config/i3 directly there. So, with this I just hit $MOD+d, type in monitor_setup and hit Enter, then $MOD+SHIFT+r to get my workspace binding the way I want, wherever I am. I think it should be possible to do this in bash with sed, but Python is just quicker for me.
Great post. I was checking continuously this blog and I am impressed! Very useful information particularly the last part ? I care for such info much. I was looking for this particular info for a long time. Thank you and best of luck.