For developers
What a Mac app has to get right to talk DDC/CI
DDC/CI has a reputation for being unreliable. Some of that is the hardware. Some of it is what happens when an app treats a two-wire bus like a function call.
The address, and why EDID reads are shallow
DDC/CI lives at I2C chip 0x37, sub-address 0x51. EDID answers at 0x50. The EDID offset register is one byte, so only the base block and the first extension block are reachable — anything deeper has to be declined rather than guessed at.
A monitor needs a beat
Monitors need a moment before the reply is composed. Send a request and read the answer back in the same breath and you will get nothing on a surprising number of panels.
One request, not two
The write and the read share a single request so the driver keeps the bus timing. Splitting them into two transactions looks tidier in code and behaves worse on real hardware.
Do not spend ten timeouts on a panel that ignores you
Probing every known VCP feature code sounds thorough. It also means a panel that ignores DDC/CI entirely costs ten timeouts before you learn what a brightness read would have told you immediately. Brightness gates the sweep.
Never hard-link a symbol your Mac may not have
The Intel path needs a user-space I2C API that the IOKit umbrella does not export. Referencing that symbol directly would fail inside dyld and the app would never launch at all — on every Mac without it. Resolving it with dlsym turns that into a graceful degradation to “no DDC” instead of a crash before the first window.
Two buses, one interface
Intel Macs reach DDC through an IOFramebuffer's I2C interfaces; Apple Silicon goes through the AV service. The requests on the Intel path are synchronous, so no locking is needed — but the code above that must not assume that stays true.
Honest limits
- Not every panel answers DDC/CI at all. When it does not, the only honest fallback is software dimming, which changes the picture, not the backlight — blacks stop being black.
- None of the above is a standard. It is what one working implementation does, with the reasoning spelled out so it can be checked against the source.
- VCP feature codes vary by panel. Reading one successfully says nothing about the next one.