External monitor auto-configuration in GalliumOS (Ubuntu/Xfce)

03 Dec 2020

I bought a 4k monitor recently for a multitude of reasons, tired of doing graphic design on a 15” laptop screen, working from home more and more because of the pandemic, and becuase I just wanted a new toy to play with. My personal laptop is an Acer C910 running GalliumOS instead of ChromeOS. GalliumOS has been great overall, built on Xubuntu for ChromeOS devices. Plugging a new monitor in has never been an issue, always immediately recognized but needed to perform a few manual steps to get the resolution & placement to work. It always wanted to default to 2560x1440 instead of 3840x2160 and I always needed to tell the display manager if I wanted to extend or mirror my desktop.

Wanting this to be more of an automatic config when I plugged the monitor into the HDMI port I turned to the internet and found a great post by Ben Fedidat. Ben’s post got me 90% there but I still had some minor changes I needed to add which is why Im making this post.

Here are the issue I encountered and needed to address:

  • Have the laptop recognize and configure the monitor everytime I plugged it in.
  • Move all active windows from external screen to laptop screen when unplugging external monitor (for proper hibernation when I eventually closed the lid).
  • When in clamshell mode and external monitor is unplugged hibernate the laptop.

Ben’s post got the monitor to be recognized ~70% when plugging the monitor in, there were times when xrandr recognized the monitor but did not see it’s available resolutions, I believe this is because the monitor was in powersave mode and cannot fully communicate with the kernel yet. There were more issues when I unplugged the monitor, mainly not going into hibernation or unextending the display screen. Because this is a laptop, when running in clamshell mode the laptop would not go to sleep because even though I had unplugged my monitor all the applications where still ‘active’ on the second screen along with the mouse cursor and therefore the laptop would not hibernate (and shortly after kill the battery).

Things of note

  • Lines 10-15 I use xdotool to find all visible windows and move them to my laptop screen
  • Line 17 I move my mouse cursor to the laptop screen
  • Line 18 I sleep for 1 second to let everything finish, Im not sure if this is necessary but seems to help
  • Line 20 I let xrandr auto configure the screens to what should only be the laptop screen at this point.
  • Lines 25-38 When the external monitor is connect I check xrandr to determine if it correctly recognizes it, if so on line 39-42 I configure both screens to the resolution I want and position them accordingly.

Everything else pretty much matches Ben’s guide mentioned above.

Script

date >> log.txt #log the date for debug purposes
sleep 1 #give the system time to setup the connection

# CHANGE THE 'HDMI-A-1' IN THE FOLLOWING LINE FOR YOUR SYSTEM
dmode="$(cat /sys/class/drm/card0-HDMI-A-1/status)"
export DISPLAY=:0
export XAUTHORITY=/home/brian/.Xauthority

if [ "${dmode}" = disconnected ]; then
	for window_id in $(xdotool search --onlyvisible ".*")
	do
        # THIS COMMAND IS MAPPED TO move-to-next-monitor.sh (https://github.com/jc00ke/move-to-next-monitor) 
        # SHOULD MOVE ALL OPEN APPLICATIONS TO LAPTOP SCREEN
		xdotool key --window $window_id Shift+Alt+Right  
	done
    # MOVE MOUSE CURSOR TO LAPTOP SCREEN
	xdotool mousemove --screen 0 10 10 
	sleep 1
	
	/usr/bin/xrandr --auto
    echo "disconnected" >> log.txt
elif [ "${dmode}" = connected ]; then	
	# LOOP IN CASE MONITOR IS COMING OUT OF POWERSAVE AND XRANDR DOESNT HAVE WHAT IT NEEDS YET
    # GIVE THE LAPTOP A CHANCE TO SEE THE RESOLUTIONS OF THE CONNECTED MONITOR
    n=0
	while [ $n -le 5 ] 
	do
		a=`xrandr | grep -c "3840"`
		if [ $a -gt 0 ]
		then
			break
	    fi
	    sleep .2		
        # LOGS NUMBER OF ATTEMPTS
		echo "Attempt ${n} resize failed" >> log.txt 	
        # INCREMENTS $n
	    n=$(( n+1 ))	 
	done
	
    if xrandr -q | grep -q "3840"; then #THE RESOLUTION SHOULD APPEAR
        #THIS IS THE XRANDR COMMAND, OUTPUT PIPED TO THE LOG
        /usr/bin/xrandr --output HDMI1 --mode 3840x2160 --pos 1920x0 --rotate normal --output VIRTUAL1 --off \
            --output eDP1 --mode 1920x1080 --pos 0x0 --rotate normal 2>&1 | tee -a log.txt
        echo "success" >> log.txt
    else
		xrandr >> log.txt
        echo "no resolution, bad setup" >> log.txt
    fi
else
    echo "unknown event" >> log.txt
fi

Issues I still have are as follows:

  • Sometimes minimized applications still stay active on external monitor screen and this laptop wont hibernate.
  • Occasionally the mouse cursor doesnt move and again the laptop won’t hibernate.
  • Terminal windows sometimes do not like to move moved with xdotool, I believe this is because the terminal takes the commands as if they were being entered instead of being applied to the window itself.

This script gets me 95% of the way there, is it perfect? No? Will other OS’s handle this better? Yes. Did I like doing this? Absolutely. Sometimes linux works 100% out of the box without need for intervention, but sometime it helps you learn how it works and makes you think a little. I like that.