Skip to content

Instantly share code, notes, and snippets.

@trustin
Created March 10, 2016 04:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save trustin/2c9e8bf24b187ba49b03 to your computer and use it in GitHub Desktop.
Save trustin/2c9e8bf24b187ba49b03 to your computer and use it in GitHub Desktop.
Trustin's single/dual monitor setup script for laptops with nVidia GPU
#!/bin/bash
if [[ $# -gt 0 ]]; then
CLONE=1
else
CLONE=0
fi
function get_max_resolution() {
xrandr | grep -A100 "^$1 " | grep -P '^\s+[0-9]+x[0-9]+\s+[0-9\.]+\s*\*?\+.*$' | head -1 | awk '{ print $1; }'
}
function get_resolution() {
local RES=$(xrandr | grep -A100 "^$1 " | grep -P '^\s+[0-9]+x[0-9]+\s+[0-9\.]+\s*\*?\+.*$' | head -1 | awk '{ print $1; }')
local REFRESH_RATE=$(xrandr | grep -A100 "^$1 " | grep -P '^\s+'"$RES"'\s+[0-9\.]+\s*\*?\+.*$' | head -1 | awk '{ print $2; }' | sed 's#\..*##g')
# Avoid using the mode with low refresh rate.
if [[ "$REFRESH_RATE" =~ (^[0-9]+$) ]] && [[ "$REFRESH_RATE" -lt 50 ]]; then
case "$RES" in
3840x2160)
echo -n '1920x1080'
;;
*)
echo -n "$RES"
;;
esac
return
fi
echo -n "$RES"
}
function get_width() {
echo "$1" | cut -dx -f1
}
function get_height() {
echo "$1" | cut -dx -f2
}
# MBP's default output is DP-2.
#PRI_PORT=LVDS1
PRI_PORT=DP-2
PRI_RES=$(get_resolution $PRI_PORT)
PRI_WIDTH=$(get_width $PRI_RES)
PRI_HEIGHT=$(get_height $PRI_RES)
echo "Primary: $PRI_PORT $PRI_RES"
SEC_PORT=$(nvidia-settings -q dpys |
grep -P '\s*\[[0-9]+\] [^ ]+ \([^\)]+\) \(connected' |
awk '{ print $3; }' | sed 's/[()]//g' | grep -vF "$PRI_PORT" | head -1)
if [[ -z "$SEC_PORT" ]]; then
# No external display
nvidia-settings -a CurrentMetaMode="
$PRI_PORT: nvidia-auto-select +0+0 {ViewPortIn=$PRI_RES}" | grep -vP '^$'
else
# Has external display
SEC_MAX_RES=$(get_max_resolution $SEC_PORT)
SEC_RES=$(get_resolution $SEC_PORT)
echo "Secondary: $SEC_PORT $SEC_RES out of $SEC_MAX_RES"
MAX_SEC_WIDTH=$(get_width $SEC_MAX_RES)
MAX_SEC_HEIGHT=$(get_height $SEC_MAX_RES)
if [[ $MAX_SEC_WIDTH -le 1900 ]] || [[ $MAX_SEC_HEIGHT -le 1200 ]]; then
# Low-DPI display - very likely to unsupport scaling from high-DPI.
SEC_IS_PRI=1
else
SEC_IS_PRI=0
fi
SEC_WIDTH=$(get_width $SEC_RES)
SEC_HEIGHT=$(get_height $SEC_RES)
OLD_PRI_WIDTH=$PRI_WIDTH
OLD_PRI_HEIGHT=$PRI_HEIGHT
OLD_PRI_RES=$PRI_RES
if [[ $SEC_IS_PRI -eq 0 ]]; then
if [[ $((PRI_WIDTH * SEC_HEIGHT)) -gt $((SEC_WIDTH * PRI_HEIGHT)) ]]; then
PRI_WIDTH=$((PRI_HEIGHT * SEC_WIDTH / SEC_HEIGHT))
else
PRI_HEIGHT=$((PRI_WIDTH * SEC_HEIGHT / SEC_WIDTH))
fi
PRI_RES=${PRI_WIDTH}x${PRI_HEIGHT}
PRI_OUT_RES=$PRI_RES
OFFSET_X=$(((OLD_PRI_WIDTH - PRI_WIDTH) / 2))
OFFSET_Y=$(((OLD_PRI_HEIGHT - PRI_HEIGHT) / 2))
if [[ $OFFSET_X -ge 0 ]]; then
OFFSET_X="+$OFFSET_X"
fi
if [[ $OFFSET_Y -ge 0 ]]; then
OFFSET_Y="+$OFFSET_Y"
fi
OFFSETS=$OFFSET_X$OFFSET_Y
else
if [[ $((PRI_HEIGHT * SEC_WIDTH)) -gt $((SEC_HEIGHT * PRI_WIDTH)) ]]; then
PRI_OUT_WIDTH=$PRI_WIDTH
PRI_OUT_HEIGHT=$((PRI_WIDTH * SEC_HEIGHT / SEC_WIDTH))
else
PRI_OUT_WIDTH=$((PRI_HEIGHT * SEC_WIDTH / SEC_HEIGHT))
PRI_OUT_HEIGHT=$PRI_HEIGHT
fi
PRI_WIDTH=$SEC_WIDTH
PRI_HEIGHT=$SEC_HEIGHT
PRI_RES=${PRI_WIDTH}x${PRI_HEIGHT}
PRI_OUT_RES=${PRI_OUT_WIDTH}x${PRI_OUT_HEIGHT}
OFFSET_X=$(((OLD_PRI_WIDTH - PRI_OUT_WIDTH) / 2))
OFFSET_Y=$(((OLD_PRI_HEIGHT - PRI_OUT_HEIGHT) / 2))
if [[ $OFFSET_X -ge 0 ]]; then
OFFSET_X="+$OFFSET_X"
fi
if [[ $OFFSET_Y -ge 0 ]]; then
OFFSET_Y="+$OFFSET_Y"
fi
OFFSETS=$OFFSET_X$OFFSET_Y
fi
echo "Primary: $PRI_PORT $PRI_RES scaled to $PRI_OUT_RES$OFFSETS"
if [[ $CLONE -ne 0 ]]; then
nvidia-settings -a CurrentMetaMode="
$SEC_PORT: $SEC_RES +0+0 {ViewPortIn=$PRI_RES, ViewPortOut=$SEC_RES},
$PRI_PORT: nvidia-auto-select +0+0 {ViewPortIn=$PRI_RES, ViewPortOut=$PRI_OUT_RES$OFFSETS}" | grep -vP '^$'
else
nvidia-settings -a CurrentMetaMode="
$SEC_PORT: $SEC_RES +0+0 {ViewPortIn=$PRI_RES, ViewPortOut=$SEC_RES}" | grep -vP '^$'
fi
fi
nvidia-settings -q CurrentMetaMode
if [[ "$EUID" -eq `id -u trustin` ]]; then
# Restart Openbox just in case it does not detect the resolution changes properly.
openbox --restart >/dev/null 2>&1 &
fi
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment