Skip to content

Instantly share code, notes, and snippets.

@NextdoorPsycho
Last active July 16, 2025 22:15
Show Gist options
  • Save NextdoorPsycho/013ad83455c8492888c9c424d7695dd7 to your computer and use it in GitHub Desktop.
Save NextdoorPsycho/013ad83455c8492888c9c424d7695dd7 to your computer and use it in GitHub Desktop.
This script enables Apple’s Game Mode by launching the Chess app in fullscreen mode, which triggers Game Mode. It then deprioritizes the Chess process to minimize its resource usage, keeping the performance boost of Game Mode active without significant CPU/GPU drain from Chess. After making Chess fullscreen, the script switches focus back to the…
#!/bin/bash
LOG_FILE="chess_script.log"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
# Requesting sudo privileges at the start
log "Requesting sudo privileges..."
sudo -v
if [ $? -ne 0 ]; then
log "Error: Failed to obtain sudo privileges."
exit 1
fi
# Keep sudo privileges alive while the script runs
while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &
# Log the start of the script
log "Starting Chess script..."
# Launch Chess if it's not running
sleep 1
if ! pgrep -x "Chess" > /dev/null; then
log "Chess is not running. Launching Chess..."
open -a Chess
sleep 2 # Give it a moment to open
if pgrep -x "Chess" > /dev/null; then
log "Chess launched successfully."
else
log "Error: Failed to launch Chess."
exit 1
fi
else
log "Chess is already running."
fi
sleep 1
# Make Chess fullscreen by simulating the keystroke Command + Control + F
osascript -e 'tell application "System Events" to keystroke "f" using {command down, control down}'
if [ $? -eq 0 ]; then
log "Chess set to fullscreen successfully."
else
log "Error: Failed to set Chess to fullscreen."
fi
sleep 1
# Switch to desktop space by simulating the keystroke Control + Left Arrow
osascript -e 'tell application "System Events" to key code 123 using {control down}'
if [ $? -eq 0 ]; then
log "Switched focus to desktop successfully."
else
log "Error: Failed to switch focus to desktop."
fi
sleep 1
# Get the process ID of Chess
chess_pid=$(pgrep -x "Chess")
# Lower the priority of Chess to give more CPU to other processes (-n 20 makes it the lowest priority)
if [ ! -z "$chess_pid" ]; then
log "Setting Chess process priority to lowest (nice value 20)..."
sudo renice 20 -p $chess_pid
if [ $? -eq 0 ]; then
log "Chess priority set successfully."
else
log "Error: Failed to set Chess priority."
fi
else
log "Error: Unable to retrieve Chess process ID."
exit 1
fi
sleep 1
# List all active window processes and allow the user to pick one to change its priority
log "Listing active window processes..."
active_processes=$(osascript -e 'tell application "System Events" to get the name of every application process whose visible is true')
IFS=", " read -r -a process_array <<< "$active_processes"
echo "Active window processes:"
for i in "${!process_array[@]}"; do
echo "[$i] ${process_array[$i]}"
done
read -p "Enter the number of the process you want to change the priority for: " process_number
if [[ $process_number -ge 0 && $process_number -lt ${#process_array[@]} ]]; then
selected_process="${process_array[$process_number]}"
selected_pid=$(pgrep -x "$selected_process")
if [ -z "$selected_pid" ]; then
log "Error: Unable to retrieve process ID for $selected_process."
exit 1
fi
read -p "Enter the new priority for $selected_process (-20 for max, 0 for average, 20 for lowest): " new_priority
if [[ $new_priority =~ ^-?[0-9]+$ && $new_priority -ge -20 && $new_priority -le 20 ]]; then
log "Setting priority of $selected_process (PID: $selected_pid) to $new_priority..."
sudo renice $new_priority -p $selected_pid
if [ $? -eq 0 ]; then
log "$selected_process priority set to $new_priority successfully."
else
log "Error: Failed to set priority for $selected_process."
fi
else
log "Invalid priority value: $new_priority. Please enter a value between -20 and 20."
exit 1
fi
else
log "Invalid selection. Please enter a number between 0 and $((${#process_array[@]} - 1))."
exit 1
fi
sleep 1
log "Chess script completed."
@iFloneUEFN
Copy link

First of all thank you so much for that, I've been waiting for such thing for softwares like UEFN. Can't wait to test it!

@iFloneUEFN
Copy link

iFloneUEFN commented Nov 24, 2024

Hello, i've improved your code as it seems you don't need to run Chess app. Here's the revised code:

#!/bin/bash
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# Requesting sudo privileges at the start
echo "${BLUE}Requesting sudo privileges...${NC}"
sudo -v
if [ $? -ne 0 ]; then
    echo "${RED}Error: Failed to obtain sudo privileges.${NC}"
    exit 1
fi

# Keep sudo privileges alive while the script runs
while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &

# Prompt the user for selecting an option
while true; do
    while true; do
        echo "${BLUE}Select an option:${RED}"
        echo "WARNING: Enabling Game mode for your process MAY or MAY NOT improve its overall performance.\nThus, keep this in mind while using this tool. In case you encounter "errors" after launching\nyour game or seems slower, restart your Mac.\n\nIf a process is already running in Game mode, please set first its priority number to 20. ${YELLOW}"
        echo "[1] Revert Game mode default behavior"
        echo "[2] Force Game mode on and run it for a specific software"
        echo "[3] Exit"
        read -p "$(echo ${BLUE}Enter the number of the option you want to select:${NC})" user_option

        if [[ $user_option -eq 1 || $user_option -eq 2 || $user_option -eq 3 ]]; then
            break
        else
            echo "${RED}Invalid option. Please enter 1, 2, or 3.${NC}"
        fi
    done
    
    if [[ $user_option -eq 1 ]]; then
        # Revert Game mode default behavior
        sudo /Applications/Xcode.app/Contents/Developer/usr/bin/gamepolicyctl game-mode set auto
        echo "${GREEN}Successfully restored Game mode default behavior.${NC}"
        exit 0
    elif [[ $user_option -eq 2 ]]; then
        # Force Game mode on
        sudo /Applications/Xcode.app/Contents/Developer/usr/bin/gamepolicyctl game-mode set on
        
        # List all active window processes and allow the user to pick one to change its priority
        echo "${BLUE}Listing active window processes...${NC}"
        active_processes=$(osascript -e 'tell application "System Events" to get the name of every application process whose visible is true')

        # Split the active_processes output by comma and handle multi-word process names correctly
        IFS=","
        read -r -a process_array <<< "$active_processes"

        echo "${BLUE}Active window processes:${NC}"
        for i in "${!process_array[@]}"; do
            process_name=$(echo "${process_array[$i]}" | xargs)
            echo "${YELLOW} [$i] $process_name${NC}"
        done

        # Select a process ID
        while true; do
            read -p "$(echo ${BLUE}Enter the number of the process you want to change the priority for:${NC})" process_number

            if [[ $process_number -ge 0 && $process_number -lt ${#process_array[@]} ]]; then
                selected_process=$(echo "${process_array[$process_number]}" | xargs)
                selected_pid=$(pgrep -x "$selected_process")

                if [ -z "$selected_pid" ]; then
                    echo "${RED}Error: Unable to retrieve process ID for $selected_process.${NC}"
                else
                    break
                fi
            else
                echo "${RED}Invalid selection. Please enter a number between 0 and $((${#process_array[@]} - 1)).${NC}"
            fi
        done

        # Set the priority number of the selected process
        while true; do
            read -p "$(echo ${BLUE}Enter the new priority for $selected_process. -20 for max, 0 for average, 20 for lowest:${NC})" new_priority

            if [[ $new_priority =~ ^-?[0-9]+$ && $new_priority -ge -20 && $new_priority -le 20 ]]; then
                echo "${GREEN}Setting priority of $selected_process (PID: $selected_pid) to $new_priority...${NC}"
                sudo renice $new_priority -p $selected_pid
                if [ $? -eq 0 ]; then
                    echo "${GREEN}$selected_process priority set to $new_priority successfully.${NC}"
                    exit 0
                else
                    echo "${RED}Error: Failed to set priority for $selected_process.${NC}"
                fi
            else
                echo "${RED}Invalid priority value: $new_priority. Please enter a value between -20 and 20.${NC}"
            fi
        done
    elif [[ $user_option -eq 3 ]]; then
        exit 0
    fi
done

sleep 1

echo "${GREEN}Game mode script completed.${NC}"

Copy link

ghost commented Mar 5, 2025

well done, but it doesnt work for me on macos 15.4 beta 2, so i changed some stuff: https://gist.github.com/daburykin/7a71a5cf92e198c627eb3e169c50f1ba

@Laesx
Copy link

Laesx commented May 20, 2025

Welp the user above deleted his account, does anyone have the updated script?

@Pentium-3
Copy link

Hello, I’ve tried out a new version. Jules from Google created this custom version. He mentions that this commit "significantly refactors and improves the chess_script.sh."

#!/bin/bash
#
# chess_script.sh: A script to manage macOS Game Mode and process priorities.
# This script allows users to:
# 1. Revert Game Mode to its default behavior.
# 2. Force Game Mode on and set the priority of a selected application.
# It requires sudo privileges for some operations.

# Color definitions for script output
RED='\033[0;31m'    # Red
GREEN='\033[0;32m'  # Green
YELLOW='\033[0;33m' # Yellow
BLUE='\033[0;34m'   # Blue
NC='\033[0m'       # No Color (to reset to default)

# Define the path to gamepolicyctl
GAMEPOLICYCTL_PATH="/Applications/Xcode.app/Contents/Developer/usr/bin/gamepolicyctl"

# Check if gamepolicyctl exists
if [ ! -f "$GAMEPOLICYCTL_PATH" ]; then
    echo "${RED}Error: gamepolicyctl not found at $GAMEPOLICYCTL_PATH. Please ensure Xcode Command Line Tools are installed.${NC}"
    exit 1
fi

# Function to request sudo privileges
# Prompts the user for sudo password upfront using `sudo -v`.
# Exits script if sudo privileges are not obtained.
# Side effects: Caches sudo credentials for subsequent sudo commands.
request_sudo() {
    echo "${BLUE}This script needs sudo privileges for some operations.${NC}"
    echo "${BLUE}Requesting sudo privileges upfront...${NC}"
    sudo -v
    if [ $? -ne 0 ]; then
        echo "${RED}Error: Failed to obtain sudo privileges. Please run the script again and enter your password when prompted.${NC}"
        exit 1
    fi
    echo "${GREEN}Sudo privileges obtained. Specific commands will be run with sudo.${NC}"
}

# Function to display the menu and get user input
# Populates the global variable `user_option` with the user's choice.
# Loops until a valid option (1, 2, or 3) is entered.
display_menu() {
    while true; do
        echo "${BLUE}Select an option:${RED}"
        echo "WARNING: Enabling Game mode for your process MAY or MAY NOT improve its overall performance.\nThus, keep this in mind while using this tool. In case you encounter "errors" after launching\nyour game or seems slower, restart your Mac.\n\nIf a process is already running in Game mode, please set first its priority number to 20. ${YELLOW}"
        echo "[1] Revert Game mode default behavior"
        echo "[2] Force Game mode on and run it for a specific software"
        echo "[3] Exit"
        read -p "$(echo ${BLUE}Enter the number of the option you want to select:${NC})" user_option

        if [[ $user_option -eq 1 || $user_option -eq 2 || $user_option -eq 3 ]]; then
            break # Exit loop if option is valid
        else
            echo "${RED}Invalid option. Please enter 1, 2, or 3.${NC}"
        fi
    done
}

# Function to revert Game mode default behavior
# Uses `gamepolicyctl` to set Game Mode to 'auto'.
# Exits the script with 0 on success, 1 on failure.
revert_game_mode() {
    echo "${BLUE}Reverting Game mode to default behavior...${NC}" # Added info message
    sudo "$GAMEPOLICYCTL_PATH" game-mode set auto
    if [ $? -ne 0 ]; then
        echo "${RED}Error: Failed to revert Game mode to default behavior.${NC}"
        exit 1 # Exit with error status
    else
        echo "${GREEN}Successfully restored Game mode default behavior.${NC}"
        exit 0 # Exit with success status
    fi
}

# Function to force Game mode on
# Uses `gamepolicyctl` to set Game Mode to 'on'.
# Returns: 0 on success, 1 on failure.
force_game_mode() {
    echo "${BLUE}Forcing Game mode on...${NC}" # Added info message
    sudo "$GAMEPOLICYCTL_PATH" game-mode set on
    if [ $? -ne 0 ]; then
        echo "${RED}Error: Failed to force Game mode on.${NC}"
        return 1 # Return with an error code
    fi
    echo "${GREEN}Game mode forced on successfully.${NC}" # Added success message
    return 0 # Return with a success code
}

# Function to list active processes
# Uses AppleScript to get a list of visible application processes with their names and PIDs.
# Populates the global array `process_array` with entries like "AppName (PID: 123)".
# Returns: 0 on success, 1 if osascript fails or no processes are found.
list_active_processes() {
    echo "${BLUE}Listing active window processes...${NC}"
    local osascript_output # Variable to store raw output from osascript

    # AppleScript to get process names and PIDs, newline-separated
    osascript_output=$(osascript -e '
        tell application "System Events"
            set procs to every application process whose visible is true
            set output_list to {}
            repeat with p in procs
                set end of output_list to (name of p & " (PID: " & unix id of p & ")")
            end repeat
            return output_list
        end tell
    ')

    if [ -z "$osascript_output" ]; then
        echo "${RED}Error: Could not retrieve process list from osascript.${NC}"
        return 1
    fi

    # Convert comma-separated string from osascript to an array
    # osascript returns items separated by ", "
    IFS=$'\n' # Temporarily change IFS to newline if osascript -e 'return output_list' returns newline separated items
    # If osascript returns "item1, item2, item3", then the original IFS approach was better.
    # Let's assume osascript -e 'return output_list' where output_list is an AppleScript list
    # results in a string where items are separated by ", ".

    # Read the osascript output into an array, splitting by ", "
    # This is tricky because process names can have spaces, but not typically commas.
    # The AppleScript list coerced to string is "item1, item2, item3"
    # We will store each "AppName (PID: 123)" as a single array element.

    # Reset IFS to default for other commands
    # Store process strings directly into process_array
    # The osascript output for a list is "item1, item2, item3"
    # We need to make this an array in bash.
    # `read -r -a my_array <<< $(echo "$str" | tr ',' '\n')` doesn't work if items have spaces.
    # A safer way for "Name1 (PID: 123), Name2 (PID: 456)"

    # Let's change applescript to return newline separated items
    osascript_output=$(osascript -e '
        tell application "System Events"
            set procs to every application process whose visible is true
            set output_str to ""
            repeat with p in procs
                set output_str to output_str & name of p & " (PID: " & unix id of p & ")" & "\n"
            end repeat
            return output_str
        end tell
    ')

    # Check if osascript command was successful and produced output
    if [ -z "$osascript_output" ]; then
        echo "${RED}Error: Could not retrieve process list from osascript.${NC}" # Simplified error message
        return 1 # Indicate failure
    fi

    # Read newline-separated osascript output into process_array (Bash 3 compatible)
    process_array=() # Initialize an empty array
    while IFS= read -r line; do
        if [[ -n "$line" ]]; then # Ensure the line is not empty
            process_array+=("$line")
        fi
    done <<< "$osascript_output" # Feed the osascript_output to the loop

    # Safeguard: Remove any empty elements from process_array that might result from extra newlines
    # This might be redundant due to the `if [[ -n "$line" ]]` check above, but kept for safety.
    local i
    for i in "${!process_array[@]}"; do
      [[ -z "${process_array[$i]}" ]] && unset "process_array[$i]"
    done
    # Re-index array to ensure it's contiguous if elements were unset
    process_array=("${process_array[@]}")

    # Check if any processes were found
    if [ ${#process_array[@]} -eq 0 ]; then
        echo "${RED}No active window processes found.${NC}"
        return 1 # Indicate failure
    fi

    echo "${BLUE}Active window processes:${NC}"
    for i in "${!process_array[@]}"; do
        echo "${YELLOW} [$i] ${process_array[$i]}${NC}" # Display each process with its index
    done
    return 0 # Indicate success
}

# Function to select a process from the list provided by list_active_processes
# Prompts the user to enter the number of the process.
# Parses the selected entry to extract process name and PID.
# Populates global variables: `selected_process` (name) and `selected_pid` (PID).
# Returns: 0 on successful selection and parsing, 1 if the process no longer exists or parsing fails.
select_process() {
    while true; do
        read -p "$(echo ${BLUE}Enter the number of the process you want to change the priority for:${NC})" process_number

        # Validate if the entered number is within the bounds of the process_array
        if [[ $process_number -ge 0 && $process_number -lt ${#process_array[@]} ]]; then
            local selected_entry="${process_array[$process_number]}"

            # Parse selected_entry "AppName (PID: 123)" to extract PID and AppName
            # PID: Extracts digits between "(PID: " and ")"
            selected_pid=$(echo "$selected_entry" | sed -n 's/.*(PID: \([0-9]*\)).*/\1/p')
            # AppName: Extracts the part before " (PID: ..."
            selected_process_name=$(echo "$selected_entry" | sed -n 's/\(.*\) (PID: [0-9]*).*/\1/p')

            # Check if parsing was successful
            if [ -z "$selected_pid" ] || [ -z "$selected_process_name" ]; then
                echo "${RED}Error: Could not parse PID or name from '$selected_entry'. Please try again.${NC}"
                # Loop again for user to re-select
                continue
            fi

            # Verify the process with the extracted PID still exists
            if ! ps -p "$selected_pid" > /dev/null; then
                echo "${RED}Error: Process $selected_process_name (PID: $selected_pid) no longer exists. Please select another process.${NC}"
                return 1 # Indicate failure, main loop should refresh or exit to menu
            fi

            # Set global variables for use in set_process_priority
            selected_process="$selected_process_name"
            echo "${GREEN}Selected: $selected_process (PID: $selected_pid)${NC}" # Confirmation message
            break # Exit loop as selection is valid
        else
            echo "${RED}Invalid selection. Please enter a number between 0 and $((${#process_array[@]} - 1)).${NC}"
        fi
    done
    return 0 # Indicate success
}

# Function to set the priority (nice value) of the selected process
# Uses `renice` command with sudo.
# `selected_process` (name) and `selected_pid` (PID) global variables should be set before calling.
# Exits script with 0 on success. Loops on failure to set priority or invalid input.
set_process_priority() {
    while true; do
        # Prompt user for priority, ensuring parentheses in the prompt are escaped for `read -p`
        read -p "$(echo -e "${BLUE}Enter the new priority for $selected_process \(PID: $selected_pid\). Value from -20 \(max\) to 20 \(lowest\):${NC}")" new_priority

        # Validate priority is an integer between -20 and 20
        if [[ $new_priority =~ ^-?[0-9]+$ && $new_priority -ge -20 && $new_priority -le 20 ]]; then
            echo "${GREEN}Setting priority of $selected_process (PID: $selected_pid) to $new_priority...${NC}"
            sudo renice "$new_priority" -p "$selected_pid" # Added quotes for safety
            if [ $? -eq 0 ]; then
                echo "${GREEN}$selected_process priority set to $new_priority successfully.${NC}"
                exit 0 # Exit script successfully
            else
                echo "${RED}Error: Failed to set priority for $selected_process. Check permissions or PID.${NC}" # Slightly more info
                # Loop again to allow user to retry or enter different value
            fi
        else
            echo "${RED}Invalid priority value: $new_priority. Please enter an integer value between -20 and 20.${NC}"
        fi
    done
}

# Main script execution
# This is the main control flow of the script.
request_sudo

# Prompt the user for selecting an option
while true; do
    display_menu

    if [[ $user_option -eq 1 ]]; then
        revert_game_mode
    elif [[ $user_option -eq 2 ]]; then
        force_game_mode
        if [ $? -ne 0 ]; then
            continue
        fi

        list_active_processes
        if [ $? -ne 0 ]; then # Check if listing processes failed
            continue # Go back to main menu
        fi

        select_process
        if [ $? -ne 0 ]; then # Check if selecting process failed (e.g. process died)
            # Potentially refresh list here or just go back to menu
            echo "${YELLOW}Process selection failed or process died. Returning to main menu.${NC}"
            continue # Go back to main menu
        fi

        set_process_priority
    elif [[ $user_option -eq 3 ]]; then
        exit 0
    fi
done

sleep 1

echo "${GREEN}Game mode script completed.${NC}"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment