wayland: dark theme detection

This commit is contained in:
IoI_xD 2026-04-15 12:25:51 -07:00
commit a3bbcf30b2
3 changed files with 158 additions and 2 deletions

View file

@ -14,6 +14,7 @@
#include <xkbcommon/xkbcommon.h>
#include <cairo/cairo.h>
#include <pthread.h>
#include <dbus/dbus.h>
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.*/

View file

@ -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";
}

View file

@ -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;