diff --git a/include/Mw/LowLevel.h b/include/Mw/LowLevel.h index cbc67f9..2ba3ff0 100644 --- a/include/Mw/LowLevel.h +++ b/include/Mw/LowLevel.h @@ -83,11 +83,15 @@ typedef struct _MwLLDBusContext { DBusMessageIter dbus_args, dbus_variant, dbus_inner_variant; } MwLLDBusContext; +typedef void (*MwLLDBusPortalPollListener)(MwLL handle, MwU32 new_value); + /* setup the dbus func table. Returns false and prints a warning if dbus couldn't be found. */ MWDECL MwBool MwLLDBusFuncSetup(MwLLDBusFuncTable* tbl); MWDECL MwBool MwLLDBusNewContext(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx); MWDECL MwBool MwLLDBusPortalGet(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx, const char* portal, const char* namespace, const char* key, void* out); +MWDECL MwBool MwLLDBusPortalWatch(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx, const char* portal); +MWDECL MwBool MwLLDBusPortalPoll(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx, MwLL handle, const char* portal, const char* namespace, const char* key, MwLLDBusPortalPollListener); MWDECL void MwLLDBusFreeContext(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx); #endif diff --git a/src/backend/wayland.c b/src/backend/wayland.c index 94bb3da..5660125 100644 --- a/src/backend/wayland.c +++ b/src/backend/wayland.c @@ -17,6 +17,8 @@ wayland_call_table_t wl_call_tbl; MwBool MwWaylandAlwaysRender = MwFALSE; +MwU32 wl_dark_theme = -1; + /* Standard procedure before most event callbacks in Wayland ("most" because the setup ones don't need this). Wait for the Mutex to be freed, if we're deadlocking for longer then a quarter of a second then do nothing with the event. */ #define WAYLAND_EVENT_OP_START(self) \ do { \ @@ -1779,14 +1781,21 @@ static void widget_setup(MwLL r, MwLL parent, int x, int y, int width, int heigh } #ifdef USE_DBUS +static void dark_theme_listener(MwLL handle, MwU32 new_value) { + wl_dark_theme = (new_value == 1) ? 1 : 0; + + MwLLDispatch(handle, dark_theme, &wl_dark_theme); +} + static void detect_dark_theme(MwLL handle) { MwU32 value = 0; - MwU32 dark_theme = 0; + MwLLDBusPortalGet(&wl_call_tbl.dbus, &handle->wayland.dbus, "org.freedesktop.portal.Settings", "org.freedesktop.appearance", "color-scheme", &value); + wl_dark_theme = (value == 1) ? 1 : 0; - dark_theme = (value == 1) ? 1 : 0; + MwLLDBusPortalWatch(&wl_call_tbl.dbus, &handle->wayland.dbus, "org.freedesktop.portal.Settings"); - MwLLDispatch(handle, dark_theme, &dark_theme); + MwLLDispatch(handle, dark_theme, &wl_dark_theme); } #endif @@ -2144,15 +2153,18 @@ static int MwLLPendingImpl(MwLL handle) { }; int pending = 0; - if(handle->wayland.dark_theme_detection) { - handle->wayland.dark_theme_detection = MwFALSE; #ifdef USE_DBUS - if(wl_call_tbl.has_dbus) { + if(wl_call_tbl.has_dbus) { + if(handle->wayland.dark_theme_detection) { + + handle->wayland.dark_theme_detection = MwFALSE; detect_dark_theme(handle); MwLLDispatch(handle, draw, NULL); + } else { + MwLLDBusPortalPoll(&wl_call_tbl.dbus, &handle->wayland.dbus, handle, "org.freedesktop.portal.Settings", "org.freedesktop.appearance", "color-scheme", &dark_theme_listener); } -#endif } +#endif if(!handle->wayland.did_initial_resize) { recursive_dispatch_resize(handle); diff --git a/src/lowlevel.c b/src/lowlevel.c index 2a874e6..935bf9b 100644 --- a/src/lowlevel.c +++ b/src/lowlevel.c @@ -176,6 +176,69 @@ MWDECL MwBool MwLLDBusPortalGet(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx, co return MwTRUE; } +MWDECL MwBool MwLLDBusPortalWatch(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx, const char* portal) { + if(!ctx->dbus_conn) { + return MwFALSE; + } + + char filter_string[2048]; + MwStringPrintIntoBuffer(filter_string, sizeof(filter_string), "type='%s',interface=%s", "signal", portal); + + dbus_bus_add_match(ctx->dbus_conn, filter_string, &ctx->dbus_err); + dbus_connection_flush(ctx->dbus_conn); + + return MwTRUE; +} + +// Technically this will swallow all other results, so this is not usable multiple times. +// TODO: Use a hashmap instead +MWDECL MwBool MwLLDBusPortalPoll(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx, MwLL handle, const char* portal, const char* namespace, const char* key, MwLLDBusPortalPollListener listener) { + DBusMessage* msg; + DBusMessageIter args, msg_value; + const char* msg_namespace; + const char* msg_key; + MwU32 msg_value_content; + + if(!ctx->dbus_conn) { + return MwFALSE; + } + + dbus_connection_read_write(ctx->dbus_conn, 0); + msg = dbus_connection_pop_message(ctx->dbus_conn); + + if (NULL == msg) { + return MwFALSE; + } + + // check if the message is a signal from the correct interface and with the correct name + if (dbus_message_is_signal(msg, portal, "SettingChanged")) { + // read the parameters + if (!dbus_message_iter_init(msg, &args)) + fprintf(stderr, "[WARNING] Message has no arguments\n"); + else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args)) + fprintf(stderr, "[WARNING] Argument is not string\n"); + else { + dbus_message_iter_get_basic(&args, &msg_namespace); + + dbus_message_iter_next(&args); + dbus_message_iter_get_basic(&args, &msg_key); + + // Check that key and namespace match + if (strcmp(msg_namespace, namespace) == 0 && strcmp(msg_key, key) == 0) { + // Assuming the value is a basic type + dbus_message_iter_next(&args); + dbus_message_iter_recurse(&args, &msg_value); + dbus_message_iter_get_basic(&msg_value, &msg_value_content); + + listener(handle, msg_value_content); + } + } + } + + // free the message + dbus_message_unref(msg); +} + void MwLLDBusFreeContext(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx) { if(!tbl->lib) { return;