JetBrains / JetBrains/JetBrainsRuntime

HiDPI scaling on Xwayland outside Gnome broken (for some multi-monitor geometries)

Open
#409 3 comments 4 reactions 1 assignee Claimed by @YaaZ View on GitHub
Dominant language
Java
Stars
2k
Forks
269
Avg merge
1h 18m
Merged PRs (30d)
1

Description

Environment: PHPStorm 2024.1.4 on JBR 17.0.11+1-b1207.24, amd64, Ubuntu 24.04, KDE 5.27.11.

Situation:
* Running JBR as an X11 application within Xwayland (i.e. not using the native Wayland support).
* One external regular display (2560x1440) and one internal hi-DPI display (3840x2400). The (Wayland) scale factor for the internal display is set to 2x, while the external display is unscaled.
* Xwayland is set to Hi-DPI, e.g. using KDE's "Legacy Applications (X11)" > "Apply scaling themselves" option in Display Configuration. This gives an `Xft.dpi` of 192. The Xwayland (Xinerama) screen for the external display will have double the native resolution (5120x2880), and will be downscaled by the Wayland compositor.
* Consider the situation where the external screen is configured to be above the internal screen, with the internal screen centered below it. The geometry in Wayland will be the external screen of 2560x1440 at (0,0) with scale = 1, and the internal screen of 1920x1200 at (320,1440) with scale = 2. In Xwayland, the geometry will be the external screen of 5120x2800 at (0, 0) and the internal screen of 3840x2400 at (640, 2880).

Symptoms:
* JBR applications will show fine on the internal screen, but be tiny on the external screen.
* Show HiDPI info on the internal screen will show:
```
Per-monitor DPI-aware : enabled
Monitor resolution : 1920x1200
Monitor scale : 2.0
User (IDE) scale : 1.0
Xft.DPI : 192
GSettings scale factor : 2.0
GDK_SCALE : undefined
GDK_DPI_SCALE : undefined
```
* Show HiDPI info on the external screen will show:
```
Per-monitor DPI-aware : enabled
Monitor resolution : 5120x2880
Monitor scale : 1.0
User (IDE) scale : 1.0
Xft.DPI : 192
GSettings scale factor : 1.0
GDK_SCALE : undefined
GDK_DPI_SCALE : undefined
```

Root cause:
* The "Monitor scale" for the external screen is incorrectly set to 1.0. In this situation, because the compositor downsamples, JBR should render at 2.0 scale. This is also what all other X11 applications do, JBR is the only application I've encountered showing this behaviour.
* The "Monitor scale" comes from [`X11GraphicsDevice.getNativeScaleFactor()`](https://github.com/JetBrains/JetBrainsRuntime/blob/223db10aa19f794abae1a61ae687727ce5b3caf4/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c#L2244). This method first checks the `waylandMonitorScales` array, and uses the value from there if it's not empty. This array is initialized on startup from [`X11GraphicsEnvironment.updateWaylandMonitorScaling()`](https://github.com/JetBrains/JetBrainsRuntime/blob/223db10aa19f794abae1a61ae687727ce5b3caf4/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c#L793). It queries both the X server (XWayland) and the Wayland compositor for display information, [matches](https://github.com/JetBrains/JetBrainsRuntime/blob/223db10aa19f794abae1a61ae687727ce5b3caf4/src/java.desktop/unix/native/libawt_xawt/awt/awt_GraphicsEnv.c#L740) them based on geometry on the Wayland and X side and, if they match, uses the scale factor from Wayland.
* Bonus: because in this situation the Wayland and X displays have a different geometry, that matching is completely broken, but that actually saves us in certain scenarios where it can't find a match. For example, if the internal screen is put on top of the external screens, the screen positions on the Wayland side are now (0, 1200) and (320, 0) for respectively the external and internal screen, while on the Xwayland side they'll be (0, 2400) and (640, 0). In this case there'll be no match between the geometries for any screens, the Wayland scale factor will be ignored, and everything will work fine.

Solution:
* To be honest I don't see any reason why JBR would need to take the scale factor of the displays in Wayland into account when running as an X application. Just use the settings from the X environment (i.e. `Xft.dpi`) and everything should work fine.
* If that's not possible for whatever reason, the [`isMonitorFramebufferScalingEnabled()`](https://github.com/JetBrains/JetBrainsRuntime/blob/223db10aa19f794abae1a61ae687727ce5b3caf4/src/java.desktop/unix/native/common/awt/systemscale/systemScale.c#L149) check should be updated to be generic instead of GNOME-specific.

Workaround:
* Don't let JBR talk to the Wayland compositor, for example by putting a fake `libwayland-client.so.0` on the library path:
```
touch empty.c
gcc -shared -fpic -o libwayland-client.so.0 empty.c
export LD_LIBRARY_PATH=$(pwd)
phpstorm
```
* Alternatively, set the Mutter GSettings key that JBR checks for. This only works as long as the Mutter GSettings schema is installed (`mutter-common` package on Debian/Ubuntu):
```
gsettings set 'org.gnome.mutter' 'experimental-features' "['scale-monitor-framebuffer']"
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.