#!/bin/sh
#
-# Automatically select a display configuration based on connected devives
+# Automatically select a display configuration based on connected devices
#
# Stefan Tomanek <stefan.tomanek@wertarbyte.de>
#
# How to use:
#
-# Save your current display configuration and setup with
-# autorandr --save mobile
+# Save your current display configuration and setup with:
+# $ autorandr --save mobile
#
-# Connect an additional display, configure your setup and save it
-# autorand --save docked
+# Connect an additional display, configure your setup and save it:
+# $ autorandr --save docked
#
# Now autorandr can detect which hardware setup is active:
-# # autorandr
-# mobile
-# docked (detected)
+# $ autorandr
+# mobile
+# docked (detected)
#
# To automatically reload your setup, just append --change to the command line
#
# To manually load a profile, you can use the --load <profile> option.
#
+# autorandr tries to avoid reloading an identical configuration. To force the
+# (re)configuration, apply --force.
+#
# To prevent a profile from being loaded, place a script call "block" in its
# directory. The script is evaluated before the screen setup is inspected, and
# in case of it returning a value of 0 the profile is skipped. This can be used
# --default <profile>
#
# Another script called "postswitch "can be placed in the directory
-# ~/.auto-disper as well as in all profile directories: The scripts are
-# executed after a mode switch has taken place and can notify window managers
-# or other applications about it.
+# ~/.autorandr as well as in all profile directories: The scripts are executed
+# after a mode switch has taken place and can notify window managers or other
+# applications about it.
+#
+#
+# While the script uses xrandr by default, calling it by the name "autodisper"
+# or "auto-disper" forces it to use the "disper" utility, which is useful for
+# controlling nvidia chipsets. The formats for fingerprinting the current setup
+# and saving/loading the current configuration are adjusted accordingly.
XRANDR=/usr/bin/xrandr
+DISPER=/usr/bin/disper
+XDPYINFO=/usr/bin/xdpyinfo
PROFILES=~/.autorandr/
+CONFIG=~/.autorandr.conf
CHANGE_PROFILE=0
+FORCE_LOAD=0
DEFAULT_PROFILE=""
SAVE_PROFILE=""
+FP_METHODS="setup_fp_xrandr_edid setup_fp_sysfs_edid"
+CURRENT_CFG_METHOD="current_cfg_xrandr"
+LOAD_METHOD="load_cfg_xrandr"
+
+SCRIPTNAME="$(basename $0)"
+# when called as autodisper/auto-disper, we assume different defaults
+if [ "$SCRIPTNAME" = "auto-disper" ] || [ "$SCRIPTNAME" = "autodisper" ]; then
+ echo "Assuming disper defaults..." >&2
+ FP_METHODS="setup_fp_disper"
+ CURRENT_CFG_METHOD="current_cfg_disper"
+ LOAD_METHOD="load_cfg_disper"
+fi
+
+if [ -f $CONFIG ]; then
+ echo "Loading configuration from '$CONFIG'" >&2
+ . $CONFIG
+fi
+
setup_fp_xrandr_edid() {
$XRANDR -q --verbose | awk '
/^[^ ]+ (dis)?connected / { DEV=$1; }
}
setup_fp_sysfs_edid() {
+ # xrandr triggers the reloading of EDID data
+ $XRANDR -q > /dev/null
# hash the EDIDs of all _connected_ devices
for P in /sys/class/drm/card*-*/; do
+ # nothing found
+ [ ! -d "$P" ] && continue
if grep -q "^connected$" < "${P}status"; then
echo -n "$(basename "$P") "
md5sum ${P}edid | awk '{print $1}'
done
}
+setup_fp_disper() {
+ $DISPER -l | grep '^display '
+}
+
setup_fp() {
- local METHODS="setup_fp_xrandr_edid setup_fp_sysfs_edid"
local FP="";
- for M in $METHODS; do
+ for M in $FP_METHODS; do
FP="$($M)"
- [ -n "$FP" ] && break;
+ if [ -n "$FP" ]; then
+ break
+ fi
done
if [ -z "$FP" ]; then
echo "Unable to fingerprint display configuration" >&2
echo "$FP"
}
-
-current_cfg() {
- $XRANDR -q | awk '
- /^[^ ]+ disconnected / {
- print "output "$1;
- print "off";
- }
- /^[^ ]+ connected / {
+current_cfg_xrandr() {
+ local PRIMARY_SETUP="";
+ if [ -x "$XDPYINFO" ]; then
+ PRIMARY_SETUP="$($XDPYINFO -ext XINERAMA | awk '/^ head #0:/ {printf $3 $5}')"
+ fi
+ $XRANDR -q | awk -v primary_setup="${PRIMARY_SETUP}" '
+ # display is connected and has a mode
+ /^[^ ]+ connected [^(]/ {
split($3, A, "+");
print "output "$1;
print "mode "A[1];
print "pos "A[2]"x"A[3];
+ if ($4 !~ /^\(/) {
+ print "rotate "$4;
+ }
+ if (A[1] A[2] "," A[3] == primary_setup)
+ print "primary";
+ next;
+ }
+ # disconnected or disabled displays
+ /^[^ ]+ (dis)?connected / ||
+ /^[^ ]+ unknown connection / {
+ print "output "$1;
+ print "off";
+ next;
}'
}
+current_cfg_disper() {
+ $DISPER -p
+}
+
+current_cfg() {
+ $CURRENT_CFG_METHOD;
+}
+
blocked() {
local PROFILE="$1"
[ ! -x "$PROFILES/$PROFILE/block" ] && return 1
"$PROFILES/$PROFILE/block" "$PROFILE"
}
+config_equal() {
+ local PROFILE="$1"
+ if [ "$(cat "$PROFILES/$PROFILE/config")" = "$(current_cfg)" ]; then
+ echo "Config already loaded"
+ return 0
+ else
+ return 1
+ fi
+}
+
+load_cfg_xrandr() {
+ sed 's!^!--!' "$1" | xargs $XRANDR
+}
+
+load_cfg_disper() {
+ $DISPER -i < "$1"
+}
+
load() {
local PROFILE="$1"
- if [ "$CHANGE_PROFILE" -eq 1 ] && [ -e "$PROFILES/$PROFILE/config" ] ; then
+ local CONF="$PROFILES/$PROFILE/config"
+ if [ -e "$CONF" ] ; then
echo " -> loading profile $PROFILE"
- sed 's!^!--!' "$PROFILES/$PROFILE/config" | xargs xrandr
+ $LOAD_METHOD "$CONF"
[ -x "$PROFILES/$PROFILE/postswitch" ] && \
"$PROFILES/$PROFILE/postswitch" "$PROFILE"
help() {
cat <<EOH
-Usage: autorandr action [profile-name]
+Usage: $SCRIPTNAME [options]
-h, --help get this small help
-c, --change reload current setup
-s, --save <profile> save your current setup to profile <profile>
-l, --load <profile> load profile <profile>
---fingerprint fingerprints your actual config
+-d, --default <profile> make profile <profile> the default profile
+--force force (re)loading of a profile
+--fingerprint fingerprint your current hardware setup
+--config dump your current xrandr setup
To prevent a profile from being loaded, place a script call "block" in its
directory. The script is evaluated before the screen setup is inspected, and
If no suitable profile can be identified, the current configuration is kept.
To change this behaviour and switch to a fallback configuration, specify
- --default <profile>
+ --default <profile>.
Another script called "postswitch "can be placed in the directory
- ~/.auto-disper as well as in all profile directories: The scripts are
- executed after a mode switch has taken place and can notify window managers
+ ~/.autorandr as well as in any profile directories: The scripts are executed
+ after a mode switch has taken place and can notify window managers.
+
+ When called by the name "autodisper" or "auto-disper", the script uses "disper"
+ instead of "xrandr" to detect, configure and save the display configuration.
EOH
exit
}
# process parameters
-OPTS=$(getopt -n autorandr -o s:l:d:cfh --long change,default:,save:,load:,fingerprint,help -- "$@")
+OPTS=$(getopt -n autorandr -o s:l:d:cfh --long change,default:,save:,load:,force,fingerprint,config,help -- "$@")
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
eval set -- "$OPTS"
-s|--save) SAVE_PROFILE="$2"; shift 2 ;;
-l|--load) LOAD_PROFILE="$2"; shift 2 ;;
-h|--help) help ;;
+ --force) FORCE_LOAD=1; shift ;;
--fingerprint) setup_fp; exit 0;;
+ --config) current_cfg; exit 0;;
--) shift; break ;;
*) echo "Error: $1"; exit 1;;
esac
echo "Saving current configuration as profile '${SAVE_PROFILE}'"
mkdir -p "$PROFILES/$SAVE_PROFILE"
echo "$CURRENT_SETUP" > "$PROFILES/$SAVE_PROFILE/setup"
- current_cfg > "$PROFILES/$SAVE_PROFILE/config"
+ $CURRENT_CFG_METHOD > "$PROFILES/$SAVE_PROFILE/config"
exit 0
fi
if [ -n "$LOAD_PROFILE" ]; then
- CHANGE_PROFILE=1 load "$LOAD_PROFILE"
+ CHANGE_PROFILE=1 FORCE_LOAD=1 load "$LOAD_PROFILE"
exit $?
fi
FILE_SETUP="$(cat "$PROFILES/$PROFILE/setup")"
if [ "$CURRENT_SETUP" = "$FILE_SETUP" ]; then
echo " (detected)"
- load "$PROFILE"
+ if [ "$CHANGE_PROFILE" -eq 1 ]; then
+ if [ "$FORCE_LOAD" -eq 1 ] || ! config_equal "$PROFILE"; then
+ load "$PROFILE"
+ fi
+ fi
# found the profile, exit with success
exit 0
else