├── 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 |
--------------------------------------------------------------------------------