From d5f07730be1d2ac9a0045e0918ab226b2fffc3f2 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
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 687cb854983e..81cbd290d70e 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 2a90e0a21c04..8174c20aff1a 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 3f0077eddb7c..4af1b0e7bfeb 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 2510ecd3000b..8d521faaf6c5 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 845e49003177..eace98072cef 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 fb93446f84ea..8c2304971a9f 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 51865752c6e4..31f74e3c35d1 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);
}
}