如何在 X11 中获取系统比例因子

How to get system scale factor in X11

我想让我的应用程序(纯 X11)支持高 DPI。为此,我需要一种方法来找出在显示设置中配置的系统比例因子。有什么方法可以从 X11 应用程序获取此系统比例因子,而无需诉诸 GTK 等更高级别的 API?

FWIW,我检查了 GTK 源代码以了解 gdk_window_get_scale_factor() 是如何做到的,它似乎读取了一个名为 GDK_SCALE 的环境变量。然而,这个环境变量在我的系统上根本不存在,即使我在我的 4K 显示器上将缩放比例设置为 1.75。

那么如何以编程方式检索系统比例因子?

为了回答我自己的问题,我现在尝试了三种方法:

  1. XRandR
  2. X11 的 DisplayWidth/HeightDisplayWidthMM/HeightMM
  3. 正在查看 xdpyinfo 输出

returns DPI 都不正确。相反,Xft.dpi Xresource 似乎是这个问题的关键。 Xft.dpi 似乎总是带有正确的 DPI,因此我们只需读取它即可获得系统比例因子。

这里有一些来源来自 here:

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xresource.h>

double _glfwPlatformGetMonitorDPI(_GLFWmonitor* monitor)
{
    char *resourceString = XResourceManagerString(_glfw.x11.display);
    XrmDatabase db;
    XrmValue value;
    char *type = NULL;
    double dpi = 0.0;

    XrmInitialize(); /* Need to initialize the DB before calling Xrm* functions */

    db = XrmGetStringDatabase(resourceString);

    if (resourceString) {
        printf("Entire DB:\n%s\n", resourceString);
        if (XrmGetResource(db, "Xft.dpi", "String", &type, &value) == True) {
            if (value.addr) {
                dpi = atof(value.addr);
            }
        }
    }

    printf("DPI: %f\n", dpi);
    return dpi;
}

这对我有用。

我不确定关于 Gtk 的回答是否有帮助,这个问题被标记为 gtk 并且作者似乎使用它。使用 GNOME,我可以在 Wayland 上为每个监视器应用不同的比例因子,X11 对所有监视器使用一个比例因子。以下代码报告比例因子:

#include <gtk/gtk.h>
#include <stdio.h>

static void activate(GtkApplication *app, gpointer user_data) {
    GtkWidget *window;
    window = gtk_application_window_new(app); // creates GtkApplicationWindow, returns a GtkWidget*
    gtk_window_set_title(GTK_WINDOW (window), "Window"); // macro, checks types and does function style cast
    gtk_window_set_default_size(GTK_WINDOW (window), 200, 200);
    gtk_widget_show_all(window);
    
    GdkWindow* gdk_window = gtk_widget_get_window(window);
    GdkDisplay* gdk_display = gdk_display_get_default();

    // prints sadly always '1' on my system
    GdkMonitor* gdk_monitor = gdk_display_get_monitor_at_window(gdk_display, gdk_window);
    int scale = gdk_monitor_get_scale_factor(gdk_monitor);
    printf("scale is %d\n", scale);

    // seems to work fine
    int n = gdk_display_get_n_monitors(gdk_display);
    for (int i = 0; i < n; ++i) {
        GdkMonitor* monitor = gdk_display_get_monitor(gdk_display, i);
        int scale = gdk_monitor_get_scale_factor(monitor);
        printf("monitor %d, scale %d\n", i, scale);
    }
}

int main(int argc, char **argv) {
    GtkApplication *app;
    int status;

    app = gtk_application_new("org.gtk.example", G_APPLICATION_FLAGS_NONE);
    g_signal_connect(app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run(G_APPLICATION (app), argc, argv);
    g_object_unref(app); // free memory of GtkApplication

    return status;
}

编译

gcc `pkg-config --cflags gtk+-3.0` -o gtk_scale gtk_scale.c `pkg-config --libs gtk+-3.0`

当我 运行 Wayland 上的应用程序时,左监视器(主要)设置为比例因子 1,右监视器设置为比例因子 2:

./gtk_scale 
scale is 1         # wrong, application windows resides on right screen with scale factor 2
monitor 0, scale 2 # correct
monitor 1, scale 1 # correct

更方便的功能gdk_display_get_monitor_at_window()似乎总是return主监视器的比例因子而不是应用程序使用的实际比例因子window。

Gets the monitor in which the largest area of window resides, or a monitor close to window if it is outside of all monitors.

因此我目前推荐 gdk_monitor_get_scale_factor(),它提供了正确的结果。如果连接了多个显示器 - 就像在我的示例中一样 - 您现在需要弄清楚您的应用程序 window 显示在哪个显示器上。

Gets the internal scale factor that maps from monitor coordinates to the actual device pixels. On traditional systems this is 1, but on very high density outputs this can be a higher value (often 2).

This can be used if you want to create pixel based data for a particular monitor, but most of the time you’re drawing to a window where it is better to use gdk_window_get_scale_factor() instead.

提到的另一个函数是 Andreas 最初研究过的函数,对我来说它 return 总是 2。