Skip to content

Instantly share code, notes, and snippets.

@Kaylebor
Created March 9, 2026 15:10
Show Gist options
  • Select an option

  • Save Kaylebor/520dcede2a592bbd39bbc98e9777c331 to your computer and use it in GitHub Desktop.

Select an option

Save Kaylebor/520dcede2a592bbd39bbc98e9777c331 to your computer and use it in GitHub Desktop.
Script to simplify display switching in KDE with Sunshine remote
#!/usr/bin/env bash
LOG_FILE="$HOME/.cache/sunshine_screen.log"
STATE_FILE="$HOME/.cache/sunshine_state"
mkdir -p "$(dirname "$STATE_FILE")"
log() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $*" >> "$LOG_FILE"; }
# Dependency check
if ! command -v kscreen-doctor &>/dev/null || ! command -v jq &>/dev/null; then
log "Error: kscreen-doctor or jq missing"
exit 1
fi
# Check for reset flag
reset_display=0
for arg in "$@"; do
[[ "$arg" == "--reset-display" ]] && reset_display=1
done
if [[ $reset_display -eq 1 ]]; then
log "Action: RESET"
if [[ -f "$STATE_FILE" ]]; then
# Read saved state: ID WIDTH HEIGHT REFRESH
read -r saved_id width height refresh < "$STATE_FILE"
log "Restoring saved state: ${width}x${height}@${refresh} on output ${saved_id}"
rm "$STATE_FILE"
id="$saved_id"
else
log "No state file found, falling back to max native resolution"
# Fallback: Restore to the highest resolution supported by the monitor
read -r id width height refresh < <(
kscreen-doctor -j | jq -r '.outputs | min_by(.priority) | . as $out | .modes | max_by(.size.width * .size.height) as $m | "\($out.id) \($m.size.width) \($m.size.height) \($m.refreshRate | floor)"'
)
fi
else
log "Action: CONNECT"
# Capture ORIGINAL state only if not already saved
if [[ ! -f "$STATE_FILE" ]]; then
# Exact filter verified against user JSON: matches currentModeId
kscreen-doctor -j | jq -r '
.outputs | min_by(.priority) | . as $out |
.modes[] | select(.id == $out.currentModeId) |
"\($out.id) \(.size.width) \(.size.height) \(.refreshRate | floor)"
' > "$STATE_FILE"
saved_state=$(cat "$STATE_FILE")
if [[ -z "$saved_state" ]]; then
log "CRITICAL: Failed to capture original state using currentModeId!"
# Last resort fallback to max mode
kscreen-doctor -j | jq -r '.outputs | min_by(.priority) | . as $out | .modes | max_by(.size.width * .size.height) as $m | "\($out.id) \($m.size.width) \($m.size.height) \($m.refreshRate | floor)"' > "$STATE_FILE"
saved_state=$(cat "$STATE_FILE")
fi
log "Saved original state: $saved_state"
fi
# Resolve target values from environment variables
width="${APOLLO_CLIENT_WIDTH:-$SUNSHINE_CLIENT_WIDTH}"
height="${APOLLO_CLIENT_HEIGHT:-$SUNSHINE_CLIENT_HEIGHT}"
refresh="${APOLLO_CLIENT_FPS:-$SUNSHINE_CLIENT_FPS}"
# Fallback to the saved native resolution if env vars are missing
if [[ -z "$width" ]]; then
# shellcheck disable=SC2034
read -r _ width height refresh < "$STATE_FILE"
fi
# Ensure we have a valid ID
id=$(awk '{print $1}' "$STATE_FILE")
log "Targeting client mode: ${width}x${height}@${refresh} on output ${id}"
fi
# Final formatting: ensure refresh is an integer
refresh=$(LC_NUMERIC=C printf '%.0f' "$refresh" 2>/dev/null || echo "$refresh")
# Execute with a retry to handle KWin busy states
if ! kscreen-doctor "output.${id}.mode.${width}x${height}@${refresh}" &>> "$LOG_FILE"; then
log "Command failed, retrying in 0.5s..."
sleep 0.5
kscreen-doctor "output.${id}.mode.${width}x${height}@${refresh}" &>> "$LOG_FILE"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment