【问题标题】:How can I get updated system DPI information from X11 in a C program?如何在 C 程序中从 X11 获取更新的系统 DPI 信息?
【发布时间】:2019-01-26 09:36:34
【问题描述】:

我正在尝试创建一个 DPI 感知应用程序,该应用程序通过调整窗口大小来响应用户请求的 DPI 更改事件。

有问题的程序是用 C 语言创建的并使用 SDL2,但是为了检索系统 DPI 信息,我直接使用 xlib,因为 X11 中缺乏 SDL DPI 支持。

我在程序启动时找到了两种获取正确DPI信息的方法,都涉及从Xresource获取Xft.dpi信息:一种是使用XGetDefault(display, "Xft", "dpi"),另一种是使用XResourceManagerString、XrmGetStringDatabase 和 XrmGetResource。创建程序时,它们都返回正确的 DPI 值。

问题是,如果用户在程序运行时更改系统规模,即使当我运行“xrdb -query | grep Xft.dpi”时,XGetDefault abd XrmGetResource 仍然返回旧的 DPI 值,该值确实发生了变化.

有人知道获取更新后的 Xft.dpi 值的方法吗?

【问题讨论】:

    标签: c x11 sdl-2 highdpi


    【解决方案1】:

    我找到了一种方法来做我想做的事,尽管它相当老套。

    解决方案(使用XLib)是使用XOpenDisplayXCloseDisplay 创建到X 服务器的新临时连接,并从该新连接轮询资源信息。

    需要这样做的原因是因为 X 在每个新连接中只获取一次资源信息,并且从不更新它。因此,通过打开一个新连接,X 将获得更新后的xresource 数据,然后可以将其用于旧的主连接。

    请注意,不断打开和关闭新的 X 连接可能不会提高性能,因此请仅在绝对需要时才这样做。在我的例子中,由于窗口有边框,我只在标题高度发生变化时检查 DPI 变化,因为 DPI 变化会由于字体大小的不同而改变标题边框的大小。

    【讨论】:

    • 你是如何获得标题高度变化事件的?谢谢。
    • 已经有一段时间了,所以我不太记得了,但它是在窗口移动或窗口调整大小事件期间。 DPI 更改触发窗口调整大小(或移动...?)事件并更改标题栏高度。因此,通过将旧标题栏高度与新标题栏高度进行比较,我可以判断移动/调整大小是由于用户移动/调整窗口大小还是可能的 DPI 变化。
    【解决方案2】:

    您可以尝试使用 xdpyinfo(1);在我的系统上,它会输出很多其他内容:

      dimensions:    1280x1024 pixels (332x250 millimeters)
      resolution:    98x104 dots per inch
      depths (7):    24, 1, 4, 8, 15, 16, 32
    

    我不知道它是否可以帮助您,因为我不知道您如何更改屏幕的 DPI,但它可能会起作用。祝你好运!

    --- 评论后更新 --- 在OP下面的评论中,据说“有一个更改DPI的设置”......我仍然不知道是哪个。无论如何,我尝试使用 Ctrl+Alt+Plus 和 Ctrl+Alt+Minus 来即时更改 X 服务器的分辨率。在更改了分辨率并看到比以前更大的所有内容后,我再次运行了 xdpyinfo。它不起作用:仍然是相同的输出。但可能是您使用的方法(哪个?),而不是工作......

    【讨论】:

    • 在 Ubuntu 和 Linux Mint(我尝试过的唯一发行版)上,您可以更改设置以将窗口缩放为 1x、2x 或 3x。那些更新 Xfi.dpi。感谢您的建议,但获取显示器尺寸并不理想,因为无论显示器的物理 DPI 如何,用户都可以选择不同的比例。
    • @JoséCadete 查看更新后的答案。无论如何,你可以试试,不是吗?
    • 在 Ubuntu 中,要更改 DPI,请转到 Preferences > Devices > Display > Scale。我认为该选项仅出现在具有某些分辨率的显示器中,无论如何它是以下链接中图像中显示的设置:reddit.com/r/linux/comments/8bt4eg/…
    • @JoséCadete 我更裸机......但那些 200% 300% 的缩放可能完全是桌面管理器,而不是“裸机”X11。我的意思是,尤其是 Ubuntu,许多桌面管理器试图隐藏系统的细节,这对用户来说可能是件好事,但对开发人员来说却是一件痛苦的事情。所以我不知道...
    • 我猜你是对的,而且 Wayland 使用了完全不同的方法。我想我会等到事情变得更加一致/SDL 改进了 DPI 支持...
    【解决方案3】:

    首先必须注意Xft.dpi 资源的值不一定准确——这取决于系统和/或用户登录脚本是否正确设置。

    同样重要的是要记住,Xft.dpi 资源旨在供 Xft 库使用,而不是供寻找屏幕分辨率的任意程序使用。

    Xft.dpi 资源可以如下设置。此示例有效地仅处理具有单个屏幕的显示,并注意它使用xdpyinfo。这也表明它可能不精确,但可以四舍五入。最后这个例子显示了水平和垂直分辨率的计算,但是 Xft 真的只想要水平分辨率:

    SCREENDPI=$(xdpyinfo | sed -n 's/^[ ]*resolution:[ ]*\([^ ][^ ]*\) .*$/\1/p;//q')
    SCREENDPI_X=$(expr "$SCREENDPI" : '\([0-9]*\)x')
    SCREENDPI_Y=$(expr "$SCREENDPI" : '[0-9]*x\([0-9]*\)')
    # N.B.:  If true screen resolution is within 10% of 100DPI it makes the most
    # sense to claim 100DPI to avoid font-scaling artifacts for bitmap fonts.
    if expr \( $SCREENDPI_X / 100 = 1 \) \& \( $SCREENDPI_X % 100 \<= 10 \) >/dev/null; then
        FontXDPI=100
    fi
    if expr \( $SCREENDPI_Y / 100 = 1 \) \& \( $SCREENDPI_Y % 100 \<= 10 \) >/dev/null; then
        FontYDPI=100
    fi
    echo "Xft.dpi: ${FontYDPI}" | xrdb -merge
    

    我真希望我知道为什么 Xft 至少不尝试找出屏幕的分辨率本身,而不是一直依赖设置的“dpi”资源,但我已经发现当前的实现只使用资源设置,所以实际上总是需要像上面那样正确设置资源(而且还必须确保 X 服务器本身已经正确配置了正确的物理屏幕尺寸)。

    在 C 程序中,您只想做 xdpyinfo 本身所做的事情,并跳过所有关于 Xft 资源的废话。这是xdpyinfo 代码释义:

    Display *dpy;
    dpy = XOpenDisplay(displayname);
    for (scr = 0; scr < ScreenCount(dpy); scr++) {
        int xres, yres;
    
        /*
         * there are 2.54 centimeters to an inch; so there are 25.4 millimeters.
         *
         *     dpi = N pixels / (M millimeters / (25.4 millimeters / 1 inch))
         *         = N pixels / (M inch / 25.4)
         *         = N * 25.4 pixels / M inch
         */
        xres = ((((double) DisplayWidth(dpy, scr)) * 25.4) /
            ((double) DisplayWidthMM(dpy, scr))) + 0.5;
        yres = ((((double) DisplayHeight(dpy, scr)) * 25.4) /
            ((double) DisplayHeightMM(dpy, scr))) + 0.5;
    }
    XCloseDisplay(dpy);
    

    还请注意,如果您出于某种奇怪的原因缩放整个显示器(例如使用xrandr),那么您应该希望字体与其他所有字体一样缩放。使用全屏缩放来缩放字体只是一个可怕的坏黑客,特别是对于大多数事情来说,告诉应用程序使用正确缩放的字体会更简单,这些字体将以恒定的屏幕点大小显示(这正是Xft 使用“dpi”资源来做什么)。我猜 Ubuntu 做了一些愚蠢的事情来改变屏幕分辨率,例如使用xrandr 来放大图标和其他屏幕小部件的外观尺寸,而无需应用程序知道屏幕尺寸和分辨率,那么它必须通过重写Xft.dpi 资源来欺骗Xft .

    请注意,如果您避免全屏缩放,那么不使用 Xft 的应用程序仍然可以通过正确请求正确缩放的字体来获得正确的字体缩放,即即使使用位图字体,您也可以获得它们通过在字体规范中使用屏幕的实际分辨率缩放到适当的物理屏幕尺寸。例如。从上面的shell片段继续:

    # For pre-Xft applications we can specify physical font text sizes IFF we also tell
    # it the screen's actual resolution when requesting a font.  Note the use of the
    # rounded values here.
    #
    DecentDeciPt="80"
    DecentPt="8"
    export DecentDeciPt DecentPt
    #
    # Best is to arrange one's font-path to get the desired one first, but....
    # If you know the name of a font family that you like and you can be sure
    # it is installed and in the font-path somewhere....
    #
    DefaultFontSpec='-*-liberation mono-medium-r-*-*-*-${DecentDeciPt}-${FontXDPI}-${FontYDPI}-m-*-iso10646-1'
    export DefaultFontSpec
    #
    # For Xft we have set the Xft.dpi resource so this allows the physical font size to
    # be specified (e.g. with Xterm's "-fs" option) and for a decent scalable font
    # to be chosen:
    #
    DefaultFTFontSpec="-*-*-medium-r-*-*-*-*-0-0-m-*-iso10646-1"
    DefaultFTFontSpecL1="-*-*-medium-r-*-*-*-*-0-0-m-*-iso8859-1"
    export DefaultFTFontSpec DefaultFTFontSpecL1
    
    # Set a default font that should work for everything
    #
    eval echo "*font: ${DefaultFontSpec}" | xrdb -merge
    

    最后,这是一个使用上述设置(即Xft.dpi 资源和上面的 shell 变量)启动 xterm(已编译为使用 Xft)以在物理位置显示文本的示例屏幕上 10.0 点的大小:

    xterm -fs 10 -fa $DefaultFTFontSpec
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-12
      • 1970-01-01
      • 2010-10-01
      • 2023-03-04
      • 1970-01-01
      • 2010-09-25
      • 2016-03-12
      • 2011-06-02
      相关资源
      最近更新 更多