Created
March 9, 2026 15:10
-
-
Save Kaylebor/520dcede2a592bbd39bbc98e9777c331 to your computer and use it in GitHub Desktop.
Script to simplify display switching in KDE with Sunshine remote
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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