├── restore ├── README.md └── enable-HiDPI.sh /restore: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 2019-present (c) syscl 4 | # restore under single mode 5 | # Execute script location 6 | REPO=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 7 | BackupRoot="${REPO}/Display-Backups" 8 | if [ ! -d ${BackupRoot} ]; then 9 | # no backup found 10 | echo "${BackupRoot} does not exist, abort." 11 | exit -1 12 | fi 13 | ListOfBackup=($(ls ${BackupRoot} |sort -r)) 14 | if [[ ${#ListOfBackup[@]} == 0 ]]; then 15 | # no backup found 16 | echo "No backups found in ${BackupRoot}, abort." 17 | exit -2 18 | fi 19 | 20 | # 21 | #-------------------------------------------------------------------------------- 22 | # 23 | 24 | function toLowerCase() 25 | { 26 | echo "`echo $1 | tr '[:upper:]' '[:lower:]'`" 27 | } 28 | 29 | # 30 | #-------------------------------------------------------------------------------- 31 | # 32 | 33 | if [[ "${#ListOfBackup[@]}" -ge 2 ]]; then 34 | echo '------------------------------------' 35 | echo ' Display Overrides Backups ' 36 | echo '------------------------------------' 37 | index=0 38 | for dbak in "${ListOfBackup[@]}" 39 | do 40 | let index++ 41 | printf "[%d] ${dbak}\n" $index 42 | done 43 | echo '------------------------------------' 44 | # Let user make a selection. 45 | printf 'Choose the backup to restore (type in e to exit)' 46 | if [[ "${#ListOfBackup[@]}" == 2 ]]; then 47 | printf "[Exit/1/2]" 48 | else 49 | printf "[Exit/1-${index}]" 50 | fi 51 | read -p ": " selection 52 | 53 | case "$(toLowerCase $selection)" in 54 | e|exit ) echo "Abort." 55 | exit -0 56 | ;; 57 | 58 | [[:digit:]]* ) # Lower selection (arrays start at zero). 59 | let selection-=1 60 | backup="${BackupRoot}/${ListOfBackup[$selection]}" 61 | ;; 62 | 63 | * ) if [[ "${#ListOfBackup[@]}" == 2 ]]; then 64 | echo 'Invalid menu action, enter 1 or 2' 65 | else 66 | echo "Invalid menu action, enter valid number among 1, ..., ${index}" 67 | fi 68 | ;; 69 | esac 70 | else 71 | backup="${BackupRoot}/${ListOfBackup}" 72 | fi 73 | 74 | # check the system release 75 | sys_minor=$(uname -r |cut -f 1 -d ".") 76 | let sys_minor-=4 77 | if [[ $sys_minor -ge 11 ]]; then 78 | target="/System/Library/Displays/Contents/Resources/Overrides" 79 | else 80 | target="/System/Library/Displays/Overrides" 81 | fi 82 | 83 | # restore back the backup 84 | cp -R "${backup}" "${target}" 85 | 86 | echo "Restore completed." 87 | exit 0 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Enable HiDPI on macOS 2 | ============ 3 | 4 | Hola, this is an ongoing project targets at enable the support of HiDPI for display with a high resolution under macOS. AFAIK, there is various way to patch/force the HiDPI mode, however, I want to create a more precise and clean way to patch the system. That's why I start to build this project. 5 | 6 | I know there's still long way to go, but this time, I want to share this patch ASAP. All the suggestions are welcome. I just added a new function which can add as much as HiDPI resolutions you want at the same time. Wish you will enjoy it! 7 | 8 | How to use? 9 | ---------------- 10 | Download the latest enable-HiDPI.sh by entering the following command in a terminal window: 11 | 12 | ``` sh 13 | curl -o ~/enable-HiDPI.sh https://raw.githubusercontent.com/syscl/Enable-HiDPI-OSX/master/enable-HiDPI.sh 14 | ``` 15 | Download the latest restore program just in case the ```enable-HiDPI``` stall the system 16 | ```sh 17 | curl -o ~/restore https://raw.githubusercontent.com/syscl/Enable-HiDPI-OSX/master/restore 18 | ``` 19 | 20 | 21 | This will download enable-HiDPI.sh and restore.sh to your home directory (~) and the next step is to change the permissions of the file (add +x) so that it can be run. 22 | 23 | ``` sh 24 | chmod +x ~/enable-HiDPI.sh 25 | chmod +x ~/restore 26 | ``` 27 | 28 | 29 | Run the script in a terminal window by: 30 | 31 | ``` sh 32 | ~/enable-HiDPI.sh 33 | ``` 34 | 35 | Once you finish injecting the HiDPI values, reboot your OS X and use Retina Display Menu (RDM) to choose the resolution you want. 36 | 37 | How to restore? 38 | ---------------- 39 | Go to ```single``` mode in macOS, enter the following 40 | ```sh 41 | /sbin/fsck -fy / 42 | /sbin/mount -uw / 43 | ``` 44 | This will make your root filesystem avaiable to read and write, we then go to the home directory of your unix user name, suppose my user name is ```syscl```, then I should type in 45 | ```sh 46 | cd /User/syscl 47 | ./restore 48 | ``` 49 | Choose the restore point you want it to restore, then type in reboot to restore back to original 50 | ```sh 51 | reboot 52 | ``` 53 | 54 | Change Log 55 | ---------------- 56 | 2019-01-19 57 | 58 | - Implemented restore script in case incorrect settings stall the system 59 | 60 | 2018-03-18 61 | 62 | - Fixed the issue #16 and issue #32 by removing the redundant prefix 63 | 64 | 2018-03-16 65 | 66 | - More lightweight program by removing ```plistbuddy``` 67 | - Cleanup redudant code 68 | 69 | 2018-02-17 70 | 71 | - Fixed ```HiDPI``` inject issue on ```10.12+``` 72 | 73 | 2016-10-4 74 | 75 | - Fixed Artoria2e5's typo credit @jqqqqqqqqqq 76 | 77 | 2016-10-1 78 | 79 | - Fixed variable errors/typo, fully support for 4K display credit @Artoria2e5. 80 | 81 | 2016-7-30 82 | 83 | - Optimised message display, more precise and easier to read credit @transtone. 84 | 85 | 2016-5-24 86 | 87 | - Fixed major bug. 88 | - Optimised code. 89 | 90 | 2016-5-23 91 | 92 | - Fixed logical issue that causes backup failure. 93 | - Optimised code. 94 | 95 | 2016-5-22 96 | 97 | - Added multi monitors/displays support credit @liusunpan see issue #4. 98 | - Optimised code. 99 | 100 | 2016-3-4 101 | 102 | - Fixed a logical problem that casue the enable failure. Tested sucessfully on my HP 2009f @1600x900(now it can be switched to 1366x768, 1440x810.) 103 | 104 | 2016-3-3 105 | 106 | - You can use the enable-HiDPI.sh directly to fully enable HiDPI on your OS X. 107 | 108 | - Note: This is the first version of enable-HiDPI, I just tested it on 10.11.3, more function will be added late. If you find bugs please let me know through the "Issuses" tab. 109 | 110 | //// 111 | -------------------------------------------------------------------------------- /enable-HiDPI.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # enable-HiDPI.sh 4 | # 5 | # 6 | # Created by syscl/lighting/Yating Zhou on 16/3/2. 7 | # 8 | 9 | #================================= GLOBAL VARS ================================== 10 | 11 | # 12 | # The script expects '0.5' but non-US localizations use '0,5' so we export 13 | # LC_NUMERIC here (for the duration of the enable-HiDPI.sh) to prevent errors. 14 | # 15 | export LC_NUMERIC="en_US.UTF-8" 16 | 17 | # 18 | # Prevent non-printable/control characters. 19 | # 20 | unset GREP_OPTIONS 21 | unset GREP_COLORS 22 | unset GREP_COLOR 23 | 24 | # 25 | # Output styling. 26 | # 27 | BOLD="\033[1m" 28 | RED="\033[1;31m" 29 | GREEN="\033[1;32m" 30 | BLUE="\033[1;34m" 31 | OFF="\033[m" 32 | STYLE_UNDERLINED="\e[4m" 33 | 34 | # 35 | # Define two status: 0 - Success, Turn on, 36 | # 1 - Failure, Turn off 37 | # 38 | kBASHReturnSuccess=0 39 | kBASHReturnFailure=1 40 | 41 | # 42 | # Repository location 43 | # 44 | REPO=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 45 | 46 | # 47 | # Define variables. 48 | # Gvariables stands for getting datas from OS X. 49 | # 50 | gDisplayVendorID_RAW="" 51 | gDisplayVendorID="" 52 | gDisplayProductID_RAW="" 53 | gDisplayProductID_reverse="" 54 | gDisplayProductID="" 55 | gConfig="" 56 | gRes_RAW="F" 57 | gRes_VAL="" 58 | gRes_ENCODE="" 59 | gHeight_HiDPI="" 60 | gWide_HiDPI="" 61 | gHeight_HiDPI_VAL="" 62 | gWide_HiDPI_VAL="" 63 | gRes_HiDPI_VAL="" 64 | gRes_HiDPI_ENCODE="" 65 | gDstPath="" 66 | # gBak_Time=$(date +%Y-%m-%d-h%H_%M_%S) 67 | gBak_Time=$(date +%Y-%m-%d@%H_%M_%S) 68 | gBak_Dir="${REPO}/Display-Backups/${gBak_Time}" 69 | 70 | # 71 | # Define gConfig 72 | # 73 | gConfig="" 74 | 75 | # 76 | #-------------------------------------------------------------------------------- 77 | # 78 | 79 | function _PRINT_MSG() 80 | { 81 | local message=$1 82 | 83 | case "$message" in 84 | OK* ) local message=$(echo $message | sed -e 's/.*OK://') 85 | echo "[ ${GREEN}OK${OFF} ] ${message}." 86 | ;; 87 | 88 | FAILED*) local message=$(echo $message | sed -e 's/.*://') 89 | echo "[${RED}FAILED${OFF}] ${message}." 90 | ;; 91 | 92 | ---* ) local message=$(echo $message | sed -e 's/.*--->://') 93 | echo "[ ${GREEN}--->${OFF} ] ${message}" 94 | ;; 95 | 96 | NOTE* ) local message=$(echo $message | sed -e 's/.*NOTE://') 97 | echo "[ ${RED}Note${OFF} ] ${message}." 98 | ;; 99 | esac 100 | } 101 | 102 | # 103 | #-------------------------------------------------------------------------------- 104 | # 105 | 106 | function _tidy_exec() 107 | { 108 | if [ $gDebug -eq 0 ]; 109 | then 110 | # 111 | # Using debug mode to output all the details. 112 | # 113 | _PRINT_MSG "DEBUG: $2" 114 | $1 115 | else 116 | # 117 | # Make the output clear. 118 | # 119 | $1 >/tmp/report 2>&1 && RETURN_VAL=${kBASHReturnSuccess} || RETURN_VAL=${kBASHReturnFailure} 120 | 121 | if [ "${RETURN_VAL}" == ${kBASHReturnSuccess} ]; 122 | then 123 | _PRINT_MSG "OK: $2" 124 | else 125 | _PRINT_MSG "FAILED: $2" 126 | cat /tmp/report 127 | fi 128 | 129 | rm /tmp/report &> /dev/null 130 | fi 131 | } 132 | 133 | # 134 | #-------------------------------------------------------------------------------- 135 | # 136 | 137 | function _getEDID() 138 | { 139 | local index=0 140 | local selection=0 141 | 142 | gDisplayInf=($(ioreg -lw0 | grep -i "IODisplayEDID" | sed -e "/[^<]*//")) 143 | 144 | if [[ "${#gDisplayInf[@]}" -ge 2 ]]; 145 | then 146 | # 147 | # Multi monitors detected. Choose target monitor. 148 | # 149 | echo ' Table of monitors ' 150 | echo '------------------------------------' 151 | echo ' Index | VendorID | ProductID ' 152 | echo '------------------------------------' 153 | for display in "${gDisplayInf[@]}" 154 | do 155 | let index++ 156 | # 157 | # Show monitors. 158 | # 159 | printf " %d | ${display:16:4} | ${display:20:4}\n" $index 160 | done 161 | # 162 | # Close the table 163 | # 164 | echo '------------------------------------' 165 | # 166 | # Let user make a selection. 167 | # 168 | printf 'Choose the display to enable HiDPI' 169 | if [[ "${#gDisplayInf[@]}" == 2 ]]; then 170 | printf "[${STYLE_UNDERLINED}E${OFF}xit/1/2]" 171 | else 172 | printf "[${STYLE_UNDERLINED}E${OFF}xit/1-${index}]" 173 | fi 174 | read -p ": " selection 175 | case "$(_toLowerCase $selection)" in 176 | e|exit ) echo "Abort." 177 | exit -0 178 | ;; 179 | 180 | [[:digit:]]* ) # 181 | # Lower selection (arrays start at zero). 182 | # 183 | let selection-=1 184 | gMonitor=${gDisplayInf[$selection]} 185 | ;; 186 | 187 | * ) if [[ "${#gDisplayInf[@]}" == 2 ]]; then 188 | echo 'Invalid menu action, enter 1 or 2' 189 | else 190 | echo "Invalid menu action, enter valid number among 1, ..., ${index}" 191 | fi 192 | ;; 193 | esac 194 | else 195 | gMonitor=${gDisplayInf} 196 | fi 197 | # 198 | # Fix for issue #16 and #32 199 | # 200 | if [[ ${gMonitor:16:1} == 0 ]]; then 201 | # get rid of the prefix 0 202 | gDisplayVendorID_RAW=${gMonitor:17:3} 203 | else 204 | gDisplayVendorID_RAW=${gMonitor:16:4} 205 | fi 206 | # convert from hex to dec 207 | gDisplayVendorID=$((0x$gDisplayVendorID_RAW)) 208 | gDisplayProductID_RAW=${gMonitor:20:4} 209 | # 210 | # Exchange two bytes 211 | # 212 | # Fix an issue that will cause wrong name of DisplayProductID 213 | # 214 | if [[ ${gDisplayProductID_RAW:2:1} == 0 ]]; then 215 | # get rid of the prefix 0 216 | gDisplayProduct_pr=${gDisplayProductID_RAW:3:1} 217 | else 218 | gDisplayProduct_pr=${gDisplayProductID_RAW:2:2} 219 | fi 220 | gDisplayProduct_st=${gDisplayProductID_RAW:0:2} 221 | gDisplayProductID_reverse="${gDisplayProduct_pr}${gDisplayProduct_st}" 222 | gDisplayProductID=$((0x$gDisplayProduct_pr$gDisplayProduct_st)) 223 | 224 | # echo $gDisplayVendorID_RAW 225 | # echo $gDisplayVendorID 226 | # echo $gDisplayProductID_RAW 227 | # echo $gDisplayProductID 228 | 229 | gConfig=${REPO}/DisplayVendorID-$gDisplayVendorID_RAW/DisplayProductID-$gDisplayProductID_reverse 230 | } 231 | 232 | # 233 | #-------------------------------------------------------------------------------- 234 | # 235 | 236 | function _printHeader() 237 | { 238 | echo '' 239 | echo '' 240 | echo '' 241 | echo '' 242 | printf '\tDisplayProductID\n' 243 | printf "\t${gDisplayProductID}\n" 244 | printf '\tDisplayVendorID\n' 245 | printf "\t${gDisplayVendorID}\n" 246 | printf '\tscale-resolutions\n' 247 | printf '\t\n' 248 | } 249 | 250 | # 251 | #-------------------------------------------------------------------------------- 252 | # 253 | 254 | function _closeField() 255 | { 256 | printf '\t\n' 257 | echo '' 258 | echo '' 259 | } 260 | 261 | # 262 | #-------------------------------------------------------------------------------- 263 | # 264 | 265 | function _create_dir() 266 | { 267 | if [ ! -d "$1" ]; 268 | then 269 | mkdir -p "$1" 270 | fi 271 | } 272 | 273 | # 274 | #-------------------------------------------------------------------------------- 275 | # 276 | 277 | function _del() 278 | { 279 | local target_file=$1 280 | 281 | if [ -d ${target_file} ]; 282 | then 283 | _tidy_exec "rm -R ${target_file}" "Remove old ${target_file}" 284 | else 285 | if [ -f ${target_file} ]; 286 | then 287 | _tidy_exec "rm ${target_file}" "Remove old ${target_file}" 288 | fi 289 | fi 290 | } 291 | 292 | # 293 | #-------------------------------------------------------------------------------- 294 | # 295 | 296 | function _cleanup() 297 | { 298 | _create_dir ${gBak_Dir} 299 | _del ${REPO}/DisplayVendorID-* 300 | _create_dir ${REPO}/DisplayVendorID-$gDisplayVendorID_RAW 301 | } 302 | 303 | # 304 | #-------------------------------------------------------------------------------- 305 | # 306 | 307 | function _toLowerCase() 308 | { 309 | echo "`echo $1 | tr '[:upper:]' '[:lower:]'`" 310 | } 311 | 312 | # 313 | #-------------------------------------------------------------------------------- 314 | # 315 | 316 | function _calcsRes() 317 | { 318 | # 319 | # Increment i stands for adding arrays. 320 | # 321 | i=0 322 | 323 | while [ "$gRes_RAW" != 0 ]; 324 | do 325 | printf "Enter the HiDPI resolution (e.g. 1600x900, 1440x910, ...), ${BOLD}0${OFF} to quit" 326 | read -p ": " gRes_RAW 327 | 328 | if [[ $gRes_RAW != 0 ]]; 329 | then 330 | # 331 | # Raw Datas 332 | # 333 | gHeightVAL=$(echo $gRes_RAW | cut -f 1 -d "x") 334 | gWideVAL=$(echo $gRes_RAW | cut -f 2 -d "x") 335 | 336 | 337 | # 338 | # Generate Resolution Values (Hex) 339 | # 340 | gRes_VAL=$(printf '%08x %08x 00000001 02000000' $gHeightVAL $gWideVAL) 341 | # HiDPI is twice the size. 342 | gRes_HiDPI_VAL=$(printf '%08x %08x 00000001 02000000' $((gHeightVAL*2)) $((gWideVAL*2))) 343 | 344 | # 345 | # Encode Resolution Values(Hex) into base64 346 | # 347 | gRes_ENCODE=$(printf $gRes_VAL | xxd -r -p | base64) 348 | gRes_HiDPI_ENCODE=$(printf $gRes_HiDPI_VAL | xxd -r -p | base64) 349 | 350 | # 351 | # Inject HiDPI values. 352 | # 353 | printf "\t\t${gRes_HiDPI_ENCODE}\n" >> "$gConfig" 354 | 355 | gRes_RAW="" 356 | i=$(($i+1)) 357 | fi 358 | done 359 | } 360 | 361 | # 362 | #-------------------------------------------------------------------------------- 363 | # 364 | 365 | function _OSCheck() 366 | { 367 | # 368 | # Extract minor version (eg. 10.9 vs. 10.10 vs. 10.11) 369 | # 370 | MINOR_VER=$([[ "$(sw_vers -productVersion)" =~ [0-9]+\.([0-9]+) ]] && echo ${BASH_REMATCH[1]}) 371 | if [[ $MINOR_VER -ge 11 ]]; 372 | then 373 | gDstPath="/System/Library/Displays/Contents/Resources/Overrides" 374 | else 375 | gDstPath="/System/Library/Displays/Overrides" 376 | fi 377 | } 378 | 379 | # 380 | #-------------------------------------------------------------------------------- 381 | # 382 | 383 | function _patch() 384 | { 385 | # 386 | # Count number indicates patch system or not. 387 | # 388 | if [ $i != 0 ]; 389 | then 390 | _tidy_exec "cp -R "$gDstPath" ${gBak_Dir}" "Backup $gDstPath" 391 | sudo defaults write /Library/Preferences/com.apple.windowserver DisplayResolutionEnabled -bool YES 392 | 393 | if [ -f "/Library/Preferences/com.apple.windowserver" ]; 394 | then 395 | sudo defaults delete /Library/Preferences/com.apple.windowserver DisplayResolutionDisabled 2>&1 >/dev/null 396 | fi 397 | 398 | sudo cp -R "${REPO}/DisplayVendorID-$gDisplayVendorID_RAW" "$gDstPath" 399 | _PRINT_MSG "OK: Done. Reboot then use Retina Display Menu (RDM) to select the HiDPI resolution just injected!" 400 | else 401 | _PRINT_MSG "NOTE: All system files remain unchanged." 402 | fi 403 | } 404 | 405 | # 406 | #-------------------------------------------------------------------------------- 407 | # 408 | 409 | function main() 410 | { 411 | # 412 | # Get argument. 413 | # 414 | gArgv=$(echo "$@" | tr '[:lower:]' '[:upper:]') 415 | if [[ $# -eq 1 && "$gArgv" == "-D" || "$gArgv" == "-DEBUG" ]]; 416 | then 417 | # 418 | # Yes, we do need debug mode. 419 | # 420 | _PRINT_MSG "NOTE: Use ${BLUE}DEBUG${OFF} mode" 421 | gDebug=0 422 | else 423 | # 424 | # No, we need a clean output style. 425 | # 426 | gDebug=1 427 | fi 428 | 429 | _getEDID 430 | _cleanup 431 | _printHeader > "$gConfig" 432 | _calcsRes 433 | _closeField >>"$gConfig" 434 | _OSCheck 435 | _patch 436 | } 437 | 438 | #==================================== START ===================================== 439 | 440 | main "$@" 441 | 442 | #================================================================================ 443 | 444 | exit 0 445 | --------------------------------------------------------------------------------