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
|
||||
|
||||
### 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
|
||||
|
||||
- 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() {
|
||||
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)
|
||||
width=$(effective SCREEN_WIDTH)
|
||||
height=$(effective SCREEN_HEIGHT)
|
||||
|
|
@ -322,6 +322,7 @@ show_state() {
|
|||
dri_prime=$(effective DRI_PRIME)
|
||||
prime_offload=$(effective __NV_PRIME_RENDER_OFFLOAD)
|
||||
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,
|
||||
# since the modes are mutually exclusive. AMD hybrid is identified by both
|
||||
|
|
@ -354,7 +355,7 @@ show_state() {
|
|||
fi
|
||||
|
||||
# Replace $HOME with ~ so the config path fits the panel column.
|
||||
local conf_display="${CONF/#$HOME/~}"
|
||||
local conf_display="${CONF/#$HOME/\~}"
|
||||
|
||||
cat <<EOF
|
||||
Gaming Mode display settings${pending_label}
|
||||
|
|
@ -363,6 +364,7 @@ Gaming Mode display settings${pending_label}
|
|||
Resolution : ${resolution_label}
|
||||
Refresh rate : ${refresh:-<auto>} Hz
|
||||
GPU mode : ${gpu_mode}
|
||||
Hide monitor : ${disable_connector:-<none>}
|
||||
|
||||
Config file : ${conf_display}
|
||||
EOF
|
||||
|
|
@ -396,6 +398,49 @@ choose_monitor() {
|
|||
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() {
|
||||
refresh_monitor_data
|
||||
local choice w h
|
||||
|
|
@ -699,13 +744,15 @@ main() {
|
|||
"Resolution" \
|
||||
"Refresh rate" \
|
||||
"GPU" \
|
||||
"Hide monitor" \
|
||||
"$save_label" \
|
||||
"$cancel_label")
|
||||
case "$action" in
|
||||
"Monitor") choose_monitor ;;
|
||||
"Resolution") choose_resolution ;;
|
||||
"Refresh rate") choose_refresh_rate ;;
|
||||
"GPU") choose_gpu ;;
|
||||
"Monitor") choose_monitor ;;
|
||||
"Resolution") choose_resolution ;;
|
||||
"Refresh rate") choose_refresh_rate ;;
|
||||
"GPU") choose_gpu ;;
|
||||
"Hide monitor") choose_monitor_to_disable ;;
|
||||
"Save and exit"*)
|
||||
if ! confirm_risky_save; then
|
||||
continue
|
||||
|
|
@ -713,7 +760,7 @@ main() {
|
|||
flush_pending
|
||||
clear
|
||||
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
|
||||
sleep 1
|
||||
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)
|
||||
# -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
|
||||
# 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 -f gamescope-session 2>/dev/null || true
|
||||
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
|
||||
sleep 0.3
|
||||
sudo -n systemctl restart sddm
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue