Change the framerate of your linux/gnome/wayland laptop when you (un-)plug it
linux-change-hz-on-ac.md edited
92 lines 3.5 kB view raw view code

Install displayconfig-mutter (Fedora)#

sudo dnf copr enable eaglesemanation/displayconfig-mutter
sudo dnf install displayconfig-mutter
displayconfig-mutter list

should return something along the lines of

┌───────────┬────────┬──────────────┬────────────┬──────────────┬─────────────────────┬─────┬─────┐
│ Connector │ Vendor │ Product name │ Resolution │ Refresh rate │ Scaling             │ VRR │ HDR │
├───────────┼────────┼──────────────┼────────────┼──────────────┼─────────────────────┼─────┼─────┤
│ eDP-1     │ CSW    │ MNG007ZA1-3  │ 3200x2000  │ 60           │ 166.66666269302368% │ No  │ No  │
└───────────┴────────┴──────────────┴────────────┴──────────────┴─────────────────────┴─────┴─────┘

In this case, my monitor is eDP-1.

Configure your env#

Here, you configure your desired connector (aka, your display), the framerate for when your laptop is unplugged (LOW_HZ) and the framerate for when AC is connected (HIGH_HZ).

cat > ~/.config/rr-autoswitch.env <<'EOF'
CONNECTOR=eDP-1
LOW_HZ=60
HIGH_HZ=165
EOF

Create the service#

cat > ~/.local/bin/rr-watch-ac.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

# Allow overrides via ~/.config/rr-autoswitch.env
[ -f "$HOME/.config/rr-autoswitch.env" ] && source "$HOME/.config/rr-autoswitch.env"
CONNECTOR="${CONNECTOR:-eDP-1}"
LOW_HZ="${LOW_HZ:-60}"
HIGH_HZ="${HIGH_HZ:-165}"

apply_rr() {
  local hz="$1"
  # Keep current resolution, only change refresh rate
  displayconfig-mutter set --connector "$CONNECTOR" --refresh-rate "$hz"
}

on_battery_now() {
  # returns 0 if on battery, 1 otherwise
  busctl get-property org.freedesktop.UPower /org/freedesktop/UPower org.freedesktop.UPower OnBattery \
    | grep -q 'true'
}

# Apply once at start
if on_battery_now; then apply_rr "$LOW_HZ"; else apply_rr "$HIGH_HZ"; fi

# React to future changes (UPower PropertiesChanged on /org/freedesktop/UPower)
dbus-monitor --system "type='signal',sender='org.freedesktop.UPower',\
interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',\
path='/org/freedesktop/UPower'" \
| while read -r line; do
    if grep -q "OnBattery" <<<"$line"; then
      # tiny debounce so we read the new state
      sleep 0.3
      if on_battery_now; then apply_rr "$LOW_HZ"; else apply_rr "$HIGH_HZ"; fi
    fi
  done
EOF
chmod +x ~/.local/bin/rr-watch-ac.sh
cat > ~/.config/systemd/user/rr-autoswitch.service <<'EOF'
[Unit]
Description=Auto-switch monitor refresh rate on AC/battery (Wayland/GNOME)
After=graphical-session.target
Wants=graphical-session.target

[Service]
EnvironmentFile=%h/.config/rr-autoswitch.env
ExecStart=%h/.local/bin/rr-watch-ac.sh
Restart=always
RestartSec=1

[Install]
WantedBy=default.target
EOF
systemctl --user daemon-reload
systemctl --user enable --now rr-autoswitch.service