diff --git a/include/Mw/LowLevel.h b/include/Mw/LowLevel.h index d5a0b1b..c958e00 100644 --- a/include/Mw/LowLevel.h +++ b/include/Mw/LowLevel.h @@ -56,6 +56,43 @@ struct _MwLLCommonPixmap { void* user; }; +#ifdef USE_DBUS +typedef struct _MwLLDBusFuncTable { + void* lib; + + 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); +} MwLLDBusFuncTable; + +typedef struct _MwLLDBusContext { + DBusConnection* dbus_conn; + DBusError dbus_err; + DBusMessage* dbus_msg; + DBusMessage* dbus_reply; + DBusMessageIter dbus_args, dbus_variant, dbus_inner_variant; +} MwLLDBusContext; + +/* 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 void MwLLDBusFreeContext(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx); + +#endif + #ifdef _MILSKO #ifdef USE_X11 #include diff --git a/include/Mw/LowLevel/Wayland.h b/include/Mw/LowLevel/Wayland.h index 6b2c08f..77e43b3 100644 --- a/include/Mw/LowLevel/Wayland.h +++ b/include/Mw/LowLevel/Wayland.h @@ -36,7 +36,10 @@ typedef struct wayland_call_table { void* lib; void* xkb_lib; void* cairo_lib; - void* dbus_lib; +#ifdef USE_DBUS + MwLLDBusFuncTable dbus; + MwBool has_dbus; +#endif int (*wl_display_dispatch_pending)(struct wl_display* display); void (*wl_display_disconnect)(struct wl_display* display); @@ -125,21 +128,6 @@ 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; @@ -240,34 +228,9 @@ 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(!wl_call_tbl.x) { \ - fprintf(stderr, "[WARNING] Will not be able to detect dark theme: dbus function %s not found.\n", #x); \ - dlclose(wl_call_tbl.dbus_lib); \ - 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); +#ifdef USE_DBUS + wl_call_tbl.has_dbus = MwLLDBusFuncSetup(&wl_call_tbl.dbus); +#endif return 0; } @@ -480,13 +443,11 @@ struct _MwLLWayland { struct zwp_locked_pointer_v1* locked_pointer; MwBool pointer_constrained; - MwBool dark_theme_detection; - time_t dbus_timer; - DBusConnection* dbus_conn; - DBusError dbus_err; - DBusMessage* dbus_msg; - DBusMessage* dbus_reply; - DBusMessageIter dbus_args, dbus_variant, dbus_inner_variant; +#ifdef USE_DBUS + MwLLDBusContext dbus; +#endif + + MwBool dark_theme_detection; /* 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/include/Mw/LowLevel/X11.h b/include/Mw/LowLevel/X11.h index 529a570..3d0e125 100644 --- a/include/Mw/LowLevel/X11.h +++ b/include/Mw/LowLevel/X11.h @@ -57,6 +57,12 @@ struct _MwLLX11 { unsigned long blue_mask; unsigned long blue_max; unsigned long blue_shift; + +#ifdef USE_DBUS + MwLLDBusContext dbus; +#endif + + MwBool dark_theme_detection; }; struct _MwLLX11Color { diff --git a/include/Mw/MachDep.h b/include/Mw/MachDep.h index 50898f6..025fc29 100644 --- a/include/Mw/MachDep.h +++ b/include/Mw/MachDep.h @@ -53,6 +53,10 @@ #include #endif +#ifdef USE_DBUS +#include +#endif + #ifndef M_PI #define M_PI 3.14159265 #endif diff --git a/pl/ostype/Linux.pl b/pl/ostype/Linux.pl index 73905d6..278cbfe 100644 --- a/pl/ostype/Linux.pl +++ b/pl/ostype/Linux.pl @@ -7,6 +7,8 @@ if (param_get("x11")) { push(@enabled_backends, "x11"); } +add_cflags("-DUSE_DBUS"); + use_backend(@enabled_backends); 1; diff --git a/src/backend/wayland.c b/src/backend/wayland.c index 3c8c9cd..1fc68cb 100644 --- a/src/backend/wayland.c +++ b/src/backend/wayland.c @@ -1830,102 +1830,16 @@ static void widget_setup(MwLL r, MwLL parent, int x, int y, int width, int heigh } } -static void dbus_setup(MwLL handle) { - MwU32 value = 0; - int translated_value = 0; - - if(!wl_call_tbl.dbus_lib) { - return; - } - - time(&handle->wayland.dbus_timer); - - 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 dbus_get(MwLL handle, const char* portal, const char* namespace, const char* key, void* out) { - 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", - portal, - "Read"); - if(!handle->wayland.dbus_msg) { - fprintf(stderr, "[WARNING] Couldn't get %s::%s: Failed to create message\n", namespace, key); - 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); - - if(wl_call_tbl.dbus_error_is_set(&handle->wayland.dbus_err)) { - fprintf(stderr, "[WARNING] Couldn't get %s::%s: Send error: %s\n", namespace, key, handle->wayland.dbus_err.message); - wl_call_tbl.dbus_error_free(&handle->wayland.dbus_err); - return; - } - if(!handle->wayland.dbus_reply) { - fprintf(stderr, "[WARNING] Couldn't get %s::%s: No reply received\n", namespace, key); - return; - } - - if(!wl_call_tbl.dbus_message_iter_init(handle->wayland.dbus_reply, &handle->wayland.dbus_args)) { - fprintf(stderr, "[WARNING] Couldn't get %s::%s: Reply has no arguments\n", namespace, key); - 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] Couldn't get %s::%s: Expected outer variant\n", namespace, key); - 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, out); - } else { - wl_call_tbl.dbus_message_iter_get_basic(&handle->wayland.dbus_variant, out); - } - wl_call_tbl.dbus_message_unref(handle->wayland.dbus_msg); -} - static void detect_dark_theme(MwLL handle) { MwU32 value = 0; MwU32 dark_theme = 0; - dbus_get(handle, "org.freedesktop.portal.Settings", "org.freedesktop.appearance", "color-scheme", &value); + MwLLDBusPortalGet(&wl_call_tbl.dbus, &handle->wayland.dbus, "org.freedesktop.portal.Settings", "org.freedesktop.appearance", "color-scheme", &value); dark_theme = (value == 1) ? 1 : 0; MwLLDispatch(handle, dark_theme, &dark_theme); } -static void dbus_destroy(MwLL handle) { - if(!wl_call_tbl.dbus_lib) { - return; - } - if(handle->wayland.dbus_reply) wl_call_tbl.dbus_message_unref(handle->wayland.dbus_reply); - if(handle->wayland.dbus_conn) 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)); @@ -1934,10 +1848,12 @@ 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) { - dbus_setup(r); +#ifdef USE_DBUS + if(!parent && wl_call_tbl.has_dbus) { + wl_call_tbl.has_dbus = MwLLDBusNewContext(&wl_call_tbl.dbus, &r->wayland.dbus); r->wayland.dark_theme_detection = MwTRUE; } +#endif return r; } @@ -1973,8 +1889,8 @@ static void MwLLDestroyImpl(MwLL handle) { pthread_mutex_unlock(&handle->wayland.eventsMutex); pthread_mutex_destroy(&handle->wayland.eventsMutex); - if(!handle->wayland.dbus_conn) { - dbus_destroy(handle); + if(!wl_call_tbl.has_dbus) { + MwLLDBusFreeContext(&wl_call_tbl.dbus, &handle->wayland.dbus); } buffer_destroy(&handle->wayland.cursor); @@ -2258,7 +2174,7 @@ static int MwLLPendingImpl(MwLL handle) { int pending = 0; if(handle->wayland.dark_theme_detection) { - if(handle->wayland.dbus_conn) { + if(wl_call_tbl.has_dbus) { detect_dark_theme(handle); handle->wayland.dark_theme_detection = MwFALSE; MwLLDispatch(handle, draw, NULL); diff --git a/src/backend/x11.c b/src/backend/x11.c index ad97d68..d8f028b 100644 --- a/src/backend/x11.c +++ b/src/backend/x11.c @@ -9,6 +9,9 @@ static struct { void* lib_xrender; MwBool has_xrender; + MwLLDBusFuncTable dbus; + MwBool has_dbus; + int (*XClearWindow)(Display* display, Window w); XImage* (*XCreateImage)(Display*, Visual*, unsigned int, int, int, char*, unsigned int, unsigned int, int, int); Display* (*XOpenDisplay)(_Xconst char*); @@ -308,6 +311,16 @@ static XVisualInfo* get_visual_info(Display* display) { return XGetVisualInfo(display, VisualIDMask, &xvi, &n); } +static void detect_dark_theme(MwLL handle) { + MwU32 value = 0; + MwU32 dark_theme = 0; + MwLLDBusPortalGet(&xsymtbl.dbus, &handle->x11.dbus, "org.freedesktop.portal.Settings", "org.freedesktop.appearance", "color-scheme", &value); + + dark_theme = (value == 1) ? 1 : 0; + + MwLLDispatch(handle, dark_theme, &dark_theme); +} + static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { MwLL r; Window p; @@ -429,6 +442,14 @@ static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { } } +#ifdef USE_DBUS + memset(&r->x11.dbus, 0, sizeof(r->x11.dbus)); + if(!parent && xsymtbl.has_dbus) { + xsymtbl.has_dbus = MwLLDBusNewContext(&xsymtbl.dbus, &r->x11.dbus); + r->x11.dark_theme_detection = MwTRUE; + } +#endif + return r; } @@ -449,6 +470,10 @@ static void MwLLDestroyImpl(MwLL handle) { if(handle->x11.toplevel) XCloseDisplay(handle->x11.display); + if(!xsymtbl.has_dbus) { + MwLLDBusFreeContext(&xsymtbl.dbus, &handle->x11.dbus); + } + free(handle); } @@ -607,6 +632,14 @@ static void MwLLFreeColorImpl(MwLLColor color) { static int MwLLPendingImpl(MwLL handle) { XEvent ev; + if(handle->x11.dark_theme_detection) { + if(xsymtbl.has_dbus) { + detect_dark_theme(handle); + handle->x11.dark_theme_detection = MwFALSE; + MwLLDispatch(handle, draw, NULL); + } + } + if(handle->x11.clipboard_pending && (MwTimeGetTick() - handle->x11.clipboard_time) >= ClipboardTimeout) { return 1; } @@ -1388,6 +1421,8 @@ static int MwLLX11CallInitImpl(void) { XRENDER_FUNC_LOAD(XRenderSetPictureTransform) XRENDER_FUNC_LOAD(XRenderComposite) #endif + + xsymtbl.has_dbus = MwLLDBusFuncSetup(&xsymtbl.dbus); return 0; } diff --git a/src/lowlevel.c b/src/lowlevel.c index 597a23a..32188aa 100644 --- a/src/lowlevel.c +++ b/src/lowlevel.c @@ -61,3 +61,122 @@ void MwLLCreateCommon(MwLL handle) { void MwLLDestroyCommon(MwLL handle) { free(handle->common.handler); } + +#ifdef USE_DBUS +MwBool MwLLDBusFuncSetup(MwLLDBusFuncTable* tbl) { + tbl->lib = MwDynamicOpen("libdbus-1.so"); + if(!tbl->lib) { + fprintf(stderr, "[WARNING] dbus library (libdbus-1.so) not found, will not be able to check for any XDG settings.\n"); + return MwFALSE; + } + +#define DBUS_FUNC(x) \ + tbl->x = MwDynamicSymbol(tbl->lib, #x); \ + if(!tbl->x) { \ + fprintf(stderr, "[WARNING] dbus function %s not found, will not be able to check for any XDG settings.\n", #x); \ + dlclose(tbl->lib); \ + return MwFALSE; \ + } + + 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 MwTRUE; +} +#endif + +MwBool MwLLDBusNewContext(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx) { + if(!tbl->lib) { + return MwFALSE; + } + + tbl->dbus_error_init(&ctx->dbus_err); + + ctx->dbus_conn = tbl->dbus_bus_get(DBUS_BUS_SESSION, &ctx->dbus_err); + if(tbl->dbus_error_is_set(&ctx->dbus_err)) { + fprintf(stderr, "[WARNING] Could not initialize dbus: Connection error: %s\n", ctx->dbus_err.message); + tbl->dbus_error_free(&ctx->dbus_err); + return MwFALSE; + } + if(!ctx->dbus_conn) { + fprintf(stderr, "[WARNING] Could not initialize dbus: Failed to connect to session bus\n"); + return MwFALSE; + } + return MwTRUE; +}; + +MWDECL MwBool MwLLDBusPortalGet(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx, const char* portal, const char* namespace, const char* key, void* out) { + if(!ctx->dbus_conn) { + return MwFALSE; + } + + ctx->dbus_msg = tbl->dbus_message_new_method_call( + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + portal, + "Read"); + if(!ctx->dbus_msg) { + fprintf(stderr, "[WARNING] Couldn't get %s::%s: Failed to create message\n", namespace, key); + return MwFALSE; + } + + tbl->dbus_message_iter_init_append(ctx->dbus_msg, &ctx->dbus_args); + tbl->dbus_message_iter_append_basic(&ctx->dbus_args, 's', &namespace); + tbl->dbus_message_iter_append_basic(&ctx->dbus_args, 's', &key); + + ctx->dbus_reply = tbl->dbus_connection_send_with_reply_and_block(ctx->dbus_conn, ctx->dbus_msg, 100, &ctx->dbus_err); + + if(tbl->dbus_error_is_set(&ctx->dbus_err)) { + fprintf(stderr, "[WARNING] Couldn't get %s::%s: Send error: %s\n", namespace, key, ctx->dbus_err.message); + tbl->dbus_error_free(&ctx->dbus_err); + return MwFALSE; + } + if(!ctx->dbus_reply) { + fprintf(stderr, "[WARNING] Couldn't get %s::%s: No reply received\n", namespace, key); + return MwFALSE; + } + + if(!tbl->dbus_message_iter_init(ctx->dbus_reply, &ctx->dbus_args)) { + fprintf(stderr, "[WARNING] Couldn't get %s::%s: Reply has no arguments\n", namespace, key); + tbl->dbus_message_unref(ctx->dbus_reply); + return MwFALSE; + } + + if(tbl->dbus_message_iter_get_arg_type(&ctx->dbus_args) != 'v') { + fprintf(stderr, "[WARNING] Couldn't get %s::%s: Expected outer variant\n", namespace, key); + tbl->dbus_message_unref(ctx->dbus_reply); + return MwFALSE; + } + tbl->dbus_message_iter_recurse(&ctx->dbus_args, &ctx->dbus_variant); + + /* Some portals wrap the value in a second variant */ + if(tbl->dbus_message_iter_get_arg_type(&ctx->dbus_variant) == 'v') { + tbl->dbus_message_iter_recurse(&ctx->dbus_variant, &ctx->dbus_inner_variant); + tbl->dbus_message_iter_get_basic(&ctx->dbus_inner_variant, out); + } else { + tbl->dbus_message_iter_get_basic(&ctx->dbus_variant, out); + } + tbl->dbus_message_unref(ctx->dbus_msg); + + return MwTRUE; +} + +void MwLLDBusFreeContext(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx) { + if(!tbl->lib) { + return; + } + if(ctx->dbus_reply) tbl->dbus_message_unref(ctx->dbus_reply); + if(ctx->dbus_conn) tbl->dbus_connection_unref(ctx->dbus_conn); +};