123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- #!/bin/sh
- #
- # 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
- #
- # 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)
- #
- # 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
- # to query the status of a docking station you are about to leave.
- #
- # 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>
- #
- # Another script called "postswitch "can be placed in the directory
- # ~/.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; }
- $1 ~ /^[a-f0-9]+$/ { ID[DEV] = ID[DEV] $1 }
- END { for (X in ID) { print X " " ID[X]; } }'
- }
- 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}'
- fi
- done
- }
- setup_fp_disper() {
- $DISPER -l | grep '^display '
- }
- setup_fp() {
- local FP="";
- for M in $FP_METHODS; do
- FP="$($M)"
- if [ -n "$FP" ]; then
- break
- fi
- done
- if [ -z "$FP" ]; then
- echo "Unable to fingerprint display configuration" >&2
- return
- fi
- echo "$FP"
- }
- 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"
- local CONF="$PROFILES/$PROFILE/config"
- if [ -e "$CONF" ] ; then
- echo " -> loading profile $PROFILE"
- $LOAD_METHOD "$CONF"
- [ -x "$PROFILES/$PROFILE/postswitch" ] && \
- "$PROFILES/$PROFILE/postswitch" "$PROFILE"
- [ -x "$PROFILES/postswitch" ] && \
- "$PROFILES/postswitch" "$PROFILE"
- fi
- }
- help() {
- cat <<EOH
- 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>
- -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
- in case of it returning a value of 0 the profile is skipped. This can be used
- to query the status of a docking station you are about to leave.
- 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>.
- Another script called "postswitch "can be placed in the directory
- ~/.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:,force,fingerprint,config,help -- "$@")
- if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
- eval set -- "$OPTS"
- while true; do
- case "$1" in
- -c|--change) CHANGE_PROFILE=1; shift ;;
- -d|--default) DEFAULT_PROFILE="$2"; shift 2 ;;
- -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
- done
- CURRENT_SETUP="$(setup_fp)"
- if [ -n "$SAVE_PROFILE" ]; then
- echo "Saving current configuration as profile '${SAVE_PROFILE}'"
- mkdir -p "$PROFILES/$SAVE_PROFILE"
- echo "$CURRENT_SETUP" > "$PROFILES/$SAVE_PROFILE/setup"
- $CURRENT_CFG_METHOD > "$PROFILES/$SAVE_PROFILE/config"
- exit 0
- fi
- if [ -n "$LOAD_PROFILE" ]; then
- CHANGE_PROFILE=1 FORCE_LOAD=1 load "$LOAD_PROFILE"
- exit $?
- fi
- for SETUP_FILE in $PROFILES/*/setup; do
- if ! [ -e $SETUP_FILE ]; then
- break
- fi
- PROFILE="$(basename $(dirname "$SETUP_FILE"))"
- echo -n "$PROFILE"
- if blocked "$PROFILE"; then
- echo " (blocked)"
- continue
- fi
- FILE_SETUP="$(cat "$PROFILES/$PROFILE/setup")"
- if [ "$CURRENT_SETUP" = "$FILE_SETUP" ]; then
- echo " (detected)"
- 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
- echo ""
- fi
- done
- # we did not find the profile, load default
- if [ -n "$DEFAULT_PROFILE" ]; then
- echo "No suitable profile detected, falling back to $DEFAULT_PROFILE"
- load "$DEFAULT_PROFILE"
- fi
- exit 1
|