Skip to content

Instantly share code, notes, and snippets.

@jrjhealey
Forked from adaugherity/patch-edid.rb
Created June 21, 2020 16:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jrjhealey/9d3476cf13b4d52593251ad683f9af1c to your computer and use it in GitHub Desktop.
Save jrjhealey/9d3476cf13b4d52593251ad683f9af1c to your computer and use it in GitHub Desktop.
#!/usr/bin/ruby
# Create display override file to force Mac OS X to use RGB mode for Display
# see http://embdev.net/topic/284710
require 'base64'
data=`ioreg -l -d0 -w 0 -r -c AppleDisplay`
edids=data.scan(/IODisplayEDID.*?<([a-z0-9]+)>/i).flatten
vendorids=data.scan(/DisplayVendorID.*?([0-9]+)/i).flatten
productids=data.scan(/DisplayProductID.*?([0-9]+)/i).flatten
displays = []
edids.each_with_index do |edid, i|
disp = { "edid_hex"=>edid, "vendorid"=>vendorids[i].to_i, "productid"=>productids[i].to_i }
displays.push(disp)
end
# Process all displays
if displays.length > 1
puts "Found %d displays! You should only install the override file for the one which" % displays.length
puts "is giving you problems.","\n"
elsif displays.length == 0
puts "No display data found! Are any external displays connected?"
end
displays.each do |disp|
# Retrieve monitor model from EDID display descriptor
i = disp["edid_hex"].index('000000fc00')
if i.nil?
monitor_name = "Display"
else
# The monitor name is stored in (up to) 13 bytes of text following 00 00 00 fc 00.
# If the name is shorter than 13 bytes, it is terminated with a newline (0a) and then padded with spaces.
monitor_name = [disp["edid_hex"][i + 10, 26].to_s].pack("H*")
monitor_name.rstrip! # remove trailing newline/spaces
end
puts "Found display '#{monitor_name}': vendor ID=#{disp["vendorid"]} (0x%x), product ID=#{disp["productid"]} (0x%x)" %
[disp["vendorid"], disp["productid"]]
puts "Raw EDID data:\n#{disp["edid_hex"]}"
bytes=disp["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" % disp["vendorid"]) rescue nil
filename = "DisplayVendorID-%x/DisplayProductID-%x" % [disp["vendorid"], disp["productid"]]
puts "Output file: #{Dir.pwd}/#{filename}"
f = File.open(filename, '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>#{monitor_name} - forced RGB mode (EDID override)</string>
<key>IODisplayEDID</key>
<data>#{Base64.encode64(bytes.pack('C*'))}</data>
<key>DisplayVendorID</key>
<integer>#{disp["vendorid"]}</integer>
<key>DisplayProductID</key>
<integer>#{disp["productid"]}</integer>
</dict>
</plist>"
f.close
puts "\n"
end # displays.each
@jrjhealey
Copy link
Author

jrjhealey commented Jun 21, 2020

A fix for weird behaviour between MacOS and some monitors. If a picture is produced, and resolutions etc all appear correct, yet text is fuzzy - this might solve the problem.

Worked for me on OSX 10.15.1.

This fix will essentially force the Mac to output an RGB colourspace to the monitor instead of the YPbPr one, which is generally used for TVs.

1. Run the above script.

  • If the above script is saved as patch-edid.rb, then: ruby patch-edid.rb.
  • It is advisable to disconnect any other monitors than the offending one, and to run the terminal on the display in question.

2. A folder with a name of the form DisplayVendorID-xxxx will be created in your home directory.

3. Reboot into recovery mode.

  • With the machine powered down, hold Cmd+R while powering it on, until the Apple logo and a progress bar appears.

4. Inside recovery mode, open a terminal window (Utilities > Terminal on the top menu bar).

5. Copy the directory to the relevant path (change username appropriately):

  • cp -r /Volumes/Macintosh\ HD/Users/username/DisplayVendorID-xxxx /Volumes/Macintosh\ HD/System/Library/Displays/Contents/Resources/Overrides/

  • If /Volumes/Macintosh\ HD/System/Library/Displays/Contents/Resources/Overrides/ doesn't exist (/Resources/Overrides may not) you can make them without issue.

  • If a folder matching the DisplayVendorID-xxxx already exists, consider copying it/backing it up somewhere first.

6. Reboot

  • This should have rectified the problem by now (if it hasn't worked, double check the output of the script relates to the right monitor, the folder has been copied and put in the right place). If it still hasn't worked then this isn't the fix for you.

UPDATE 08.12.2020

It appears that on Big Sur and Catalina, it is no longer necessary to use the above approach and try to edit system partitions. The OS protections will stop this. Instead, the process can all be done within OSX, as follows:

1. Run the script as before

2. Make the filestructure if it doesn't already exist

  • sudo mkdir -p /Library/Displays/Contents/Resources/Overrides/

3. Copy the directory produced by the ruby script as before, to this newly created destination:

  • sudo cp -r ~/DisplayVendorID-xxxx/ /Library/Displays/Contents/Resources/Overrides/

  • This also means you do not need to reboot in to recovery mode or attempt to bypass the SIP (csrutil).

4. Unplug and replug the offending monitor, and the changes should apply

Useful further reading about the latter approach here: https://gist.github.com/ejdyksen/8302862#gistcomment-3460110

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