Browse Source

x11: Expose the text/global scaling factor as the display content scale

Expose the text scaling factor (aka the global scale factor on KDE) as the display content scale value so applications can scale themselves as necessary.

If D-Bus is unavailable or retrieving the setting fails, fall back to trying the legacy GDK_SCALE envvar before reverting to the default 1.0 value.
Frank Praznik 1 year ago
parent
commit
32ab1183c7
1 changed files with 87 additions and 0 deletions
  1. 87 0
      src/video/x11/SDL_x11modes.c

+ 87 - 0
src/video/x11/SDL_x11modes.c

@@ -39,6 +39,91 @@
  */
 /* #define XRANDR_DISABLED_BY_DEFAULT */
 
+#ifdef SDL_USE_LIBDBUS
+
+static DBusMessage *ReadDBusSetting(SDL_DBusContext *dbus, const char *key)
+{
+    static const char *iface = "org.gnome.desktop.interface";
+
+    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 */
+
+    if (msg) {
+        if (dbus->message_append_args(msg, DBUS_TYPE_STRING, &iface, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) {
+            reply = dbus->connection_send_with_reply_and_block(dbus->session_conn, msg, DBUS_TIMEOUT_USE_DEFAULT, NULL);
+        }
+        dbus->message_unref(msg);
+    }
+
+    return reply;
+}
+
+static SDL_bool ParseDBusReply(SDL_DBusContext *dbus, DBusMessage *reply, int type, void *value)
+{
+    DBusMessageIter iter[3];
+
+    dbus->message_iter_init(reply, &iter[0]);
+    if (dbus->message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_VARIANT) {
+        return SDL_FALSE;
+    }
+
+    dbus->message_iter_recurse(&iter[0], &iter[1]);
+    if (dbus->message_iter_get_arg_type(&iter[1]) != DBUS_TYPE_VARIANT) {
+        return SDL_FALSE;
+    }
+
+    dbus->message_iter_recurse(&iter[1], &iter[2]);
+    if (dbus->message_iter_get_arg_type(&iter[2]) != type) {
+        return SDL_FALSE;
+    }
+
+    dbus->message_iter_get_basic(&iter[2], value);
+
+    return SDL_TRUE;
+}
+
+#endif
+
+static float GetGlobalContentScale()
+{
+    static const char *text_scaling_factor = "text-scaling-factor";
+    static double scale_factor = 0.0;
+
+    if (scale_factor <= 0.0) {
+        /* First try the settings portal via D-Bus for the text scaling factor (aka 'Global Scale' on KDE) */
+#ifdef SDL_USE_LIBDBUS
+        DBusMessage *reply;
+        SDL_DBusContext *dbus = SDL_DBus_GetContext();
+
+        if (dbus) {
+            if ((reply = ReadDBusSetting(dbus, text_scaling_factor))) {
+                ParseDBusReply(dbus, reply, DBUS_TYPE_DOUBLE, &scale_factor);
+                dbus->message_unref(reply);
+            }
+        }
+
+        /* If that failed, try the GDK_SCALE envvar... */
+        if (scale_factor <= 0.0)
+#endif
+        {
+            const char *scale_str = SDL_getenv("GDK_SCALE");
+            if (scale_str) {
+                scale_factor = SDL_atoi(scale_str);
+            }
+        }
+
+        /* Nothing or a bad value, just fall back to 1.0 */
+        if (scale_factor <= 0.0) {
+            scale_factor = 1.0;
+        }
+    }
+
+    return scale_factor;
+}
+
 static int get_visualinfo(Display *display, int screen, XVisualInfo *vinfo)
 {
     const char *visual_id = SDL_getenv("SDL_VIDEO_X11_VISUALID");
@@ -384,6 +469,7 @@ static int X11_AddXRandRDisplay(SDL_VideoDevice *_this, Display *dpy, int screen
         display.name = display_name;
     }
     display.desktop_mode = mode;
+    display.content_scale = GetGlobalContentScale();
     display.driverdata = displaydata;
     if (SDL_AddVideoDisplay(&display, send_event) == 0) {
         return -1;
@@ -591,6 +677,7 @@ static int X11_InitModes_StdXlib(SDL_VideoDevice *_this)
     SDL_zero(display);
     display.name = (char *)"Generic X11 Display"; /* this is just copied and thrown away, it's safe to cast to char* here. */
     display.desktop_mode = mode;
+    display.content_scale = GetGlobalContentScale();
     display.driverdata = displaydata;
     if (SDL_AddVideoDisplay(&display, SDL_TRUE) == 0) {
         return -1;