From a3bbcf30b2b94c88a3b59f86260a59cee3d6715d Mon Sep 17 00:00:00 2001 From: IoI_xD Date: Wed, 15 Apr 2026 12:25:51 -0700 Subject: [PATCH] wayland: dark theme detection --- include/Mw/LowLevel/Wayland.h | 53 ++++++++++++++++- pl/rules.pl | 2 + src/backend/wayland.c | 105 ++++++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 2 deletions(-) diff --git a/include/Mw/LowLevel/Wayland.h b/include/Mw/LowLevel/Wayland.h index ce1630c..9377bc8 100644 --- a/include/Mw/LowLevel/Wayland.h +++ b/include/Mw/LowLevel/Wayland.h @@ -14,6 +14,7 @@ #include #include #include +#include MWDECL int MwLLWaylandCallInit(void); @@ -35,8 +36,7 @@ typedef struct wayland_call_table { void* lib; void* xkb_lib; void* cairo_lib; -#if WAYLAND_LOAD_OPENGL -#endif + void* dbus_lib; int (*wl_display_dispatch_pending)(struct wl_display* display); void (*wl_display_disconnect)(struct wl_display* display); @@ -125,6 +125,21 @@ typedef struct wayland_call_table { void (*cairo_set_antialias)(cairo_t*, cairo_antialias_t); void (*cairo_set_operator)(cairo_t* cr, cairo_operator_t op); + void (*dbus_error_init)(DBusError* error); + DBusConnection* (*dbus_bus_get)(DBusBusType type, DBusError* error); + dbus_bool_t (*dbus_error_is_set)(const DBusError* error); + void (*dbus_error_free)(DBusError* error); + DBusMessage* (*dbus_message_new_method_call)(const char* bus_name, const char* path, const char* iface, const char* method); + void (*dbus_message_iter_init_append)(DBusMessage* message, DBusMessageIter* iter); + dbus_bool_t (*dbus_message_iter_append_basic)(DBusMessageIter* iter, int type, const void* value); + DBusMessage* (*dbus_connection_send_with_reply_and_block)(DBusConnection* connection, DBusMessage* message, int timeout_milliseconds, DBusError* error); + void (*dbus_message_unref)(DBusMessage* message); + dbus_bool_t (*dbus_message_iter_init)(DBusMessage* message, DBusMessageIter* iter); + int (*dbus_message_iter_get_arg_type)(DBusMessageIter* iter); + void (*dbus_message_iter_recurse)(DBusMessageIter* iter, DBusMessageIter* sub); + void (*dbus_connection_unref)(DBusConnection* connection); + void (*dbus_message_iter_get_basic)(DBusMessageIter* iter, void* value); + } wayland_call_table_t; extern wayland_call_table_t wl_call_tbl; @@ -225,6 +240,34 @@ MwInline int wayland_load_funcs() { CAIRO_FUNC(cairo_pattern_set_filter); #undef CAIRO_FUNC + wl_call_tbl.dbus_lib = MwDynamicOpen("libdbus-1.so"); + if(!wl_call_tbl.dbus_lib) { + fprintf(stderr, "[WARNING] Will not be able to detect dark theme: dbus library (libdbus-1.so) not found.\n"); + return 0; + } + +#define DBUS_FUNC(x) \ + wl_call_tbl.x = MwDynamicSymbol(wl_call_tbl.dbus_lib, #x); \ + if(!x) { \ + fprintf(stderr, "[WARNING] Will not be able to detect dark theme: dbus function %s (libdbus-1.so) not found.\n", #x); \ + return 0; \ + } + + DBUS_FUNC(dbus_error_init); + DBUS_FUNC(dbus_bus_get); + DBUS_FUNC(dbus_error_is_set); + DBUS_FUNC(dbus_error_free); + DBUS_FUNC(dbus_message_new_method_call); + DBUS_FUNC(dbus_message_iter_init_append); + DBUS_FUNC(dbus_message_iter_append_basic); + DBUS_FUNC(dbus_connection_send_with_reply_and_block); + DBUS_FUNC(dbus_message_unref); + DBUS_FUNC(dbus_message_iter_init); + DBUS_FUNC(dbus_message_iter_get_arg_type); + DBUS_FUNC(dbus_message_iter_recurse); + DBUS_FUNC(dbus_connection_unref); + DBUS_FUNC(dbus_message_iter_get_basic); + return 0; } @@ -436,6 +479,12 @@ struct _MwLLWayland { struct zwp_locked_pointer_v1* locked_pointer; MwBool pointer_constrained; + DBusConnection* dbus_conn; + DBusError dbus_err; + DBusMessage* dbus_msg; + DBusMessage* dbus_reply; + DBusMessageIter dbus_args, dbus_variant, dbus_inner_variant; + /* clipboard related stuff. * Note that unlike most interfaces, we don't keep zwp_primary_selection stuff in a wayland_protocol_t because we use wl_data_device as a fallback and want to have it share memory space.*/ diff --git a/pl/rules.pl b/pl/rules.pl index 3da1928..d6f72fb 100644 --- a/pl/rules.pl +++ b/pl/rules.pl @@ -49,6 +49,8 @@ if (grep(/^wayland$/, @backends)) { scan_wayland_protocol("unstable", "pointer-constraints", "-unstable-v1"); scan_wayland_protocol("unstable", "relative-pointer", "-unstable-v1"); + add_cflags("-I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include"); + $gl_libs = "-lGL -lGLU"; } diff --git a/src/backend/wayland.c b/src/backend/wayland.c index 8bb6f3e..e1e8ee4 100644 --- a/src/backend/wayland.c +++ b/src/backend/wayland.c @@ -1829,6 +1829,99 @@ static void widget_setup(MwLL r, MwLL parent, int x, int y, int width, int heigh MwLLEndDraw(r); } } + +static void dark_theme_detection_setup(MwLL handle) { + + if(!wl_call_tbl.dbus_lib) { + return; + } + + wl_call_tbl.dbus_error_init(&handle->wayland.dbus_err); + + handle->wayland.dbus_conn = wl_call_tbl.dbus_bus_get(0 /* DBUS_BUS_SESSION */, &handle->wayland.dbus_err); + if(wl_call_tbl.dbus_error_is_set(&handle->wayland.dbus_err)) { + fprintf(stderr, "[WARNING] Could not detect dark theme: Connection error: %s\n", handle->wayland.dbus_err.message); + wl_call_tbl.dbus_error_free(&handle->wayland.dbus_err); + return; + } + if(!handle->wayland.dbus_conn) { + fprintf(stderr, "[WARNING] Could not detect dark theme: Failed to connect to session bus\n"); + return; + } +} + +static void detect_dark_theme(MwLL handle) { + const char* namespace = "org.freedesktop.appearance"; + const char* key = "color-scheme"; /* 0=no preference, 1=dark, 2=light */ + MwU32 value = 0; + int translated_value = 0; + + if(!handle->wayland.dbus_conn) { + return; + } + + handle->wayland.dbus_msg = wl_call_tbl.dbus_message_new_method_call( + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.Settings", + "Read"); + if(!handle->wayland.dbus_msg) { + fprintf(stderr, "[WARNING] Could not detect dark theme: Failed to create message\n"); + return; + } + + wl_call_tbl.dbus_message_iter_init_append(handle->wayland.dbus_msg, &handle->wayland.dbus_args); + wl_call_tbl.dbus_message_iter_append_basic(&handle->wayland.dbus_args, 's', &namespace); + wl_call_tbl.dbus_message_iter_append_basic(&handle->wayland.dbus_args, 's', &key); + + handle->wayland.dbus_reply = wl_call_tbl.dbus_connection_send_with_reply_and_block(handle->wayland.dbus_conn, handle->wayland.dbus_msg, 100, &handle->wayland.dbus_err); + wl_call_tbl.dbus_message_unref(handle->wayland.dbus_msg); + + if(wl_call_tbl.dbus_error_is_set(&handle->wayland.dbus_err)) { + fprintf(stderr, "[WARNING] Could not detect dark theme: Send error: %s\n", handle->wayland.dbus_err.message); + wl_call_tbl.dbus_error_free(&handle->wayland.dbus_err); + return; + } + if(!handle->wayland.dbus_reply) { + fprintf(stderr, "[WARNING] Could not detect dark theme: No reply received\n"); + return; + } + + if(!wl_call_tbl.dbus_message_iter_init(handle->wayland.dbus_reply, &handle->wayland.dbus_args)) { + fprintf(stderr, "[WARNING] Could not detect dark theme: Reply has no arguments\n"); + wl_call_tbl.dbus_message_unref(handle->wayland.dbus_reply); + return; + } + + if(wl_call_tbl.dbus_message_iter_get_arg_type(&handle->wayland.dbus_args) != 'v') { + fprintf(stderr, "[WARNING] Could not detect dark theme: Expected outer variant\n"); + wl_call_tbl.dbus_message_unref(handle->wayland.dbus_reply); + return; + } + wl_call_tbl.dbus_message_iter_recurse(&handle->wayland.dbus_args, &handle->wayland.dbus_variant); + + /* Some portals wrap the value in a second variant */ + if(wl_call_tbl.dbus_message_iter_get_arg_type(&handle->wayland.dbus_variant) == 'v') { + wl_call_tbl.dbus_message_iter_recurse(&handle->wayland.dbus_variant, &handle->wayland.dbus_inner_variant); + wl_call_tbl.dbus_message_iter_get_basic(&handle->wayland.dbus_inner_variant, &value); + } else { + wl_call_tbl.dbus_message_iter_get_basic(&handle->wayland.dbus_variant, &value); + } + + translated_value = (value == 1) ? 1 : 0; + + MwLLDispatch(handle, dark_theme, &translated_value); +} + +static void dark_theme_detection_destroy(MwLL handle) { + if(!wl_call_tbl.dbus_lib) { + return; + } + + wl_call_tbl.dbus_message_unref(handle->wayland.dbus_reply); + wl_call_tbl.dbus_connection_unref(handle->wayland.dbus_conn); +} + static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { MwLL r; r = malloc(sizeof(*r)); @@ -1837,6 +1930,10 @@ static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { widget_setup(r, parent, x, y, width, height, MWLL_WAYLAND_UNKNOWN); + if(!parent) { + dark_theme_detection_setup(r); + } + return r; } @@ -1856,6 +1953,10 @@ static void MwLLDestroyImpl(MwLL handle) { MwLLDestroyCommon(handle); + if(!handle->wayland.dbus_conn) { + dark_theme_detection_destroy(handle); + } + event_loop(handle); /* sleep long enough that any active attempts to wait for the mutex will have given up and we can safely unlock/destroy it */ @@ -2144,6 +2245,10 @@ static int MwLLPendingImpl(MwLL handle) { }; int pending = 0; + if(handle->wayland.dbus_conn) { + detect_dark_theme(handle); + } + if(!handle->wayland.did_initial_resize) { recursive_dispatch_resize(handle); handle->wayland.did_initial_resize = 1;