Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ddcutil-service for openSUSE:Factory checked in at 2025-03-10 18:04:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ddcutil-service (Old) and /work/SRC/openSUSE:Factory/.ddcutil-service.new.19136 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ddcutil-service" Mon Mar 10 18:04:42 2025 rev:10 rq:1251477 version:1.0.14 Changes: -------- --- /work/SRC/openSUSE:Factory/ddcutil-service/ddcutil-service.changes 2024-11-28 22:42:51.354225887 +0100 +++ /work/SRC/openSUSE:Factory/.ddcutil-service.new.19136/ddcutil-service.changes 2025-03-10 18:05:03.573900021 +0100 @@ -1,0 +2,17 @@ +Sat Mar 8 21:34:11 UTC 2025 - Michael Hamilton <mich...@actrix.gen.nz> + +- 1.0.14 + - A hotplug and DPMS tidy up to accommodate quirks exhibited by drivers, GPUs and monitors. + - Default to libddcutil event detection for libddcutil >= 2.2 (for faster response to changes). + - Add option --prefer-libddcutil-events as a better name for deprecated option --prefer-drm. + - Deprecate option --prefer-drm as it's name is misleading. + - Add option --disable-connectivity-signals to allow connectivity signals to be turned off. + - Add options --disable-hotplug-polling and --disable-dpms-polling to accommodate quirky monitors. + - Always internally poll for DPMS changes (DPMS is not covered by libddcutil events). + - Add method ListDetected to take advantage of hotplug detection in libddcutil >= 2.2. + - Add the list command to ddcutil-client to provide access to the new ListDetected method. + - Add wait, wait-connection-change, and wait-vcp-change commands to ddcutil-client. + - Log more information when get_vcp fails. + + +------------------------------------------------------------------- Old: ---- ddcutil-service-1.0.12.tar.gz New: ---- ddcutil-service-1.0.14.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ddcutil-service.spec ++++++ --- /var/tmp/diff_new_pack.7kbLF8/_old 2025-03-10 18:05:04.317930841 +0100 +++ /var/tmp/diff_new_pack.7kbLF8/_new 2025-03-10 18:05:04.321931008 +0100 @@ -18,7 +18,7 @@ Name: ddcutil-service -Version: 1.0.12 +Version: 1.0.14 Release: 0 Summary: D-Bus service for libddcutil VESA DDC Monitor Virtual Control Panel License: GPL-2.0-or-later ++++++ ddcutil-service-1.0.12.tar.gz -> ddcutil-service-1.0.14.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddcutil-service-1.0.12/Makefile new/ddcutil-service-1.0.14/Makefile --- old/ddcutil-service-1.0.12/Makefile 2024-11-05 19:51:52.000000000 +0100 +++ new/ddcutil-service-1.0.14/Makefile 2025-03-08 21:14:51.000000000 +0100 @@ -5,9 +5,10 @@ CFLAGS_DDCUTIL = $(shell pkg-config --cflags --libs ddcutil) # Uncomment to compile against a developer local libddcutil #CFLAGS_DDCUTIL = -isystem $(HOME)/Downloads/ddcutil-2.1.5-dev/src/public -L $(HOME)/Downloads/ddcutil-2.1.5-dev/src/.libs -lddcutil -CFLAGS_DDCUTIL = -isystem $(HOME)/Downloads/ddcutil-2.1.5-dev-clion/build/src/public -isystem $(HOME)/Downloads/ddcutil-2.1.5-dev-clion/src/public -L $(HOME)/Downloads/ddcutil-2.1.5-dev-clion/build/src/.libs -lddcutil +#CFLAGS_DDCUTIL = -isystem $(HOME)/Downloads/ddcutil-2.1.5-dev-clion/build/src/public -isystem $(HOME)/Downloads/ddcutil-2.1.5-dev-clion/src/public -L $(HOME)/Downloads/ddcutil-2.1.5-dev-clion/build/src/.libs -lddcutil #CFLAGS_DDCUTIL = -isystem $(HOME)/Downloads/ddcutil-2.1.4-dev/src/public -L $(HOME)/Downloads/ddcutil-2.1.4-dev/src/.libs -lddcutil #CFLAGS_DDCUTIL = -isystem $(HOME)/Downloads/ddcutil-2.0.0/src/public -L $(HOME)/Downloads/ddcutil-2.0.0/src/.libs -lddcutil +#CFLAGS_DDCUTIL = -isystem $(HOME)/Downloads/ddcutil-2.2.0/src/public -L $(HOME)/Downloads/ddcutil-2.2.0/src/.libs -lddcutil CFLAGS += -g -std=gnu11 -Werror -Wall -Wformat-security -Og SOURCE = ddcutil-service.c diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddcutil-service-1.0.12/README.md new/ddcutil-service-1.0.14/README.md --- old/ddcutil-service-1.0.12/README.md 2024-11-05 19:51:52.000000000 +0100 +++ new/ddcutil-service-1.0.14/README.md 2025-03-08 21:14:51.000000000 +0100 @@ -139,6 +139,21 @@ granting the [Open Source development license]( https://jb.gg/OpenSourceSupport). ### Version History +- 1.0.14 + - Default to libddcutil event detection for libddcutil >= 2.2 (for faster response to changes). + - Add option --prefer-libddcutil-events as a better name for deprecated option --prefer-drm. + - Deprecate option --prefer-drm as it's name is misleading. + - Add option --disable-connectivity-signals to allow connectivity signals to be turned off. + - Add options --disable-hotplug-polling and --disable-dpms-polling to accommodate quirky monitors. + - Always internally poll for DPMS changes (DPMS is not covered by libddcutil events). + - Add method ListDetected to take advantage of hotplug detection in libddcutil >= 2.2. + - Add the list command to ddcutil-client to provide access to the new ListDetected method. + - Add wait, wait-connection-change, and wait-vcp-change commands to ddcutil-client. + - Log more information when get_vcp fails. +- 1.0.13 + - Version 1.0.13 existed in development for some months, but was not released. + - Connectivity DBUS signalling was going to be on by default, but drivers, GPUs, and + monitors proved too inconsistent. - 1.0.12 - Return the error status-code if enable_ddca_watch_displays fails - was returning OK even on failure. - 1.0.11 @@ -149,8 +164,8 @@ - 1.0.9 - Fixed a GetCapabilitiesMetadata bug that caused some VCP features to lack metadata values. - Fixed the return of feature-name and feature-description from GetVcpMetadata. - - Fixed potential hot-plug/DPMS polling memory leaks and simplified event locking. - - Recoded hot-plug/DPMS polling to avoid a potential libddcutil assertion failure. + - Fixed potential hotplug/DPMS polling memory leaks and simplified event locking. + - Recoded hotplug/DPMS polling to avoid a potential libddcutil assertion failure. - Fixed code/doc typos, improved code readability/structure, reduced IDE warnings. - Updated documentation to caution against excessive updates when coding loops, as this may impact VDU NVRAM lifespan. - Updated documentation to caution against experimenting with non-standard features, as it may risk damage to the VDU. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddcutil-service-1.0.12/ddcutil-client.1 new/ddcutil-service-1.0.14/ddcutil-client.1 --- old/ddcutil-service-1.0.12/ddcutil-client.1 2024-11-05 19:51:52.000000000 +0100 +++ new/ddcutil-service-1.0.14/ddcutil-client.1 2025-03-08 21:14:51.000000000 +0100 @@ -25,6 +25,9 @@ .B detect Detect connected displays. .TP +.B list +List the currently detected VDUs without performing using a new detect. +.TP .B capabilities Display detailed capabilities of the specified display in a indented format and include value option names. .TP @@ -37,8 +40,17 @@ .B setvcp \fI0xNN n\fR Set the value of the VCP code specified by \fI0xNN\fR to \fIn\fR. .TP -.B getvcp-metadata \fI0xNN +.B getvcp-metadata \fI0xNN\fR Get the metadata for the VCP code specified by \fI0xNN\fR. +.TP +.B wait-for-connection-change +Wait for a \fBConnectedDisplaysChanged\fR signal, then exit. +.TP +.B wait-for-vcp-change +Wait for a \fBVcpValueChanged\fR signal, then exit. +.TP +.B wait +Wait for a \fBConnectedDisplaysChanged\fR or \fBVcpValueChanged\fR signal, then exit. .SH OPTIONS .TP diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddcutil-service-1.0.12/ddcutil-client.c new/ddcutil-service-1.0.14/ddcutil-client.c --- old/ddcutil-service-1.0.12/ddcutil-client.c 2024-11-05 19:51:52.000000000 +0100 +++ new/ddcutil-service-1.0.14/ddcutil-client.c 2025-03-08 21:14:51.000000000 +0100 @@ -357,8 +357,8 @@ * @param connection dbus connection to ddcutil-service * @return COMPLETED_WITHOUT_ERROR, SERVICE_ERROR or DBUS_ERROR */ -static cmd_status_t call_detect(GDBusConnection *connection) { - const char *operation_name = "Detect"; +static cmd_status_t call_detect(GDBusConnection *connection, gboolean list_only) { + const char *operation_name = list_only ? "ListDetected" : "Detect" ; GError *error = NULL; GVariant *result; @@ -484,6 +484,42 @@ return handle_dbus_error("set_property", error); } +void waiter(GDBusConnection* connection, + const gchar* sender_name, + const gchar* object_path, + const gchar* interface_name, + const gchar* signal_name, + GVariant* parameters, + gpointer user_data) { + g_print("Recieved signal %s %s\n", signal_name, g_variant_print(parameters, FALSE)); + exit(0); +} + +cmd_status_t wait_for_signal(gchar* signals[]) { + for (gchar **ptr = signals; *ptr; ptr++) { + const gchar *signal_name = *ptr; + guint id = g_dbus_connection_signal_subscribe(connection, + DBUS_BUS_NAME, + DBUS_INTERFACE_NAME, + signal_name, + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + waiter, + NULL, + NULL); + if (id != 0) { + g_print("waiting for %s signal...\n", signal_name); + } + else { + g_print("failed to wait for %s signal\n", signal_name); + return -1; + } + } + g_main_loop_run(g_main_loop_new(NULL, 0)); + return 0; +} + /** * Parse a command line int argument using strtol * @param input_str the command line argument @@ -616,7 +652,7 @@ }; context = g_option_context_new( - "[detect | capabilities | capabilities-terse | getvcp 0xNN | setvcp 0xNN n]"); + "[detect | list | capabilities | capabilities-terse | getvcp 0xNN | setvcp 0xNN n | wait-for-connection-change | wait-for-vcp-change | wait]"); g_option_context_add_main_entries(context, entries, NULL); if (!g_option_context_parse(context, &argc, &argv, &error)) { g_printerr("ERROR: Error parsing options: %s\n", error->message); @@ -678,7 +714,9 @@ g_printerr("ERROR: You must provide a method (detect, getvcp, or setvcp) and appropriate arguments.\n"); exit_status = SYNTAX_ERROR; } else if (g_strcmp0(method, "detect") == 0) { - exit_status = call_detect(connection); + exit_status = call_detect(connection, FALSE); + } else if (g_strcmp0(method, "list") == 0) { + exit_status = call_detect(connection, TRUE); } else if (g_strcmp0(method, "setvcp") == 0) { exit_status = parse_display_and_edid(&display_number, edid_txt); if (exit_status == COMPLETED_WITHOUT_ERROR) { @@ -743,7 +781,16 @@ if (exit_status == COMPLETED_WITHOUT_ERROR) { exit_status = call_capabilities(connection, display_number, edid_txt); } - } else { + } else if (g_strcmp0(method, "wait-for-connection-change") == 0) { + gchar* signals[] = {"ConnectedDisplaysChanged", NULL}; + exit_status = wait_for_signal(signals); + } else if (g_strcmp0(method, "wait-for-vcp-change") == 0) { + gchar* signals[] = {"VcpValueChanged", NULL}; + exit_status = wait_for_signal(signals); + } else if (g_strcmp0(method, "wait") == 0) { + gchar* signals[] = {"ConnectedDisplaysChanged", "VcpValueChanged", NULL}; + exit_status = wait_for_signal(signals); + } else { g_printerr("ERROR: Unknown command: %s\n", method); exit_status = SYNTAX_ERROR; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddcutil-service-1.0.12/ddcutil-service.1 new/ddcutil-service-1.0.14/ddcutil-service.1 --- old/ddcutil-service-1.0.12/ddcutil-service.1 2024-11-05 19:51:52.000000000 +0100 +++ new/ddcutil-service-1.0.14/ddcutil-service.1 2025-03-08 21:14:51.000000000 +0100 @@ -4,15 +4,23 @@ .SH SYNOPSIS .B ddcutil-service -.I --help +.B --help | -.I --version +.B --version | -.I --introspect +.B --introspect .br .B ddcutil-service [ -.B --emit-connectivity-signals +.B --enable-connectivity-signals +] +| +[ +.B --disable-hotplug-polling +] +| +[ +.B --disable-dpms-polling ] | [ @@ -20,7 +28,7 @@ ] | [ -.B --prefer-drm +.B --prefer-libddcutil-events ] | [ @@ -100,41 +108,63 @@ Outputs the XML introspection text for the service and exits. .TP -.B "--emit-connectivity-signals" +.B "--enable-connectivity-signals" -Enable the -.B ConnectedDisplaysChanged signal -sent to clients and also enable any monitoring for changes. -Once the service is running, this setting can be toggled by altering the -.B ServiceEmitConnectivitySignals -property. -See \fBSERVICE SIGNALS\fP. +Enable monitor hotplug detection and DPMS-state detection. These connectivity +events generate the \fBConnectedDisplaysChanged signal\fP. Connectivity-detection +defaults to off because it may not work consistently for all monitors +and all GPU drivers. +One enabled, further connectivity related options may be employed to try and +obtain a workable configuration. + +.TP +.B "--disable-hotplug-polling" + +Disable internal polling for hotplug events. +Polling may wake some monitor models from DPMS sleep (seems rare). +(Disables polling \fBlibddcutil detect\fP.) + +.TP +.B "--disable-dpms-polling" + +Disable the internal polling for DPMS events. +Polling for DPMS state may wake some monitor models from DPMS sleep (seems rare). +(Disables polling \fBlibddcutil getvcp 0xd6\fP.) .TP .B "--prefer-polling" -Set polling to be the preferred method for detecting display connectivity changes -for the \fBConnectedDisplaysChanged signal\fP. This is the default. +Set service internal polling to be the preferred method for detecting display connectivity changes +for the \fBConnectedDisplaysChanged signal\fP. This is the default prior to \fBlibddcutil 2.2\fP +and may still be a better choice for monitors connected via DVI if they behave inconsistently +when using libddcutil event detection. .TP -.B "--prefer-drm" +.B "--prefer-libddcutil-events" Use -.B libddcutil DRM-lookups +.B libddcutil event detection as the preferred method for detecting display connectivity changes -for the \fBConnectedDisplaysChanged signal\fP. This option should detect changes sooner -with less overheads, but may fail to detect changes for some combinations or drivers and hardware. +for the \fBConnectedDisplaysChanged signal\fP. +This is the default when using \fBlibddcutil 2.2\fP and above. +Enabling this option should detect hotplug events sooner +with less overheads. Some internal polling will still occur to monitor +for DPMS events not covered by libddcutil. Libddcutil event detection +may fail to work reliably for some combinations of drivers, hardware, +and connectors, in which case, try \fB--prefer-polling\fP instead. +In particular, eventing from monitors hotplugged via DVI connectors seems +to be more inconsistent. .TP .B "--poll-interval" \fIseconds\fP -If polling is enabled, this option defines how often to check for display +This option defines how often to internally poll for display connectivity changes. Default 30 seconds, minimum 10 seconds, zero to disable polling. .TP .B "--poll-cascade-interval" \fIseconds\fP -If polling is enabled, this option defines the minimum interval between +This option defines the internal polling minimum interval between events within a cascade of events. For example, a cascade of events will occur when a session is locked and all displays are put into DPMS sleep. Default 0.5 seconds, minimum 0.1 seconds. @@ -214,6 +244,12 @@ Return a list of monitors detected along with their properties. .TP +.B ListDetected +Return the list of previously detected monitors along with their properties. +This method is particularly useful for \fBlibddcutil 2.2+\fP where detection +may occur in the background automatically. + +.TP .B GetVcp Query a display settings by VCP code, for example, brightness is VCP code 0x10. @@ -312,47 +348,32 @@ .TP .B ConnectedDisplaysChanged -The service may optionally emit a +The service may emit a .B ConnectedDisplaysChanged D-Bus signal when a display undergoes a connectivity status change -due to hot-plug and DPMS events. -This feature is optional because the manual -experimentation required to configure it is unnecessary for display -configurations that remain static. - -Change-detection can be enabled by passing -.B --emit-connectivity-signals -on the command line, or by setting the -.B ServiceEmitConnectivitySignals -property. +due to hotplug and DPMS events. This feature must be enabled +by using the \fB--enable-connectivity-signals\fP option. -To permanently enable change-detection, the -.B --emit-connectivity-signals -option can be appended to the -.B Exec -line of the -system or user D-Bus -.B com.ddcutil.DdcutilService.service -file (see \fBFILES\fP). - -Changes are detected in one of two ways. -The service defaults to periodic polling by -issuing \fBlibddcutil DDCA detects\fP. Polling is -likely to work for a wide variety of drivers and hardware. -Polling for changes will be subject to delays because -the polling interval defaults to 30 seconds (with a minimum of 10 seconds). -Alternatively the service can use \fBlibddcutil DRM access \fP to provide -a more efficient method for change detection, -this requires \fBddcutil/libddcutil version 2.1.0+\fP, a GPU configured for \fBDRM\fP, and -the \fB--enable-watch-displays\fP to be added to \fI[libddcutil] options\fP -in \fB$HOME/.config/ddcutil/ddcutilrc\fP. +When utilizing libddcutil 2.2, or above, the service defaults to +using libddcutil's inbuilt change detection. This is a portable change +detection mechanism which should detect changes without delay for +most desktop environments. + +For versions of libddcutil prior to 2.2, libddcutil's change detection +was somewhat more dependent on driver support. To provide a more portable +solution, for libddcutil prior to 2.2 the service defaults to doing it's own +internal polling for all changes. +Although portable, polling is slower to detect changes, the polling +interval defaults to 30 seconds (minimum 10 seconds). + +In either case, the options \fB--prefer-polling\fP and +\fB--prefer-libddcutil-events\fP can be used to override the default +for change detection. Not all displays, GPUs, GPU-drivers, or cabling, provide the necessary support for detecting connection status changes. Results may vary depending on the mix of desktop components, such as KDE, Gnome, X11, and Wayland. -DisplayPort behaves differently to DVI and HDMI when -a display is turned off but remains connected. Some drivers that -support DRM don't properly support the necessary change detection features. +See \fBLIMITATIONS\fP below for further details. .TP .B VcpValueChanged @@ -385,7 +406,7 @@ List the event-types sent by the .B ConnectedDisplaysChanged signal along with their text names. -Events are included for display connection/disconnection (hot-plug), DPMS-sleep, and DPMS-wake. +Events are included for display connection/disconnection (hotplug), DPMS-sleep, and DPMS-wake. If the list is empty, the GPU, GPU-driver, or .B libddcutil version doesn't support display event detection. @@ -503,7 +524,7 @@ Exec=/usr/bin/ddcutil-service .fi -Service options, such as \fB--emit-connectivity-signals\fP or \fB--prefer-drm\fP, +Service options, such as \fB--prefer-polling\fP or \fB--prefer-libddcutil-events\fP, should be appended to the end of \fBExec=\fP line. .TP @@ -663,10 +684,27 @@ full 16-bit values and passes them unaltered to the VDU. Some mixes of VPUs and GPUs don't consistently update -DRM metadata for hot-plug events. If \fBConnectedDisplaysChanged\fP -signals are not being raised, try manually adding \fB--prefer-polling\fP -option, to force the service to poll for changes. Polling is less responsive, -but it doesn't require DRM, and is likely to always work. +DRM metadata for hotplug events. Some drivers that +support DRM don't properly support the necessary hotplug detection features. +Monitors connected by DisplayPort behave differently to those connected by DVI +and HDMI when a display is turned off but remains connected. +hotplugging DVI connections appear to behave more inconsistently +than DisplayPort with some drivers (for multple OEMs). + +If \fBConnectedDisplaysChanged\fP signals are not being raised, you can +try manually adding \fB--prefer-polling\fP option, to force the service to +poll internally for changes. Polling is less responsive, +but it is more likely to work. + +DPMS state can only be reliably determined by periodically polling +monitors that support DPMS. +In the event that DPMS-polling causes any issues, it can disabled +by adding the \fB--disable-dpms-polling\fP option. + +The two internal polling options have been reported to wake at least +one model of monitor from sleep. If this occurs, each can be independently +disabled by the options \fB--disable-hotplug-polling\fP +and \fB--disable-dpms-polling\fP. Some GPU drivers and VDUs have buggy implementations of DDC. If you have the choice, a diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddcutil-service-1.0.12/ddcutil-service.7 new/ddcutil-service-1.0.14/ddcutil-service.7 --- old/ddcutil-service-1.0.12/ddcutil-service.7 2024-11-05 19:51:52.000000000 +0100 +++ new/ddcutil-service-1.0.14/ddcutil-service.7 2025-03-08 21:14:51.000000000 +0100 @@ -2,12 +2,12 @@ .\" Title: com.ddcutil.DdcutilInterface .\" Author: Michael Hamilton .\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/> -.\" Date: 07/02/2024 +.\" Date: 12/19/2024 .\" Manual: Miscellaneous .\" Source: ddcutil-service .\" Language: English .\" -.TH "COM\&.DDCUTIL\&.DDCU" "7" "07/02/2024" "ddcutil\-service" "Miscellaneous" +.TH "COM\&.DDCUTIL\&.DDCU" "7" "12/19/2024" "ddcutil\-service" "Miscellaneous" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -42,6 +42,11 @@ OUT a(iiisssqsu) detected_displays, OUT i error_status, OUT s error_message); +ListDetected (IN u flags, + OUT i number_of_displays, + OUT a(iiisssqsu) detected_displays, + OUT i error_status, + OUT s error_message); GetVcp (IN i display_number, IN s edid_txt, IN y vcp_code, @@ -260,6 +265,57 @@ .RE .PP OUT a(iiisssqsu) \fIdetected_displays\fR: +.RS 4 +An array of structures describing the VDUs\&. +.RE +.PP +OUT i \fIerror_status\fR: +.RS 4 +A libddcutil DDCRC error status\&. DDCRC_OK (zero) if no errors have occurred\&. +.RE +.PP +OUT s \fIerror_message\fR: +.RS 4 +Text message for error_status\&. +.RE +.SS "The ListDetected() method" +.sp +.if n \{\ +.RS 4 +.\} +.nf +ListDetected (IN u flags, + OUT i number_of_displays, + OUT a(iiisssqsu) detected_displays, + OUT i error_status, + OUT s error_message); +.fi +.if n \{\ +.RE +.\} +.PP +Returns the currently detected VDUs without performing using a new detect\&. This method is particularly useful for libddcutil 2\&.2+ where detection may occur in the background automatically\&. +.PP +The array +\fIdetected_displays\fR +will be of length +\fInumber_of_displays\fR\&. +.PP +Each element of +\fIdetected_displays\fR +array will contain the fields specified by the AttributesReturnedByDetect property\&. The fields will include the libddcutil display\-number and a base64\-encoded EDID\&. +.PP +IN u \fIflags\fR: +.RS 4 +For future use +.RE +.PP +OUT i \fInumber_of_displays\fR: +.RS 4 +.sp +.RE +.PP +OUT a(iiisssqsu) \fIdetected_displays\fR: .RS 4 An array of structures describing the VDUs\&. .RE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddcutil-service-1.0.12/ddcutil-service.c new/ddcutil-service-1.0.14/ddcutil-service.c --- old/ddcutil-service-1.0.12/ddcutil-service.c 2024-11-05 19:51:52.000000000 +0100 +++ new/ddcutil-service-1.0.14/ddcutil-service.c 2025-03-08 21:14:51.000000000 +0100 @@ -58,7 +58,7 @@ #include <ddcutil_status_codes.h> #include <ddcutil_macros.h> -#define DDCUTIL_DBUS_INTERFACE_VERSION_STRING "1.0.12" +#define DDCUTIL_DBUS_INTERFACE_VERSION_STRING "1.0.14" #define DDCUTIL_DBUS_DOMAIN "com.ddcutil.DdcutilService" #if DDCUTIL_VMAJOR == 2 && DDCUTIL_VMINOR == 0 && DDCUTIL_VMICRO < 2 @@ -176,6 +176,31 @@ </method> <!-- + ListDetected: + @flags: For future use + @detected_displays: An array of structures describing the VDUs. + @error_status: A libddcutil DDCRC error status. DDCRC_OK (zero) if no errors have occurred. + @error_message: Text message for error_status. + + Returns the currently detected VDUs without performing using a new detect. + This method is particularly useful for libddcutil 2.2+ where detection + may occur in the background automatically. + + The array @detected_displays will be of length @number_of_displays. + + Each element of @detected_displays array will contain the fields + specified by the AttributesReturnedByDetect property. The fields + will include the libddcutil display-number and a base64-encoded EDID. + --> + <method name='ListDetected'> + <arg name='flags' type='u' direction='in'/> + <arg name='number_of_displays' type='i' direction='out'/> + <arg name='detected_displays' type='a(iiisssqsu)' direction='out'/> + <arg name='error_status' type='i' direction='out'/> + <arg name='error_message' type='s' direction='out'/> + </method> + + <!-- GetVcp: @display_number: The libddcutil/ddcutil display number to query @edid_txt: The base-64 encoded EDID of the display @@ -707,7 +732,7 @@ G_STATIC_ASSERT(G_N_ELEMENTS(flag_options) == G_N_ELEMENTS(flag_options_names)); // Boilerplate /* ---------------------------------------------------------------------------------------------------- - * Define the service's connectivity monitoring options for VDU hot-plug/DPMS events + * Define the service's connectivity monitoring options for VDU hotplug/DPMS events * * Detecting events is optional. Events can be detected byinternal event polling or by setting * up libddcutil callbacks. However, libddcutil needs fully functioning DRM to trap events, which @@ -718,9 +743,13 @@ MONITOR_BY_LIBDDCUTIL_EVENTS, } Monitoring_Preference_Type; -static gboolean display_status_detection_enabled = FALSE; +static gboolean display_status_detection_enabled = FALSE; // TODO this seems to always be TRUE - get rid of it? static gboolean enable_connectivity_signals = FALSE; +// User overrides for polling: +static gboolean disable_hotplug_polling = FALSE; +static gboolean disable_dpms_polling = FALSE; + static Monitoring_Preference_Type monitoring_preference = MONITOR_BY_INTERNAL_POLLING; #define MIN_POLL_SECONDS 10 @@ -961,7 +990,8 @@ DDCA_Status detect_status = ddca_get_display_info_list2(include_invalid, dlist_loc); // Pre libddcutil 2.1.5 ddca_get_display_info_list2 could return DDCRC_OTHER if some VDUs were invalid, - // For libddcutil 2.1.5+ ddca_get_display_info_list2 will return DDCRC_OK, but set error_detail if some VDUs are invalid. + // For libddcutil 2.1.5+ ddca_get_display_info_list2 will return DDCRC_OK, but set error_detail if some VDUs + // are invalid. DDCA_Error_Detail *error_detail = ddca_get_error_detail(); if (error_detail != NULL) { // libddcutil 2.1.5 and abouve if (error_detail->status_code == DDCRC_OTHER) { @@ -1100,7 +1130,7 @@ * @param parameters inbound parameters * @param invocation originating D-Bus method call */ -static void detect(GVariant* parameters, GDBusMethodInvocation* invocation) { +static void detect(GVariant* parameters, GDBusMethodInvocation* invocation, gboolean list_only) { u_int32_t flags; g_variant_get(parameters, "(u)", &flags); @@ -1113,9 +1143,13 @@ g_info("Detect flags=%x", flags); - DDCA_Status detect_status = ddca_redetect_displays(); + DDCA_Status detect_status = DDCRC_OK; char* detect_message_text = NULL; + if (!list_only) { + detect_status = ddca_redetect_displays(); + } + if (detect_status != DDCRC_OK) { detect_message_text = get_status_message(detect_status); g_warning("Detect: ddca_redetect_displays failed status=%d message=%s", detect_status, detect_message_text); @@ -1226,6 +1260,14 @@ } ddca_close_display(disp_handle); } + else { + g_warning("GetVcp open failed for vcp_code=%d display_num=%d edid=%.30s... status=%d", + vcp_code, display_number, edid_encoded, status); + } + } + else { + g_warning("GetVcp get_display_info failed for vcp_code=%d display_num=%d edid=%.30s...", + vcp_code, display_number, edid_encoded); } char* message_text = get_status_message(status); GVariant* result = g_variant_new( @@ -1311,7 +1353,17 @@ } ddca_close_display(disp_handle); } + else { + g_info("GetMultipleVcp open failed for display_num=%d edid=%.30s...", + display_number, edid_encoded); + } + } + else { + g_info("GetMultipleVcp get_display_info failed for display_num=%d edid=%.30s...", + display_number, edid_encoded); } + g_info("GetMultipleVcp open looks ok for display_num=%d edid=%.30s...", + display_number, edid_encoded); char* message_text = get_status_message(status); GVariant* result = g_variant_new("(a(yqqs)is)", value_array_builder, status, message_text); g_dbus_method_invocation_return_value(invocation, result); // Think this frees the result @@ -1896,7 +1948,6 @@ */ static DDCA_Status enable_ddca_watch_displays(void) { #if defined(LIBDDCUTIL_HAS_CHANGES_CALLBACK) - g_message("ServiceEmitConnectivitySignals using libddcutil change detection"); int status = DDCRC_OK; DDCA_Display_Event_Class classes_loc; const bool running = ddca_get_active_watch_classes(&classes_loc) == DDCRC_OK; @@ -1909,7 +1960,6 @@ status = ddca_start_watch_displays(DDCA_EVENT_CLASS_ALL); if (status == DDCRC_OK) { g_message("registering libddcutil display status callback"); - poll_interval_micros = 0; // Disable internal polling status = ddca_register_display_status_callback(display_status_event_callback); if (status == DDCRC_OK) { return status; @@ -1974,7 +2024,10 @@ } if (g_strcmp0(method_name, "Detect") == 0) { - detect(parameters, invocation); + detect(parameters, invocation, FALSE); + } + else if (g_strcmp0(method_name, "ListDetected") == 0) { + detect(parameters, invocation, TRUE); } else if (g_strcmp0(method_name, "GetVcp") == 0) { get_vcp(parameters, invocation); @@ -2109,6 +2162,44 @@ return ret; } +static void configure_display_connectivity_detection(void) { + if (enable_connectivity_signals) { + if (monitoring_preference == MONITOR_BY_LIBDDCUTIL_EVENTS) { + g_message("ConnectedDisplaysChanged: ddcutil-service will use libddcutil events for hotplug"); + if (enable_ddca_watch_displays() != DDCRC_OK) { + g_warning("ConnectedDisplaysChanged: ddcutil-service falling back to internal polling."); + monitoring_preference = MONITOR_BY_INTERNAL_POLLING; + // Note - DPMS events aren't supported by libddcutil, so we will still do it by polling + } + } + else { + disable_ddca_watch_displays(); // just in case we are switching preferences. + } + // Need to poll for at least DPMS, but not hotplug if using libddcutil + if (!disable_hotplug_polling && monitoring_preference == MONITOR_BY_INTERNAL_POLLING) { + g_message("ConnectedDisplaysChanged: ddcutil-service will internally poll for hotplug"); + } else { + g_message("ConnectedDisplaysChanged: ddcutil-service internal hotplug polling disabled"); + } + if (!disable_dpms_polling) { + g_message("ConnectedDisplaysChanged: ddcutil-service will internally poll for DPMS"); + } else { + g_message("ConnectedDisplaysChanged: ddcutil-service internal polling for DPMS disabled"); + } + if (!disable_dpms_polling || !disable_hotplug_polling) { + g_message("ConnectedDisplaysChanged: ddcutil-service internal " + "poll-interval=%ld secs poll-cascade-interval=%5.3f secs", + poll_interval_micros / 1000000, poll_cascade_interval_micros / 100000.0); + } + } + else { + if (monitoring_preference == MONITOR_BY_LIBDDCUTIL_EVENTS) { + disable_ddca_watch_displays(); + } + g_message("ConnectedDisplaysChanged: disabled."); + } +} + /** * @brief Handles calls to DdcutilService D-Bus org.freedesktop.DBus.Properties.Set. * @@ -2164,34 +2255,9 @@ g_warning("Property ServiceEmitSignals is deprecated, please use ServiceEmitConnectivitySignals"); } enable_connectivity_signals = g_variant_get_boolean(value); - g_message("ServiceEmitConnectivitySignals set property to %s", - enable_connectivity_signals ? "enabled" : "disabled"); - switch (monitoring_preference) { - case MONITOR_BY_LIBDDCUTIL_EVENTS: - g_message("Detect changes using libddcutil callbacks."); - poll_interval_micros = 0; - if (enable_connectivity_signals) { - if (enable_ddca_watch_displays() == DDCRC_OK) { - break; // Success, exit the switch - } - // Failed, fall through to polling - } else { - disable_ddca_watch_displays(); - break; - } - case MONITOR_BY_INTERNAL_POLLING: - default: - if (enable_connectivity_signals) { - if (poll_interval_micros == 0) { // If not already set - poll_interval_micros = DEFAULT_POLL_SECONDS * 1000000; - } - g_message("ServiceEmitConnectivitySignals ddcutil-service polling every %ld seconds", - poll_interval_micros / 1000000); - } else { - poll_interval_micros = 0; - } - break; - } + g_message("ServiceEmitConnectivitySignals: setting property to %s", + enable_connectivity_signals ? "enabled" : "disabled"); + configure_display_connectivity_detection(); } else if (g_strcmp0(property_name, "ServicePollInterval") == 0) { const uint secs = g_variant_get_uint32(value); @@ -2262,6 +2328,9 @@ } static bool is_dpms_capable(const DDCA_Display_Info *vdu_info) { + if (disable_dpms_polling) { + return FALSE; + } DDCA_Status status; DDCA_Feature_Metadata *meta_0xd6 = NULL; #if defined(USE_DREF_CHECK_FOR_DPMS) @@ -2282,7 +2351,7 @@ return status == DDCRC_OK; } -static bool poll_dpms_awake(const DDCA_Display_Info* vdu_info) { +static bool is_dpms_awake(const DDCA_Display_Info* vdu_info) { DDCA_Display_Handle disp_handle; DDCA_Status status = ddca_open_display2(vdu_info->dref, 1, &disp_handle); if (status == DDCRC_OK) { @@ -2303,17 +2372,27 @@ const long now_in_micros = g_get_monotonic_time(); bool event_is_ready = FALSE; if (now_in_micros >= next_poll_time) { - g_debug("Poll for display connection changes"); - // Masking the logging is a bit hacky - it depends on internal knowledge of how libddcutil is logging. - // The author of libddcutil regards the normal messages as quite important, so they should normally be logged. - // A compromise: when the service is not logging debug/info, change the syslog mask, and then restore it. - int old_mask = 0; - if (!service_info_logging) { - old_mask = setlogmask(LOG_UPTO(LOG_WARNING)); // Temporarily disable notice msgs from libddcutil - } - const DDCA_Status detect_status = ddca_redetect_displays(); // Do not call too frequently, delays the main-loop - if (!service_info_logging) { - setlogmask(old_mask); // Restore original logging mask + // When monitoring_preference == MONITOR_BY_INTERNAL_POLLING, this function handles + // both hotplug and DPMS detection. + // When monitoring_preference == MONITOR_BY_LIBDDCUTIL_EVENTS libddcutil + // handles hotplug detection, but this function still handles DPMS detection. + const bool handle_hotplug_detection = + monitoring_preference == MONITOR_BY_INTERNAL_POLLING && !disable_hotplug_polling; + g_debug("Internal Poll check: %s", handle_hotplug_detection ? "hotplug and DPMS check" : "DPMS only check"); + + DDCA_Status detect_status = DDCRC_OK; + if (handle_hotplug_detection) { // Need to do expensive ddca_redected_displays() for hotplug detection + // Masking the logging is a bit hacky - it depends on internal knowledge of how libddcutil is logging. + // The author of libddcutil regards the normal messages as quite important, so they should be logged. + // A compromise: when the service is not logging debug/info, change the syslog mask, and then restore it. + int old_mask = 0; + if (!service_info_logging) { + old_mask = setlogmask(LOG_UPTO(LOG_WARNING)); // Temporarily disable notice msgs from libddcutil + } + detect_status = ddca_redetect_displays(); // Do not call too frequently, delays the main-loop + if (!service_info_logging) { + setlogmask(old_mask); // Restore original logging mask + } } if (detect_status == DDCRC_OK) { DDCA_Display_Info_List* dlist; @@ -2327,23 +2406,23 @@ // Check all displays, mark existing ones as connected, add new ones. for (int ndx = 0; ndx < dlist->ct && !event_is_ready; ndx++) { - const DDCA_Display_Info* vdu_info = &dlist->info[ndx]; - gchar* edid_encoded = edid_encode(vdu_info->edid_bytes); - const GList* ptr = g_list_find_custom(poll_list, edid_encoded, pollcmp); - if (ptr != NULL) { // Found it, mark it as connected - Poll_List_Item* vdu_poll_data = (Poll_List_Item *)(ptr->data); + const DDCA_Display_Info* ddca_dinfo_ptr = &dlist->info[ndx]; + gchar* edid_encoded = edid_encode(ddca_dinfo_ptr->edid_bytes); + const GList* list_ptr = g_list_find_custom(poll_list, edid_encoded, pollcmp); + if (list_ptr != NULL) { // Found it, mark it as connected + Poll_List_Item* vdu_poll_data = (Poll_List_Item *)(list_ptr->data); const gboolean previous_dpms_awake = vdu_poll_data->dpms_awake; vdu_poll_data->connected = TRUE; - g_debug("Poll check: existing-connection disp=%d %.30s...", ndx + 1, edid_encoded); + g_debug("Internal Poll check: existing-connection disp=%d %.30s...", ndx + 1, edid_encoded); if (vdu_poll_data->has_dpms) { - vdu_poll_data->dpms_awake = poll_dpms_awake(vdu_info); + vdu_poll_data->dpms_awake = is_dpms_awake(ddca_dinfo_ptr); if (previous_dpms_awake != vdu_poll_data->dpms_awake) { g_message("Poll signal event - dpms changed to %s %d %.30s...", vdu_poll_data->dpms_awake ? "awake" : "asleep", ndx + 1, edid_encoded); Event_Data_Type* event = g_malloc(sizeof(Event_Data_Type)); event->event_type = vdu_poll_data->dpms_awake ? DDCA_EVENT_DPMS_AWAKE : DDCA_EVENT_DPMS_ASLEEP; - event->dref = vdu_info->dref; + event->dref = ddca_dinfo_ptr->dref; g_free(atomic_event_exchange(&signal_event_data, event)); // keep latest only event_is_ready = TRUE; // Only one event on each poll - terminate loop } @@ -2354,35 +2433,42 @@ Poll_List_Item* vdu_poll_data = g_malloc(sizeof(Poll_List_Item)); vdu_poll_data->edid_encoded = edid_encoded; vdu_poll_data->connected = TRUE; - vdu_poll_data->has_dpms = is_dpms_capable(vdu_info); - vdu_poll_data->dpms_awake = vdu_poll_data->has_dpms ? poll_dpms_awake(vdu_info) : TRUE; + vdu_poll_data->has_dpms = is_dpms_capable(ddca_dinfo_ptr); + vdu_poll_data->dpms_awake = vdu_poll_data->has_dpms ? is_dpms_awake(ddca_dinfo_ptr) : TRUE; poll_list = g_list_append(poll_list, vdu_poll_data); g_debug("Poll check: new-connection disp=%d %.30s... has_dpms=%d awake=%d ", ndx + 1, edid_encoded, vdu_poll_data->has_dpms, vdu_poll_data->dpms_awake); - if (next_poll_time) { // Not on first time through - g_message("Poll signal event - connected %d %.30s...", ndx + 1, edid_encoded); - Event_Data_Type* event = g_malloc(sizeof(Event_Data_Type)); - event->event_type = DDCA_EVENT_DISPLAY_CONNECTED; - g_free(atomic_event_exchange(&signal_event_data, event)); // keep latest only - event_is_ready = TRUE; // Only one event on each poll - terminate loop + if (handle_hotplug_detection) { + if (next_poll_time) { // Not on first time through + g_message("Poll signal event - connected %d %.30s...", ndx + 1, edid_encoded); + Event_Data_Type *event = g_malloc(sizeof(Event_Data_Type)); + event->event_type = DDCA_EVENT_DISPLAY_CONNECTED; + g_free(atomic_event_exchange(&signal_event_data, event)); // keep latest only + event_is_ready = TRUE; // Only one event on each poll - terminate loop + } } } } // Check if any displays are still marked as disconnected - for (GList* ptr = poll_list; ptr != NULL && !event_is_ready;) { - GList* next = ptr->next; - Poll_List_Item* vdu_poll_data = ptr->data; + for (GList* list_ptr = poll_list; list_ptr != NULL && !event_is_ready;) { + GList* list_next_ptr = list_ptr->next; // Save this now because we may delete list_ptr + Poll_List_Item* vdu_poll_data = list_ptr->data; if (!vdu_poll_data->connected) { - g_message("Poll signal event - disconnected %.30s...\n", vdu_poll_data->edid_encoded); - Event_Data_Type* event = g_malloc(sizeof(Event_Data_Type)); - event->event_type = DDCA_EVENT_DISPLAY_DISCONNECTED; - g_free(atomic_event_exchange(&signal_event_data, event)); // keep latest only + if (handle_hotplug_detection) { + g_message("Poll signal event - disconnected %.30s...", vdu_poll_data->edid_encoded); + Event_Data_Type *event = g_malloc(sizeof(Event_Data_Type)); + event->event_type = DDCA_EVENT_DISPLAY_DISCONNECTED; + g_free(atomic_event_exchange(&signal_event_data, event)); // keep latest only + event_is_ready = TRUE; // Only one event on each poll - terminate loop + } + else { + g_debug("Poll check: remove-connection %.30s... ", vdu_poll_data->edid_encoded); + } g_free(vdu_poll_data->edid_encoded); g_free(vdu_poll_data); - poll_list = g_list_delete_link(poll_list, ptr); - event_is_ready = TRUE; // Only one event on each poll - terminate loop + poll_list = g_list_delete_link(poll_list, list_ptr); } - ptr = next; + list_ptr = list_next_ptr; } ddca_free_display_info_list(dlist); } @@ -2430,7 +2516,7 @@ return FALSE; } - if (poll_interval_micros > 0) { + if (!disable_hotplug_polling || !disable_dpms_polling) { poll_for_changes(); } @@ -2644,8 +2730,8 @@ gboolean introspect_request = FALSE; // g_option_context_parse will overrun gboolean log_info = FALSE; // TODO should all bool be changed to gboolean for safety? - gboolean prefer_polling = TRUE; - gboolean prefer_drm = FALSE; + gboolean prefer_polling = FALSE; + gboolean prefer_libddcutil_events = FALSE; int poll_seconds = -1; // -1 flags no argument supplied double poll_cascade_interval_seconds = 0.0; @@ -2656,7 +2742,6 @@ gint ddca_syslog_level = DDCA_SYSLOG_NOTICE; gint ddca_init_options = 0; // DDCA_INIT_OPTIONS_CLIENT_OPENED_SYSLOG - // Use the glib command line parser... const GOptionEntry entries[] = { { @@ -2668,12 +2753,24 @@ "print introspection xml and exit", NULL }, { + "enable-connectivity-signals", 'q', 0, G_OPTION_ARG_NONE, &enable_connectivity_signals, + "enable DBUS signalling of display connection events", NULL + }, + { + "disable-hotplug-polling", 'u', 0, G_OPTION_ARG_NONE, &disable_hotplug_polling, + "disable internal polling for hotplug events (disable polling libddcutil detect)", NULL + }, + { + "disable-dpms-polling", 'm', 0, G_OPTION_ARG_NONE, &disable_dpms_polling, + "disable internal polling for DPMS events (disable polling getvcp 0xd6)", NULL + }, + { "prefer-polling", 'p', 0, G_OPTION_ARG_NONE, &prefer_polling, - "prefer polling for detecting display connection events", NULL + "prefer internal polling for detecting display connection events", NULL }, { - "prefer-drm", 'd', 0, G_OPTION_ARG_NONE, &prefer_drm, - "prefer libddcutil DRM-lookups for detecting display connection events", NULL + "prefer-libddcutil-events", 'd', 0, G_OPTION_ARG_NONE, &prefer_libddcutil_events, + "prefer libddcutil for detecting display connection events", NULL }, { "poll-interval", 't', 0, G_OPTION_ARG_INT, &poll_seconds, @@ -2684,14 +2781,6 @@ "polling minimum interval between cascading events in seconds, 0.1 minimum", NULL }, { - "emit-connectivity-signals", 'e', 0, G_OPTION_ARG_NONE, &enable_connectivity_signals, - "enable the D-Bus ConnectedDisplaysChanged signal and associated change monitoring", NULL - }, - { - "emit-signals", 'e', 0, G_OPTION_ARG_NONE, &enable_connectivity_signals, - "deprecated, replaced by --emit-connectivity-signals", NULL - }, - { "return-raw-values", 'r', 0, G_OPTION_ARG_NONE, &return_raw_values, "return high-byte and low-byte for all values, including Simple Non-Continuous values", NULL }, @@ -2700,7 +2789,7 @@ "lock configuration, make properties and sleep-multipliers read only, disable the restart method", NULL }, { - "log-info", 'i', 0, G_OPTION_ARG_NONE, &log_info, + "log-info", 'I', 0, G_OPTION_ARG_NONE, &log_info, "log service info and debug messages", NULL }, { @@ -2711,6 +2800,10 @@ "ddca-init-options", 'i', 0, G_OPTION_ARG_INT, &ddca_init_options, "1=Disable-Config-File", NULL }, + { + "prefer-drm", 'd', 0, G_OPTION_ARG_NONE, &prefer_libddcutil_events, + "deprecated nondescript name, same as --prefer-libddcutil-events", NULL + }, {NULL} }; @@ -2762,7 +2855,6 @@ exit(1); } - // Handle ddcutil ddc_init() arguments char* argv_null_terminated[argc]; for (int i = 0; i < argc; i++) { @@ -2804,43 +2896,34 @@ GMainLoop* main_loop = g_main_loop_new(NULL, FALSE); - prefer_polling = !prefer_drm; - monitoring_preference = prefer_polling ? MONITOR_BY_INTERNAL_POLLING : MONITOR_BY_LIBDDCUTIL_EVENTS; - - if (enable_connectivity_signals) { - switch (monitoring_preference) { - case MONITOR_BY_LIBDDCUTIL_EVENTS: { - const int enable_watch_status = enable_ddca_watch_displays(); - if (enable_watch_status == DDCRC_OK) { - poll_interval_micros = 0; - break; - } - g_warning("Falling back to service internal polling for change detection"); - } - case MONITOR_BY_INTERNAL_POLLING: - default: - g_message("ConnectedDisplaysChanged signal - using service internal polling for change detection"); - if (poll_seconds >= 0 && !update_poll_interval(poll_seconds)) { - g_print("Polling interval parameter must be at least %d seconds.", MIN_POLL_SECONDS); - exit(1); - } - if (poll_cascade_interval_seconds > 0.0 - && !update_poll_cascade_interval(poll_cascade_interval_seconds)) { - g_print("Polling cascade interval parameter must be at least %5.3f seconds.", - MIN_POLL_CASCADE_INTERVAL_SECONDS); - exit(1); - } - g_message("ConnectedDisplaysChanged signal - poll-interval=%ld poll-cascade-interval=%5.3f", - poll_interval_micros / 1000000, poll_cascade_interval_micros / 100000.0); - break; - } + if (prefer_polling) { + monitoring_preference = MONITOR_BY_INTERNAL_POLLING; + } + else if (prefer_libddcutil_events) { + monitoring_preference = MONITOR_BY_LIBDDCUTIL_EVENTS; } else { - g_message("ConnectedDisplaysChanged signals disabled" - "(add --emit_connectivity_signals to enable them)."); - poll_interval_micros = 0; // Disable internal polling + const gboolean has_reliable_events = ddca_ddcutil_version().major > 2 || + (ddca_ddcutil_version().major == 2 && ddca_ddcutil_version().minor >= 2); + if (has_reliable_events) { + g_message("libddcutil version >= 2.2 - service will default to libddcutil-events for change detection."); + } + monitoring_preference = has_reliable_events ? MONITOR_BY_LIBDDCUTIL_EVENTS : MONITOR_BY_INTERNAL_POLLING; } + if (poll_seconds >= 0 && !update_poll_interval(poll_seconds)) { + g_print("Polling interval parameter must be at least %d seconds.", MIN_POLL_SECONDS); + exit(1); + } + if (poll_cascade_interval_seconds > 0.0 + && !update_poll_cascade_interval(poll_cascade_interval_seconds)) { + g_print("Polling cascade interval parameter must be at least %5.3f seconds.", + MIN_POLL_CASCADE_INTERVAL_SECONDS); + exit(1); + } + + configure_display_connectivity_detection(); + enable_custom_source(main_loop); // May do nothing - but a client may enable events or polling later g_main_loop_run(main_loop); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddcutil-service-1.0.12/ddcutil-service.spec new/ddcutil-service-1.0.14/ddcutil-service.spec --- old/ddcutil-service-1.0.12/ddcutil-service.spec 2024-11-05 19:51:52.000000000 +0100 +++ new/ddcutil-service-1.0.14/ddcutil-service.spec 2025-03-08 21:14:51.000000000 +0100 @@ -18,7 +18,7 @@ Name: ddcutil-service -Version: 1.0.12 +Version: 1.0.14 Release: 0 Summary: D-Bus service for libddcutil VESA DDC Monitor Virtual Control Panel License: GPL-2.0-or-later diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddcutil-service-1.0.12/docs/html/ddcutil-service.1.html new/ddcutil-service-1.0.14/docs/html/ddcutil-service.1.html --- old/ddcutil-service-1.0.12/docs/html/ddcutil-service.1.html 2024-11-05 19:51:52.000000000 +0100 +++ new/ddcutil-service-1.0.14/docs/html/ddcutil-service.1.html 2025-03-08 21:14:51.000000000 +0100 @@ -1,5 +1,5 @@ <!-- Creator : groff version 1.23.0 --> -<!-- CreationDate: Tue Jul 2 16:41:23 2024 --> +<!-- CreationDate: Sun Mar 9 09:14:36 2025 --> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> @@ -60,11 +60,12 @@ -<p style="margin-left:9%; margin-top: 1em"><b>ddcutil-service</b> -<i>--help</i> | <i>--version</i> | <i>--introspect</i> -<b><br> -ddcutil-service</b> [ <b>--emit-connectivity-signals</b> ] | -[ <b>--prefer-polling</b> ] | [ <b>--prefer-drm</b> ] | [ +<p style="margin-left:9%; margin-top: 1em"><b>ddcutil-service +--help</b> | <b>--version</b> | <b>--introspect <br> +ddcutil-service</b> [ <b>--enable-connectivity-signals</b> ] +| [ <b>--disable-hotplug-polling</b> ] | [ +<b>--disable-dpms-polling</b> ] | [ <b>--prefer-polling</b> +] | [ <b>--prefer-libddcutil-events</b> ] | [ <b>--polling-interval</b> <i>seconds</i> ] | [ <b>--return-raw-values</b> ] | [ <b>--lock</b> ] | [ <b>--log-info</b> ] [ <b>--ddca-syslog-level</b> <i>N</i> ] @@ -141,48 +142,76 @@ introspection text for the service and exits.</p> -<p style="margin-left:9%;"><b>--emit-connectivity-signals</b></p> +<p style="margin-left:9%;"><b>--enable-connectivity-signals</b></p> + +<p style="margin-left:18%; margin-top: 1em">Enable monitor +hotplug detection and DPMS-state detection. These +connectivity events generate the <b>ConnectedDisplaysChanged +signal</b>. Connectivity-detection defaults to off because +it may not work consistently for all monitors and all GPU +drivers. One enabled, further connectivity related options +may be employed to try and obtain a workable +configuration.</p> + + +<p style="margin-left:9%;"><b>--disable-hotplug-polling</b></p> -<p style="margin-left:18%; margin-top: 1em">Enable the -<b>ConnectedDisplaysChanged signal</b> sent to clients and -also enable any monitoring for changes. Once the service is -running, this setting can be toggled by altering the -<b>ServiceEmitConnectivitySignals</b> property. See -<b>SERVICE SIGNALS</b>.</p> +<p style="margin-left:18%; margin-top: 1em">Disable +internal polling for hotplug events. Polling may wake some +monitor models from DPMS sleep (seems rare). (Disables +polling <b>libddcutil detect</b>.)</p> + + +<p style="margin-left:9%;"><b>--disable-dpms-polling</b></p> + +<p style="margin-left:18%; margin-top: 1em">Disable the +internal polling for DPMS events. Polling for DPMS state may +wake some monitor models from DPMS sleep (seems rare). +(Disables polling <b>libddcutil getvcp 0xd6</b>.)</p> <p style="margin-left:9%;"><b>--prefer-polling</b></p> -<p style="margin-left:18%; margin-top: 1em">Set polling to -be the preferred method for detecting display connectivity -changes for the <b>ConnectedDisplaysChanged signal</b>. This -is the default.</p> +<p style="margin-left:18%; margin-top: 1em">Set service +internal polling to be the preferred method for detecting +display connectivity changes for the +<b>ConnectedDisplaysChanged signal</b>. This is the default +prior to <b>libddcutil 2.2</b> and may still be a better +choice for monitors connected via DVI if they behave +inconsistently when using libddcutil event detection.</p> + -<p style="margin-left:9%;"><b>--prefer-drm</b></p> +<p style="margin-left:9%;"><b>--prefer-libddcutil-events</b></p> <p style="margin-left:18%; margin-top: 1em">Use -<b>libddcutil DRM-lookups</b> as the preferred method for -detecting display connectivity changes for the -<b>ConnectedDisplaysChanged signal</b>. This option should -detect changes sooner with less overheads, but may fail to -detect changes for some combinations or drivers and -hardware.</p> +<b>libddcutil event detection</b> as the preferred method +for detecting display connectivity changes for the +<b>ConnectedDisplaysChanged signal</b>. This is the default +when using <b>libddcutil 2.2</b> and above. Enabling this +option should detect hotplug events sooner with less +overheads. Some internal polling will still occur to monitor +for DPMS events not covered by libddcutil. Libddcutil event +detection may fail to work reliably for some combinations of +drivers, hardware, and connectors, in which case, try +<b>--prefer-polling</b> instead. In particular, eventing +from monitors hotplugged via DVI connectors seems to be more +inconsistent.</p> <p style="margin-left:9%;"><b>--poll-interval</b> <i>seconds</i></p> -<p style="margin-left:18%; margin-top: 1em">If polling is -enabled, this option defines how often to check for display +<p style="margin-left:18%; margin-top: 1em">This option +defines how often to internally poll for display connectivity changes. Default 30 seconds, minimum 10 seconds, zero to disable polling.</p> <p style="margin-left:9%;"><b>--poll-cascade-interval</b> <i>seconds</i></p> -<p style="margin-left:18%; margin-top: 1em">If polling is -enabled, this option defines the minimum interval between -events within a cascade of events. For example, a cascade of -events will occur when a session is locked and all displays -are put into DPMS sleep. Default 0.5 seconds, minimum 0.1 +<p style="margin-left:18%; margin-top: 1em">This option +defines the internal polling minimum interval between events +within a cascade of events. For example, a cascade of events +will occur when a session is locked and all displays are put +into DPMS sleep. Default 0.5 seconds, minimum 0.1 seconds.</p> <p style="margin-left:9%;"><b>--return-raw-values</b></p> @@ -272,11 +301,24 @@ <p><b>Detect</b></p></td> <td width="1%"></td> -<td width="82%"> +<td width="81%"> <p>Return a list of monitors detected along with their -properties.</p> </td></tr> +properties.</p> </td> +<td width="1%"> +</td></tr> +</table> + +<p style="margin-left:9%;"><b>ListDetected</b></p> + +<p style="margin-left:18%;">Return the list of previously +detected monitors along with their properties. This method +is particularly useful for <b>libddcutil 2.2+</b> where +detection may occur in the background automatically.</p> + +<table width="100%" border="0" rules="none" frame="void" + cellspacing="0" cellpadding="0"> <tr valign="top" align="left"> <td width="9%"></td> <td width="8%"> @@ -403,50 +445,38 @@ <p style="margin-left:9%;"><b>ConnectedDisplaysChanged</b></p> -<p style="margin-left:18%;">The service may optionally emit -a <b>ConnectedDisplaysChanged</b> D-Bus signal when a -display undergoes a connectivity status change due to -hot-plug and DPMS events. This feature is optional because -the manual experimentation required to configure it is -unnecessary for display configurations that remain -static.</p> - - -<p style="margin-left:18%; margin-top: 1em">Change-detection -can be enabled by passing <b>--emit-connectivity-signals</b> -on the command line, or by setting the -<b>ServiceEmitConnectivitySignals</b> property.</p> - -<p style="margin-left:18%; margin-top: 1em">To permanently -enable change-detection, the -<b>--emit-connectivity-signals</b> option can be appended to -the <b>Exec</b> line of the system or user D-Bus -<b>com.ddcutil.DdcutilService.service</b> file (see -<b>FILES</b>).</p> - -<p style="margin-left:18%; margin-top: 1em">Changes are -detected in one of two ways. The service defaults to -periodic polling by issuing <b>libddcutil DDCA detects</b>. -Polling is likely to work for a wide variety of drivers and -hardware. Polling for changes will be subject to delays -because the polling interval defaults to 30 seconds (with a -minimum of 10 seconds). Alternatively the service can use -<b>libddcutil DRM access</b> to provide a more efficient -method for change detection, this requires -<b>ddcutil/libddcutil version 2.1.0+</b>, a GPU configured -for <b>DRM</b>, and the <b>--enable-watch-displays</b> to be -added to <i>[libddcutil] options</i> in -<b>$HOME/.config/ddcutil/ddcutilrc</b>.</p> +<p style="margin-left:18%;">The service may emit a +<b>ConnectedDisplaysChanged</b> D-Bus signal when a display +undergoes a connectivity status change due to hotplug and +DPMS events. This feature must be enabled by using the +<b>--enable-connectivity-signals</b> option.</p> + +<p style="margin-left:18%; margin-top: 1em">When utilizing +libddcutil 2.2, or above, the service defaults to using +libddcutil’s inbuilt change detection. This is a +portable change detection mechanism which should detect +changes without delay for most desktop environments.</p> + +<p style="margin-left:18%; margin-top: 1em">For versions of +libddcutil prior to 2.2, libddcutil’s change detection +was somewhat more dependent on driver support. To provide a +more portable solution, for libddcutil prior to 2.2 the +service defaults to doing it’s own internal polling +for all changes. Although portable, polling is slower to +detect changes, the polling interval defaults to 30 seconds +(minimum 10 seconds).</p> + +<p style="margin-left:18%; margin-top: 1em">In either case, +the options <b>--prefer-polling</b> and +<b>--prefer-libddcutil-events</b> can be used to override +the default for change detection.</p> <p style="margin-left:18%; margin-top: 1em">Not all displays, GPUs, GPU-drivers, or cabling, provide the necessary support for detecting connection status changes. Results may vary depending on the mix of desktop components, -such as KDE, Gnome, X11, and Wayland. DisplayPort behaves -differently to DVI and HDMI when a display is turned off but -remains connected. Some drivers that support DRM don’t -properly support the necessary change detection -features.</p> +such as KDE, Gnome, X11, and Wayland. See <b>LIMITATIONS</b> +below for further details.</p> <p style="margin-left:9%;"><b>VcpValueChanged</b></p> @@ -481,7 +511,7 @@ <p style="margin-left:18%;">List the event-types sent by the <b>ConnectedDisplaysChanged</b> signal along with their text names. Events are included for display -connection/disconnection (hot-plug), DPMS-sleep, and +connection/disconnection (hotplug), DPMS-sleep, and DPMS-wake. If the list is empty, the GPU, GPU-driver, or <b>libddcutil</b> version doesn’t support display event detection.</p> @@ -640,9 +670,9 @@ Exec=/usr/bin/ddcutil-service</p> <p style="margin-left:18%; margin-top: 1em">Service -options, such as <b>--emit-connectivity-signals</b> or -<b>--prefer-drm</b>, should be appended to the end of -<b>Exec=</b> line.</p> +options, such as <b>--prefer-polling</b> or +<b>--prefer-libddcutil-events</b>, should be appended to the +end of <b>Exec=</b> line.</p> <p style="margin-left:9%;"><b>$HOME/.local/share/dbus-1/services/com.ddcutil.DdcutilService.service</b></p> @@ -840,11 +870,33 @@ <p style="margin-left:9%; margin-top: 1em">Some mixes of VPUs and GPUs don’t consistently update DRM metadata -for hot-plug events. If <b>ConnectedDisplaysChanged</b> -signals are not being raised, try manually adding -<b>--prefer-polling</b> option, to force the service to poll -for changes. Polling is less responsive, but it -doesn’t require DRM, and is likely to always work.</p> +for hotplug events. Some drivers that support DRM +don’t properly support the necessary hotplug detection +features. Monitors connected by DisplayPort behave +differently to those connected by DVI and HDMI when a +display is turned off but remains connected. hotplugging DVI +connections appear to behave more inconsistently than +DisplayPort with some drivers (for multple OEMs).</p> + +<p style="margin-left:9%; margin-top: 1em">If +<b>ConnectedDisplaysChanged</b> signals are not being +raised, you can try manually adding <b>--prefer-polling</b> +option, to force the service to poll internally for changes. +Polling is less responsive, but it is more likely to +work.</p> + +<p style="margin-left:9%; margin-top: 1em">DPMS state can +only be reliably determined by periodically polling monitors +that support DPMS. In the event that DPMS-polling causes any +issues, it can disabled by adding the +<b>--disable-dpms-polling</b> option.</p> + +<p style="margin-left:9%; margin-top: 1em">The two internal +polling options have been reported to wake at least one +model of monitor from sleep. If this occurs, each can be +independently disabled by the options +<b>--disable-hotplug-polling</b> and +<b>--disable-dpms-polling</b>.</p> <p style="margin-left:9%; margin-top: 1em">Some GPU drivers and VDUs have buggy implementations of DDC. If you have the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ddcutil-service-1.0.12/docs/html/ddcutil-service.7.html new/ddcutil-service-1.0.14/docs/html/ddcutil-service.7.html --- old/ddcutil-service-1.0.12/docs/html/ddcutil-service.7.html 2024-11-05 19:51:52.000000000 +0100 +++ new/ddcutil-service-1.0.14/docs/html/ddcutil-service.7.html 2025-03-08 21:14:51.000000000 +0100 @@ -1,5 +1,5 @@ <!-- Creator : groff version 1.23.0 --> -<!-- CreationDate: Tue Jul 2 16:41:23 2024 --> +<!-- CreationDate: Thu Feb 20 14:08:26 2025 --> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> @@ -28,6 +28,7 @@ <a href="#METHOD DETAILS">METHOD DETAILS</a><br> <a href="#The Restart() method">The Restart() method</a><br> <a href="#The Detect() method">The Detect() method</a><br> +<a href="#The ListDetected() method">The ListDetected() method</a><br> <a href="#The GetVcp() method">The GetVcp() method</a><br> <a href="#The GetMultipleVcp() method">The GetMultipleVcp() method</a><br> <a href="#The SetVcp() method">The SetVcp() method</a><br> @@ -89,6 +90,11 @@ OUT a(iiisssqsu) detected_displays, <br> OUT i error_status, <br> OUT s error_message); <br> +ListDetected (IN u flags, <br> +OUT i number_of_displays, <br> +OUT a(iiisssqsu) detected_displays, <br> +OUT i error_status, <br> +OUT s error_message); <br> GetVcp (IN i display_number, <br> IN s edid_txt, <br> IN y vcp_code, <br> @@ -342,6 +348,60 @@ <p style="margin-left:9%; margin-top: 1em">OUT a(iiisssqsu) <i>detected_displays</i>:</p> + +<p style="margin-left:14%;">An array of structures +describing the VDUs.</p> + +<p style="margin-left:9%; margin-top: 1em">OUT i +<i>error_status</i>:</p> + +<p style="margin-left:14%;">A libddcutil DDCRC error +status. DDCRC_OK (zero) if no errors have occurred.</p> + +<p style="margin-left:9%; margin-top: 1em">OUT s +<i>error_message</i>:</p> + +<p style="margin-left:14%;">Text message for +error_status.</p> + +<h3>The ListDetected() method +<a name="The ListDetected() method"></a> +</h3> + + +<p style="margin-left:14%; margin-top: 1em">ListDetected +(IN u flags, <br> +OUT i number_of_displays, <br> +OUT a(iiisssqsu) detected_displays, <br> +OUT i error_status, <br> +OUT s error_message);</p> + +<p style="margin-left:9%; margin-top: 1em">Returns the +currently detected VDUs without performing using a new +detect. This method is particularly useful for libddcutil +2.2+ where detection may occur in the background +automatically.</p> + +<p style="margin-left:9%; margin-top: 1em">The array +<i>detected_displays</i> will be of length +<i>number_of_displays</i>.</p> + +<p style="margin-left:9%; margin-top: 1em">Each element of +<i>detected_displays</i> array will contain the fields +specified by the AttributesReturnedByDetect property. The +fields will include the libddcutil display−number and +a base64−encoded EDID.</p> + +<p style="margin-left:9%; margin-top: 1em">IN u +<i>flags</i>:</p> + +<p style="margin-left:14%;">For future use</p> + +<p style="margin-left:9%; margin-top: 1em">OUT i +<i>number_of_displays</i>:</p> + +<p style="margin-left:9%; margin-top: 1em">OUT a(iiisssqsu) +<i>detected_displays</i>:</p> <p style="margin-left:14%;">An array of structures describing the VDUs.</p>