From d5f07730be1d2ac9a0045e0918ab226b2fffc3f2 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Thu, 18 May 2023 17:42:08 -0400 Subject: [PATCH] x11: Dynamically update the scale factor If the text-scaling-factor setting is available via D-Bus, add a listener and update the content scale values for the displays if the value is changed during runtime. Factors out the D-Bus message pump from the system theme detection code to the general D-Bus code, as it's now used for more purposes than just the system theme. --- src/core/linux/SDL_dbus.c | 12 ++++ src/core/linux/SDL_dbus.h | 2 + src/core/linux/SDL_system_theme.c | 15 ----- src/core/linux/SDL_system_theme.h | 1 - src/video/wayland/SDL_waylandevents.c | 4 +- src/video/x11/SDL_x11events.c | 4 +- src/video/x11/SDL_x11modes.c | 95 ++++++++++++++++++++++++--- 7 files changed, 105 insertions(+), 28 deletions(-) diff --git a/src/core/linux/SDL_dbus.c b/src/core/linux/SDL_dbus.c index 687cb8549..81cbd290d 100644 --- a/src/core/linux/SDL_dbus.c +++ b/src/core/linux/SDL_dbus.c @@ -498,4 +498,16 @@ SDL_DBus_ScreensaverInhibit(SDL_bool inhibit) return SDL_TRUE; } + +void SDL_DBus_PumpEvents(void) +{ + if (dbus.session_conn) { + dbus.connection_read_write(dbus.session_conn, 0); + + while (dbus.connection_dispatch(dbus.session_conn) == DBUS_DISPATCH_DATA_REMAINS) { + /* Do nothing, actual work happens in DBus_MessageFilter */ + SDL_DelayNS(SDL_US_TO_NS(10)); + } + } +} #endif diff --git a/src/core/linux/SDL_dbus.h b/src/core/linux/SDL_dbus.h index 2a90e0a21..8174c20af 100644 --- a/src/core/linux/SDL_dbus.h +++ b/src/core/linux/SDL_dbus.h @@ -94,6 +94,8 @@ extern SDL_bool SDL_DBus_QueryPropertyOnConnection(DBusConnection *conn, const c extern void SDL_DBus_ScreensaverTickle(void); extern SDL_bool SDL_DBus_ScreensaverInhibit(SDL_bool inhibit); +extern void SDL_DBus_PumpEvents(void); + #endif /* HAVE_DBUS_DBUS_H */ #endif /* SDL_dbus_h_ */ diff --git a/src/core/linux/SDL_system_theme.c b/src/core/linux/SDL_system_theme.c index 3f0077edd..4af1b0e7b 100644 --- a/src/core/linux/SDL_system_theme.c +++ b/src/core/linux/SDL_system_theme.c @@ -158,18 +158,3 @@ SDL_SystemTheme_Get(void) { return system_theme_data.theme; } - -void -SDL_SystemTheme_PumpEvents(void) -{ - SDL_DBusContext *dbus = system_theme_data.dbus; - DBusConnection *conn; - if (dbus == NULL) return; - conn = dbus->session_conn; - dbus->connection_read_write(conn, 0); - - while (dbus->connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS) { - /* Do nothing, actual work happens in DBus_MessageFilter */ - usleep(10); - } -} diff --git a/src/core/linux/SDL_system_theme.h b/src/core/linux/SDL_system_theme.h index 2510ecd30..8d521faaf 100644 --- a/src/core/linux/SDL_system_theme.h +++ b/src/core/linux/SDL_system_theme.h @@ -26,6 +26,5 @@ extern SDL_bool SDL_SystemTheme_Init(void); extern SDL_SystemTheme SDL_SystemTheme_Get(void); -extern void SDL_SystemTheme_PumpEvents(void); #endif /* SDL_system_theme_h_ */ diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 845e49003..eace98072 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -391,7 +391,7 @@ int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) #endif #ifdef SDL_USE_LIBDBUS - SDL_SystemTheme_PumpEvents(); + SDL_DBus_PumpEvents(); #endif /* If key repeat is active, we'll need to cap our maximum wait time to handle repeats */ @@ -464,7 +464,7 @@ void Wayland_PumpEvents(SDL_VideoDevice *_this) #endif #ifdef SDL_USE_LIBDBUS - SDL_SystemTheme_PumpEvents(); + SDL_DBus_PumpEvents(); #endif #ifdef HAVE_LIBDECOR_H diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index fb93446f8..8c2304971 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -1706,7 +1706,7 @@ int X11_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) #endif #ifdef SDL_USE_LIBDBUS - SDL_SystemTheme_PumpEvents(); + SDL_DBus_PumpEvents(); #endif return 1; } @@ -1751,7 +1751,7 @@ void X11_PumpEvents(SDL_VideoDevice *_this) #endif #ifdef SDL_USE_LIBDBUS - SDL_SystemTheme_PumpEvents(); + SDL_DBus_PumpEvents(); #endif /* FIXME: Only need to do this when there are pending focus changes */ diff --git a/src/video/x11/SDL_x11modes.c b/src/video/x11/SDL_x11modes.c index 51865752c..31f74e3c3 100644 --- a/src/video/x11/SDL_x11modes.c +++ b/src/video/x11/SDL_x11modes.c @@ -41,15 +41,22 @@ #ifdef SDL_USE_LIBDBUS +#define SCALE_FACTOR_NODE "org.freedesktop.portal.Desktop" +#define SCALE_FACTOR_PATH "/org/freedesktop/portal/desktop" +#define SCALE_FACTOR_INTERFACE "org.freedesktop.portal.Settings" +#define SCALE_FACTOR_NAMESPACE "org.gnome.desktop.interface" +#define SCALE_FACTOR_SIGNAL_NAME "SettingChanged" +#define SCALE_FACTOR_KEY "text-scaling-factor" + static DBusMessage *ReadDBusSetting(SDL_DBusContext *dbus, const char *key) { - static const char *iface = "org.gnome.desktop.interface"; + static const char *iface = SCALE_FACTOR_NAMESPACE; DBusMessage *reply = NULL; - DBusMessage *msg = dbus->message_new_method_call("org.freedesktop.portal.Desktop", /* Node */ - "/org/freedesktop/portal/desktop", /* Path */ - "org.freedesktop.portal.Settings", /* Interface */ - "Read"); /* Method */ + DBusMessage *msg = dbus->message_new_method_call(SCALE_FACTOR_NODE, + SCALE_FACTOR_PATH, + SCALE_FACTOR_INTERFACE, + "Read"); /* Method */ if (msg) { if (dbus->message_append_args(msg, DBUS_TYPE_STRING, &iface, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) { @@ -85,11 +92,75 @@ static SDL_bool ParseDBusReply(SDL_DBusContext *dbus, DBusMessage *reply, int ty return SDL_TRUE; } +static void UpdateDisplayContentScale(float scale) +{ + SDL_VideoDevice *viddevice = SDL_GetVideoDevice(); + int i; + + if (viddevice) { + for (i = 0; i < viddevice->num_displays; ++i) { + SDL_SetDisplayContentScale(&viddevice->displays[i], scale); + } + } +} + +static DBusHandlerResult DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data) +{ + SDL_DBusContext *dbus = SDL_DBus_GetContext(); + double *scale_factor = (double *)data; + double new_scale = 0.0; + + if (dbus->message_is_signal(msg, SCALE_FACTOR_INTERFACE, SCALE_FACTOR_SIGNAL_NAME)) { + DBusMessageIter signal_iter, variant_iter; + const char *namespace, *key; + + dbus->message_iter_init(msg, &signal_iter); + /* Check if the parameters are what we expect */ + if (dbus->message_iter_get_arg_type(&signal_iter) != DBUS_TYPE_STRING) { + goto not_our_signal; + } + dbus->message_iter_get_basic(&signal_iter, &namespace); + if (SDL_strcmp(SCALE_FACTOR_NAMESPACE, namespace) != 0) { + goto not_our_signal; + } + if (!dbus->message_iter_next(&signal_iter)) { + goto not_our_signal; + } + if (dbus->message_iter_get_arg_type(&signal_iter) != DBUS_TYPE_STRING) { + goto not_our_signal; + } + dbus->message_iter_get_basic(&signal_iter, &key); + if (SDL_strcmp(SCALE_FACTOR_KEY, key) != 0) { + goto not_our_signal; + } + if (!dbus->message_iter_next(&signal_iter)) { + goto not_our_signal; + } + if (dbus->message_iter_get_arg_type(&signal_iter) != DBUS_TYPE_VARIANT) { + goto not_our_signal; + } + dbus->message_iter_recurse(&signal_iter, &variant_iter); + if (dbus->message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_DOUBLE) { + goto not_our_signal; + } + dbus->message_iter_get_basic(&variant_iter, &new_scale); + + if (new_scale > 0.0) { + *scale_factor = new_scale; + UpdateDisplayContentScale(new_scale); + } + + return DBUS_HANDLER_RESULT_HANDLED; + } + +not_our_signal: + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + #endif static float GetGlobalContentScale() { - static const char *text_scaling_factor = "text-scaling-factor"; static double scale_factor = 0.0; if (scale_factor <= 0.0) { @@ -99,8 +170,16 @@ static float GetGlobalContentScale() SDL_DBusContext *dbus = SDL_DBus_GetContext(); if (dbus) { - if ((reply = ReadDBusSetting(dbus, text_scaling_factor))) { - ParseDBusReply(dbus, reply, DBUS_TYPE_DOUBLE, &scale_factor); + if ((reply = ReadDBusSetting(dbus, SCALE_FACTOR_KEY))) { + if (ParseDBusReply(dbus, reply, DBUS_TYPE_DOUBLE, &scale_factor)) { + /* If the setting exists, register a listener for scale changes. */ + dbus->bus_add_match(dbus->session_conn, + "type='signal', interface='"SCALE_FACTOR_INTERFACE"'," + "member='"SCALE_FACTOR_SIGNAL_NAME"', arg0='"SCALE_FACTOR_NAMESPACE"'," + "arg1='"SCALE_FACTOR_KEY"'", NULL); + dbus->connection_add_filter(dbus->session_conn, &DBus_MessageFilter, &scale_factor, NULL); + dbus->connection_flush(dbus->session_conn); + } dbus->message_unref(reply); } }