From f28657d882d340158ef0af3974ff0d04d712a344 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 6 May 2019 10:20:21 -0400 Subject: fix-dpi: Better GTK+ support --- .config/bash/rc.d/95_gtk3.sh | 9 +++ .local/bin/fix-dpi | 187 ++++++++++++++++++++++++++++++------------- .local/bin/get-dpi | 42 ++++++++++ 3 files changed, 183 insertions(+), 55 deletions(-) create mode 100644 .config/bash/rc.d/95_gtk3.sh create mode 100755 .local/bin/get-dpi diff --git a/.config/bash/rc.d/95_gtk3.sh b/.config/bash/rc.d/95_gtk3.sh new file mode 100644 index 0000000..99ed337 --- /dev/null +++ b/.config/bash/rc.d/95_gtk3.sh @@ -0,0 +1,9 @@ +#!/hint/bash + +_gtk_set_shell_DPI() { + if [[ -n $DISPLAY ]]; then + eval "$(systemctl --user show-environment | grep -e ^GDK_SCALE= -e ^GDK_DPI_SCALE=)" + export GDK_SCALE GDK_DPI_SCALE + fi +} +PROMPT_COMMAND+='_gtk_set_shell_DPI;' diff --git a/.local/bin/fix-dpi b/.local/bin/fix-dpi index ee0412d..b700ea9 100755 --- a/.local/bin/fix-dpi +++ b/.local/bin/fix-dpi @@ -1,69 +1,146 @@ #!/usr/bin/env bash # Copyright 2019 Luke Shumaker -# -# Adjusts the xrandr 1.2, xrandr 1.3, and xrdb DPI settings according -# to each output's current mode (pixel resolution) and self-reported -# physical dimensions (mm). - -# env-var: MAX_DPI: integer -# env-var: DRY_RUN: empty=false / non-empty=true - -( # Phase 1: Probe outputs - export LC_ALL=C - xrandr | sed -rn -e 's@(.*) connected( .*)? ([0-9]+)x([0-9]+)\+([0-9]+)\+([0-9]+)( .*)? ([0-9]+)mm x ([0-9]+)mm( .*)?@\1 \3 \4 \8 \9@p' -e 's@^ ([0-9]+)x([0-9]+)i? .*\*.*@ \1 \2@p' | sed '/^\S/{ N; s/\n//; }' - # 1 2 3 4 5 6 7 8 9 10 1 2 - # `-outputName | `-fb_xpx `-fb_ypx `-fb_xoff `-fb_yoff `- hw_xmm `- hw_ymm | `-hw_xpx `-hw_ypx - # `-discard `- discard `- discard -) | ( # Phase 2: Translate that to a set of actions to perform - max_xdpi=${MAX_DPI:-96} - max_ydpi=${MAX_DPI:-96} - - dpi=$(xrdb -query|sed -n 's/^Xft\.dpi:\s*//p') - xdpi=${dpi%%x*} - ydpi=${dpi#*x} - if (( xdpi > max_xdpi )); then - max_xdpi=$xdpi - fi - if (( ydpi > max_ydpi )); then - max_ydpi=$ydpi + +sanitize() { + [[ $? == 0 ]] || return $? + printf '%g\n' "$@" +} + +calc() { + [[ $? == 0 ]] || return $? + sanitize "$(bc <<<"scale=6; $1")" +} + +round() { + [[ $? == 0 ]] || return $? + printf '%.f\n' "$@" +} + +roundup_by() { + [[ $? == 0 ]] || return $? + awk -v by="$2" 'function ceil(x) { return (x == int(x)) ? x : int(x)+1 } { print ceil($1/96)*96 }' <<<"$1" +} + +max() { + [[ $? == 0 ]] || return $? + awk -v a="$1" -v b="$2" '{ print (a > b) ? a : b }' <<<'' +} + +errusage() { + if (( $# > 0 )); then + printf '%s: %s\n' "${0##*/}" "$(printf "$@")" >&2 fi + printf "Try '%s --help' for more information.\n" "${0##*/}" >&2 + exit 2 +} - declare -A outputs - while read -r output fb_xpx fb_ypx hw_xmm hw_ymm hw_xpx hw_ypx; do - hw_xdpi=$(bc <<<"($hw_xpx*25.4)/$hw_xmm") - hw_ydpi=$(bc <<<"($hw_ypx*25.4)/$hw_ymm") +usage() { + printf 'Usage: %s [OPTIONS]\n' "${0##*/}" + printf "Adjust DPI settings to reflect each output's current mode\n" + printf "(pixel resolution) and self-reported physical dimensions (mm).\n" + echo + printf 'OPTIONS:\n' + printf ' -h, --help Show this message\n' + printf ' -n, --dry-run Print what would be done, without doing it\n' + printf ' --render-dpi=\n' + printf ' Specify what DPI applications should render\n' + printf ' at (default: auto)\n' +} - fb_xdpi=$(bc <<<"($fb_xpx*25.4)/$hw_xmm") - fb_ydpi=$(bc <<<"($fb_ypx*25.4)/$hw_ymm") +set -euE -o pipefail +args=$(getopt -n "${0##*/}" -o 'hn' -l 'help,dry-run,render-dpi:' -- "$@") || errusage +eval "set -- $args" - if (( hw_xdpi > max_xdpi )); then - max_xdpi=$hw_xdpi - fi - if (( hw_ydpi > max_ydpi )); then - max_ydpi=$hw_ydpi - fi +arg_dry_run=false +arg_render_dpi=auto +while (( $# > 0 )); do + case "$1" in + -h|--help) + usage + exit 0 + ;; + -n|--dry-run) + arg_dry_run=true + shift + ;; + --render-dpi) + arg_render_dpi=$2 + case "$2" in + auto|auto-hwonly) + arg_render_dpi=$2 + ;; + *) + if ! arg_render_dpi="$(sanitize "${2%%x*}" 2>/dev/null)x$(sanitize "${2#*x}" 2>/dev/null)"; then + errusage 'Invalid --render-dpi=%q' "$2" + fi + ;; + esac + shift 2 + ;; + --) + shift + break + ;; + esac +done +if (( $# > 0 )); then + errusage +fi - if (( fb_xdpi > max_xdpi )); then - max_xdpi=$fb_xdpi - fi - if (( fb_ydpi > max_ydpi )); then - max_ydpi=$fb_ydpi +dpis="$(get-dpi)" + +if [[ $arg_render_dpi == auto ]] || [[ $arg_render_dpi == auto-hwonly ]]; then + arg_render_xdpi=96 + arg_render_ydpi=96 + while read -r source item dpi; do + if [[ $arg_render_dpi == auto-hwonly ]] && [[ $source != X11-RandR-hw ]]; then + continue fi + arg_render_xdpi=$(max "$(round "${dpi%%x*}")" "$arg_render_xdpi") + arg_render_ydpi=$(max "$(round "${dpi#*x}")" "$arg_render_ydpi") + done <<<"$dpis" - outputs["$output"]="$hw_xdpi $hw_ydpi" - done + # GDK 3 only supports integer up-scale factors, because + # they're morons. Round arg_render_dpi up to a multiple of '96'. + arg_render_xdpi=$(roundup_by "$arg_render_xdpi" 96) + arg_render_ydpi=$(roundup_by "$arg_render_ydpi" 96) +else + arg_render_xdpi=${arg_render_dpi%%x*} + arg_render_ydpi=${arg_render_dpi#*x} +fi + +( echo 'xrandr \' - printf ' --dpi %q \\\n' "${max_xdpi}x${max_ydpi}" - for output in "${!outputs[@]}"; do - read -r xdpi ydpi <<<"${outputs[$output]}" - printf ' --output %q --scale %q \\\n' "$output" "$(bc <<<"scale=5; $max_xdpi/$xdpi")x$(bc <<<"scale=5; $max_ydpi/$ydpi")" - done + printf ' --dpi %q \\\n' "${arg_render_xdpi}x${arg_render_ydpi}" + while read -r source item dpi; do + if [[ $source != X11-RandR-hw ]]; then + continue + fi + hw_xdpi="$(sanitize "${dpi%%x*}")" + hw_ydpi="$(sanitize "${dpi#*x}")" + + fb_xdpi="$(calc "$arg_render_xdpi/$hw_xdpi")" + fb_ydpi="$(calc "$arg_render_ydpi/$hw_ydpi")" + printf ' --output %q --scale %q \\\n' "$item" "${fb_xdpi}x${fb_ydpi}" + done <<<"$dpis" + echo - printf "xrdb -merge <<<'Xft.dpi: %s'\n" "${max_xdpi}x${max_ydpi}" -) | ( # Phase 3: Apply those actions - if [[ -z "$DRY_RUN" ]]; then - bash -v - else + + printf "xrdb -merge <<<'Xft.dpi: %s'\n" "${arg_render_xdpi}x${arg_render_ydpi}" + + echo + + GDK_SCALE="$(calc "(${arg_render_xdpi}+${arg_render_ydpi})/2 /96")" + printf 'export GDK_SCALE=%q\n' "$GDK_SCALE" + # You're not "supposed" to set XRDB Xft.dpi if GDK_SCALE is + # set. Undo Xft.dpi for GDK applications. + GDK_DPI_SCALE="$(calc "1/$GDK_SCALE")" + printf 'export GDK_DPI_SCALE=%q\n' "$GDK_DPI_SCALE" + echo systemctl --user import-environment GDK_SCALE GDK_DPI_SCALE +) | ( + if $arg_dry_run; then cat + else + bash -v fi ) diff --git a/.local/bin/get-dpi b/.local/bin/get-dpi new file mode 100755 index 0000000..b9979bd --- /dev/null +++ b/.local/bin/get-dpi @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# Copyright 2019 Luke Shumaker + +sanitize() { + printf '%g\n' "$@" +} + +calc() { + sanitize "$(bc <<<"scale=6; $1")" +} + +# GDK +dpi=$(calc "$(sanitize "${GDK_SCALE:-1}") * 96") +echo environment GDK-widget ${dpi}x${dpi} + +dpi=$(calc "$(sanitize "${GDK_DPI_SCALE:-1}") * 96") +echo environment GDK-text ${dpi}x${dpi} + +# Xft +dpi=$(xrdb -query|sed -n 's/^Xft\.dpi:\s*//p') +[[ -n "$dpi" ]] || dpi=96x96 +xdpi=$(sanitize "${dpi%%x*}") +ydpi=$(sanitize "${dpi#*x}") +echo X11-resources Xft ${xdpi}x${ydpi} + +# RandR +( + export LC_ALL=C + xrandr | sed -rn -e 's@(.*) connected( .*)? ([0-9]+)x([0-9]+)\+([0-9]+)\+([0-9]+)( .*)? ([0-9]+)mm x ([0-9]+)mm( .*)?@\1 \3 \4 \8 \9@p' -e 's@^ ([0-9]+)x([0-9]+)i? .*\*.*@ \1 \2@p' | sed '/^\S/{ N; s/\n//; }' + # 1 2 3 4 5 6 7 8 9 10 1 2 + # `-outputName | `-fb_xpx `-fb_ypx `-fb_xoff `-fb_yoff `- hw_xmm `- hw_ymm | `-hw_xpx `-hw_ypx + # `-discard `- discard `- discard +) | while read -r output fb_xpx fb_ypx hw_xmm hw_ymm hw_xpx hw_ypx; do + hw_xdpi=$(calc "($hw_xpx*25.4)/$hw_xmm") + hw_ydpi=$(calc "($hw_ypx*25.4)/$hw_ymm") + + fb_xdpi=$(calc "($fb_xpx*25.4)/$hw_xmm") + fb_ydpi=$(calc "($fb_ypx*25.4)/$hw_ymm") + + echo X11-RandR-hw $output ${hw_xdpi}x${hw_ydpi} + echo X11-RandR-fb $output ${fb_xdpi}x${fb_ydpi} +done -- cgit v1.1-4-g5e80