Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@resoau
Created September 13, 2019 09:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save resoau/d34935f598ed7c6d55414a2f8fcc0fab to your computer and use it in GitHub Desktop.
Save resoau/d34935f598ed7c6d55414a2f8fcc0fab 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'
def replacebytes (edid, pos, thebytes, newlen)
bytepos = pos * 2
thelen = thebytes.length
if newlen.to_s. != ''
thelen = newlen * 2
end
edid = edid[0, bytepos] + thebytes + edid[(bytepos + thelen)..-1]
return edid
end
def repairchecksums (edid)
blockoffset = 0
bytes = edid.scan(/../).map{|x|Integer("0x#{x}")}.flatten
while blockoffset * 2 < edid.length do
checksum = (0x100-(bytes[blockoffset,126].reduce(:+) % 256)) % 256
edid = replacebytes(edid, blockoffset + 127, sprintf("%02x", checksum), "")
blockoffset += 128
end
return edid
end
def deleteblock (edid, blockoffset)
if blockoffset * 2 < edid.length
edid = replacebytes(edid, blockoffset, "", 128)
edid = replacebytes(edid, 126, sprintf("%02x", Integer(edid[252,2], 16) - 1), "")
else
puts "Block at #{blockoffset} doesn't exist"
end
return edid
end
def deleteblocktype (edid, blocktype)
blockoffset = 128
while blockoffset * 2 < edid.length do
blocktag=Integer(edid[blockoffset*2, 2], 16)
if blocktag == blocktype
edid = deleteblock(edid, blockoffset)
else
blockoffset += 128
end
end
end
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"
end
displays.each do |disp|
# Retrieve monitor model from edid data
monitor_name=[disp["edid_hex"].match(/000000fc00(.*?)0a/){|m|m[1]}.to_s].pack("H*")
if monitor_name.empty?
monitor_name = "Display"
end
puts "found display '#{monitor_name}': vendorid #{disp["vendorid"]}, productid #{disp["productid"]}, edid:\n#{disp["edid_hex"]}"
bytes = disp["edid_hex"].scan(/../).map{|x|Integer("0x#{x}")}.flatten
edid = disp["edid_hex"]
edidversion = Float(Integer(edid[0x12 * 2, 2], 16).to_s + "." + Integer(edid[0x13 * 2, 2], 16).to_s) # 1.3 or 1.4
isdigital = Integer(edid[0x14 * 2, 1], 16) > 7 # 0 or 1
puts "edid #{edidversion}"
puts "Is Digital #{isdigital}"
featuresupportbyte = Integer(edid[0x18 * 2, 2], 16)
featuresupport = (featuresupportbyte >> 3) & 3
puts "Featuresupport #{featuresupport}"
if edidversion < 1.4
case featuresupport
when 0
puts " Monochrome or Grayscale"
when 1
puts " RGB color"
when 2
puts " Non-RGB color"
when 3
puts " Undefined"
end
else
case (isdigital ? "1" : "0") + featuresupport.to_s
when "00"
puts " Monochrome or Grayscale"
when "01"
puts " RGB color"
when "02"
puts " Non-RGB color"
when "03"
puts " Undefined"
when "10"
puts " RGB 4:4:4"
when "11"
puts " RGB 4:4:4 + YCrCb 4:4:4"
when "12"
puts " RGB 4:4:4 + YCrCb 4:2:2"
when "13"
puts " RGB 4:4:4 + YCrCb 4:4:4 + YCrCb 4:2:2"
end
newfeaturesupportbyte = (featuresupportbyte & ~0x18) | (0 << 3)
edid = replacebytes(edid, 0x18, sprintf("%02x", newfeaturesupportbyte), "")
puts ' changed to "Monochrome or Grayscale"'
end
blockoffset = 128
while blockoffset * 2 < edid.length do
theblock = edid[blockoffset * 2, 254]
blocktag = Integer(theblock[0, 2], 16)
if blocktag == 2
ctaversion = Integer(theblock[2, 2], 16)
if ctaversion > 01
puts "cta-861 extension block with new version #{ctaversion} at #{blockoffset}"
yCbCrSupportbyte = Integer(theblock[6, 2], 16)
yCbCrSupport = (yCbCrSupportbyte >> 4) & 3
case yCbCrSupport
when 0
puts " no yCbCr support"
when 1
puts " yCbCr 4:2:2"
when 2
puts " yCbCr 4:4:4"
when 3
puts " yCbCr 4:4:4, yCbCr 4:2:2"
end
newyCbCrSupportbyte = (yCbCrSupportbyte & ~0x30) | (0 << 4)
edid = replacebytes(edid, blockoffset + 0x03, sprintf("%02x", newyCbCrSupportbyte), "")
puts ' changed to "no yCbCr support"'
if ctaversion > 02
detailedTimingDescriptorsOffset = Integer(theblock[4, 2], 16)
ctadatablockoffset = 4
while ctadatablockoffset < detailedTimingDescriptorsOffset do
ctadatablocklength = (Integer(theblock[ctadatablockoffset * 2, 2], 16) & 0x1f) + 1
ctadatablock = theblock[ctadatablockoffset * 2, ctadatablocklength * 2]
ctatagcode = (Integer(ctadatablock[0, 2], 16) >> 5) & 0x1f
if ctatagcode == 7
ctatagcode = "e" + Integer(ctadatablock[2, 2], 16).to_s
end
puts "#{ctadatablockoffset}: tag:#{ctatagcode} length:#{ctadatablocklength} block:#{ctadatablock}"
case ctatagcode
when 3
IEEEOUI = ctadatablock[6, 2] + ctadatablock[4, 2] + ctadatablock[2, 2]
puts "Vendor Specific IEEEOUI:"
case IEEEOUI
when "000c03"
puts " HDMI Licensing, LLC -> h14b VSDB"
h14bByte = Integer(ctadatablock[12, 2], 16)
h14b = (h14bByte >> 3) & 1
case h14b
when 0
puts " Support yCbCr 4:4:4 - No"
when 1
puts " Support yCbCr 4:4:4 - Yes"
end
newh14bByte = (h14bByte & ~0x08) | (0 << 3) # 0: No (RGB only), 1: Yes (yCbCr 4:4:4)
edid = replacebytes(edid, blockoffset + ctadatablockoffset + 6, sprintf("%02x", newh14bByte), "")
puts " changed to No"
when "c45dd8"
puts " HDMI Forum -> hf-VSDB"
hfByte = Integer(ctadatablock[14, 2], 16)
hf = (hfByte >> 0) & 7
case hf
when 0
puts " 4:2:0 10/12/16 bpc - No"
when 1
puts " 4:2:0 10 bpc - Yes"
when 2
puts " 4:2:0 12 bpc - Yes"
when 3
puts " 4:2:0 10/12 bpc - Yes"
when 4
puts " 4:2:0 16 bpc - Yes"
when 5
puts " 4:2:0 10/16 bpc - Yes"
when 6
puts " 4:2:0 12/16 bpc - Yes"
when 7
puts " 4:2:0 10/12/16 bpc - Yes"
end
newhfByte = (hfByte & ~0x07) | (0 << 0)
edid = replacebytes(edid, blockoffset + ctadatablockoffset + 7, sprintf("%02x", newhfByte), "")
puts " changed to No"
else
echo " Unknown OUI"
end
when "e14"
puts "yCbCr 4:2:0 Video Data Block"
when "e15"
puts "yCbCr 4:2:0 Capability Map Data Block at #{blockoffset+ctadatablockoffset} : #{ctadatablock}"
ctadatablock = ctadatablock[0, 4] + ((ctadatablock[4..-1]).gsub! '.' '0')
edid = replacebytes(edid, blockoffset + ctadatablockoffset, ctadatablock)
puts " Changed to ctadatablock"
end
ctadatablockoffset += ctadatablocklength
end
end
else
puts "cta-861 extension block with old version #{ctaversion} at #{blockoffset}"
end
else
puts "Extension block at #{blockoffset}: type:#{blocktag}"
end
blockoffset += 128
end
edid = repairchecksums(edid)
bytes = edid.scan(/../).map{|x|Integer("0x#{x}")}.flatten
puts "new edid:\n#{bytes.map{|b|"%02X"%b}.join}"
Dir.mkdir("DisplayVendorID-%x" % disp["vendorid"]) rescue nil
f = File.open("DisplayVendorID-%x/DisplayProductID-%x" % [disp["vendorid"], disp["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>#{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
@resoau
Copy link
Author

resoau commented Oct 18, 2020

Check out https://github.com/mbruggmann/osx-edid-overrides which is probably the best option for this currently.

@mamuf
Copy link

mamuf commented Dec 2, 2020

@resoau Oh god, yes, that works!

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