Skip to content

X server is not starting with correct DPI #3473

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
vscfreire opened this issue Mar 24, 2025 · 6 comments
Open

X server is not starting with correct DPI #3473

vscfreire opened this issue Mar 24, 2025 · 6 comments
Labels

Comments

@vscfreire
Copy link

xrdp version

0.10.2

Detailed xrdp version, build options

Operating system & version

Rocky Linux 9.5

Installation method

dnf / apt / zypper / pkg / etc

Which backend do you use?

Xorgxrdp

What desktop environment do you use?

KDE

Environment xrdp running on

VM

What's your client?

mstsc.exe (Windows 11)

Area(s) with issue?

Other

Steps to reproduce

Connect to xrdp with the scaling factor set to 150%.

✔️ Expected Behavior

xrdp's display should have the same DPI as the display.

❌ Actual Behavior

xrdp's display defaults to 96 DPI.

Anything else?

Hi,

I'm trying to use xrdp on a HiDPI display with a 3840x2160 resolution and a 150% scaling factor (the recommended scaling factor). The desktop environment I am running isn't automatically adjusting to the display's scaling factor, meaning everything looks too small. This problem is very similar to the problem described in issue #3200, where the proposed solution is changing the DPI after xrdp has established the session and the display information has been passed to xrandr. However, I find this solution hard to work with for two reasons:

Firstly, it does not seem to account for different scaling factors, i.e., possible different DPIs. Indeed, the issue suggests using the resolution and physical size of the monitor passed to xrandr to calculate a "proper" DPI for the display, similarly to what is done in https://github.com/neutrinolabs/xrdp/wiki/Scaleable-login-screen, which might not correspond to the current DPI of the display. For example, starting a session with the scaling factor set to 150% outputs in the logs:

Login screen monitor height is 2160 pixels over 336 mm (163 DPI)

While starting a session with the scaling factor set to 200% also outputs in the logs:

Login screen monitor height is 2160 pixels over 336 mm (163 DPI)

What this indicates is that there is a disconnect between the DPI set by myself and this "proper" DPI that can be calculated through the sent resolution and physical size. This seems odd, given the fact that the RDP protocol communicates this desktop scale factor in the initial capabilities exchange https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/db6713ee-1c0e-4064-a3b3-0fac30b4037b, more specifically, in ClientCoreData https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/00f1da4a-ee9c-421a-852f-c19f92343d73.

Secondly, one has to change the DPI after the session has started, again, this is strange since the desktop scale factor is sent by the client. Is it not possible to launch the X server with the DPI defined by the received desktop scale factor? For example, if the received desktop scale factor is 150% why can't the X server be configured to start with a DPI of 144 (96 * 1.5)?

Thanks.

@vscfreire vscfreire added the bug label Mar 24, 2025
@matt335672
Copy link
Member

Hi @vscfreire

As usual from you, these are all good questions/points.

The current state of affairs is that we have a simple scaling solution for the login screen, and nothing yet for the desktop. I'll take these separately, as I'm familiar with what I did for the login screen.

Login screen

The code we use for the login screen is pixel-based. Consequently, the login screen scaling is not at all proportional. We just use a bigger font if we're looking at a finer screen.

You're quite right in that the core data contains a desktop scale factor setting. That may well be a better choice to use than the physical dimensions for the login screen - the physical dimensions are only sent over if the client is full-screen and it's possible the desktop scale factor may work where the physical dimensions don't. I'll have a look at that.

Desktop

This is an area I'm not that familiar with, so please correct any assumptions I'm making here.

Having looked into it, we could set the DPI when the X server starts, but we also need to set it via XRANDR when the client connects. There are two reasons for this:-

  1. A session might be started using xrdp-sesrun where no client information is available.
  2. A user might (for example) be using a higher resolution monitor at home, and a lower resolution monitor at work. We need to support the user using the session between these two locations. This is a similar use-case to unplugging one monitor and plugging in another one.

@jsorg71 - I'd very much appreciate your thoughts on this conversation. You've got a much better idea that I have what is and isn't possible in this area.

@matt335672
Copy link
Member

@vscfreire - as a workaround for now you could try passing -dpi 150 to the X server manually in /etc/xrdp/sesman.ini. I've not idea what would happen, but it might be interesting to find out!

@vscfreire
Copy link
Author

Hi,

Thank you for the quick response.

@vscfreire - as a workaround for now you could try passing -dpi 150 to the X server manually in /etc/xrdp/sesman.ini. I've not idea what would happen, but it might be interesting to find out!

Unfortunately that DPI only seems to be applied in some cases and never "fully". If KDE's scale factor is set to 100% then that DPI is applied to the desktop and taskbar but it isn't applied to some apps such as firefox. If KDE's scale factor is different from 100% then that DPI is never applied.

For instance, passing -dpi 144 to sesman.ini with the KDE global scaling factor to 100% (default setting) and running xdpyinfo | grep dots outputs:

resolution: 144x144 dots per inch

And running xrdb -query | grep Xtf.dpi doesn't output anything. The desktop is indeed scaled to this DPI, however, as I said, some apps don't respect this scaling.

Afterwards, if I set the KDE to 300% and start a new session for the same user xdpyinfo | grep dots outputs:

resolution: 144x144 dots per inch

And xrdb -query | grep Xft.dpi outputs:

Xft.dpi: 288

In this case, the session doesn't respect the dpi set in sesman at all.

I'm guessing that the dpi also needs to passed down to xsettings either before startup or after startup. I'm currently trying to create a script to check if this is even possible.

Having looked into it, we could set the DPI when the X server starts, but we also need to set it via XRANDR when the client connects.

With my current environment the command xrandr --dpi 120 throws the following error:

X Error of failed request:  BadMatch (invalid parameter attributes)
  Major opcode of failed request:  140 (RANDR)
  Minor opcode of failed request:  7 (RRSetScreenSize)
  Serial number of failed request:  21
  Current serial number in output stream:  23

This might be a limitation of my virtualized environment but I am guessing others will have the same problem. Consequently, I don't know if it is possible change it via XRANDR directly.

This might be achievable by changing the code in xorgxrdp to also use the desktop scale factor, similarly to what was done previously for physical size.

A session might be started using xrdp-sesrun where no client information is available.

I am not really familiar with the use case for this executable so I am unable to give proper informed feedback on this. However, intuitively, if there is no client information I think it's ok for the session to use the default DPI.

Another problem arises in a multi-monitor setup since the client might send two different scaling factors and the environment can really only use one these values, meaning xrdp would have to decide between these two values. I would love to hear your thoughts on this problem.

Thanks.

@matt335672
Copy link
Member

Thanks for the info.

On a physical machine, the xrandr command works for me:-

$ xdpyinfo | grep resolution
  resolution:    96x96 dots per inch
$ xrdb -query | grep dpi
$ xrandr --dpi 120
$ xdpyinfo | grep resolution
  resolution:    120x120 dots per inch
$ xrdb -query | grep dpi
$ xrandr --dpi 96
$ xdpyinfo | grep resolution
  resolution:    96x96 dots per inch

No difference in existing applications is observed however - there's no dynamic reconfiguration. Looking at the manpage for xrandr it looks like this is a legacy setting anyway.

From this (scant) evidence, it looks like reconnecting at a different pixel density isn't going to be honoured.

Looking again at [MS-RDPBCGR], I also found these statements in "Appendix A: Product Behavior":-

<7> Section 2.2.1.3.2: The deviceScaleFactor field is processed only in Windows 8.1.

and

<14> Section 2.2.1.3.10.1: The deviceScaleFactor field is processed only in Windows 8.1.

so there's possibly not much we can do with this value.

Whether we can do something with Glamor to implement some sort of hardware scaling in xorgxrdp is outside my knowledge base. So at the moment I'm out of ideas.

@vscfreire
Copy link
Author

Hi,

I've some follow up regarding this discussion.

First of all, I would like to start by pointing out that the value that you mentioned above is the deviceScaleFactor and we are interested in the desktopScaleFactor. It is true, however, that these values are related to each other: "If the deviceScaleFactor field is not present, this field MUST be ignored" (see desktopScaleFactor in https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/00f1da4a-ee9c-421a-852f-c19f92343d73). It is unclear to me what they actually mean by "processed" in the paragraph you appended since I managed to get scaling on a Windows 10 machine.

With this in mind, I thought it best to experiment with multiple clients to see if they are indeed sending a desktop scale factor. Looking at the source code, more specifically at xrdp_sec_process_mcs_data_CS_CORE, it seems that xrdp isn't reading the desktop scale factor field sent in the initial capabilities exchange, so I had to modify the code to read this value (it also isn't reading the deviceScaleFactor optional field). I came up with the following observations:

  • Microsoft Client - the client never sends a desktop scale factor. When I try to read TS_UD_CS_CORE's desktopScaleFactor portion its value is always 0. This is especially odd given that desktop scaling does occur when connecting in fullscreen mode to Windows servers, meaning it might deserve some further investigation.

  • FreeRDP Client - the client always sends a desktop scale factor with a value of 100 (the default value of 100%).

  • Remmina Client - the client sends a desktop scale factor defined in the its preferences.

Consequently, the sent desktop scale factor is client-dependent. Despite this, I would like to ask why these values aren't being read into memory, is it because they aren't being used?

Furthermore, as I mentioned in the previous comment, I’ve been working on an executable that adjusts the desktop's scale factor based on the value received by xrdp. So far, I’ve managed to build a working version of this for KDE using Remmina and it works somewhat as follows:

  1. XRDP receives the desktop scale factor (in the initial capabilities exchange or in DISPLAYCONTROL_MONITOR_LAYOUT_PDU).
  2. XRDP communicates to the executable this desktop scale factor.
  3. The executable adjusts KDE to this scaling factor.

With this I managed to get a session whose scaling is adjusted to the sent scaling factor.

I am aware that changing scaling factor / dpi is highly dependent on the chosen graphical environment, as https://wiki.archlinux.org/title/HiDPI#Multiple_displays outlines. Therefore, it is unclear to me what changes can be made to xrdp besides saving this value into memory and communicating it to the other proper modules (such as xorgxrdp).

Additionally, one thing I also noticed is that the monitor count seems to be off sometimes in single-monitor sessions in full screen. For instance, in the xrdp.log, I have:

[2025-04-07T17:20:50.459+0100] [INFO ] xrdp_egfx_reset_graphics: width 3840 height 2160 monitorcount 1
[2025-04-07T17:20:50.460+0100] [DEBUG] xrdp_egfx_send_reset_graphics: xrdp_egfx_send_s error 0
[2025-04-07T17:20:50.461+0100] [INFO ] xrdp_mm_egfx_caps_advertise: xrdp_egfx_send_reset_graphics error 0 monitorCount 0

And in xorgxrdp's log I get:

[162612.975]   client can not do multimon
[162612.975] rdpRRSetRdpOutputs: numCrtcs 1 numOutputs 1 monitorCount 0

With all this being said, I guess what I would like to know from your side is what can be done regarding sent desktop scale factors, and if it is within the scope of xrdp to support handling of these values. Specifically, would it make sense for xrdp to start reading and storing the desktopScaleFactor and deviceScaleFactor fields when present, and then communicate them to modules like xorgxrdp to provide the session with better scaling awareness?

Thanks.

@matt335672
Copy link
Member

Thanks for the investigation, and the thought-provoking post.

...I would like to ask why these values aren't being read into memory, is it because they aren't being used?

That's exactly right. However, it's not all that simple. With multi-monitor, the DISPLAYCONTROL_MONITOR_LAYOUT_PDU is read and passed through to xorgxrdp. The PR is neutrinolabs/xorgxrdp#337 which shipped in xorgxrdp v0.10.3

If you've got that version (or later) of xorgxrdp, in a full-screen session you should see that the physical monitor sizes are readable from the output of xrandr.

The monitor count of 0 or 1 is indeed confusing. The original specification did not include multi-monitor support at all, and this codepath is still present with a monitor count of 0. Later developments (like GFX) always report a monitor count of 1. So, depending on the message you are looking at, you may see a monitor count or 0 or 1 for a single monitor. Messages related to configurations with more than one monitor should always be correct.

Can you investigate the output of xrandr and see if it can be used to meet your use-case? There's more that could be done in communicating other values, but given the differences in client behaviour, it may be difficult to do more than report the values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants