Controlling EIZO displays

This document is a collection of cleaned up personal notes. Feel free to contact me if you’re interested in continuing the efforts.

Contents

Overview

Goals

To be able to change the brightness and signal input of an EIZO ColorEdge display simply and quickly from all of Linux/Windows/macOS, but primarily Linux, which is my daily driver OS. Note that EIZO monitors are exceptional in that they do not support DDC/CI, for which I already have a solution.

I haven’t paid this much money for gatekeeping, and changing those settings on CG2700S has become even more annoying than on a CS2730.

What’s particularly insulting is that EIZO does provide such software for their cheap FlexScan series (called Screen InStyle), and that it could be rather easily adjusted to do ColorEdge as well, with an unimportant caveat.

Concrete goals

  • ✓ Reuse official libemc libraries, which covers all target platforms. While relatively easy to achieve, this doesn’t really integrate into my environment.

    I’m leaving this as an exercise for the reader, you should have enough information.

  • ✓ Create a fully open source, multiplatform tool to change the brightness and the current input of connected EIZO monitors. The result has a command line mode, and on Windows/macOS, also a systray utility.

  • ✓ Create a filter tool for macOS/Linux tshark JSON output, in order to figure out what EIZO software is doing over the wires.

Tools and also targets

  • Windows, macOS, Linux computers

  • Self-destroyed EIZO CS2730, new EIZO CG2700S bought on sale

Previous works

  • eilin no longer applies, the USB protocol has changed entirely.

  • eizomon is also mostly irrelevant, as the protocol for the old Foris differs substantially.

I have found the successful approaches too late, read on for further explanations:

  • EIZO-Brightness-Control interfaces with libemc, using interfaces decompiled from Screen InStyle.

  • myEIZOSensor contains an odd alternative libemc called libEizMonCtrl, with a corresponding header from somewhere. The author has forgotten where he got it from. Due to stripped symbols, it is otherwise not useful.

  • hid-monitor-control is somewhat hackish and limited, but actually reimplements the right thing.

  • hideizo and the related libeizo are a jackpot full of forbidden knowledge, however I can only use them as information resources, due to some of the author’s choices.


Reverse engineering

It took me way too much time to find out that my goals are actually technically possible at all. I had been a suffering customer for naught.

Targets

  • ColorNavigator 6 can calibrate an EIZO display, but is overall fairly limited in what else it can do.

  • ColorNavigator 7 can, in addition, read out and change the brightness and channel gain.

  • EIZO applications are actually primarily user interfaces to an internal library called libemc, short for EIZO Monitor Control. That includes Screen InStyle and Quick Color Match, where the macOS versions just need some install_name_tool adjustments.

  • As it turns out, libemc and the underlying libmctrl can control almost anything in both FlexScan and ColorEdge monitors.

  • The applications, and by extension libemc, can only be downloaded off the Internet for macOS and Windows. However, ColorNavigator 7 can also be obtained for Red Hat Linux by explicitly asking your local EIZO representative for it, as you’re instructed in its user manual. These Linux versions are missing from the online index. Portability-wise, the included libemc has very few dependencies, the most important of which is libusb 0.1; the rest is fairly generic for Linux C++ binaries.

  • We can reuse and inspect libemc further. Sadly, whether you reuse it, or reimplement it, you need to resort to some reverse engineering. There are no real header files to be found.

Trivia

  • ColorNavigator locks the monitor, so that you can’t interrupt its operations.

  • ColorNavigator appears to use a hackish method to load and store profile settings, as it visibly switches through them, and it takes time.

Frontends

  • ColorNavigator 6 is a Flash application, and can be decompiled with jpegxs-decompiler.

    • There is not too much to learn from it.

    • The monitor capability database is encrypted with the key in CNMonitorCapabilityData.as, see documentation; I’ve already decrypted its modern version.

  • ColorNavigator 7 is a QtWebEngine application, and has prettifyable Javascript sources.

    • On Windows, the actual sources are in \ProgramData\EIZO, not Program Files.

    • On macOS, the actual sources are in /Library/Application Support/EIZO, not /Applications.

    • find . -name '*.js' -exec uglifyjs --beautify --output {}.full.js — {} \;

    • There is not too much to learn from the Javascript, however html/scripts/lib/salt.js names integer constants applicable to libemc (for clean reverse engineering).

    • It uses REST APIs internally for things like the brightness setting, however this is separate from the external API. It points to sublibraries. In any case, it helps with reverse engineering.

    • It also includes some encrypted data, AES ECB/CBC encrypted, with keys from cn7::crypto::EmbededKeyWithName().

  • Color InStyle is a .Net application, and the top level executables decompile well with usual .Net decompilers (for clean reverse engineering).

Tools

  • Native binaries: Ghidra + Binary Ninja in combination seem like the best choices

  • Screen InStyle: either ILSpy or dotPeek

Target platforms

It was the most convenient for me to primarily use a MacBook throughout, however the decompiling tools do not care what you load in them.

  • Linux x86_64: seems to decompile the best

  • macOS x86_64: also decompiles well

  • macOS arm64: decompiles poorly, but this is what you’ll run on modern macOS

  • Windows x86 & x86_64: stripped symbols, still theoretically useful

Target binaries

Windows

The Screen InStyle .Net executable perhaps provides the cleanest information on the use of libemc, to the extent that it actually uses it. Note that some of the functions are missing from macOS/Linux libemc. The application hardcodes some monitor data on its own, e.g., FlexScan ports. (The ports list for most EIZO monitors is also hardcoded into the libemc library, though I haven’t figured out how to access it through its API.)

EIZO installers can be unpacked on Linux/macOS with innoextract—​unzip and 7za don’t work, although there are clearly some ZIP entries within.

macOS

macOS packages can be unpacked without installing them by using bsdtar recursively, just mind that the paths below don’t strictly apply.

/Library/Application Support/EIZO/ColorNavigator 7/tools/CN7ResetTool/*

ColorNavigator 7’s bundled reset tool is a good minimal example of how libemc should be used, and it comes with its own library bundle. The library versions here might be older, though.

/Library/Application Support/EIZO/ColorNavigator 7/plugins/exports/mac/{libimpronta2,libetk}.dylib

This is a logging library loaded on runtime that might provide a few runtime hints, but it seems like a hassle to get working.

/Library/Application Support/EIZO/ColorNavigator 7/plugins/…​/libcn7*.dylib

ColorNavigator 7’s own various libraries decompile very well, and they generally show how individual libemc functions are supposed to be used.

  • The cn7::monitor namespace is seemingly statically linked to all of them.

  • com.eizo.cn7.core/mac/libcnCore.dylib is about as good as any other.

/Library/Application Support/EIZO/ColorNavigator 7/plugins/com.eizo.framework.salt/mac/libsalt.dylib
  • The "bagel" namespace seems interesting.

  • bagel::emerror_string() is an odd one, though it pertains directly to libemc rather than to the wire protocol.

/Applications/Screen InStyle.app/Contents/Resources/Libs/*.dylib

Screen InStyle is a bit more involved for reuse:

fixup() (install_name_tool \
  -change @executable_path/../Resources/Libs/$1 @rpath/$1 $2)
fixup libmptag.dylib libemc.dylib
fixup libHIDmctrl.dylib libemc.dylib
fixup libcolour.dylib libemc.dylib
fixup libmctrl.dylib libHIDmctrl.dylib
/Library/Application Support/EIZO/QuickColorMatch/plugins/exports/mac/*.dylib

Quick Color Match is similar, libraries need patching up.

libfwu.dylib
libfwupdate.dylib

Firmware updaters include an interesting library that names more HID usages, which aren’t actually even used by libemc. This is of interest for implementing custom interfaces.

Firmware files are encrypted using Blowfish (.efw) or AES-CBC (.efw2), and it is not made straight-forward to figure out. But you still can.

Linux

Ask your local EIZO support for a Linux package of ColorNavigator 7, then locate and extract the libraries. For reuse, they need some fixing up:

for i in lib*.so.?.*.*; do mv "$i" "${i%.*.*}"; done
for i in lib*.so.?; do ln -s "$i" "${i%.?}"; done
patchelf --set-rpath '$ORIGIN' lib*.so.?

Library notes

  • EIZOHIDMonitor is a concrete class for an interface, and it’s all an abstract mess from there.

  • The encrypted monitor capability database is not particularly interesting, it contains candela values and some feature flags:

    • …​/com.eizo.cn7.capability/MonitorCapability

    • …​/com.eizo.cn7.core/mac/libcn7Core.dylib: cn7::CapabilityCache::loadCapabilityFile

    • plusaes is this library. You can set a breakpoint on it to extract all keys.

    • The algorithm used is AES-128 CBC (some stuff can also be ECB):

      openssl aes-128-cbc -d -in MonitorCapability -K 31d7f8280c1b0319c241131fee62402b -iv 7dc27f06199827f338be1c0e98b567c4
      openssl aes-128-cbc -d -in SensorCapability -K c0a6d9a0ca597811e52d4d8924464f21 -iv ccb62a8b56e0c2e06820bd6968e4de60
    • The Linux build seems to be more obfuscated than macOS, however the keys were the same. Why bother.

  • EIZOMonitorProfile::GetAdditionalData() hardcodes available ports as EIZOInputPort constructors for some but not all models (my CS2730 is notably missing from there, but it provides profile data 0x53, which is again missing in the newer CG2700S). USB-C inputs simply report as DisplayPort (USB-C Alt Mode), but they may be identified by monitor profile data 0x61.

    Refer to ConvertPortValueToEmcIP() to get the external values you can get the names of, but it is roughly D-Sub, DVI, DisplayPort, HDMI, SDI, …​ times 0x100, plus index.

    This reconstructed listing might not be accurate:

    CG243W  | DVI1, DVI2, DP1
    CG223W  | DVI1, DVI2, DP1
    CG245W  | DVI1, DVI2, DP1
    CG275W  | DVI1, DP1, DP2
    SX2462W | DVI1, DVI2, DP1
    SX2262W | DVI1, DVI2, DP1
    SX2762W | DVI1, DP1, DP2
    S2232W  | DVI1, DSUB1
    S2432W  | DVI1, DSUB1
    S2242W  | DVI1, DSUB1
    S2233W  | DP1, DVI1, DSUB1
    S2433W  | DP1, DVI1, DSUB1
    S2243W  | DP1, DVI1, DSUB1
    EV2333W | DP1, DVI1, DSUB1
    EV2334W | HDMI1, DVI1, DSUB1
    EV2436W | DP1, DVI1, DSUB1
    EV3237  | DVI1, DP1, DP2, HDMI1
    EV2450  | DSUB1, DVI1, DP1, HDMI1
    EV2455  | DSUB1, DVI1, DP1, HDMI1
    EV2750  | DVI1, DP1, HDMI1
    EV2451  | DSUB1, DVI1, DP1, HDMI1
    EV2456  | DSUB1, DVI1, DP1, HDMI1
    EV2457  | DVI1, DP1, HDMI1
    EV2480  | DP1, DP2, HDMI1
    EV2485  | DP1, DP2, HDMI1
    EV2490  | DP1, DP2, HDMI1
    EV2495  | DP1, DP2, HDMI1
    EV2780  | DP1, DP2, HDMI1
    EV2795  | DP1, DP2, HDMI1
    EV3895  | DP1, DP2, HDMI1, HDMI2
    CG3145  | DP1, DP2, HDMI1, HDMI2
    CG3146  | SDI1, SDI2, SDI3, SDI4, SDIDUAL12, SDIDUAL34, SDIQUAD14, DP1, HDMI1
    EV2785  | DP1, DP2, HDMI1, HDMI2
    EV3285  | DP1, DP2, HDMI1, HDMI2
    CG319X  | DP1, DP2, HDMI1, HDMI2
    CG279X  | DVI1, DP1, DP2, HDMI1
    CG2700X | DP1, DP2, HDMI1
    CG2700S | DP1, DP2, HDMI1
    CS2731  | DVI1, DP1, DP2, HDMI1
    CS2410  | DVI1, DP1, HDMI1
    CS2740  | DP1, DP2, HDMI1
    EV2760  | DVI1, DP1, DP2, HDMI1
    EV2360  | DSUB1, DP1, HDMI1
    EV2460  | DSUB1, DVI1, DP1, HDMI1
    EV2781  | DP1, DP2, HDMI1
    EV2736W | DP1, DVI1

Library reuse

Use EmCtrlFind(NULL, 0) with EmCtrlGetSerialProduct() to initialize the library and retrieve monitor ID pairs, then you can just try to use any libemc function you have a prototype of. A Screen InStyle decompile should get you far.

Linking

To cross-compile Windows utilities, you will need gendef to create a .a file for linking with MinGW:

  • MSYS2: pacman -S mingw-w64-x86_64-tools-git

  • Arch Linux: pacaur -S mingw-w64-tools

In Makefile terms:

%.def: %.dll
        gendef - $< > $@
%.a: %.def
        dlltool --input-def $< --output-lib $@

To make executables look at their own directory for the reused libraries:

  • macOS: install_name_tool -add_rpath @executable_path

  • Linux: -Wl,-rpath,'$ORIGIN'


Software for debugging

macOS

  • Ghidra’s lldb integration cannot find Python’s psutil, and Homebrew’s pip3 doesn’t allow installing it.

  • The free version of Binary Ninja doesn’t support the arm64 architecture.

  • It seems like one has to trace functions with the debugger and Ghidra/BN side by side, at least if he doesn’t want to pay.

  • Qt Creator sucks massively with lldb.

  • Xcode is kind of fine: Debug - Debug Executable…​

    • breakpoint set --func-regex ^Em[^p]

    • breakpoint set --func-regex ^CHIDDevice

Linux

It should be possible to generate debugging symbols and use any capable GDB UI, although I haven’t tried doing so. These projects are incidentally ELF-only: ghidra2dwarf, ghidra-ExportDwarfELFSymbols.

And you should be able to actually use both Ghidra’s and Binary Ninja’s debugger modes.


USB packet capture setup

Wireshark’s USBHID filter/dissector (AnalyzeEnabled Protocols…​) sucks in that while it makes a few fields immediately readable, you also stop being able to select report data. With it disabled, remember that bRequests 1 and 9 are Get_Report and Set_Report respectively, and a wValue of 0x03XX is a Feature request concerning Report ID XX.

macOS

  • sudo ifconfig XHC{0,1,2} up

  • brew install --cask wireshark

  • Homebrew usbutils is close to pointless.

  • Homebrew usb.ids is sparse, and not used by lsusb either.

Windows

  • Wireshark can coïnstall USBPcap, which requires rebooting after installation.

  • Preferrably don’t toy around with the installation where you want to actually use ColorNavigator, such as by installing "USB Analyzer", and don’t install CN6 next to CN7. One of those actions made CN7 extremely laggy for me, and I don’t know how to fix it.

Linux

  • sudo modprobe usbmon

  • Either run the Windows version of ColorNavigator in VirtualBox, and pass it the monitor’s USB device, or run the Linux version.


Tracing and USB packet format

The monitor kind-of follows USB HID, however it doesn’t use something standard like MCCS over USB, but rather a custom, evolving, complicated protocol. Report IDs are always included at the beginning of report data as usual.

How libemc behaves on the CS2730

 - Initialization: EmCtrlFind

      EIZOMonitor::FindAllMonitor -> EIZOMonitor::FindAllHIDMonitor
       -> EmHIDFind -> EmFind -> EmFindEx
           -> CHIDDevice::FindDevices
               -> CHIDDevice::open -> CHHIDDeviceImplLeopard::open
                   -> CHHidDeviceImplLeopard::getCommandElements
               -> CHIDDevice::payoutPINCode
                   -> CHIDDevice::getFixedData
                       -> CHIDDeviceImplLeopard::getFixedData
                   -> CHIDDevice::getFeatureValue
                       -> CHIDDeviceImplLeopard::getFeatureValue

    - Read descriptor:    bRequest 1, wValue 0x0306, wIndex 0, wLength 3

               -> CHIDDevice::loadReportDescriptor
                   -> CHIDDevice::getFixedData
                       -> CHIDDeviceImplLeopard::getFixedData
                   -> CHIDDevice::setFeatureArray
                       -> CHIDDeviceImplLeopard::setFeatureArray

    - Write request type: bRequest 9, wValue 0x0301, wIndex 0, wLength 517

                   -> CHIDDevice::getFeatureArray
                       -> CHIDDeviceImplLeopard::getFeatureArray

    - Read response:      bRequest 1, wValue 0x0301, wIndex 0, wLength 517
      <repeats until the data is complete>

               -> CHIDDeviceImplLeopard::setProperty
           -> CHIDDevice::GetAllDevices
           -> CountDevices
               -> CHIDDevice{,ImplLeopard}::getProperty
               -> CHIDDevice::GetProductGroup
       -> EmHIDRetrieve -> EmRetrieve -> EmRetrieveEx
       -> EmHIDGetFixedData -> EmGetFixedData -> EmGetFixedDataRaw
       -> EmHIDGetData
           -> EmGetFixedDataEx, EmGetFeatureArray

    - Read serial product bRequest 1, wValue 0x0308, wIndex 0, wLength 25

           -> EmCheckResult
      EIZOMonitor:EmParseMonitorProfile
       -> EIZOMonitorInterface::GetFixedDataMonitorProfile
           -> EmHIDGetFixedData -> EmGetFixedData -> EmGetFixedDataRaw
       -> EIZOMonitorInterface::GetMonitorprofile
           -> EmHIDGetData
               -> EmGetFixedDataEx, EmGetFeatureArray

    - Read response:      bRequest 1, wValue 0x0309, wIndex 0, wLength 112

           -> EmCheckResult
       -> EIZOMonitorInterface::Supported3DLUT
           -> EmHIDGetFixedData -> EmGetFixedData
       -> EIZOMonitorProfile::EIZOMonitorProfile
           -> EmpTagParse, EmpGetTagDataSize, EmpGetTagData, Emp*...
      EIZOMonitor::EmParseModeProfile
       -> EmHIDGetFixedData -> EmGetFixedData
       -> EmHIDGetData
           -> EmGetFixedDataEx, EmGetFeatureArray, EmIsExclusiveMode

    - Write request type: bRequest 9, wValue 0x0305, wIndex 0, wLength 529
    - Read response:      bRequest 1, wValue 0x0305, wIndex 0, wLength 529

           -> EmCheckResult

    - Read result:        bRequest 1, wValue 0x0307, wIndex 0, wLength 8

       -> EIZOModeProfile::EIZOModeProfile
           -> EIZOModeProfile::parseProfileData -> Emp*...

 - Read brightness: EmCtrlGetBrightnessForPercentage

      EIZOMonitor::EmGetBrightness
       -> EIZOMonitorInterface::GetBrightness
           -> EIZOHIDMonitor::GetBrightness
               -> EmHIDGetFixedData -> EmGetFixedData
               -> EmHIDGetValue
                   -> EmGetFixedDataEx, EmGetFeatureValue
                       -> EmIsExclusiveMode
                       -> CHIDDeviceImplLeopard::setFeature*

    - Write request type: bRequest 9, wValue 0x0303, wIndex 0, wLength 39

                       -> CHIDDeviceImplLeopard::getFeatureArray

    - Read response:      bRequest 1, wValue 0x0303, wIndex 0, wLength 39

                   -> EmCheckResult

    - Read result:        bRequest 1, wValue 0x0307, wIndex 0, wLength 8

 - Write brightness: EmCtrlSetBrightnessForPercentage

    - Write request type: bRequest 9, wValue 0x0302, wIndex 0, wLength 39
    - Read result:        bRequest 1, wValue 0x0307, wIndex 0, wLength 8

I stopped bothering to check code paths near the end, it’s actually fairly straight-forward once you realize how the USB interface is structured.

Dumping report descriptors

Linux makes looking at HID report descriptors the easiest, though this won’t get you the interesting one:

# uniq -c /sys/kernel/debug/hid/*:056D:*/rdesc
$ hexdump /sys/bus/hid/devices/*:056D:*/report_descriptor

HID reports

Reports 1, 3, 5, 12, 14 use an odd scheme for retrieval where you first set Feature reports before you get them back with their values. Note that these reports all use the 0xff30 Usage Page:

1: [Receive] legacy report descriptor (Feature)

The monitor adds a layer to the HID protocol, in that this Feature report returns another HID report descriptor that describes data contained within reports 2—​5, 11—​14.

Tha data start with two 16-bit fields denoting the starting offset, and the total size of data.

2: Send small data (Feature)
4: Send large data (Feature)

These two differ by report size. If it’s more than 32 bytes, use the larger report.

The data consist of: 16-bit HID Usage Page, 16-bit HID Usage ID, 16-bit PIN code, and finally the subreport, without its Report ID.

3: Receive small data (Feature, Input)
5: Receive large data (Feature, Input)

Analogical to setting features, but mind the set/get oddity.

6: [Get] PIN code (Feature)

This returns a linearly increasing 16-bit field that presumably serves to help resolve race conditions.

7: [Receive] last command result (Feature)

Used to check whether the last operation has been successful.

The data consist of: 16-bit HID Usage Page, 16-bit HID Usage ID, 16-bit PIN code, and an 8-bit result code that should not be negative.

8: [Receive] serial number [and] model name (Feature)

The serial number is always 8 characters long, the rest of the data will be the model name, padded with spaces.

9: [Receive] monitor profile (Feature)

Key-value database of: 8-bit key, 8-bit length, data. See the EIZOMonitorProfile class.

10: Enter/exit critical section (Feature)

This has been added in later models, such as the CG2700S.

The data is a 16-bit number, which you can both get and set. libemc sets it either to zero, or to its PIN code.

11, 12, 13, 14: Send/receive small/large data (Feature)

Like 2, 3, 4, 5, but for critical sections.

The data is additionally prepended by the 16-bit critical section number.

Input reports

Button presses alone generate at least 4 Input reports, so the monitor is rather talkative. That said, not all possible changes are reported.

Monitor profile contents

Presence of keys differs by monitor. For me, the important ones were:

  • 0x53: a sequence of: 16-bit input port number, and some other 16-bit number

  • 0x61: 16-bit USB-C input port numbers

Other databases

The monitor maintains a "filesystem" where files have names in the scheme of ABC123. Filenames do not make a lot of sense. VER (firmware version?), EFW (more firmware information?), AIC (ASIC/FPGA data?), PRM (parameters?), EEP (EEPROM?), FLA (flash?), SYS (system?), INP (input?), LUT (look-up table?), MON (mode name?), INI (initial parameters?), BAK, BKL, MET, MOD, TMP…​

"prg000" is used for firmware uploads.


Firmware decoding

I have captured a firmware update of my CS2730. This is the easiest way to decrypt at least parts of it—​the .efw was over 2 MiB, the monitor received 1 MiB. There’s half of my plaintext!

$ tshark -r cs2730-fw-update.pcapng.gz -l -T ek --disable-protocol usbhid \
  | NO_COLOR=1 go run eizo-pcap-decode.go \
  | awk '$2 == "OUT" && $3 == "<>" && $4 == "ff0200fe" && $7 ~ /^prg000/ {
      print substr($6, 21) }' \
  | xxd -r -p > cs2730-fw-update.bin

The main microprocessor uses the Thumb-2 instruction set in little-endian. And there are symbols in the form of name references to the current function. It’s just not clear how to pair them automatically. Making sense of the code is still a difficult undertaking.


Reimplementing libemc

  • Linux’s generic HID driver takes over the USB device. It is preferrable to use hidapi-hidraw to communicate with that driver, rather than detaching it by using libusb. Either way, you will need to set up some evdev rules, or run as the superuser, to access the device.

    On Windows and macOS, no special privileges are needed to communicate.

    Keep in mind that the monitors accept firmware updates.

  • You have to parse both layers of HID report descriptors. Luckily you only need a very basic parser to retrieve the necessary data.

  • libemc is gnarly and full of special cases, so don’t aim unnecessarily high.

  • libeizo has the basics more or less right, and it names a ton of HID usages.

  • The rest of my attained know-how comes in the form of source code, see Goals.


Display notes

EIZO is overpriced. I went for a second one because I wanted a mostly uniform display, and after the backlight of my former BenQ PD3220U failed just out of warranty, the second deciding factor was a decent warranty period.

My experiences say that with EIZO, you pay for properties rather than durability. LCDs suck.

CS2730 woes

  • Burnt panel after mere roughly 18 thousand hours, through single-person usage. It’s hard to take an accurate picture of what it looks like, as it appears oddly smooth, but suck it still does.

    EIZO CS2730 with burnt panel
  • With a MacBook Pro M1 Pro connected over HDMI, after staying for too long in standby:

    • With a USB cable: the display decides to flash its power light orange and white, and refuse to cooperate until turned off and on through its power switch.

      This is addressed by the 1.0002 firmware, which can’t be installed with new macOS.

    • Without a USB cable, or occasionally with new firmware: HDMI input goes haywire, showing static, until the display is turned off and on through the touch power button. Sometimes additional waiting and fiddling is necessary.

    • This seems like a possible MacBook Pro issue, maybe with HDCP, since one would expect encrypted signal to be noisy. It happens to a lot of people.

CG2700S woes

  • Two dark subpixels appeared after a few weeks, within the same pixel near the edge. This is the first time I’ve even seen this kind of defect on a computer monitor. And no, you can’t do much about it.

Comments

Use e-mail, webchat, or the form below. I'll also pick up on new HN, Lobsters, and Reddit posts.