Archlinux i3-wm on a docking station
I’m using Archlinux with i3 tiling window manager. On the hardware side, this laptop sometimes sits on a docking station with external monitor attached to it. Periodically, I take it away from my desk to work in a different environment. Here are several tips’n’tricks I found useful.
Monitor hotplugging
First thing that is really convenient is to automate monitor setup. It is either one laptop screen or a combination of laptop and external monitor.
There are lots of recipes on the internet that employ udev
rules and custom shell scripts. I won’t suggest any novel ideas andjust share my solution with description and some problems workarounds.
First, here is udev
rule (place it into `/etc/udev/rules.d/*.conf):
KERNEL=="card0", ACTION=="change", SUBSYSTEM=="drm", ENV{DISPLAY}=":0", ENV{XAUTHORITY}="/home/USERNAME/.Xauthority", RUN+="/home/USERNAME/bin/hotplug
_monitor.sh"
Some things to note:
- Replace USERNAME with actual username
- Use
udevadm monitor
to monitor events and tweakKERNEL
variable as needed (could becard1
, for example) XAuthority
file location could different, check your distro- Call
udevadm control --reload-rules
after any changes to the file
Adding this rule will call specified script on every plug/unplug event. Here is my sample script:
#!/usr/bin/bash
export DISPLAY=:0
export XAUTHORITY=/home/USERNAME/.Xauthority
set -e
MONITOR='DP-3'
function wait_for_monitor {
xrandr | grep $MONITOR | grep '\bconnected'
while [ $? -ne 0 ]; do
logger -t "waiting for 100ms"
sleep 0.1
xrandr | grep $MONITOR | grep '\bconnected'
done
}
EXTERNAL_MONITOR_STATUS=$(cat /sys/class/drm/card0-$MONITOR/status )
if [ $EXTERNAL_MONITOR_STATUS == "connected" ]; then
wait_for_monitor
xrandr --output $MONITOR --auto --primary --output LVDS-1 --auto --left-of $MONITOR
/home/USERNAME/bin/i3plug.py restore
else
/home/USERNAME/bin/i3plug.py save
xrandr --output $MONITOR --off
fi
feh --bg-scale /home/USERNAME/wallpaper.jpg
Pretty simple: it checks specified monitor status and applies either configuration depending on its connectivity. Important trick is wait_for_monitor
function. I found that X server (or xrandr
) sometimes significantly lags behind udev/kernel event. This function ensures xrandr
actually sees externel monitor connected.
Another cool thing here is i3plug.py
. Initially I found this script somewhere on the web and slightly modified it to my needs. It saves current i3 layout and can restore it some time later. Obviously, it saves the layout on monitor unplug (undocking) and restores when laptop again finds its place on the docking station.
#!/usr/bin/env python
import os
import i3
import sys
import pickle
PATH = "/home/USERNAME/.i3/workspace_mapping"
def showHelp():
print(sys.argv[0] + " [save|restore]")
if __name__ == '__main__':
if len(sys.argv) < 2:
showHelp()
sys.exit(1)
if sys.argv[1] == 'save':
pickle.dump(i3.get_workspaces(), open(PATH, "wb"))
elif sys.argv[1] == 'restore':
try:
workspace_mapping = pickle.load(open(PATH, "rb"))
except Exception:
print("Can't find existing mappings...")
sys.exit(1)
for workspace in workspace_mapping:
i3.msg('command', 'workspace %s' % workspace['name'])
i3.msg('command', 'move workspace to output %s' % workspace['output'])
for workspace in filter(lambda w: w['visible'], workspace_mapping):
i3.msg('command', 'workspace %s' % workspace['name'])
else:
showHelp()
sys.exit(1)
Wired and wireless internet switch
Another useful addition is to use wired connection (with RJ45 cable attached to dock station) when using dock and WiFi without it. This is achieved with a help of netctl
failover profile. Archwiki article perfectly explains how to do that.
In short, install ifenslave
package and enable bonding
:
$ cat /etc/modules-load.d/bonding.conf
bonding
$ cat /etc/modprobe.d/bonding.conf
options bonding mode=active-backup miimon=100 primary=eth0 max_bonds=0
Create failover
netctl profile (cat /etc/netctl/failover
), tune wired and wireless interfaces names:
Description='A wired connection with failover to wireless'
Interface='bond0'
Connection=bond
BindsToInterfaces=('enp0s25' 'wlp3s0')
IP='dhcp'
Create wpa_supplicant
service:
$ cat /etc/wpa_supplicant/wpa_supplicant-wlan0.conf
ctrl_interface=/run/wpa_supplicant
update_config=1
network={
ssid="SSID"
psk=PSK
}
Finally, enable netctl
and wpa_supplicant@
template service. That’s it.