Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
d6b96d0f8c major surgery on dbus code 2026-04-17 10:12:00 +02:00
30c84a915a minor adjustments for compliance 2026-04-17 09:22:30 +02:00
4 changed files with 113 additions and 52 deletions

View file

@ -66,11 +66,17 @@ typedef struct _MwLLDBusFuncTable {
DBusMessage* (*dbus_message_new_method_call)(const char* bus_name, const char* path, const char* iface, const char* method); 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); 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); dbus_bool_t (*dbus_message_iter_append_basic)(DBusMessageIter* iter, int type, const void* value);
void (*dbus_connection_flush)(DBusConnection* connection);
dbus_bool_t (*dbus_connection_read_write)(DBusConnection* connection, int timeout_milliseconds);
DBusMessage* (*dbus_connection_send_with_reply_and_block)(DBusConnection* connection, DBusMessage* message, int timeout_milliseconds, DBusError* error); DBusMessage* (*dbus_connection_send_with_reply_and_block)(DBusConnection* connection, DBusMessage* message, int timeout_milliseconds, DBusError* error);
DBusMessage* (*dbus_connection_pop_message)(DBusConnection* connection);
void (*dbus_bus_add_match)(DBusConnection* connection, const char* rule, DBusError* error);
void (*dbus_message_unref)(DBusMessage* message); void (*dbus_message_unref)(DBusMessage* message);
dbus_bool_t (*dbus_message_iter_init)(DBusMessage* message, DBusMessageIter* iter); dbus_bool_t (*dbus_message_iter_init)(DBusMessage* message, DBusMessageIter* iter);
dbus_bool_t (*dbus_message_is_signal)(DBusMessage* message, const char* iface, const char* signal_name);
int (*dbus_message_iter_get_arg_type)(DBusMessageIter* iter); int (*dbus_message_iter_get_arg_type)(DBusMessageIter* iter);
void (*dbus_message_iter_recurse)(DBusMessageIter* iter, DBusMessageIter* sub); void (*dbus_message_iter_recurse)(DBusMessageIter* iter, DBusMessageIter* sub);
void (*dbus_message_iter_next)(DBusMessageIter* iter);
void (*dbus_connection_unref)(DBusConnection* connection); void (*dbus_connection_unref)(DBusConnection* connection);
void (*dbus_message_iter_get_basic)(DBusMessageIter* iter, void* value); void (*dbus_message_iter_get_basic)(DBusMessageIter* iter, void* value);
} MwLLDBusFuncTable; } MwLLDBusFuncTable;
@ -80,7 +86,6 @@ typedef struct _MwLLDBusContext {
DBusError dbus_err; DBusError dbus_err;
DBusMessage* dbus_msg; DBusMessage* dbus_msg;
DBusMessage* dbus_reply; DBusMessage* dbus_reply;
DBusMessageIter dbus_args, dbus_variant, dbus_inner_variant;
} MwLLDBusContext; } MwLLDBusContext;
typedef void (*MwLLDBusPortalPollListener)(MwLL handle, MwU32 new_value); typedef void (*MwLLDBusPortalPollListener)(MwLL handle, MwU32 new_value);

View file

@ -454,6 +454,7 @@ struct _MwLLWayland {
#endif #endif
MwBool dark_theme_detection; MwBool dark_theme_detection;
MwU32 dark_theme;
/* clipboard related stuff. /* 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.*/ * 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

@ -17,8 +17,6 @@
wayland_call_table_t wl_call_tbl; wayland_call_table_t wl_call_tbl;
MwBool MwWaylandAlwaysRender = MwFALSE; 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. */ /* 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) \ #define WAYLAND_EVENT_OP_START(self) \
do { \ do { \
@ -1782,20 +1780,20 @@ static void widget_setup(MwLL r, MwLL parent, int x, int y, int width, int heigh
#ifdef USE_DBUS #ifdef USE_DBUS
static void dark_theme_listener(MwLL handle, MwU32 new_value) { static void dark_theme_listener(MwLL handle, MwU32 new_value) {
wl_dark_theme = (new_value == 1) ? 1 : 0; handle->wayland.dark_theme = (new_value == 1) ? 1 : 0;
MwLLDispatch(handle, dark_theme, &wl_dark_theme); MwLLDispatch(handle, dark_theme, &handle->wayland.dark_theme);
} }
static void detect_dark_theme(MwLL handle) { static void detect_dark_theme(MwLL handle) {
MwU32 value = 0; MwU32 value = 0;
MwLLDBusPortalGet(&wl_call_tbl.dbus, &handle->wayland.dbus, "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);
wl_dark_theme = (value == 1) ? 1 : 0; handle->wayland.dark_theme = (value == 1) ? 1 : 0;
MwLLDBusPortalWatch(&wl_call_tbl.dbus, &handle->wayland.dbus, "org.freedesktop.portal.Settings"); MwLLDBusPortalWatch(&wl_call_tbl.dbus, &handle->wayland.dbus, "org.freedesktop.portal.Settings");
MwLLDispatch(handle, dark_theme, &wl_dark_theme); MwLLDispatch(handle, dark_theme, &handle->wayland.dark_theme);
} }
#endif #endif
@ -1811,6 +1809,7 @@ static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) {
if(!parent && wl_call_tbl.has_dbus) { if(!parent && wl_call_tbl.has_dbus) {
wl_call_tbl.has_dbus = MwLLDBusNewContext(&wl_call_tbl.dbus, &r->wayland.dbus); wl_call_tbl.has_dbus = MwLLDBusNewContext(&wl_call_tbl.dbus, &r->wayland.dbus);
r->wayland.dark_theme_detection = MwTRUE; r->wayland.dark_theme_detection = MwTRUE;
r->wayland.dark_theme = -1;
} }
#endif #endif
@ -2439,7 +2438,7 @@ static void MwLLShowImpl(MwLL handle, int show) {
static void MwLLMakePopupImpl(MwLL handle, MwLL parent) { static void MwLLMakePopupImpl(MwLL handle, MwLL parent) {
(void)handle; (void)handle;
(void)parent; (void)parent;
/* Wayland doesn't have "popups" in the Milsko sense persay. xdg_popup is closer to ToolWindow and as such is what we use there. So just like the Mac backend, this is just left alone. */ /* Wayland doesn't have "popups" in the Milsko sense per se. xdg_popup is closer to ToolWindow and as such is what we use there. So just like the Mac backend, this is just left alone. */
} }
static void MwLLSetSizeHintsImpl(MwLL handle, int minx, int miny, int maxx, int maxy) { static void MwLLSetSizeHintsImpl(MwLL handle, int minx, int miny, int maxx, int maxy) {

View file

@ -82,16 +82,22 @@ MwBool MwLLDBusFuncSetup(MwLLDBusFuncTable* tbl) {
DBUS_FUNC(dbus_error_init); DBUS_FUNC(dbus_error_init);
DBUS_FUNC(dbus_bus_get); DBUS_FUNC(dbus_bus_get);
DBUS_FUNC(dbus_bus_add_match);
DBUS_FUNC(dbus_error_is_set); DBUS_FUNC(dbus_error_is_set);
DBUS_FUNC(dbus_error_free); DBUS_FUNC(dbus_error_free);
DBUS_FUNC(dbus_message_new_method_call); DBUS_FUNC(dbus_message_new_method_call);
DBUS_FUNC(dbus_message_is_signal);
DBUS_FUNC(dbus_message_iter_init_append); DBUS_FUNC(dbus_message_iter_init_append);
DBUS_FUNC(dbus_message_iter_append_basic); DBUS_FUNC(dbus_message_iter_append_basic);
DBUS_FUNC(dbus_connection_flush);
DBUS_FUNC(dbus_connection_pop_message);
DBUS_FUNC(dbus_connection_read_write);
DBUS_FUNC(dbus_connection_send_with_reply_and_block); DBUS_FUNC(dbus_connection_send_with_reply_and_block);
DBUS_FUNC(dbus_message_unref); DBUS_FUNC(dbus_message_unref);
DBUS_FUNC(dbus_message_iter_init); DBUS_FUNC(dbus_message_iter_init);
DBUS_FUNC(dbus_message_iter_get_arg_type); DBUS_FUNC(dbus_message_iter_get_arg_type);
DBUS_FUNC(dbus_message_iter_recurse); DBUS_FUNC(dbus_message_iter_recurse);
DBUS_FUNC(dbus_message_iter_next);
DBUS_FUNC(dbus_connection_unref); DBUS_FUNC(dbus_connection_unref);
DBUS_FUNC(dbus_message_iter_get_basic); DBUS_FUNC(dbus_message_iter_get_basic);
@ -121,6 +127,8 @@ 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 MwLLDBusPortalGet(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx, const char* portal, const char* namespace, const char* key, void* out) {
DBusMessageIter dbus_args, dbus_variant, dbus_inner_variant;
char arg_type;
if(!ctx->dbus_conn) { if(!ctx->dbus_conn) {
return MwFALSE; return MwFALSE;
} }
@ -135,9 +143,9 @@ MWDECL MwBool MwLLDBusPortalGet(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx, co
return MwFALSE; return MwFALSE;
} }
tbl->dbus_message_iter_init_append(ctx->dbus_msg, &ctx->dbus_args); tbl->dbus_message_iter_init_append(ctx->dbus_msg, &dbus_args);
tbl->dbus_message_iter_append_basic(&ctx->dbus_args, 's', &namespace); tbl->dbus_message_iter_append_basic(&dbus_args, 's', &namespace);
tbl->dbus_message_iter_append_basic(&ctx->dbus_args, 's', &key); tbl->dbus_message_iter_append_basic(&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); ctx->dbus_reply = tbl->dbus_connection_send_with_reply_and_block(ctx->dbus_conn, ctx->dbus_msg, 100, &ctx->dbus_err);
@ -151,25 +159,35 @@ MWDECL MwBool MwLLDBusPortalGet(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx, co
return MwFALSE; return MwFALSE;
} }
if(!tbl->dbus_message_iter_init(ctx->dbus_reply, &ctx->dbus_args)) { if(!tbl->dbus_message_iter_init(ctx->dbus_reply, &dbus_args)) {
fprintf(stderr, "[WARNING] Couldn't get %s::%s: Reply has no arguments\n", namespace, key); fprintf(stderr, "[WARNING] Couldn't get %s::%s: Reply has no arguments\n", namespace, key);
tbl->dbus_message_unref(ctx->dbus_reply); tbl->dbus_message_unref(ctx->dbus_reply);
return MwFALSE; return MwFALSE;
} }
if(tbl->dbus_message_iter_get_arg_type(&ctx->dbus_args) != 'v') { if((arg_type = tbl->dbus_message_iter_get_arg_type(&dbus_args)) != 'v') {
fprintf(stderr, "[WARNING] Couldn't get %s::%s: Expected outer variant\n", namespace, key); fprintf(stderr, "[WARNING] Couldn't get %s::%s: Expected outer variant, got: %c\n", namespace, key, arg_type);
tbl->dbus_message_unref(ctx->dbus_reply); tbl->dbus_message_unref(ctx->dbus_reply);
return MwFALSE; return MwFALSE;
} }
tbl->dbus_message_iter_recurse(&ctx->dbus_args, &ctx->dbus_variant); tbl->dbus_message_iter_recurse(&dbus_args, &dbus_variant);
/* Some portals wrap the value in a second variant */ /* Some portals wrap the value in a second variant */
if(tbl->dbus_message_iter_get_arg_type(&ctx->dbus_variant) == 'v') { arg_type = tbl->dbus_message_iter_get_arg_type(&dbus_variant);
tbl->dbus_message_iter_recurse(&ctx->dbus_variant, &ctx->dbus_inner_variant); if(arg_type == 'v') {
tbl->dbus_message_iter_get_basic(&ctx->dbus_inner_variant, out); tbl->dbus_message_iter_recurse(&dbus_variant, &dbus_inner_variant);
if((arg_type = tbl->dbus_message_iter_get_arg_type(&dbus_inner_variant)) != 'u') {
fprintf(stderr, "[WARNING] Signal reply for %s::SettingChanged: Expected 'u' (uint32) for 'value', got: %c\n", portal, arg_type);
tbl->dbus_message_unref(ctx->dbus_reply);
return MwFALSE;
}
tbl->dbus_message_iter_get_basic(&dbus_inner_variant, out);
} else if (arg_type == 'u') {
tbl->dbus_message_iter_get_basic(&dbus_variant, out);
} else { } else {
tbl->dbus_message_iter_get_basic(&ctx->dbus_variant, out); fprintf(stderr, "[WARNING] Couldn't get %s::%s: Expected variant or string, got: %c\n", namespace, key, arg_type);
tbl->dbus_message_unref(ctx->dbus_reply);
return MwFALSE;
} }
tbl->dbus_message_unref(ctx->dbus_msg); tbl->dbus_message_unref(ctx->dbus_msg);
@ -177,66 +195,104 @@ MWDECL MwBool MwLLDBusPortalGet(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx, co
} }
MWDECL MwBool MwLLDBusPortalWatch(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx, const char* portal) { MWDECL MwBool MwLLDBusPortalWatch(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx, const char* portal) {
char filter_string[2048];
if(!ctx->dbus_conn) { if(!ctx->dbus_conn) {
return MwFALSE; return MwFALSE;
} }
char filter_string[2048];
MwStringPrintIntoBuffer(filter_string, sizeof(filter_string), "type='%s',interface=%s", "signal", portal); MwStringPrintIntoBuffer(filter_string, sizeof(filter_string), "type='%s',interface=%s", "signal", portal);
dbus_bus_add_match(ctx->dbus_conn, filter_string, &ctx->dbus_err); tbl->dbus_bus_add_match(ctx->dbus_conn, filter_string, &ctx->dbus_err);
dbus_connection_flush(ctx->dbus_conn); tbl->dbus_connection_flush(ctx->dbus_conn);
return MwTRUE; return MwTRUE;
} }
// Technically this will swallow all other results, so this is not usable multiple times. /* Technically this will swallow all other results, so this is not usable multiple times.
// TODO: Use a hashmap instead 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) { 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, msg_value_inner;
DBusMessageIter args, msg_value; const char* msg_namespace;
const char* msg_namespace; const char* msg_key;
const char* msg_key; MwU32 msg_value_content;
MwU32 msg_value_content; char arg_type;
if(!ctx->dbus_conn) { if(!ctx->dbus_conn) {
return MwFALSE; return MwFALSE;
} }
dbus_connection_read_write(ctx->dbus_conn, 0); tbl->dbus_connection_read_write(ctx->dbus_conn, 0);
msg = dbus_connection_pop_message(ctx->dbus_conn); tbl->dbus_connection_read_write(ctx->dbus_conn, 0);
ctx->dbus_reply = tbl->dbus_connection_pop_message(ctx->dbus_conn);
if (NULL == msg) { if(tbl->dbus_error_is_set(&ctx->dbus_err)) {
fprintf(stderr, "[WARNING] Signal reply for %s::SettingChanged: Poll error: %s\n", portal, ctx->dbus_err.message);
tbl->dbus_error_free(&ctx->dbus_err);
return MwFALSE;
}
if(!ctx->dbus_reply) {
return MwFALSE; return MwFALSE;
} }
// check if the message is a signal from the correct interface and with the correct name /* check if the message is a signal from the correct interface and with the correct name */
if (dbus_message_is_signal(msg, portal, "SettingChanged")) { if(tbl->dbus_message_is_signal(ctx->dbus_reply, portal, "SettingChanged")) {
// read the parameters /* read the parameters */
if (!dbus_message_iter_init(msg, &args)) if(!tbl->dbus_message_iter_init(ctx->dbus_reply, &args))
fprintf(stderr, "[WARNING] Message has no arguments\n"); fprintf(stderr, "[WARNING] Signal reply for %s::SettingChanged has no arguments\n", portal);
else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args))
fprintf(stderr, "[WARNING] Argument is not string\n");
else { else {
dbus_message_iter_get_basic(&args, &msg_namespace); if((arg_type = tbl->dbus_message_iter_get_arg_type(&args)) != 's') {
fprintf(stderr, "[WARNING] Signal reply for %s::SettingChanged: Expected 's' (string) for 'namespace', got: %c\n", portal, arg_type);
tbl->dbus_message_unref(ctx->dbus_reply);
return MwFALSE;
}
tbl->dbus_message_iter_get_basic(&args, &msg_namespace);
dbus_message_iter_next(&args); tbl->dbus_message_iter_next(&args);
dbus_message_iter_get_basic(&args, &msg_key); if((arg_type = tbl->dbus_message_iter_get_arg_type(&args)) != 's') {
fprintf(stderr, "[WARNING] Signal reply for %s::SettingChanged: Expected 's' (string) for 'key', got: %c\n", portal, arg_type);
tbl->dbus_message_unref(ctx->dbus_reply);
return MwFALSE;
}
tbl->dbus_message_iter_get_basic(&args, &msg_key);
// Check that key and namespace match /* Check that key and namespace match */
if (strcmp(msg_namespace, namespace) == 0 && strcmp(msg_key, key) == 0) { if(strcmp(msg_namespace, namespace) == 0 && strcmp(msg_key, key) == 0) {
// Assuming the value is a basic type /* Assuming the value is a basic type */
dbus_message_iter_next(&args); tbl->dbus_message_iter_next(&args);
dbus_message_iter_recurse(&args, &msg_value);
dbus_message_iter_get_basic(&msg_value, &msg_value_content); if((arg_type = tbl->dbus_message_iter_get_arg_type(&args)) != 'v') {
fprintf(stderr, "[WARNING] Couldn't get %s::%s: Expected outer variant, got: %c\n", namespace, key, arg_type);
tbl->dbus_message_unref(ctx->dbus_reply);
return MwFALSE;
}
tbl->dbus_message_iter_recurse(&args, &msg_value);
/* Some portals wrap the value in a second variant */
arg_type = tbl->dbus_message_iter_get_arg_type(&msg_value);
if(arg_type == 'v') {
tbl->dbus_message_iter_recurse(&msg_value, &msg_value_inner);
if((arg_type = tbl->dbus_message_iter_get_arg_type(&msg_value_inner)) != 'u') {
fprintf(stderr, "[WARNING] Signal reply for %s::SettingChanged: Expected 'u' (uint32) for 'value', got: %c\n", portal, arg_type);
tbl->dbus_message_unref(ctx->dbus_reply);
return MwFALSE;
}
tbl->dbus_message_iter_get_basic(&msg_value_inner, &msg_value_content);
} else if (arg_type == 'u') {
tbl->dbus_message_iter_get_basic(&msg_value, &msg_value_content);
} else {
fprintf(stderr, "[WARNING] Couldn't get %s::%s: Expected variant or string, got: %c\n", namespace, key, arg_type);
tbl->dbus_message_unref(ctx->dbus_reply);
return MwFALSE;
}
listener(handle, msg_value_content); listener(handle, msg_value_content);
} }
} }
} }
// free the message /* free the message */
dbus_message_unref(msg); tbl->dbus_message_unref(ctx->dbus_reply);
return MwTRUE;
} }
void MwLLDBusFreeContext(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx) { void MwLLDBusFreeContext(MwLLDBusFuncTable* tbl, MwLLDBusContext* ctx) {