v0.1.11 — Multi-monitor handling: disable an auxiliary monitor before Gaming Mode
Reported on a Framework Desktop (AMD AI MAX 380) + Gigabyte M27Q +
LG DualUp setup: with both monitors attached, gamescope would either
land on the wrong screen or refuse to start, and writing
OUTPUT_CONNECTOR=DP-X alone wasn't enough to fix it. The workaround
shipped by the user was to manually patch /usr/share/gamescope-session-plus
to disable the other monitor before launching gamescope.
DeckShift now handles this natively, without touching the
gamescope-session-plus script (which is ChimeraOS's, not ours):
- New env var OUTPUT_CONNECTOR_TO_DISABLE (single connector or
comma list) written to ~/.config/environment.d/gamescope-session-plus.conf
alongside the other display keys.
- switch-to-gaming reads it and runs `hyprctl keyword monitor X,disable`
for each listed connector BEFORE the SDDM restart, while Hyprland is
still alive (hyprctl needs a live IPC socket). The disable is
runtime-only — Hyprland's static config isn't touched — so when the
user returns from Gaming Mode the new Hyprland reads its config fresh
and the monitor comes back automatically. No re-enable step needed.
- Settings TUI exposes this as a "Hide monitor" main-menu item. The
picker lists every connected monitor EXCEPT the gaming one (so a
user can't accidentally pick the same connector they just set as
OUTPUT_CONNECTOR) and includes a "(clear)" entry.
Also fixes a latent bug in v0.1.10's config-path shortening:
${CONF/#$HOME/~} was supposed to render the conf path with ~ but
bash applies tilde-expansion to the replacement side, re-expanding ~
to $HOME and making the substitution a no-op. Escaped as \~ now.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
aaa2f3d768
commit
52c883b780
3 changed files with 84 additions and 8 deletions
|
|
@ -10,6 +10,13 @@ Lineage: forked from [Super-Shift-S-Omarchy-Deck-Mode](https://git.no-signal.uk/
|
||||||
|
|
||||||
## What's New
|
## What's New
|
||||||
|
|
||||||
|
### v0.1.11 — Multi-monitor handling: disable an auxiliary monitor before Gaming Mode
|
||||||
|
|
||||||
|
- New env var `OUTPUT_CONNECTOR_TO_DISABLE` (single connector or comma list). When set, `switch-to-gaming` runs `hyprctl keyword monitor <conn>,disable` for each listed connector *before* SDDM restart, while Hyprland is still alive. The disable is runtime-only — when the user returns from Gaming Mode, the new Hyprland reads its static config fresh and the monitor comes back automatically.
|
||||||
|
- Settings TUI exposes this as a **"Hide monitor"** option in the main menu and on the state panel. The picker lists every connected monitor *except* the gaming one, plus a "(clear)" entry to remove the override.
|
||||||
|
- Fixes a reported issue on multi-monitor setups (e.g. Framework Desktop + LG DualUp + Gigabyte M27Q) where gamescope would either land on the wrong screen or refuse to start when both monitors were attached. The previous workaround was to physically unplug the second monitor.
|
||||||
|
- Also fixes a latent bug from v0.1.10: the config-file path in the TUI was supposed to render with `~` instead of `/home/<user>` to fit the panel, but bash's tilde-expansion on the replacement side of `${var/#pat/~}` re-expanded `~` back to `$HOME`, making the substitution a no-op. The replacement is now escaped as `\~`.
|
||||||
|
|
||||||
### v0.1.10 — Settings TUI layout polish
|
### v0.1.10 — Settings TUI layout polish
|
||||||
|
|
||||||
- Banner, state panel, menu header, and menu items now share a single centred panel column rather than each block centring itself independently. The TUI feels visibly aligned in a Walker floating window of any width — no more drifting elements off to the left while the menu floats to the right.
|
- Banner, state panel, menu header, and menu items now share a single centred panel column rather than each block centring itself independently. The TUI feels visibly aligned in a Walker floating window of any width — no more drifting elements off to the left while the menu floats to the right.
|
||||||
|
|
|
||||||
|
|
@ -307,7 +307,7 @@ list_gpus() {
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
show_state() {
|
show_state() {
|
||||||
local connector width height refresh vk_adapter dri_prime prime_offload mesa_vk_select
|
local connector width height refresh vk_adapter dri_prime prime_offload mesa_vk_select disable_connector
|
||||||
connector=$(effective OUTPUT_CONNECTOR)
|
connector=$(effective OUTPUT_CONNECTOR)
|
||||||
width=$(effective SCREEN_WIDTH)
|
width=$(effective SCREEN_WIDTH)
|
||||||
height=$(effective SCREEN_HEIGHT)
|
height=$(effective SCREEN_HEIGHT)
|
||||||
|
|
@ -322,6 +322,7 @@ show_state() {
|
||||||
dri_prime=$(effective DRI_PRIME)
|
dri_prime=$(effective DRI_PRIME)
|
||||||
prime_offload=$(effective __NV_PRIME_RENDER_OFFLOAD)
|
prime_offload=$(effective __NV_PRIME_RENDER_OFFLOAD)
|
||||||
mesa_vk_select=$(effective MESA_VK_DEVICE_SELECT)
|
mesa_vk_select=$(effective MESA_VK_DEVICE_SELECT)
|
||||||
|
disable_connector=$(effective OUTPUT_CONNECTOR_TO_DISABLE)
|
||||||
|
|
||||||
# Single GPU-mode line — shows the active mode rather than half-empty rows,
|
# Single GPU-mode line — shows the active mode rather than half-empty rows,
|
||||||
# since the modes are mutually exclusive. AMD hybrid is identified by both
|
# since the modes are mutually exclusive. AMD hybrid is identified by both
|
||||||
|
|
@ -354,7 +355,7 @@ show_state() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Replace $HOME with ~ so the config path fits the panel column.
|
# Replace $HOME with ~ so the config path fits the panel column.
|
||||||
local conf_display="${CONF/#$HOME/~}"
|
local conf_display="${CONF/#$HOME/\~}"
|
||||||
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Gaming Mode display settings${pending_label}
|
Gaming Mode display settings${pending_label}
|
||||||
|
|
@ -363,6 +364,7 @@ Gaming Mode display settings${pending_label}
|
||||||
Resolution : ${resolution_label}
|
Resolution : ${resolution_label}
|
||||||
Refresh rate : ${refresh:-<auto>} Hz
|
Refresh rate : ${refresh:-<auto>} Hz
|
||||||
GPU mode : ${gpu_mode}
|
GPU mode : ${gpu_mode}
|
||||||
|
Hide monitor : ${disable_connector:-<none>}
|
||||||
|
|
||||||
Config file : ${conf_display}
|
Config file : ${conf_display}
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -396,6 +398,49 @@ choose_monitor() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# choose_monitor_to_disable — picks a connector to physically disable (via
|
||||||
|
# `hyprctl keyword monitor X,disable`) before Gaming Mode launches.
|
||||||
|
#
|
||||||
|
# Why this exists: with multiple monitors connected, gamescope-session-plus
|
||||||
|
# can't reliably target a single output via OUTPUT_CONNECTOR alone — on some
|
||||||
|
# setups it picks the wrong screen, on others it fails to start at all
|
||||||
|
# (reported on a Framework Desktop + LG DualUp + Gigabyte M27Q setup).
|
||||||
|
# Disabling the auxiliary monitor right before SDDM restart guarantees
|
||||||
|
# gamescope only sees the gaming display.
|
||||||
|
#
|
||||||
|
# The disable is runtime-only (hyprctl keyword, not a config edit), so when
|
||||||
|
# the user returns from Gaming Mode the fresh Hyprland reads its static
|
||||||
|
# config and the monitor comes back automatically — no re-enable step needed.
|
||||||
|
choose_monitor_to_disable() {
|
||||||
|
local choice gaming_monitor
|
||||||
|
gaming_monitor=$(effective OUTPUT_CONNECTOR)
|
||||||
|
mapfile -t connected < <(list_connected_monitors)
|
||||||
|
local -a labels=()
|
||||||
|
local entry conn
|
||||||
|
for entry in "${connected[@]}"; do
|
||||||
|
conn="${entry%%|*}"
|
||||||
|
# Exclude the gaming monitor — disabling it would be self-defeating.
|
||||||
|
[[ "$conn" == "$gaming_monitor" ]] && continue
|
||||||
|
labels+=("$entry")
|
||||||
|
done
|
||||||
|
labels+=("(clear / don't hide any monitor)")
|
||||||
|
if (( ${#labels[@]} == 1 )); then
|
||||||
|
gum confirm "Only the gaming monitor is connected. Set a connector to disable manually?" || return 0
|
||||||
|
local connector
|
||||||
|
connector=$(ginput --prompt "Connector to disable (e.g. HDMI-A-1): ")
|
||||||
|
[[ -z "$connector" ]] && return 0
|
||||||
|
pending_set OUTPUT_CONNECTOR_TO_DISABLE "$connector"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
choice=$(cmenu "Select monitor to disable while gaming" "${labels[@]}")
|
||||||
|
[[ -z "$choice" ]] && return 0
|
||||||
|
if [[ "$choice" == "(clear"* ]]; then
|
||||||
|
pending_unset OUTPUT_CONNECTOR_TO_DISABLE
|
||||||
|
else
|
||||||
|
pending_set OUTPUT_CONNECTOR_TO_DISABLE "${choice%%|*}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
choose_resolution() {
|
choose_resolution() {
|
||||||
refresh_monitor_data
|
refresh_monitor_data
|
||||||
local choice w h
|
local choice w h
|
||||||
|
|
@ -699,13 +744,15 @@ main() {
|
||||||
"Resolution" \
|
"Resolution" \
|
||||||
"Refresh rate" \
|
"Refresh rate" \
|
||||||
"GPU" \
|
"GPU" \
|
||||||
|
"Hide monitor" \
|
||||||
"$save_label" \
|
"$save_label" \
|
||||||
"$cancel_label")
|
"$cancel_label")
|
||||||
case "$action" in
|
case "$action" in
|
||||||
"Monitor") choose_monitor ;;
|
"Monitor") choose_monitor ;;
|
||||||
"Resolution") choose_resolution ;;
|
"Resolution") choose_resolution ;;
|
||||||
"Refresh rate") choose_refresh_rate ;;
|
"Refresh rate") choose_refresh_rate ;;
|
||||||
"GPU") choose_gpu ;;
|
"GPU") choose_gpu ;;
|
||||||
|
"Hide monitor") choose_monitor_to_disable ;;
|
||||||
"Save and exit"*)
|
"Save and exit"*)
|
||||||
if ! confirm_risky_save; then
|
if ! confirm_risky_save; then
|
||||||
continue
|
continue
|
||||||
|
|
@ -713,7 +760,7 @@ main() {
|
||||||
flush_pending
|
flush_pending
|
||||||
clear
|
clear
|
||||||
echo ""
|
echo ""
|
||||||
gum style --foreground 212 "Settings saved to ${CONF/#$HOME/~}" | pad_block
|
gum style --foreground 212 "Settings saved to ${CONF/#$HOME/\~}" | pad_block
|
||||||
gum style --foreground 244 "Changes apply next time you enter Gaming Mode (Super+Shift+S)." | pad_block
|
gum style --foreground 244 "Changes apply next time you enter Gaming Mode (Super+Shift+S)." | pad_block
|
||||||
sleep 1
|
sleep 1
|
||||||
return 0
|
return 0
|
||||||
|
|
|
||||||
24
deckshift.sh
24
deckshift.sh
|
|
@ -34,7 +34,7 @@ set -Euo pipefail
|
||||||
# -u: Treat unset variables as errors (catches typos in variable names)
|
# -u: Treat unset variables as errors (catches typos in variable names)
|
||||||
# -o pipefail: A pipeline fails if ANY command in it fails, not just the last one
|
# -o pipefail: A pipeline fails if ANY command in it fails, not just the last one
|
||||||
|
|
||||||
DECKSHIFT_VERSION="0.1.10"
|
DECKSHIFT_VERSION="0.1.11"
|
||||||
|
|
||||||
# Resolve the directory this script lives in so we can find sibling files like
|
# Resolve the directory this script lives in so we can find sibling files like
|
||||||
# bin/deckshift-settings and applications/deckshift-settings.desktop when
|
# bin/deckshift-settings and applications/deckshift-settings.desktop when
|
||||||
|
|
@ -2219,6 +2219,28 @@ notify-send -u normal -t 2000 "Gaming Mode" "Switching to Gaming Mode..." 2>/dev
|
||||||
pkill -9 gamescope 2>/dev/null || true
|
pkill -9 gamescope 2>/dev/null || true
|
||||||
pkill -9 -f gamescope-session 2>/dev/null || true
|
pkill -9 -f gamescope-session 2>/dev/null || true
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
|
# Multi-monitor handling — gamescope-session-plus picks an output by env, but
|
||||||
|
# with two monitors connected it sometimes lands on the wrong one (or refuses
|
||||||
|
# to start). If OUTPUT_CONNECTOR_TO_DISABLE is set in the user's env conf,
|
||||||
|
# disable those connectors via hyprctl while Hyprland is still alive so
|
||||||
|
# gamescope only sees the gaming display. The disable is runtime-only (no
|
||||||
|
# config edit) so when the user returns from Gaming Mode the new Hyprland
|
||||||
|
# reads its static config fresh and the monitor comes back automatically.
|
||||||
|
ENV_CONF="$HOME/.config/environment.d/gamescope-session-plus.conf"
|
||||||
|
if [[ -f "$ENV_CONF" ]]; then
|
||||||
|
TO_DISABLE=$(awk -F= '$1=="OUTPUT_CONNECTOR_TO_DISABLE" { sub(/^[^=]*=/,""); v=$0 } END { print v }' "$ENV_CONF")
|
||||||
|
if [[ -n "$TO_DISABLE" ]]; then
|
||||||
|
IFS=',' read -ra DISABLE_LIST <<< "$TO_DISABLE"
|
||||||
|
for conn in "${DISABLE_LIST[@]}"; do
|
||||||
|
conn="${conn// /}"
|
||||||
|
[[ -z "$conn" ]] && continue
|
||||||
|
hyprctl keyword monitor "${conn},disable" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
sleep 0.5
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
sudo -n chvt 2 2>/dev/null || true
|
sudo -n chvt 2 2>/dev/null || true
|
||||||
sleep 0.3
|
sleep 0.3
|
||||||
sudo -n systemctl restart sddm
|
sudo -n systemctl restart sddm
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue