Skip to content

Instantly share code, notes, and snippets.

@nicolas-oliveira
Last active April 17, 2025 16:12
Show Gist options
  • Save nicolas-oliveira/709e93d87e7fb2bd56cb054392c3767b to your computer and use it in GitHub Desktop.
Save nicolas-oliveira/709e93d87e7fb2bd56cb054392c3767b to your computer and use it in GitHub Desktop.
#!/bin/bash
# Absolute path to xrandr
TARGET_WIDTH=${TARGET_WIDTH:-2560}
TARGET_HEIGHT=${TARGET_HEIGHT:-1440}
POSITION="right"
XRANDR="/usr/bin/xrandr"
# NVIDIA="$(nvidia-settings -q CurrentMetaMode -t)"
LAPTOP=$($XRANDR | awk '/ connected/ && /eDP/{print $1}')
EXT=$(xrandr | awk '/ connected/ && !/eDP/{print $1}')
if [[ -z "$LAPTOP" ]]; then
echo "Error: Could not detect laptop (eDP) output." >&2
exit 1
fi
if [[ -z "$EXT" ]]; then
echo "Apenas um monitor foi detectado. Redefinindo: $LAPTOP."
$XRANDR --output "$LAPTOP" --auto
exit 0
fi
# FUNÇÃO QUE PREPARA O SCRIPT PARA SER EXECUTADO EM CASOS
# EM QUE O USUÁRIO ATUAL NÃO É USUÁRIO QUE ESTÁ USANDO O MONITOR
# ESSA FUNÇÃO FOI CRIADA JUNTAMENTE COM A TENTATIVA DE EXECUTAR ESSE
# SCRIPT COMO DAEMON PORÉM NÃO FUNCIONOU
# function run-in-user-session() {
# _display_id=":$(find /tmp/.X11-unix/* | sed 's#/tmp/.X11-unix/X##' | head -n 1)"
# _username=$(who | grep "\(${_display_id}\)" | awk '{print $1}')
# _user_id=$(id -u "$_username")
# _environment=("DISPLAY=$_display_id" "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$_user_id/bus")
# sudo -Hu "$_username" env "${_environment[@]}" "$@"
# }
second_monitor_commands() {
gsettings set org.gnome.shell.extensions.dash-to-dock dash-max-icon-size 50
gsettings set org.gnome.desktop.interface text-scaling-factor 1.05
gsettings set org.gnome.desktop.interface monospace-font-name 'SF Mono Powerline 11'
gsettings set org.gnome.desktop.interface font-name 'SF Pro Display 11'
gsettings set org.gnome.desktop.interface document-font-name 'SF Pro Display 11'
gsettings set org.gnome.desktop.wm.preferences titlebar-font 'SF Pro Text Regular 11'
# LER AS VARIÁVEIS ORIUNDAS DO XRANDR
read LAP_W LAP_H < <(xrandr | awk -v MON="$LAPTOP" '$1==MON && /connected/{getline; print $1}' | awk -Fx '{print $1, $2}')
read EXT_W EXT_H < <(xrandr | awk -v MON="$EXT" '$1==MON && /connected/{getline; print $1}' | awk -Fx '{print $1, $2}')
# CALCULAR A RESOLUÇÃO FINAL
scaleX=$(awk "BEGIN {printf \"%.10f\", $TARGET_WIDTH/$EXT_W}")
scaleY=$(awk "BEGIN {printf \"%.10f\", $TARGET_HEIGHT/$EXT_H}")
# Calcular as dimensões gerais do framebuffer
FB_W=$(awk "BEGIN {printf \"%d\", $LAP_W + $EXT_W * $scaleX}")
FB_H=$(awk "BEGIN {printf \"%d\", ( $LAP_H > $EXT_H * $scaleY ? $LAP_H : $EXT_H * $scaleY ) }")
echo $FB_W
echo $FB_H
# APLICAR O NOVO FRAMEBUFFER
$XRANDR --fb ${FB_W}x${FB_H}
# Determine positions based on --position
if [[ "$POSITION" == "right" ]]; then
# External on left, laptop on right
EXT_POS="0x0"
LAP_POS=$(awk "BEGIN {printf \"%d\", $EXT_W * $scaleX}")"x0"
PRIMARY="$EXT"
else
# Laptop on left, external on right
LAP_POS="0x0"
EXT_POS=$(awk "BEGIN {printf \"%d\", $LAP_W}")"x0"
PRIMARY="$LAPTOP"
fi
# Apply scaling and positioning
$XRANDR --output "$EXT" --mode ${EXT_W}x${EXT_H} \
--scale ${scaleX}x${scaleY} --pos $EXT_POS --primary
$XRANDR --output "$LAPTOP" --mode ${LAP_W}x${LAP_H} \
--scale 1x1 --pos $LAP_POS
# DESABILITADO POR INSTABILIDADE DO DRIVER
# if [[ "${NVIDIA}" != "" ]]; then
# NVIDIA="${NVIDIA#*" :: "}"
# nvidia-settings -a CurrentMetaMode="${NVIDIA//\}/, ForceFullCompositionPipeline=On\}}"
# fi
}
only_one_monitor_commands() {
gsettings set org.gnome.desktop.interface text-scaling-factor 0.97
gsettings set org.gnome.desktop.interface monospace-font-name 'SF Mono Powerline 10'
gsettings set org.gnome.desktop.interface font-name 'SF Pro Display 10'
gsettings set org.gnome.desktop.interface document-font-name 'SF Pro Display 10'
gsettings set org.gnome.desktop.wm.preferences titlebar-font 'SF Pro Text Regular 10'
gsettings set org.gnome.shell.extensions.dash-to-dock dash-max-icon-size 44
}
check_hdmi() {
if [ "$XDG_SESSION_TYPE" = "x11" ]; then
if $XRANDR | awk '/ connected/ && !/eDP/{print $1}'; then
second_monitor_commands &&
notify-send "Segundo monitor identificado!" "Configurações aplicadas automaticamente"
else
only_one_monitor_commands &&
notify-send "Único monitor identificado!" "Configurações aplicadas automaticamente"
fi
fi
}
# DESABILITADO POIS NÃO TEM UMA FORMA DE FAZER O MÉTODO NO WAYLAND
# if [ $XDG_SESSION_TYPE == "wayland" ]; then
# if $XRANDR | grep -q "XWAYLAND1 connected"; then
# second_monitor_commands
# else
# only_one_monitor_commands
# fi
# fi
check_hdmi
# CASO FOSSE POSSÍVEL COLOCAR EM UM DAEMON ESSA FUNÇÃO VIGIARIA A MUDANÇA DE HDMI
# udevadm monitor --udev --subsystem-match=drm | while read -r line; do
# if echo "$line" | grep -q "change"; then
# check_hdmi
# fi
# done
@nicolas-oliveira
Copy link
Author

nicolas-oliveira commented Apr 17, 2025

Correção ChatGPT

#!/usr/bin/env

# Improved HDMI/Laptop multi-monitor scaling script
# Dynamically detects eDP (laptop) and external HDMI outputs
# Computes scale factors to achieve a target virtual resolution
# Supports positioning external monitor to the left or right

XRANDR="/usr/bin/xrandr"
# Set your desired virtual resolution for the external monitor
TARGET_WIDTH=${TARGET_WIDTH:-2560}
TARGET_HEIGHT=${TARGET_HEIGHT:-1440}
# Default position: "right" (external monitor on the left, laptop on the right)
POSITION="right"

# Parse optional arguments
function usage() {
    echo "Usage: $0 [--position left|right]"
    exit 1
}
while [[ "$1" =~ ^- ]]; do
  case "$1" in
    --position)
      POSITION="$2"; shift 2;;
    -h|--help)
      usage;;
    *)
      echo "Unknown option: $1"; usage;;
  esac
done

# Detect connected outputs
LAPTOP=$(xrandr | awk '/ connected/ && /eDP/{print $1}')
EXT=$(xrandr | awk '/ connected/ && !/eDP/{print $1}')

if [[ -z "$LAPTOP" ]]; then
    echo "Error: Could not detect laptop (eDP) output." >&2
    exit 1
fi

if [[ -z "$EXT" ]]; then
    echo "Only one monitor detected. Resetting to auto on $LAPTOP."
    $XRANDR --output "$LAPTOP" --auto
    exit 0
fi

# Query native resolutions
read LAP_W LAP_H < <(xrandr | awk -v MON="$LAPTOP" '$1==MON && /connected/{getline; print $1}' | awk -Fx '{print $1, $2}')
read EXT_W EXT_H < <(xrandr | awk -v MON="$EXT" '$1==MON && /connected/{getline; print $1}' | awk -Fx '{print $1, $2}')

# Compute floating-point scale factors
scaleX=$(awk "BEGIN {printf \"%.10f\", $TARGET_WIDTH/$EXT_W}")
scaleY=$(awk "BEGIN {printf \"%.10f\", $TARGET_HEIGHT/$EXT_H}")

# Compute overall framebuffer dimensions
FB_W=$(awk "BEGIN {printf \"%d\", $LAP_W + $EXT_W * $scaleX}")
FB_H=$(awk "BEGIN {printf \"%d\", ( $LAP_H > $EXT_H * $scaleY ? $LAP_H : $EXT_H * $scaleY ) }")

# Apply new framebuffer
$XRANDR --fb ${FB_W}x${FB_H}

# Determine positions based on --position
if [[ "$POSITION" == "right" ]]; then
    # External on left, laptop on right
    EXT_POS="0x0"
    LAP_POS=$(awk "BEGIN {printf \"%d\", $EXT_W * $scaleX}")"x0"
    PRIMARY="$EXT"
else
    # Laptop on left, external on right
    LAP_POS="0x0"
    EXT_POS=$(awk "BEGIN {printf \"%d\", $LAP_W}")"x0"
    PRIMARY="$LAPTOP"
fi

# Apply scaling and positioning
$XRANDR --output "$EXT" --mode ${EXT_W}x${EXT_H} \
        --scale ${scaleX}x${scaleY} --pos $EXT_POS --primary
$XRANDR --output "$LAPTOP" --mode ${LAP_W}x${LAP_H} \
        --scale 1x1 --pos $LAP_POS

# Optional: adjust GNOME scaling for consistency (run in user session)
# Uncomment and customize if you use GNOME:
# function run-in-user-session() {
#   _display_id=":$(find /tmp/.X11-unix/* | sed 's#/tmp/.X11-unix/X##' | head -n1)"
#   _user=$(who | grep "(${_display_id})" | awk '{print $1}')
#   _uid=$(id -u "$_user")
#   env DISPLAY=$_display_id DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$_uid/bus \
#       sudo -Hu "$_user" gsettings set org.gnome.desktop.interface text-scaling-factor $(awk "BEGIN {printf \"%.2f\", $scaleX}")
#}
#run-in-user-session

echo "Applied scaling for $EXT: ${scaleX}x${scaleY} (target ${TARGET_WIDTH}x${TARGET_HEIGHT}), framebuffer ${FB_W}x${FB_H}."

Não funcionou

Segunda correção

#!/usr/bin/env bash

# Improved HDMI/Laptop multi-monitor scaling script
# Dynamically detects internal (eDP) and external outputs via xrandr --listmonitors
# Computes scale factors to achieve a target virtual resolution on the external display
# Correctly assigns scaling to the intended monitor regardless of UI primary settings

set -euo pipefail

XRANDR="/usr/bin/xrandr"
# Default target virtual resolution for external monitor (override via env vars)
TARGET_WIDTH=${TARGET_WIDTH:-2560}
TARGET_HEIGHT=${TARGET_HEIGHT:-1440}
# Position of external relative to laptop: "right" or "left"
POSITION="right"
# Whether to explicitly set --primary on external (yes|no)
MAKE_PRIMARY="no"

function usage() {
    cat << EOF
Usage: $0 [--position left|right] [--primary yes|no]

  --position    Place external monitor to the left or right of laptop (default: right)
  --primary     Mark external as primary display (yes or no, default: no)

You can also override TARGET_WIDTH and TARGET_HEIGHT via environment variables.
EOF
    exit 1
}

# Parse arguments
while [[ $# -gt 0 ]]; do
    case "$1" in
        --position)
            POSITION="$2"; shift 2;;
        --primary)
            MAKE_PRIMARY="$2"; shift 2;;
        -h|--help)
            usage;;
        *)
            echo "Unknown option: $1" >&2; usage;;
    esac
done

# Gather monitor list and primary from xrandr --listmonitors
readarray -t MONS < <($XRANDR --listmonitors | awk 'NR>1{print $4}')
PRIMARY_MON=$($XRANDR --listmonitors | awk '/\*/{print $4}')

# Assign laptop (internal) and external monitors
if [[ "$PRIMARY_MON" =~ eDP ]]; then
    LAPTOP="$PRIMARY_MON"
    for M in "${MONS[@]}"; do [[ "$M" != "$LAPTOP" ]] && EXT="$M"; done
else
    EXT="$PRIMARY_MON"
    for M in "${MONS[@]}"; do [[ "$M" != "$EXT" ]] && LAPTOP="$M"; done
fi

if [[ -z "${LAPTOP:-}" || -z "${EXT:-}" ]]; then
    echo "Error: Could not uniquely detect both laptop (eDP) and external monitor." >&2
    exit 1
fi

# Function to get native resolution of a monitor
function get_native() {
    local MON=$1
    $XRANDR --query | awk -v MON="$MON" '$1==MON { if (match($0, /([0-9]+)x([0-9]+)\+/, a)) print a[1], a[2] }'
}
read LAP_W LAP_H < <(get_native "$LAPTOP")
read EXT_W EXT_H < <(get_native "$EXT")

# Calculate precise scale factors
scaleX=$(awk "BEGIN {printf \"%.10f\", $TARGET_WIDTH/$EXT_W}")
scaleY=$(awk "BEGIN {printf \"%.10f\", $TARGET_HEIGHT/$EXT_H}")

# Compute framebuffer size (rounded up)
FB_W=$(awk "BEGIN {printf \"%d\", ($LAP_W + $EXT_W*$scaleX + 0.5)}")
FB_H=$(awk "BEGIN {tmp = ($LAP_H > $EXT_H*$scaleY ? $LAP_H : $EXT_H*$scaleY); printf \"%d\", (tmp + 0.5)}")

# Apply framebuffer
$XRANDR --fb ${FB_W}x${FB_H}

# Compute positions
if [[ "$POSITION" == "left" ]]; then
    EXT_POS="0x0"
    LAP_POS=$(awk "BEGIN {printf \"%d\", $TARGET_WIDTH}")"x0"
else
    LAP_POS="0x0"
    EXT_POS=$(awk "BEGIN {printf \"%d\", $LAP_W}")"x0"
fi

# Apply laptop (internal) monitor with no scaling
$XRANDR --output "$LAPTOP" --mode ${LAP_W}x${LAP_H} --scale 1x1 --pos $LAP_POS

# Apply external monitor scaling and optional primary
PRIMARY_FLAG=""
[[ "$MAKE_PRIMARY" == "yes" ]] && PRIMARY_FLAG="--primary"
$XRANDR --output "$EXT" --mode ${EXT_W}x${EXT_H} --scale ${scaleX}x${scaleY} --pos $EXT_POS $PRIMARY_FLAG

# Summary
echo "Applied scaling to $EXT: ${scaleX}x${scaleY} (target ${TARGET_WIDTH}x${TARGET_HEIGHT}); framebuffer ${FB_W}x${FB_H}."
echo "Laptop ($LAPTOP) reset to ${LAP_W}x${LAP_H} at pos $LAP_POS."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment