Skip to content

Instantly share code, notes, and snippets.

@ejdyksen
Last active November 13, 2023 09:17
Star You must be signed in to star a gist
Embed
What would you like to do?
A script to fix EDID problems on external monitors in macOS

patch-edid.rb

A script to fix EDID problems on external monitors in macOS.

Instructions

  1. Connect only the problem display.

  2. Create this directory structure (if it doesn't already exist):

    sudo mkdir -p /Library/Displays/Contents/Resources/Overrides
  3. Download this ruby script in that directory:

    cd /Library/Displays/Contents/Resources/Overrides
    sudo curl -O https://gist.githubusercontent.com/ejdyksen/8302862/raw/patch-edid.rb

    Note: You may want to use adaugherity's version of the script instead.

  4. Run the script we just downloaded (as root again). This creates a new display override plist file.

    cd /Library/Displays/Contents/Resources/Overrides
    sudo ruby patch-edid.rb
  5. Unplug and replug in the problem display.

Additional reading/acknowledgements

  • The original forum thread
  • An improved version of the script by adaugherity
  • An explaination of the problem from Atomic Object's blog
  • Thanks so much to @stackrainbow for pointing out that this can be done without disabling SIP.
  • This version appears to work in Catalina and Big Sur. See earlier revisions for what worked (with disabling SIP) in earlier versions of macOS, which require the override plist to be in a different directory.
#!/usr/bin/ruby
# Create display override file to force Mac OS X to use RGB mode for Display
# see http://embdev.net/topic/284710
#
# Update 2013-06-24: added -w0 option to prevent truncated lines
require 'base64'
data=`ioreg -l -w0 -d0 -r -c AppleDisplay`
edid_hex=data.match(/IODisplayEDID.*?<([a-z0-9]+)>/i)[1]
vendorid=data.match(/DisplayVendorID.*?([0-9]+)/i)[1].to_i
productid=data.match(/DisplayProductID.*?([0-9]+)/i)[1].to_i
puts "found display: vendorid #{vendorid}, productid #{productid}, EDID:\n#{edid_hex}"
bytes=edid_hex.scan(/../).map{|x|Integer("0x#{x}")}.flatten
puts "Setting color support to RGB 4:4:4 only"
bytes[24] &= ~(0b11000)
puts "Number of extension blocks: #{bytes[126]}"
puts "removing extension block"
bytes = bytes[0..127]
bytes[126] = 0
bytes[127] = (0x100-(bytes[0..126].reduce(:+) % 256)) % 256
puts
puts "Recalculated checksum: 0x%x" % bytes[127]
puts "new EDID:\n#{bytes.map{|b|"%02X"%b}.join}"
Dir.mkdir("DisplayVendorID-%x" % vendorid) rescue nil
f = File.open("DisplayVendorID-%x/DisplayProductID-%x" % [vendorid, productid], 'w')
f.write '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">'
f.write "
<dict>
<key>DisplayProductName</key>
<string>Display with forced RGB mode (EDID override)</string>
<key>IODisplayEDID</key>
<data>#{Base64.encode64(bytes.pack('C*'))}</data>
<key>DisplayVendorID</key>
<integer>#{vendorid}</integer>
<key>DisplayProductID</key>
<integer>#{productid}</integer>
</dict>
</plist>"
f.close
@GetVladimir
Copy link

GetVladimir commented Jun 20, 2022

How to enable HiDPI on M1 based Macs on any resolution

I have some interesting news: I found a solution to enable HiDPI on M1 based Macs on any resolution, even on 1080p screens, without the need to use mirroring or other workarounds.

Here is an example of HiDPI running on a Dell U2415 monitor that only supports a maximum resolution of 1920x1200:
1200p HiDPI

What you need to do is to edit the com.apple.windowserver.displays.plist file located in
/Library/Preferences/com.apple.windowserver.displays.plist

and change the key <key>Scale</key> to have a value of 2.

This is an example of how the section in the displays plist file should look like after the change:

					<key>CurrentInfo</key>
					<dict>
						<key>Depth</key>
						<integer>8</integer>
						<key>High</key>
						<real>1200</real>
						<key>Hz</key>
						<real>60</real>
						<key>IsLink</key>
						<false/>
						<key>OriginX</key>
						<real>0.0</real>
						<key>OriginY</key>
						<real>0.0</real>
						<key>Rotation</key>
						<integer>0</integer>
						<key>Scale</key>
						<real>2</real>
						<key>Wide</key>
						<real>1920</real>
					</dict>

Make sure that you change only the Scale key for the monitor that you need, and nothing else.

You can follow the step-by-step video tutorial on how to make the changes in the displays plist file here:
https://www.youtube.com/watch?v=Z1EqH3fd0V4

After that, restart your Mac and you should get HiDPI and noticeably more crisp and anti-aliased text, and the high resolution macOS icons.

An easy way to confirm that the HiDPI changes work is by looking at the Safari icon on the dock. If the HiDPI version of the icon is enabled, it will have a lot more sections in the compass than the low resolution icon. It should look something like this:
Safari HiDPI

Please note that while this might make the image look better even on 1080p screens, they will still be running at 1080p resolution, so the change might not be as drastic as running on a higher resolution screen.

If you have any questions about the procedure, please feel free to contact me or comment below.

@tsujp
Copy link

tsujp commented Jun 21, 2022

How to enable HiDPI on M1 based Macs on any resolution

I have some interesting news: I found a solution to enable HiDPI on M1 based Macs on any resolution, even on 1080p screens, without the need to use mirroring or other workarounds.

Here is an example of HiDPI running on a Dell U2415 monitor that only supports a maximum resolution of 1920x1200: 1200p HiDPI

What you need to do is to edit the com.apple.windowserver.displays.plist file located in /Library/Preferences/com.apple.windowserver.displays.plist

and change the key <key>Scale</key> to have a value of 2.

This is an example of how the section in the displays plist file should look like after the change:

					<key>CurrentInfo</key>
					<dict>
						<key>Depth</key>
						<integer>8</integer>
						<key>High</key>
						<real>1200</real>
						<key>Hz</key>
						<real>60</real>
						<key>IsLink</key>
						<false/>
						<key>OriginX</key>
						<real>0.0</real>
						<key>OriginY</key>
						<real>0.0</real>
						<key>Rotation</key>
						<integer>0</integer>
						<key>Scale</key>
						<real>2</real>
						<key>Wide</key>
						<real>1920</real>
					</dict>

Make sure that you change only the Scale key for the monitor that you need, and nothing else.

You can follow the step-by-step video tutorial on how to make the changes in the displays plist file here: youtube.com/watch?v=Z1EqH3fd0V4

After that, restart your Mac and you should get HiDPI and noticeably more crisp and anti-aliased text, and the high resolution macOS icons.

An easy way to confirm that the HiDPI changes work is by looking at the Safari icon on the dock. If the HiDPI version of the icon is enabled, it will have a lot more sections in the compass than the low resolution icon. It should look something like this: Safari HiDPI

Please note that while this might make the image look better even on 1080p screens, they will still be running at 1080p resolution, so the change might not be as drastic as running on a higher resolution screen.

If you have any questions about the procedure, please feel free to contact me or comment below.

Going to give this a shot when I get home from work. If this is indeed correct I'm going to be ecstatic because holy smokes without HiDPI macOS looks garbage.

@GetVladimir
Copy link

@tsujp Thank you so much for the reply! Let us know how it goes

@tsujp
Copy link

tsujp commented Jun 21, 2022

@tsujp Thank you so much for the reply! Let us know how it goes

Okay first attempt didn't work but I'm fairly sure this is because I've got some borked "display state". I had previously been messing around with RDM, BetterDummy, SwitchResX and what not and I have ~1000 lines in my com.apple.windowserver.displays.plist file. I did a fresh install of macOS in a VM from Apple's official IPSW and it has.. the single display in its com.apple.windowserver.displays.plist; oh boy. UUIDs don't save me here since my displays UUID is repeated multiple times and I am not sure where these definitions are coming from (the other UUIDs) so I can manually remove them.

Would you have any idea? I'll keep poking around. The alternative is a re-install but I really don't want to do that currently.

Update 1:

So in addition to deleting /Library/Preferences/com.apple.windowserver.displays.plist you also have to delete ~/Library/Preferences/ByHost/com.apple.windowserver.displays.UUID.plist. In place of UUID there will be an actual UUID.

That cleaned it up and now I have a single entry, very nice. Let's try again.

Update 2:

I cannot get it to work with an LG 27GL850. The change to the plist is staying in-place but there's no UI scaling nor any option to in System Preferences.

@GetVladimir
Copy link

GetVladimir commented Jun 21, 2022

@tsujp Thank you for the update.

You can try making a backup of the displays plist file and deleting it completely. After a restart, macOS should recreate that file with only the UUID of your monitor.

If the CurrentInfo key is not in the newly created file, you can do the rotation trick (rotate your monitor to 180 in System Preferences > Displays and rotate it back to 0) and it should create it too.

If you use a VM or a Dock, it will not create the UUID of the monitor, so that might be the cause of the issue.

Please check to make sure that there is no duplicate displays plist file in /Users/[YourUsername]/Library/Preferences/ByHost

If there is, you can make a backup of it and delete it (or you'll need to modify that one as well)

Also, please note that the scaling slider doesn't appear in the System Preferences > Displays. It just works with the applied settings in the plist file (until you manually change the resolution again).

@tsujp
Copy link

tsujp commented Jun 21, 2022

@tsujp Thank you for the update.

Hehe no worries mate thank you for the help.

You can try making a backup of the displays plist file and deleting it completely. After a restart, macOS should recreate that file with only the UUID of your monitor.

So in my prior message what I did was completely delete both the apple.com.windowserver.displays plist file in /Library/Preferences as well as in ~/Library/Preferences/ByHost. I then rebooted and it recreates only the plist file in /Library/Preferences which contains no CurrentInfo until I do the rotation trick; that gives me a new fresh plist.

If the CurrentInfo key is not in the newly created file, you can do the rotation trick (rotate your monitor to 180 in System Preferences > Displays and rotate it back to 0) and it should create it too.

Yep, the resulting file (with the scale edited) looks like this: note it doesn't work even if I set the scale in the UnmirrorInfo too.

I'm sanity checking this UUID using the ColorSync Utility app which shows the displays UUID, you can see here they are the same:

Screen Shot 2022-06-21 at 23 09 25

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>DisplayAnyUserSets</key>
	<dict>
		<key>Configs</key>
		<array>
			<array>
				<dict>
					<key>CurrentInfo</key>
					<dict>
						<key>Depth</key>
						<integer>8</integer>
						<key>High</key>
						<real>1440</real>
						<key>Hz</key>
						<real>144</real>
						<key>IsLink</key>
						<false/>
						<key>OriginX</key>
						<real>0.0</real>
						<key>OriginY</key>
						<real>0.0</real>
						<key>Rotation</key>
						<integer>0</integer>
						<key>Scale</key>
						<real>2</real>
						<key>Wide</key>
						<real>2560</real>
					</dict>
					<key>Rotation</key>
					<real>0.0</real>
					<key>UUID</key>
					<string>BE7805F6-7870-4FDB-8B01-434588225703</string>
					<key>UnmirrorInfo</key>
					<dict>
						<key>Depth</key>
						<integer>7</integer>
						<key>High</key>
						<real>1440</real>
						<key>Hz</key>
						<real>144</real>
						<key>IsLink</key>
						<false/>
						<key>OriginX</key>
						<real>0.0</real>
						<key>OriginY</key>
						<real>0.0</real>
						<key>Rotation</key>
						<integer>0</integer>
						<key>Scale</key>
						<real>1</real>
						<key>Wide</key>
						<real>2560</real>
					</dict>
				</dict>
			</array>
		</array>
		<key>Orientations</key>
		<dict>
			<key>BE7805F6-7870-4FDB-8B01-434588225703</key>
			<integer>0</integer>
		</dict>
		<key>Underscan</key>
		<dict>
			<key>BE7805F6-7870-4FDB-8B01-434588225703</key>
			<real>1</real>
		</dict>
		<key>Version</key>
		<integer>1</integer>
	</dict>
	<key>DisplayUUIDMappings</key>
	<dict>
		<key>045C36B3-C9E6-F147-0F93-5A69417CA049</key>
		<string>BE7805F6-7870-4FDB-8B01-434588225703</string>
	</dict>
</dict>
</plist>

If you use a VM or a Dock, it will not create the UUID of the monitor, so that might be the cause of the issue.

No laptop dock or VM is being used here; the VM from before was to see what a default /Library/Preferences/com.apple ... plist looked like. I'm using the HDMI port which is part of the laptop shell. I've also tried this at different refresh rates too (but that shouldn't matter I think since the scaling is local to the machine only, there are still only 2560x1440 pixels going over the wire).

Please check to make sure that there is no duplicate displays plist file in /Users/[YourUsername]/Library/Preferences/ByHost

There is none.

Also, please note that the scaling slider doesn't appear in the System Preferences > Displays. It just works with the applied settings in the plist file (until you manually change the resolution again).

Gotcha. Well after a fresh reboot there is no effect for me:

Screen Shot 2022-06-21 at 23 08 17

@GetVladimir
Copy link

GetVladimir commented Jun 21, 2022

@tsujp Thank you so much for the detailed explanation and for checking this! I appreciate it a lot.

There must be something that we're missing.

Do you have any custom Display Override file created by an app in
/Library/Displays/Contents/Resources/Overrides/DisplayVendorID-[xxxx]/DisplayProductID-[xxxx]

the [xxxx] is the hex of the Manufacturer and Model of your Monitor.

If nothing else works, the other thing that comes to mind is to check if setting the resolution to 1920x1080 at 60Hz before making the plist change would make any difference. This is just for reference, to make sure that the resolution or the high 144Hz refresh rate doesn't prevent the HiDPI.

@tsujp
Copy link

tsujp commented Jun 22, 2022

There must be something that we're missing.

That's what I am hoping but it's not looking good.

Do you have any custom Display Override file created by an app in /Library/Displays/Contents/Resources/Overrides/DisplayVendorID-[xxxx]/DisplayProductID-[xxxx]

None, that folder is empty and was empty in all prior runs.

If nothing else works, the other thing that comes to mind is to check if setting the resolution to 1920x1080 at 60Hz before making the plist change would make any difference. This is just for reference, to make sure that the resolution or the high 144Hz refresh rate doesn't prevent the HiDPI.

I tried on my Mac Mini M1 also to the same result. I cannot get it to work. I might try one more time this weekend; are you able to get this working on more than your specific setup? Is anyone else? We've got your data point where it works and mine where it doesn't. I'm sure it's something with my setup (because I'd love for it to work) but so far nothing :'(

@GetVladimir
Copy link

GetVladimir commented Jun 22, 2022

@tsujp That's a good point. Let's see if there are any other users that can get this to work on their specific setup.

I do have an older Mac mini (Late 2014), but that one is not M1 based and has completely different plist file. This solution will not work on it.

For reference example, I'm attaching my working M1 display plist file here:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>DisplayAnyUserSets</key>
	<dict>
		<key>Configs</key>
		<array>
			<array>
				<dict>
					<key>CurrentInfo</key>
					<dict>
						<key>Depth</key>
						<integer>8</integer>
						<key>High</key>
						<real>1200</real>
						<key>Hz</key>
						<real>60</real>
						<key>IsLink</key>
						<false/>
						<key>OriginX</key>
						<real>0.0</real>
						<key>OriginY</key>
						<real>0.0</real>
						<key>Rotation</key>
						<integer>0</integer>
						<key>Scale</key>
						<real>2</real>
						<key>Wide</key>
						<real>1920</real>
					</dict>
					<key>LinkDescription</key>
					<dict>
						<key>BitDepth</key>
						<integer>8</integer>
						<key>EOTF</key>
						<integer>0</integer>
						<key>PixelEncoding</key>
						<integer>0</integer>
						<key>Range</key>
						<integer>1</integer>
					</dict>
					<key>Rotation</key>
					<real>0.0</real>
					<key>UUID</key>
					<string>4354AB62-F516-45E6-971F-19EFC3C6B5BB</string>
					<key>UnmirrorInfo</key>
					<dict>
						<key>Depth</key>
						<integer>8</integer>
						<key>High</key>
						<real>1200</real>
						<key>Hz</key>
						<real>60</real>
						<key>IsLink</key>
						<false/>
						<key>OriginX</key>
						<real>0.0</real>
						<key>OriginY</key>
						<real>0.0</real>
						<key>Rotation</key>
						<integer>0</integer>
						<key>Scale</key>
						<real>1</real>
						<key>Wide</key>
						<real>1920</real>
					</dict>
				</dict>
			</array>
		</array>
		<key>Orientations</key>
		<dict>
			<key>4354AB62-F516-45E6-971F-19EFC3C6B5BB</key>
			<integer>0</integer>
		</dict>
		<key>Underscan</key>
		<dict>
			<key>4354AB62-F516-45E6-971F-19EFC3C6B5BB</key>
			<real>0.0</real>
		</dict>
		<key>Version</key>
		<integer>1</integer>
	</dict>
	<key>DisplayUUIDMappings</key>
	<dict>
		<key>9B603E2E-98E1-E840-0F93-5A69417CA049</key>
		<string>533B0F79-281D-4197-A73E-F10466A7CF93</string>
		<key>C96C073C-1382-DB8E-0F93-5A69417CA049</key>
		<string>4354AB62-F516-45E6-971F-19EFC3C6B5BB</string>
	</dict>
</dict>
</plist>

As a last resort, you can replace the UUID's with your own and test it, but it's not something I can recommend. This is just for a reference example.

Thank you again for all your help for testing this workaround and for posting your detailed findings.

@casos92
Copy link

casos92 commented Mar 15, 2023

I am getting

patch-edid.rb:11:in <main>': undefined method []' for nil:NilClass (NoMethodError)

when running sudo ruby patch-edid.rb

@dikamsiyoung
Copy link

dikamsiyoung commented Mar 27, 2023

✅ Confirmed working with:

  • Ventura 13.2.1
  • Adaugherity's Patch-edid.rb Script as in the OP
  • Sony X85J 4K TV
  • HDMI to DisplayPort Converter
  • Stone Henge Thunderbolt 3 Dock

@lelandbr
Copy link

lelandbr commented Apr 4, 2023

I can also confirm this is working with:

  • Intel MBP 2019
  • DELL U2410 display
  • MacOS Ventura 13.1
  • HDMI cable, connected to HDMI to USB-C adapter

This is the best solution I've found for fixing blurry/jagged text on an external display with MacOS. Much better than other methods from top google hits! I've used it over the years with various laptops and displays and it's always worked great. Thanks so much for creating and maintaining this script!!!

@mazingtech
Copy link

After override EDID, refresh rate is fixed, only 60Hz available, my screen is 100Hz, how to fix this? Thanks!
image

@cooltig
Copy link

cooltig commented Jul 13, 2023

When I run the script it shows an error:[
](patch-edid.rb:11:in <main>': undefined method []' for nil:NilClass (NoMethodError))

@apassiou
Copy link

Here is my issue, I am sending signal from Mac to a monitor via HDbaseT adapter, and I get a display like this: https://i.imgur.com/yO1ArVp.png

Whats strange is if I rotate the picture 90 or 180 degrees it looks completely perfect. But setting it back to Standard it goes back looking like the picture. I tried running this script, but it didnt improve it.

@joevt
Copy link

joevt commented Oct 15, 2023

@apassiou What Mac, GPU, resolution, adapter, monitor? Do you mean 90° and 270° rotation? If it were 180°, then you could just turn the display upside down. 90° or 270° means the height becomes the width which is lower than the original width. So maybe the adapter doesn't like the original width. This seems like a limitation of the adapter, gpu, or the display. Did you try without the adapter?

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