Fix RGB Range Limit on MacOS Based on the provided script, this article provides a bash script for macOS that forces RGB color output and full range on M1 and M2 Macs for a selected external display. The script works by copying and modifying the system's display configuration plist file, allowing the user to choose a display and set its bit depth to 8 or 10 bits. It is designed to fix issues where external monitors display limited or incorrect color ranges on Apple Silicon Macs. rgb-fix.sh 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 Show hidden characters /usr/bin/env bash Script to force RGB Color Output on M1 and M2 based Macs for a selected display Function to display an error message and exit function error exit { echo "Error: $1" &2 exit 1 } Function to check if a command executed successfully function check success { if $? -ne 0 ; then error exit "Command failed: $1" fi } set -e ORIGINAL PLIST="/Library/Preferences/com.apple.windowserver.displays.plist" TEMP DIR="$HOME/Downloads" TEMP PLIST="$TEMP DIR/com.apple.windowserver.displays.plist" Step 1: Unlock the file if locked sudo chflags nouchg "$ORIGINAL PLIST" || true echo "Step 1: File unlocked if necessary." Step 2: Copy the plist to temp directory sudo cp "$ORIGINAL PLIST" "$TEMP PLIST" sudo chown "$USER" "$TEMP PLIST" check success "Copying the plist file" echo "Step 2: File copied successfully." set +e List displays from plist, matched with system profiler names by resolution num=$ python3 -c " import plistlib, subprocess, json, sys file = '$TEMP PLIST' with open file, 'rb' as f: data = plistlib.load f displays = data 'DisplayAnyUserSets' 'Configs' 0 'DisplayConfig' sp json = json.loads subprocess.check output 'system profiler', 'SPDisplaysDataType', '-json' , stderr=subprocess.DEVNULL sp displays = for gpu in sp json.get 'SPDisplaysDataType', : sp displays.extend gpu.get 'spdisplays ndrvs', name by res = {} for d in sp displays: pixels = d.get ' spdisplays pixels', '' name = d.get ' name', '' if pixels and name: name by res pixels.replace ' ', '' = name print len displays print 'Connected displays:', file=sys.stderr for idx, disp in enumerate displays : info = disp.get 'CurrentInfo', disp.get 'UnmirrorInfo', {} w = info.get 'Wide', 0 h = info.get 'High', 0 scale = info.get 'Scale', 1 hz = info.get 'Hz', 0 pw, ph = int w scale , int h scale name = name by res.get f'{pw}x{ph}', f'Display {idx}' print f'{idx + 1}: {name} {pw}x{ph} @ {hz}Hz ', file=sys.stderr " if -z "$num" || "$num" -eq 0 ; then error exit "Unable to parse number of displays from plist." fi Ask user to choose read -p "Enter the number of the display to update 1-${num} : " choice if "$choice" =~ ^ 0-9 +$ || "$choice" -lt 1 || "$choice" -gt "$num" ; then error exit "Invalid choice." fi i=$ choice - 1 Get the UUID of the selected display uuid=$ python3 -c " import plistlib with open '$TEMP PLIST', 'rb' as f: data = plistlib.load f print data 'DisplayAnyUserSets' 'Configs' 0 'DisplayConfig' $i 'UUID' " 2 /dev/null if -z "$uuid" ; then error exit "Unable to retrieve UUID for the selected display." fi echo "Modifying ${uuid}..." Ask user for bit depth read -p "Enter the bit depth 8 or 10 : " bit depth choice if "$bit depth choice" == "8" || "$bit depth choice" == "10" ; then error exit "Invalid bit depth. Please enter 8 or 10." fi Step 4: Modify the plist using embedded Python with plistlib python3 -c " import plistlib, sys file = '$TEMP PLIST' uuid = '$uuid' bit depth = int '$bit depth choice' link desc = { 'BitDepth': bit depth, 'EOTF': 0, 'PixelEncoding': 0, 0 = RGB 'Range': 1 1 = Full Range } with open file, 'rb' as f: data = plistlib.load f modified count = 0 Modify DisplayAnyUserSets section if 'DisplayAnyUserSets' in data and 'Configs' in data 'DisplayAnyUserSets' : for config in data 'DisplayAnyUserSets' 'Configs' : if 'DisplayConfig' in config: for disp in config 'DisplayConfig' : if 'UUID' in disp and disp 'UUID' == uuid: disp 'LinkDescription' = link desc.copy modified count += 1 Modify DisplaySets section CRITICAL - was missing before if 'DisplaySets' in data and 'Configs' in data 'DisplaySets' : for config in data 'DisplaySets' 'Configs' : if 'DisplayConfig' in config: for disp in config 'DisplayConfig' : if 'UUID' in disp and disp 'UUID' == uuid: disp 'LinkDescription' = link desc.copy modified count += 1 print f'Modified {modified count} display config entries for UUID {uuid}' with open file, 'wb' as f: plistlib.dump data, f, fmt=plistlib.FMT BINARY " check success "Modifying plist with Python" echo "Step 4: Plist modified successfully." Step 5: No conversion needed since edited in binary Step 6: Validate the plist plutil -lint "$TEMP PLIST" check success "Validating plist" echo "Step 6: Plist validated successfully." Step 7: Backup original file only if backup doesn't exist, to preserve original if -f "${ORIGINAL PLIST} backup" ; then sudo cp "$ORIGINAL PLIST" "${ORIGINAL PLIST} backup" check success "Backing up original file" echo "Step 7: Original file backed up successfully." else echo "Step 7: Backup already exists, skipping preserving original backup ." fi Step 8: Remove user preferences if exists they can override system settings if -f "$HOME/Library/Preferences/com.apple.windowserver.displays.plist" ; then if -f "$HOME/Library/Preferences/com.apple.windowserver.displays.plist backup" ; then mv "$HOME/Library/Preferences/com.apple.windowserver.displays.plist" "$HOME/Library/Preferences/com.apple.windowserver.displays.plist backup" check success "Backing up user preferences" echo "Step 8: User preferences backed up successfully." else rm "$HOME/Library/Preferences/com.apple.windowserver.displays.plist" echo "Step 8: User preferences removed backup already exists ." fi fi Step 9: Remove ByHost preferences if exists they can override system settings for file in "$HOME/Library/Preferences/ByHost/com.apple.windowserver.displays." .plist; do if -f "$file" && "$file" = backup ; then if -f "${file} backup" ; then mv "$file" "${file} backup" check success "Backing up ByHost file" echo "Step 9: ByHost file backed up successfully." else rm "$file" echo "Step 9: ByHost file removed backup already exists ." fi fi done Step 10: Copy modified plist back sudo chown "root" "$TEMP PLIST" sudo mv "$TEMP PLIST" "$ORIGINAL PLIST" check success "Copying modified plist back" echo "Step 10: Modified file copied back successfully." Step 11: Set Stationery flag and lock the file as per original gist Set Stationery Pad flag BEFORE locking using SetFile if available if command -v SetFile & /dev/null; then sudo SetFile -a T "$ORIGINAL PLIST" 2 /dev/null || true fi Lock the file sudo chflags uchg "$ORIGINAL PLIST" check success "Locking the file" echo "Step 11: File locked successfully." Step 12: Verify the changes were applied echo "" echo "=== Verification ===" link count=$ python3 -c " import plistlib with open '$ORIGINAL PLIST', 'rb' as f: data = plistlib.load f count = 0 for section in 'DisplayAnyUserSets', 'DisplaySets' : if section in data and 'Configs' in data section : for config in data section 'Configs' : for disp in config.get 'DisplayConfig', : if disp.get 'UUID' == '$uuid' and 'LinkDescription' in disp: count += 1 print f'{section}: BitDepth={disp \"LinkDescription\" .get \"BitDepth\" }, PixelEncoding={disp \"LinkDescription\" .get \"PixelEncoding\" }, Range={disp \"LinkDescription\" .get \"Range\" }' print f'Total entries modified: {count}' " echo "$link count" echo "" Step 13: Ask for reboot read -p "Changes applied. Reboot required. Do you want to reboot now? Y/N : " reboot choice case "$reboot choice" in y | Y sudo shutdown -r now ;; n | N echo "Reboot canceled. Please reboot manually for changes to take effect." ;; echo "Invalid choice. Reboot canceled. Please reboot manually." ;; esac verify.sh 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 Show hidden characters /usr/bin/env bash set -e ORIGINAL PLIST="/Library/Preferences/com.apple.windowserver.displays.plist" TEMP DIR="$ mktemp -d " TEMP PLIST="$TEMP DIR/com.apple.windowserver.displays.plist" trap 'rm -rf "$TEMP DIR"' EXIT sudo cp "$ORIGINAL PLIST" "$TEMP PLIST" sudo chown "$USER" "$TEMP PLIST" python3 <