Automatically switch Xfce panel layout when plugging in a monitor

Xfce has great multi-monitor support. Different panels on each monitor, it all works great. I’ve used it with multiple monitors on my desktop for 5+ years. Love it.

But you run into some annoyances with laptops. Half the time I’m using my laptop on its own, and half the time I’m using it with an external monitor. You plug the monitor in for the first time, then you create a new panel for it and drag it over. But then when you unplug the monitor, that second panel doesn’t go away. Instead, it moves over to the main laptop screen and the only way to hide it is to delete it. Then when you plug the external monitor in again, you need to recreate it, readd all the items and so on.

Well, the Xubuntu team has made a tool called Xfce Panel Switch (Arch package: xfpanel-switch).

I’ll quickly walk though how to setup automatic switching.

Step One – Panel Configs

First, setup your panel structure the way you like it when you’re using just your laptop screen. Open Xfce Panel Switch, select Current Configuration, click Export, and save it as laptop.tar.bz2. Then plug your monitor in, setup your panels the way you like, select Current Configuration again, and export as externalmon.tar.bz2.

If you want to test out the panel switching, use the commands:

python3 /usr/share/xfpanel-switch/xfpanel-switch/panelconfig.py load /home/colin/laptop.tar.bz2

python3 /usr/share/xfpanel-switch/xfpanel-switch/panelconfig.py load /home/colin/externalmon.tar.bz2

Step Two – Udev and systemd

We could stop here and create a desktop shortcut we click each time, but where’s the fun is that?

Create a new udev rule to trigger our systemd service: /etc/udev/rules.d/95-monitor-hotplug.rules

ACTION=="change", KERNEL=="card0", SUBSYSTEM=="drm", RUN+="/usr/bin/systemctl start hot_plug.service"

Create our hot_plug service: /etc/systemd/system/hot_plug.service

[Unit]
Description=Monitor hotplug

[Service]
Type=simple
RemainAfterExit=no
User=colin
ExecStart=/usr/bin/bash /usr/local/bin/hotplug_monitor.sh

[Install]
WantedBy=multi-user.target

Replace colin with your own username

Create the script that our service executes: nano /usr/local/bin/hotplug_monitor.sh

#!/bin/bash

# Replace colin with your username
X_USER=colin
export DISPLAY=:0
export XAUTHORITY=/home/$X_USER/.Xauthority
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

function connect()
{
    # Remember to change the path to wherever you are storing your configs
    python3 /usr/share/xfpanel-switch/xfpanel-switch/panelconfig.py load /home/colin/externalmon.tar.bz2
}

function disconnect()
{
    # Remember to change the path to wherever you are storing your configs
    python3 /usr/share/xfpanel-switch/xfpanel-switch/panelconfig.py load /home/colin/laptop.tar.bz2
}

# Replace card0-DP-1 with the card and port you are using
if [ $(cat /sys/class/drm/card0-DP-1/status) == "connected" ] ; then
  connect
elif [ $(cat /sys/class/drm/card0-DP-1/status) == "disconnected" ] ; then
  disconnect
else
  exit
fi

Remember to make it executable chmod +x /usr/local/bin/hotplug_monitor.sh

Important: you may need to replace card0-DP-1 in the above script with the card and port that you are using. I’m using VGA, for HDMI you probably need to check card0-HDMI-A-1 instead.

Also remember to change the paths to the location where you a storing your panel configs. I personally store them at /home/colin/.config/xfce4-panel-switcher/

(Optional) Step Three – Set monitor layout

Now that we have a script which will execute anything we like whenever we plug or unplug our monitor, lets also use it to have xrandr setup our monitor layout.

In the connect function, I have:

xrandr --output eDP1 --left-of DP1 --preferred --output eDP1 --primary

and in the disconnect function:

xrandr --output DP1 --off

This sets up my laptop monitor (eDP1) as my primary, and my VGA monitor (DP1) as a secondary monitor which is positioned to the right of it. You’ll have to change eDP1 and DP1 to reflect what your devices are. Look at the xrandr article on the Arch Wiki for more information.

Final thoughts

This saves me at least a couple minutes every time I plug my laptop in. Most of the time I didn’t even bother setting up panels on the secondary monitor because having to redo every time was getting old.

The obvious downside here is that every time you make any changes to your panel, you need to open the Xfce Panel Switcher app and export the new config to wherever it is you’re keeping them. But how often do you really need to change your panel layout? My desktop one hasn’t changed in years.

I also want to give thanks and credit to Iah on the Arch forums for posting how to detect the udev event for a monitor being plugged in.

Hope this helps!

8 Comments on “Automatically switch Xfce panel layout when plugging in a monitor

  1. This really helped me, thank you. Here’s some fixes to the instructions that I had to do to get it to work on my system:
    1. Make sure to set hot_plug.service executable, can be done with:
    sudo chmod +x /etc/systemd/system/hot_plug.service
    2. The file hotplug_monitor.sh needs some modifications:
    Line function connect() needs to be rewritten to:
    function connect {
    Make sure ‘ {‘ is directly after connect. Also remove ‘()’
    The same applies for disconnect
    The python3 commands looks in the wrong directory for the laptop.tar.bz2, the same applies for the externalmon.tar.bz2 file. Set these to the correct path.
    3. xrandr is really useful, for HDMI it’s HDMI1 instead of DP1
    4. Make sure the script is executable, sudo chmod +x /usr/local/bin/hotplug_monitor.sh

    • Glad I could help!

      Some quick notes:

      1. You shouldn’t need to set the hot_plug.service as executable. If you look in /etc/systemd/system/ none of the other services are set as executable (by default)

      2. Bash functions work just fine with parentheses (see here – http://tldp.org/LDP/abs/html/functions.html)

      I used /tmp/ as an example directory, because I didn’t want to make assumptions about where people want to store their configs. But I added a comment to alert people to that. Personally, I created a new directory at ~/.config/xfce4-panel-switcher/ and I store them there. And you are right that the bash script needs to be executable, I added a comment for that too.

      • Hey, you were right about not making the service executable. As for the parentheses you seem to be right, but I remember running into an error when simply calling it with bash the_script. I looked up on writing Bash functions here – http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-8.html

        Then again I also ran into the issue that one of my systems doesn’t detect a change at all when I disconnect the monitor, which must be a driver issue. Forcing a xrandr –query seemed to detect that I had disconnected my monitor. That in turn required me to turn this whole process in more of polling situation for that machine…

  2. For me, python script fails with the following lines:

    Traceback (most recent call last):
    File “/usr/share/xfpanel-switch/xfpanel-switch/panelconfig.py”, line 155, in
    connection = Gio.bus_get_sync(session_bus, cancellable)
    GLib.Error: g-io-error-quark: Could not connect: No such file or directory (1)

  3. But if I run python line manually from terminal – it is executed without problem…

  4. Thank you so much for this. Using your advice, I was able to get my dream setup with manjaro xfce + i3 as a window manager, and get the panels and workspaces displaying on the correct monitors. The only modification I had to make was to use xfce4-panel-profiles instead of xfpanel-switch (the name got updated). But I’m sure you already knew that!

    If anyone is reading this and uses the new xfce4-panel-profiles program, all you need to do is, instead of executing the panelconfig.py script with python3, you’ll need to call the binary directly like ‘xfce4-panel-profiles load /path/to/exported_layout.tar.bz2’

  5. Hey, thanks for your article. That was really helpful and I learned a bit about udev and systemd!

    However, I also had a little trouble: The udev rule wouldn’t work. Trying to debug with “udevadm test” I got an error like ‘invalid key SUBSYSTEM’. So I removed the ‘SUBSYSTEM==”drm”‘ part, and now it work.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.