Shared Library Changes for Release 2.2.0

Shared library libddcutil is backwardly compatible with the one in ddcutil 2.1.4. The SONAME is unchanged as libddcutil.so.5. The released library file is libddcutil.so.5.2.0.

Display Change Reporting

General Comments

Detection of display connection and disconnection has been extensively reworked. Unfortunately, multiple issues cause tradeoffs in the implementation.

  • First, there's a tension in display change detection between minimizing the time between when a monitor connection/disconnection occurs, libddcutil becomes aware of the change, checking and rechecinging failed states (e.g. DDC not working), and invoking the client callback function to inform the client of the change.

    • Performance impact of how aggressively libddcutil watches for changes.
    • It is possible for a monitor to briefly disappear as disconnected and then immediately reconnect.
    • Conversely, it's also possible for a monitor to appear, then immediately disappear before the detection process is complete
    • There can be a delay between time the EDID can be read and DDC becomes functional.
  • Display change detection is unreliable with the nvidia proprietary driver.

    • The nvidia driver does not use /sys in the same way as amdgpu, i915, nouveau and other drm enabled drivers that are part of the Linux kernel.
    • Depending on driver version the phase of the moon, the /sys file system does not reflect display changes and UDEV events or X11 events are not generated. In that case no display changes are reported.
  • Whether a display appears to be connected depends on whether the EDID can be read. For DVI/HDMI devices, power to the EDID EEPROM is provided over the cable. As a result, the EDID is readable so long as a cable is connected, even if the display is powered off. For DisplayPort devices, the EDID is readable only if the monitor is supplying it power. Even here, there is a difference between plugging/unplugging a monitor power cord and pressing the power button on the display itself. In the latter case, whether power is supplied to the EEPROM and the EDID is readable depends on the monitor implementation.

  • Depending on driver, changes in monitor state may or may not be immediately relected in /sys.

  • Connection of Multi-Stream Transport (MST) hub devices may or may not be detected depending on the driver/device combination.

Consequently two algorithms. aka watch modes, exist for detecting display changes. The first is more efficient, the second more reliable:

  • watch mode XEVENT.

    -Scans for changes using the X11 API extension RANDR (which also works on Wayland), and if possible reads the EDID from /sys.

  • watch mode POLL

    • Attempts to read an EDID from every (plausible) /dev/i2c device in polling loop.
    • Doesn't use X11 or rely on /sys
    • Can consume a significant amount of CPU time on older machines.
      • Users with all but the most powerful machines may find it much too resource consumptive.
      • Impact can be adjusted by reducing the polling frequency (see option --poll-watch-loop-millisec) at the expense of responsiveness.

The possibility of a delay between the time a monitor is connected and the time that DDC becomes enabled is addressed in the following way:

  • If a monitor is detected but DDC is not working, the display connection event is reported to the client with bit DDCA_DISPLAY_EVENT_DDC_NOT_WORKING set in newly added flags field, and the display reference goes onto a recheck queue processed by a separate thread.
  • Later, there will be an event of type DDCA_EVENT_DDC_ENABLED if and when the recheck thread determines that DDC is working.

Sleep State Detection

  • Detecting and reporting sleep mode proved very problematic. libddcutil no longer watches for and reports changes to DPMS state. However, if an API function is called to perform an operation that requires communiation with a display that is asleep, status DDCRC_DPMS_ASLEEP is returned.

Options Affecting Display Change Detection.

Typically, these options are added to the [libddcutil] section of configuration file ddcutilrc. They can also be included in the opts argument string passed to ddca_init2().

  • --watch-mode XEVENT, POLL, DYNAMIC (default DYNAMIC) Specifies the watch algorithm to be used. DYNAMIC resolves to XEVENT if it appears that algorithm will work, otherwise it resolves to POLL.

  • --enable-try-get-edid-from-sysfs (default), --disable-try-get-edid-from-sysfs. Get the EDID from /sys if possible instead of reading it from the display. This can improve performance, but may affect of reliability.

  • --xevent-watch-loop-millisec. Override the interval for watch-mode XEVENT at whcih libddcutil checks for an X11 screen change notification. Default: 100 milliseconds

  • --poll-watch-loop-millisec. Override the interval for watch-mode POLL at which libddcutil attempts to read an EDID from every /dev/i2c device. Default: 2000 milliseconds.

  • --disable-watch-displays. Completely disables display change detection. This blocks ddca_start_watch_displays() from starting the threads that watch for display changes. Workaround for github issue 470. Unlike the use of KDE Plasma environment variable POWERDEVIL_NO_DDCUTIL=1, this disables display change detection while leaving other libddcutil services available.

  • --ignore-mmid . Exclude a specific monitor model from display change detection. The argument for this option is a monitor model id, e.g. e.g. SAM-U32H75x-3587. The monitor model id can be found in the output of command ddcutil detect --verbose, and is the same monitor model id that is used in the name of a user defined features file. This option exists because some monitors behave in ways that make KDE PowerDevil unstable. See, for example, ddcutil github issue 466:Samsung Odyssey G5 S27DG50: Setting brightness causes monitor to crash, github issue 470:DDCA_EVENT_DPMS_AWAKE sent shortly after DDCA_EVENT_DPMS_ASLEEP, and KDE bug 495746:https://bugs.kde.org/show_bug.cgi?id=495746.

API Changes

  • ddca_start_watch_displays() has been modifed (in a backwards compatible way) to reflect the fact that sleep state changes are no longer reported. .
    • The only event class that can be enabled is DDCA_EVENT_CLASS_DISPLAY_DETECTION.
    • DDCA_EVENT_CLASS_ALL is effectively the same as DDCA_EVENT_CLASS_DISPLAY_CONNECTION.
    • It is an error if either DDCA_EVENT_CLASS_DPMS or DDCA_EVENT_CLASS_NONE are specified.
  • Struct DDCA_Display_Status_Event
    • Add field flags in the unused secton of DDCA_Display_Status Event. It has one bit defined, DDCA_DISPLAY_EVENT_DDC_WORKING. Normally, this bit is set on display connection events. In case DDC is not immediately available after EDID detection, this bit is not set. If DDC subsequently becomes enabled, an event of type DDCA_EVENT_DDC_ENABLED is emitted.
  • Add value DDCA_EVENT_DDC_ENABLED in enum DDCA_Display_Event_Type as a possible value of field event_type.

  • Handle possible delay between time that EDID can be read and DDC becomes functional

  • ddca_get_display_watch_settings(), ddca_set_display_watch_settings()

  • Added value DDC_EVENT_DDC_ENABLED to enum DDCA_Event_Type

  • Modified struct DDCA_Display_Status_Event
  • Field event_type can take the value DDC_EVENT_DDC_ENABLED, which indicates that DDC communication has become enabled some time after the display was detected
  • Added field flags, with bit DDCA_DISPLAY_EVENT_DDC_WORKING. This bit is normally set when event_type is DDC_EVENT_DISPLAY_CONNECTED. If it is not set, it indicaets that DDC communication is not working.
  • Added ddca_get_display_watch_settings(), ddca_set_display_watch_settings(), and struct DDCA_DW_Settings, enabling the client program to modify display watch configuration settings.

Other Shared Library Changes

Quiesce operation:

  • Quiesce the API during ddca_redetect_displays(). Operations that access display state are not permitted, and return DDCRC_QUIESCED.

Status codes:

  • Most functions that specify a DDCA_Display_Ref now return status code DDCRC_DISCONNECTED if the display reference is no longer valid.
  • Status code DDCRC_INVALID_CONFIG_FILE has been renamed to more general DDCRC_CONFIG_ERROR, and is used instead of DDCRC_BAD_DATA for User Defined Feature File errors. DDCRC_INVALID_CONFIG_FILE is a valid alias.

System log:

  • Write build date and time to system log when starting libddcutil.
  • Rework libdccutil output to avoid duplicate messages in the system log when all terminal output is directed to the log, as with KDE Plasma.
  • (Almost) all syslog messages are prefixed similarly with thread and timestamp.
  • Write syslog messages for sleeps incurred by display change detection.

Miscellaneous:

  • Option --disable-api completely disables the API, causing most API calls, including those performing DDC communication, to fail. This can be useful for testing whether libddcutil is the source of a system error in the case of client applications, e.g. KDE PowerDevil, that will not build without the shared library.

  • Use mutexes to control access to corruptable data structures, such as the table of open monitors.

  • Enum value DDCA_STATS_API is added to enum DDCA_Stats_Type, for reporting API specific statistics.

  • In some situations, it is meaningful to specify a display reference for a display that has been removed.

  • ddca_get_display_refs() and ddca_get_display_info_list2() always return 0, even if an error occured when examining a particular monitor. Addresses github issue 417:libddcutil ddca_get_display_info_list2 returns DDCRC_OTHER (-3022)

    • Errors that occur opening individual displays or reading their EDIDs are are still reported using ddca_get_error_detail(). In addition, error messages are written to the terminal and, depending on the current syslog level, to the system log.
  • API calls ddca_get_display_refs() and ddca_get_display_info_list2() do not include displays that have been marked removed. Addresses ddcutil issue #417.

  • The opaque value of DDCA_Display_Ref is now an integer display reference id number instead of an actual pointer to the internal display reference, making it slightly more opaque. The published type remains (void*), so no client changes should be needed.

  • libddcutil maintains a table of DDCA_Display_Refs that have been passed to the client by the API. This table is used to further validate display references the client passes as arguments.

  • Disable the check, in the prolog to API functions, that a monitor is not asleep. It is possible that a display is still responsive to DDC requests in this case. If it is not, the error will be detected when actually performing the requested operation.

  • ddca_get_display_info(), which does not itself perform any DDC operations, succeeds even if DDC communication is not working. Addresses issue ddcui issue #55.

  • Option --skip-ddc-checks sets the detected VCP version to "Unknown". Addresses a possible segfault when this option is used with display change detection.

  • Avoid hang waiting for watch thread to stop when client terminated using ctl-C.