From c2bc24de35873f143c00523d51fbc5fa2ad9779f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 5 Apr 2022 15:36:03 +0530 Subject: [PATCH] Read appearance setting from desktop portal as well --- glfw/linux_desktop_settings.c | 198 ++++++++++++++++++++++++---------- 1 file changed, 142 insertions(+), 56 deletions(-) diff --git a/glfw/linux_desktop_settings.c b/glfw/linux_desktop_settings.c index 081027ceb..4fca7c7d1 100644 --- a/glfw/linux_desktop_settings.c +++ b/glfw/linux_desktop_settings.c @@ -10,77 +10,118 @@ #include #include -static const char *DESKTOP_SERVICE = "org.freedesktop.portal.Desktop"; -static const char *DESKTOP_PATH = "/org/freedesktop/portal/desktop"; -static const char *DESKTOP_INTERFACE = "org.freedesktop.portal.Settings"; -static const char *GNOME_DESKTOP_NAMESPACE = "org.gnome.desktop.interface"; +#define DESKTOP_SERVICE "org.freedesktop.portal.Desktop" +#define DESKTOP_PATH "/org/freedesktop/portal/desktop" +#define DESKTOP_INTERFACE "org.freedesktop.portal.Settings" +#define GNOME_DESKTOP_NAMESPACE "org.gnome.desktop.interface" +#define FDO_DESKTOP_NAMESPACE "org.freedesktop.appearance" +#define FDO_APPEARANCE_KEY "color-scheme" -static char theme_name[64] = {0}; +static char theme_name[128] = {0}; static int theme_size = -1; -static bool gnome_cursor_theme_read = false, gnome_cursor_size_read = false; +static uint32_t appearance = 0; +static bool is_gnome = false; +static bool cursor_theme_changed = false; -static bool -parse_dbus_message_for_type(DBusMessage *const reply, const char *errmsg, const int type, void *value) { - DBusMessageIter iter[3]; - dbus_message_iter_init(reply, &iter[0]); -#define FAIL { _glfwInputError(GLFW_PLATFORM_ERROR, "%s", errmsg); return false; } - if (dbus_message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_VARIANT) FAIL; - dbus_message_iter_recurse(&iter[0], &iter[1]); - if (dbus_message_iter_get_arg_type(&iter[1]) != DBUS_TYPE_VARIANT) FAIL; - dbus_message_iter_recurse(&iter[1], &iter[2]); - if (dbus_message_iter_get_arg_type(&iter[2]) != type) FAIL; - dbus_message_iter_get_basic(&iter[2], value); - return true; -#undef FAIL -} - -#define HANDLER(name) void name(DBusMessage *msg, const char* errmsg, void *data) { \ +#define HANDLER(name) static void name(DBusMessage *msg, const char* errmsg, void *data) { \ (void)data; \ if (errmsg) { \ _glfwInputError(GLFW_PLATFORM_ERROR, "%s: failed with error: %s", #name, errmsg); \ return; \ } -HANDLER(on_gnome_cursor_theme_read) - const char *name; - if (!parse_dbus_message_for_type(msg, "Failed to get cursor theme name from reply", DBUS_TYPE_STRING, &name)) return; - if (name && name[0]) { - gnome_cursor_theme_read = true; - strncpy(theme_name, name, sizeof(theme_name) - 1); - if (gnome_cursor_size_read) _glfwPlatformChangeCursorTheme(); +static void +process_fdo_setting(const char *key, DBusMessageIter *value) { + if (strcmp(key, FDO_APPEARANCE_KEY) == 0) { + if (dbus_message_iter_get_arg_type(value) == DBUS_TYPE_UINT32) { + dbus_message_iter_get_basic(value, &appearance); + if (appearance > 2) appearance = 0; + } } } -HANDLER(on_gnome_cursor_size_read) - int32_t sz; - if (!parse_dbus_message_for_type(msg, "Failed to get cursor theme size from reply", DBUS_TYPE_INT32, &sz)) return; - gnome_cursor_size_read = true; - theme_size = sz; - if (gnome_cursor_theme_read) _glfwPlatformChangeCursorTheme(); -} -#undef HANDLER - - -static bool -call_read(DBusConnection *session_bus, dbus_pending_callback callback, const char *namespace, const char *key) { - return glfw_dbus_call_method_with_reply( - session_bus, DESKTOP_SERVICE, DESKTOP_PATH, DESKTOP_INTERFACE, "Read", DBUS_TIMEOUT_USE_DEFAULT, - callback, NULL, DBUS_TYPE_STRING, &namespace, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID); -} - static void -get_from_gnome(void) { - theme_size = 32; - DBusConnection *session_bus = glfw_dbus_session_bus(); - if (session_bus) { - const char *theme_key = "cursor-theme"; - call_read(session_bus, on_gnome_cursor_theme_read, GNOME_DESKTOP_NAMESPACE, theme_key); - const char *size_key = "cursor-size"; - call_read(session_bus, on_gnome_cursor_size_read, GNOME_DESKTOP_NAMESPACE, size_key); +process_gnome_setting(const char *key, DBusMessageIter *value) { + if (strcmp(key, "cursor-size") == 0) { + if (dbus_message_iter_get_arg_type(value) == DBUS_TYPE_INT32) { + int32_t sz; + dbus_message_iter_get_basic(value, &sz); + if (sz > 0 && sz != theme_size) { + theme_size = sz; + cursor_theme_changed = true; + } + } + } else if (strcmp(key, "cursor-theme") == 0) { + if (dbus_message_iter_get_arg_type(value) == DBUS_TYPE_STRING) { + const char *name; + dbus_message_iter_get_basic(value, &name); + if (name) { + strncpy(theme_name, name, sizeof(theme_name) - 1); + cursor_theme_changed = true; + } + } } } +static void +process_settings_dict(DBusMessageIter *array_iter, void(process_setting)(const char *, DBusMessageIter*)) { + DBusMessageIter item_iter, value_iter; + while (dbus_message_iter_get_arg_type(array_iter) == DBUS_TYPE_DICT_ENTRY) { + dbus_message_iter_recurse(array_iter, &item_iter); + if (dbus_message_iter_get_arg_type(&item_iter) == DBUS_TYPE_STRING) { + const char *key; + dbus_message_iter_get_basic(&item_iter, &key); + if (dbus_message_iter_next(&item_iter) && dbus_message_iter_get_arg_type(&item_iter) == DBUS_TYPE_VARIANT) { + dbus_message_iter_recurse(&item_iter, &value_iter); + process_setting(key, &value_iter); + } + } + if (!dbus_message_iter_next(array_iter)) break; + } +} + +HANDLER(process_desktop_settings) + cursor_theme_changed = false; + DBusMessageIter root, array, item, settings; + dbus_message_iter_init(msg, &root); +#define die(...) { _glfwInputError(GLFW_PLATFORM_ERROR, __VA_ARGS__); return; } + if (dbus_message_iter_get_arg_type(&root) != DBUS_TYPE_ARRAY) die("Reply to request for desktop settings is not an array"); + dbus_message_iter_recurse(&root, &array); + while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { + dbus_message_iter_recurse(&array, &item); + if (dbus_message_iter_get_arg_type(&item) == DBUS_TYPE_STRING) { + const char *namespace; + dbus_message_iter_get_basic(&item, &namespace); + if (dbus_message_iter_next(&item) && dbus_message_iter_get_arg_type(&item) == DBUS_TYPE_ARRAY) { + dbus_message_iter_recurse(&item, &settings); + if (strcmp(namespace, FDO_DESKTOP_NAMESPACE) == 0) { + process_settings_dict(&settings, process_fdo_setting); + } else if (is_gnome && strcmp(namespace, GNOME_DESKTOP_NAMESPACE) == 0) { + process_settings_dict(&settings, process_gnome_setting); + } + } + } + if (!dbus_message_iter_next(&array)) break; + } +#undef die + if (cursor_theme_changed) _glfwPlatformChangeCursorTheme(); +} + +#undef HANDLER + +static bool +read_desktop_settings(DBusConnection *session_bus) { + DBusMessage *msg = dbus_message_new_method_call(DESKTOP_SERVICE, DESKTOP_PATH, DESKTOP_INTERFACE, "ReadAll"); + if (!msg) return false; + DBusMessageIter iter, array_iter; + dbus_message_iter_init_append(msg, &iter); + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &array_iter)) { dbus_message_unref(msg); return false; } + if (!dbus_message_iter_close_container(&iter, &array_iter)) { dbus_message_unref(msg); return false; } + bool ok = call_method_with_msg(session_bus, msg, DBUS_TIMEOUT_USE_DEFAULT, process_desktop_settings, NULL); + dbus_message_unref(msg); + return ok; +} void glfw_current_cursor_theme(const char **theme, int *size) { @@ -100,10 +141,55 @@ get_cursor_theme_from_env(void) { } } +static void +on_color_scheme_change(DBusMessage *message) { + DBusMessageIter iter[2]; + dbus_message_iter_init (message, &iter[0]); + int current_type; + while ((current_type = dbus_message_iter_get_arg_type (&iter[0])) != DBUS_TYPE_INVALID) { + if (current_type == DBUS_TYPE_VARIANT) { + dbus_message_iter_recurse(&iter[0], &iter[1]); + if (dbus_message_iter_get_arg_type(&iter[1]) == DBUS_TYPE_UINT32) { + uint32_t val = 0; + dbus_message_iter_get_basic(&iter[1], &val); + if (val > 2) val = 0; + if (val != appearance) { + appearance = val; + } + } + break; + } + dbus_message_iter_next(&iter[0]); + } +} + +static DBusHandlerResult +setting_changed(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data UNUSED) { + /* printf("session_bus settings_changed invoked interface: %s member: %s\n", dbus_message_get_interface(msg), dbus_message_get_member(msg)); */ + if (dbus_message_is_signal(msg, DESKTOP_INTERFACE, "SettingChanged")) { + const char *namespace = NULL, *key = NULL; + if (glfw_dbus_get_args(msg, "Failed to get namespace and key from SettingChanged notification signal", DBUS_TYPE_STRING, &namespace, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) { + if (strcmp(namespace, FDO_DESKTOP_NAMESPACE) == 0) { + if (strcmp(key, FDO_APPEARANCE_KEY) == 0) { + on_color_scheme_change(msg); + } + } + } + + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + void glfw_initialize_desktop_settings(void) { get_cursor_theme_from_env(); const char *desktop = getenv("XDG_CURRENT_DESKTOP"); - bool is_gnome = desktop && strstr(desktop, "GNOME"); - if (is_gnome) get_from_gnome(); + is_gnome = desktop && strstr(desktop, "GNOME"); + DBusConnection *session_bus = glfw_dbus_session_bus(); + if (session_bus) { + if (!read_desktop_settings(session_bus)) _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to read desktop settings, make sure you have the desktop portal running."); + dbus_bus_add_match(session_bus, "type='signal',interface='" DESKTOP_INTERFACE "',member='SettingChanged'", NULL); + dbus_connection_add_filter(session_bus, setting_changed, NULL, NULL); + } }