Skip to content

Commit

Permalink
watch-mode Xevent uses X11 API to detect display connection changes
Browse files Browse the repository at this point in the history
- set by --watch-mode xevent
- doesn't work if not in a gui
- logic for --watch-mode dynamic remains to be worked out
  • Loading branch information
rockowitz committed Dec 12, 2024
1 parent f07210c commit 6bf1062
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 8 deletions.
4 changes: 3 additions & 1 deletion src/cmdline/cmd_parser_goption.c
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,8 @@ static bool parse_watch_mode(

if ( is_abbrev(v2, "POLL", 3))
parsed_cmd->watch_mode = Watch_Mode_Poll;
else if (is_abbrev(v2, "XEVENT", 3))
parsed_cmd->watch_mode = Watch_Mode_Xevent;
else if (is_abbrev(v2, "UDEV", 3))
parsed_cmd->watch_mode = Watch_Mode_Udev;
else if (is_abbrev(v2, "DYNAMIC", 3))
Expand Down Expand Up @@ -1228,7 +1230,7 @@ parse_command(
{"disable-watch-displays", '\0', G_OPTION_FLAG_REVERSE,
G_OPTION_ARG_NONE, &enable_watch_displays, "Do not watch for display change events", NULL },
{"watch-mode", '\0', G_OPTION_FLAG_HIDDEN,
G_OPTION_ARG_STRING, &watch_mode_work, "How to watch for display changes", "UDEV|POLL"},
G_OPTION_ARG_STRING, &watch_mode_work, "How to watch for display changes", "UDEV|POLL|XEVENT|DYNAMIC"},
#ifdef ENABLE_USB
{"enable-usb", '\0', G_OPTION_FLAG_NONE,
G_OPTION_ARG_NONE, &enable_usb_flag, enable_usb_expl, NULL},
Expand Down
3 changes: 2 additions & 1 deletion src/ddc/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ libddc_la_SOURCES += \
ddc_watch_displays_common.c \
ddc_watch_displays_main.c \
ddc_watch_displays_poll.c \
ddc_watch_displays_udev.c
ddc_watch_displays_udev.c \
ddc_watch_displays_xevent.c
endif
endif

Expand Down
2 changes: 2 additions & 0 deletions src/ddc/ddc_services.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include "ddc/ddc_vcp.h"
#include "ddc/ddc_vcp_version.h"
#ifdef BUILD_SHARED_LIB
#include "ddc/ddc_watch_displays_xevent.h"
#include "ddc/ddc_watch_displays_udev.h"
#include "ddc/ddc_watch_displays_poll.h"
#include "ddc/ddc_watch_displays_main.h"
Expand Down Expand Up @@ -232,6 +233,7 @@ void init_ddc_services() {
init_ddc_vcp();
init_ddc_vcp_version();
// #ifdef BUILD_SHARED_LIB
init_ddc_watch_displays_xevent();
init_ddc_watch_displays_udev();
init_ddc_watch_displays_poll();
init_ddc_watch_displays_common();
Expand Down
15 changes: 13 additions & 2 deletions src/ddc/ddc_watch_displays_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include <sys/stat.h>
#include <unistd.h>



#include "util/coredefs.h"
#include "util/data_structures.h"
#include "util/debug_util.h"
Expand Down Expand Up @@ -53,20 +55,23 @@
#include "ddc/ddc_packet_io.h"
#include "ddc/ddc_status_events.h"
#include "ddc/ddc_vcp.h"
#include "ddc/ddc_watch_displays_xevent.h"

#include "ddc_watch_displays_common.h"

// Trace class for this file
static DDCA_Trace_Group TRACE_GROUP = DDCA_TRC_NONE;

bool terminate_watch_thread = false;

int extra_stabilization_millisec = DEFAULT_EXTRA_STABILIZATION_MILLISEC;
int stabilization_poll_millisec = DEFAULT_STABILIZATION_POLL_MILLISEC;
int watch_loop_poll_multiplier = 1;
int explicit_udev_poll_loop_millisec = 0;
int explicit_nonudev_poll_loop_millisec = 0;
int calculated_watch_loop_millisec = 0;

int explicit_xevent_loop_millisec = 500; // temp


int calc_poll_loop_millisec(DDC_Watch_Mode watch_mode) {
assert(watch_mode != Watch_Mode_Dynamic);
Expand All @@ -78,6 +83,13 @@ int calc_poll_loop_millisec(DDC_Watch_Mode watch_mode) {
else
final_answer = watch_loop_poll_multiplier * DEFAULT_UDEV_POLL_LOOP_MILLISEC;
}
else if (watch_mode == Watch_Mode_Xevent) {
if (explicit_xevent_loop_millisec)
final_answer = explicit_nonudev_poll_loop_millisec;
else {
final_answer = watch_loop_poll_multiplier * DEFAULT_NONUDEV_POLL_LOOP_MILLISEC;
}
}
else {
if (explicit_nonudev_poll_loop_millisec)
final_answer = explicit_nonudev_poll_loop_millisec;
Expand Down Expand Up @@ -530,7 +542,6 @@ ddc_i2c_stabilized_buses_bs(Bit_Set_256 bs_prior, bool some_displays_disconnecte
return bs_prior;
}


void init_ddc_watch_displays_common() {
#ifdef WATCH_ASLEEP
RTTI_ADD_FUNC(ddc_i2c_check_bus_asleep);
Expand Down
9 changes: 8 additions & 1 deletion src/ddc/ddc_watch_displays_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
#include "util/data_structures.h"
#include "util/linux_util.h"

extern bool terminate_watch_thread;
#include "base/displays.h"

#include "ddc/ddc_watch_displays_xevent.h"


extern int extra_stabilization_millisec;
extern int stabilization_poll_millisec;

Expand Down Expand Up @@ -40,6 +44,8 @@ typedef struct {
pid_t main_process_id;
pid_t main_thread_id;
DDCA_Display_Event_Class event_classes;
DDC_Watch_Mode watch_mode;
XEvent_Data * evdata;
#ifdef OLD_HOTPLUG_VERSION
Display_Change_Handler display_change_handler;
Bit_Set_32 drm_card_numbers;
Expand Down Expand Up @@ -79,6 +85,7 @@ bool ddc_i2c_hotplug_change_handler(
Bit_Set_256 bs_buses_w_edid_added,
GArray * events_queue);


void init_ddc_watch_displays_common();

#endif /* DDC_WATCH_DISPLAYS_COMMON_H_ */
21 changes: 19 additions & 2 deletions src/ddc/ddc_watch_displays_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "util/string_util.h"
#include "util/sysfs_util.h"
#include "util/udev_util.h"
#include "util/x11_util.h"

#include "base/core.h"
#include "base/displays.h"
Expand All @@ -54,8 +55,9 @@
#include "ddc/ddc_vcp.h"

#include "ddc_watch_displays_common.h"
#include "ddc/ddc_watch_displays_udev.h"
#include "ddc_watch_displays_udev.h"
#include "ddc_watch_displays_poll.h"
#include "ddc_watch_displays_xevent.h"

#include "ddc_watch_displays_main.h"

Expand Down Expand Up @@ -91,6 +93,8 @@ ddc_start_watch_displays(DDCA_Display_Event_Class event_classes) {
ddc_watch_mode_name(ddc_watch_mode), watch_thread, event_classes, SBOOL(all_video_adapters_implement_drm));
Error_Info * err = NULL;

// register_for_x11_screen_change_notification();

if (!all_video_adapters_implement_drm) {
err = ERRINFO_NEW(DDCRC_INVALID_OPERATION, "Requires DRM video drivers");
goto bye;
Expand All @@ -101,17 +105,27 @@ ddc_start_watch_displays(DDCA_Display_Event_Class event_classes) {
goto bye;
}

XEvent_Data * xev_data = ddc_init_xevent_screen_change_notification();
if (ddc_watch_mode == Watch_Mode_Xevent && !xev_data) {
err = ERRINFO_NEW(DDCRC_INVALID_OPERATION, "X11 API unavailable. Watching for display changes disabled");
goto bye;
}


bool sysfs_fully_reliable = true;
sysfs_fully_reliable = false;
#ifndef ENABLE_UDEV
ddc_watch_mode = Watch_Mode_Poll;
#else
if (ddc_watch_mode == Watch_Mode_Dynamic) {
// TODO: incorporate Watch_Mode_Xevent into algorithm
ddc_watch_mode = Watch_Mode_Udev;
sysfs_fully_reliable = is_sysfs_reliable();
if (!sysfs_fully_reliable)
ddc_watch_mode = Watch_Mode_Poll;
}
// if (xev_data && ddc_watch_mode == Watch_Mode_Xevent)
// ddc_watch_mode = Watch_Mode_Poll;
#endif

int calculated_watch_loop_millisec = calc_poll_loop_millisec(ddc_watch_mode);
Expand Down Expand Up @@ -148,8 +162,11 @@ ddc_start_watch_displays(DDCA_Display_Event_Class event_classes) {
data->main_thread_id = get_thread_id(); // alt = syscall(SYS_gettid);
// event_classes &= ~DDCA_EVENT_CLASS_DPMS; // *** TEMP ***
data->event_classes = event_classes;
data->watch_mode = ddc_watch_mode;
if (xev_data)
data->evdata = xev_data;

GThreadFunc watch_thread_func = (ddc_watch_mode == Watch_Mode_Poll)
GThreadFunc watch_thread_func = (ddc_watch_mode == Watch_Mode_Poll || ddc_watch_mode == Watch_Mode_Xevent)
? ddc_watch_displays_without_udev
: ddc_watch_displays_udev;

Expand Down
12 changes: 11 additions & 1 deletion src/ddc/ddc_watch_displays_poll.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@

#include "ddc/ddc_displays.h"
#include "ddc/ddc_watch_displays_common.h"
#include "ddc/ddc_watch_displays_xevent.h"
#include "ddc/ddc_packet_io.h"
#include "ddc/ddc_status_events.h"
#include "ddc/ddc_vcp.h"
Expand Down Expand Up @@ -311,12 +312,15 @@ gpointer ddc_watch_displays_without_udev(gpointer data) {
sizeof(DDCA_Display_Status_Event));
bool skip_next_sleep = false;
int slept = 0; // will contain length of final sleep
if (wdd->watch_mode == Watch_Mode_Xevent) {
}

while (!terminate_watch_thread) {
if (deferred_events && deferred_events->len > 0) {
ddc_i2c_emit_deferred_events(deferred_events);
}
else { // skip polling loop sleep if deferred events were output
if (!skip_next_sleep) {
if (!skip_next_sleep && wdd->watch_mode != Watch_Mode_Xevent) {
slept = split_sleep();
}
}
Expand All @@ -325,6 +329,12 @@ gpointer ddc_watch_displays_without_udev(gpointer data) {
continue;
terminate_if_invalid_thread_or_process(cur_pid, cur_tid);

if (wdd->watch_mode == Watch_Mode_Xevent && wdd->evdata) {
bool event_found = ddc_detect_xevent_screen_change(wdd->evdata, /* poll_interval*/ 500);
if (!event_found)
continue;
}

#ifdef OLD
GPtrArray * cur_buses = i2c_detect_buses0();
Bit_Set_256 bs_cur_buses_w_edid = buses_bitset_from_businfo_array(cur_buses, true);
Expand Down
99 changes: 99 additions & 0 deletions src/ddc/ddc_watch_displays_xevent.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/** @file ddc_watch_displays_xevent.c */

// Copyright (C) 2024 Sanford Rockowitz <[email protected]>
// SPDX-License-Identifier: GPL-2.0-or-later

#include <stdbool.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>

#include "base/core.h"
#include "base/displays.h" // for terminate_watch_thread
#include "base/rtti.h"
#include "base/sleep.h"

#include "ddc_watch_displays_xevent.h"


void ddc_free_xevent_data(XEvent_Data * evdata) {
if (evdata->dpy)
XCloseDisplay(evdata->dpy);
free(evdata);
}


XEvent_Data * ddc_init_xevent_screen_change_notification() {
bool debug = true;
DBGTRC_STARTING(debug, DDCA_TRC_NONE, "");

bool ok = false;
// check for extension
XEvent_Data * evdata = calloc(1, sizeof(XEvent_Data));
evdata->dpy = XOpenDisplay(NULL);
if (!evdata->dpy)
goto bye;
evdata->screen = DefaultScreen(evdata->dpy);
evdata->w = RootWindow(evdata->dpy, evdata->screen);

bool have_rr = XRRQueryExtension(evdata->dpy, &evdata->rr_event_base, &evdata->rr_error_base);
if (!have_rr) {
DBGTRC(true, DDCA_TRC_NONE, "XRR Extension unavailable");
goto bye;
}
// TODO: additional checks

evdata->screen_change_eventno = evdata->rr_event_base + RRScreenChangeNotify;
XRRSelectInput(evdata->dpy, evdata->w, RRScreenChangeNotifyMask);
ok = true;

bye:
if (!ok) {
ddc_free_xevent_data(evdata);
evdata = NULL;
}

DBGTRC_DONE(debug, DDCA_TRC_NONE, "Returning %p", evdata);
return evdata;
}


_Bool ddc_detect_xevent_screen_change(XEvent_Data *evdata, int poll_interval) {
bool debug = true;
DBGTRC_STARTING(debug, DDCA_TRC_NONE, "evdata=%p, poll_interval=%d", evdata, poll_interval);
bool found = false;
XEvent event;
while (true) {
if (terminate_watch_thread)
break;
found = XCheckTypedEvent(evdata->dpy, evdata->screen_change_eventno, &event);
if (found) {
DBGTRC_NOPREFIX(debug, DDCA_TRC_NONE, "Received event type %d", event.type);
XAnyEvent *e = (XAnyEvent*) &event;
DBGTRC_NOPREFIX(debug, DDCA_TRC_NONE,
"windows change event serial %ld, synthetic %s, window 0x%lx,", e->serial,
sbool(e->send_event), e->window);

bool more = true;
int flushct = 0;
while (more) {
more = XCheckTypedEvent(evdata->dpy, evdata->screen_change_eventno, &event);
flushct++;
}
DBGTRC_NOPREFIX(debug, DDCA_TRC_NONE, "Flushed %d events", flushct);
break;
} else {
// DBGF(debug, "Not found");
// DBGF(debug, "sleeping");
// sleep(2);
// split_sleep();
sleep_millis(poll_interval);
}
}
DBGTRC_RET_BOOL(debug, DDCA_TRC_NONE, found, "");
return found;
}

void init_ddc_watch_displays_xevent() {
RTTI_ADD_FUNC(ddc_detect_xevent_screen_change);
RTTI_ADD_FUNC(ddc_init_xevent_screen_change_notification);
}
27 changes: 27 additions & 0 deletions src/ddc/ddc_watch_displays_xevent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/** @file ddc_watch_displays_xevent.h */

// Copyright (C) 2024 Sanford Rockowitz <[email protected]>
// SPDX-License-Identifier: GPL-2.0-or-later

#ifndef DDC_WATCH_DISPLAYS_XEVENT_H_
#define DDC_WATCH_DISPLAYS_XEVENT_H_

#include <stdbool.h>
#include <X11/Xlib.h>

typedef struct {
Display* dpy;
int screen;
Window w;
int rr_event_base;
int rr_error_base;
int screen_change_eventno;
} XEvent_Data;

void ddc_free_xevent_data(XEvent_Data * evdata);
XEvent_Data * ddc_init_xevent_screen_change_notification();
bool ddc_detect_xevent_screen_change(XEvent_Data * evdata, int poll_interval);

void init_ddc_watch_displays_xevent();

#endif /* DDC_WATCH_DISPLAYS_XEVENT_H_ */

0 comments on commit 6bf1062

Please sign in to comment.