« Previous 1 2
Using a Bash script to mirror external monitors
Mirror, Mirror
Detection Service
The workflow in the automirror.sh
script is simple and emulates the human decision-making path. Lines 4 to 8 start by setting global environmental variables. You can easily use variables to replace the external programs if you need to design tests. The script uses the connected_displays
variable (line 40) to save the active screen output and its resolution in millimeters; display_list
(line 45) defines the best possible resolution to the current screen output and its height in lines. Xrandr sorts the list of resolutions so that the first resolution is typically also the recommended resolution. The ugly sed
expression saves me many lines of Bash, and I hope you will excuse its use.
Lines 55 through 57 define a primary display: The primary display is either the laptop screen or the screen with the largest number of lines.
If the primary display is not attached to the LVDS1 connection, as assumed in line 7, you can use the PRIMARY_DISPLAY
environmental variable to set up different connection. The script then defines the virtual display resolution for the frame buffer (line 65) for this display; the remaining displays end up in the other_display_list
(line 68).
The while
loop in line 72 generates the xrandr
command lines with the native display resolutions and determines the modes via the get_display_resolution()
function in that starts in line 18. Line 74 then calibrates the scaling of the PRIMARY_DISPLAY
with that of the other displays. Line 75 generates a string for desktop messaging from the determined resolutions. In line 81, xrandr configures all the displays, and notify-send
in line 83 informs the user of the resolution decisions (Figure 1 ).
Test Operation
I continually stumble across new combinations of internal and external display devices that force me to modify the script. To avoid inadvertently destroying the working scenario, the GitHub project for Automirror [5] contains unit tests for all scenarios.
The runtests.sh
script in Listing 2 iterates in lines 19 through 32 across the test cases in the testdata/
directory, running the do_test
function from lines 4 to 15 for each test case. A test case consists of a line of text with the output from xrandr for a specific configuration, along with the response file (*_result.txt
) with the anticipated xrandr calls.
Listing 2
runtests.sh
01 #!/bin/bash 02 set -e -E -u 03 04 function do_test { 05 local xrandr_output="$1" ; shift 06 local script_output="$1" ; shift 07 res="$( XRANDR_STATUS_PROGRAM="cat $xrandr_output" \ XRANDR_SET_PROGRAM=echo AUTOMIRROR_NOTIFY_COMMAND=: \ ./automirror.sh)" 08 if [[ "$res" == "$(< $script_output)" ]] ; then 09 return 0 10 else 11 echo "Difference:" 12 diff -u --label EXPECTED_RESULT --label ACTUAL_RESULT -- \ $script_output - <<<"$res" 13 return 1 14 fi 15 } 16 17 failed=0 18 19 for test_case in testdata/*.txt ; do 20 [[ "$test_case" == *result.txt ]] && continue 21 script_output=${test_case%.txt}_result.txt 22 if [[ "$script_output" && -r "$script_output" ]] ; then 23 if do_test "$test_case" "$script_output" ; then 24 echo "OK $test_case" 25 else 26 let failed++ 1 27 echo "FAILED $test_case" 28 fi 29 else 30 echo "Missing test result file '$script_output'" 31 fi 32 done 33 34 if (( failed == 0 )) ; then 35 exit 0 36 else 37 echo $failed TESTS FAILED 38 exit 1 39 fi
For the test run, cat
and echo
in line 7 use the environmental variables XRANDR_STATUS_PROGRAM
and XRANDR_SET_PROGRAM
to replace the xrandr
and notify-send
program. The automirror.sh
script then needs to work in all test cases; otherwise, runtests.sh
shows the failed test and the anticipated result.
Packaged
If the script passes all the tests, the makefile builds a matching Debian package for installing [6]. The matching Debian changelog and the version are automatically generated from the Git commits by the git-dch
tool.
To support a new configuration, I first design a new test case, then modify the script until the old and new tests all work. The commands git commit -a
and make release deb
produce a new Debian package, which is immediately sent to GitHub's software repository.
Infos
- Connection problems: http://blog.schlomo.schapiro.org/2014/03/opening-window-to-wider-world.html
- DDC: http://en.wikipedia.org/wiki/Display_Data_Channel
- Debian on RandR: https://wiki.debian.org/XStrikeForce/HowToRandR12
- RandR project: http://www.x.org/wiki/Projects/XRandR/
- Automirror on Github: https://github.com/schlomo/automirror
- Packaging instead of copying: http://blog.schlomo.schapiro.org/2015/04/better-package-than-copy.html
« Previous 1 2
Buy this article as PDF
(incl. VAT)
Buy ADMIN Magazine
Subscribe to our ADMIN Newsletters
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Most Popular
Support Our Work
ADMIN content is made possible with support from readers like you. Please consider contributing when you've found an article to be beneficial.