HiDPI on dual 4K monitors with Linux

Vincent Bernat

I have been using a Lenovo Thinkpad X1 Carbon laptop (210 DPI) for four years and a Nokia 8 phone (550 DPI) for a year. I enjoy their HiDPI screens: text is crisp and easy to read. To get a similar experience for my workstation, I bought a pair of Dell P2415Q monitors:

Two Dell P2415Q
Dual screen setup with two Dell P2415Q monitors


The Dell P2415Q is a 24” display featuring an IPS panel with a 3840×2160 resolution (185 DPI) and a complete coverage of the sRGB color space. It was released in 2015 and its price is now below $400. It received positive reviews.

One of my units arrived with a dead pixel. I thought it was a problem from the past but Dell policy on dead pixels says:

During LCD manufacturing process, it is not uncommon for one or more sub-pixels to become fixed in an unchanging state. A display with a 1 to 5 fixed sub-pixel is considered normal and within industry standards.

Update (2018.12)

Three new dead pixels have appeared. Therefore, I definitely do not recommend these screens.

Another issue is the presence of faint horizontal grey lines, (barely) visible on white background. The issue seems to not be uncommon but Dell is dismissive about it. If I sit correctly, the lines become invisible.

I am a bit disappointed by these issues, but I think they are bearable and it is an opportunity to help fix my obsession over such details.

Graphic card⚓︎

To drive a 4K display at 60 Hz, you need at least either HDMI 2.0 or DisplayPort 1.2. The Dell P2415Q has been upgraded to HDMI 2.0 in 2016 and also provides a DP 1.2 input. There is a port for daisy-chaining a second monitor but, without support for DP 1.3, the refresh rate would drop to 30 Hz.

There is currently no available Radeon card around $100 able to drive two 4K displays. On the Nvidia side, the GeForce GT 1030 fits the bill with a power consumption of 20 W for the GDDR4 version. I opted for a passively cooled model from MSI. On Linux, the result is quite disappointing:

Update (2019.04)

With version 410 and Linux 4.19, the performance is slightly improved but suspend-to-RAM is broken. Versions 415 and 148 didn’t bring much change.

In the future, I may replace this card with a Radeon one. While this may or may not improve performance,2 the amdgpu driver should be more stable and usable.

Update (2019.05)

I have switched to an ASUS Phoenix Radeon RX 550. With such a card, everything runs smoothly, using the open source driver amdgpu. The card comes with a fan but it is PWM-controlled. See ArchWiki for more details. In automatic mode, when /sys/class/drm/card0/device/hwmon/hwmon1/pwm1_enable is set to 2, it spins at 1050 RPM:

$ sensors amdgpu-pci-0100
Adapter: PCI adapter
vddgfx:       +0.88 V
fan1:        1047 RPM
temp1:        +51.0°C  (crit = +94.0°C, hyst = -273.1°C)
power1:       17.20 W  (cap =  35.00 W)

HiDPI support on Linux with X11⚓︎

Gnome and KDE now supports HiDPI displays out of the box when running with X11. For other environments, it’s quite easy to setup, thanks to xsettingsd. The following snippet should be invoked from ~/.xsession or when the DPI value changes:

# Target DPI

# For applications supporting XSettings, `Xft/DPI' sets font scaling
# (and sometimes interface scaling), `Gdk/WindowScalingFactor' sets
# interface scaling with GTK 3, and `Gdk/UnscaledDPI' undo font scaling
# for GTK 3 applications.
> ~/.xsettingsd cat <<EOF
Xft/DPI $(( $dpi*1024 ))
Gdk/WindowScalingFactor $(( $dpi/96 ))
Gdk/UnscaledDPI $(( $dpi*1024/($dpi/96) ))
pkill -HUP xsettingsd || xsettingsd &

# For QT applications.

# For miscellaneous applications.
echo Xft.dpi: $dpi | xrdb -merge

Then, it is up to each application to know how to render on a HiDPI display. The table below gives an overview of HiDPI support for various ones, using the above settings. “Text scaling” indicates if an application is able to adapt the font size. This is usually the most essential feature. “Interface scaling” tells if it is able to scale the whole interface, including the icons. Ideally, applications are also expected to be able to dynamically update scaling when notified through XSettings, which is useful when switching to an external display.

Application Text scaling Interface scaling No restart needed?
Most QT 5 apps ✔️ ✔️ ✔️
Chromium3 ✔️ ✔️ ✔️
Most Electron apps4 ✔️ ✔️ ✔️
Firefox ✔️ ✔️
Blender5 ✔️ ✔️
Emacs 26.1 (GTK 3) ✔️ 😐 ✔️
VTE terminals (GTK 3) ✔️ 😐 ✔️
Most GTK 3 apps ✔️ 😐 ✔️
Gimp (GTK 2) ✔️ ✔️
Inkscape (GTK 2) ✔️ ✔️
Most GTK 2 apps ✔️ ✔️
Most Java apps6 🙄 🙄
xterm and rxvt (with Xft) ✔️ n/a
Most other applications

QT 5 applications offer very good support. GTK 3 applications can currenly only scale their interfaces using an integer factor, which is annoying when you need an 1.5× factor. GTK 2 applications will only scale the text and there are still many of them—notably Gimp. For more details, have a look at the dedicated page on ArchWiki. Beyond X11, Wayland is able to provide per-monitor scaling and to scale applications without HiDPI support.

In conclusion, today’s situation still depends heavily on the applications you run. As I spend most of my time in a VTE terminal, in Emacs and in Firefox, I am mostly happy with the result.

  1. Without this setting, it’s impossible to play an HD or a 4K video in Firefox. This is a bit a pity we are still unable to get accelerated video rendering on Linux while other platforms have this feature since a long time. For Chromium, patches exist but are left unapplied. ↩︎

  2. Compared to my previous setup, the number of pixels is quadrupled. The card is using 4-lane links over PCI 3.0 which translates to a bandwidth of 25 Gbits/s. This barely allows the transfer of 7680×2160 pixels at 60 Hz using 24-bit colors. This may explain the difficulty to render unaccelerated 4K videos. ↩︎

  3. To detect changes, Chromium watches for RandR events. They may be caught before XSettings variables are updated. ↩︎

  4. If it doesn’t work, try with the --force-device-scale-factor=2 flag. ↩︎

  5. Blender relies on the Xft.dpi resource value. ↩︎

  6. Java applications need to be invoked with the GDK_SCALE=2 environment variable. Otherwise, they don’t scale. ↩︎

Share this article