The One and the Many

Configuring mixed DPI monitors with xrandr

I've recently started using a high DPI laptop together with a regular DPI monitor in a dual monitor setup. While there are GUI tools to configure dual monitors, I wanted to do it through the CLI. The usual way to configure monitor settings via the CLI in Linux is with a program called xrandr. As it took me a bit of trial and error to get everything working well, I thought it would be useful to write about the commands I settled on and how they work.

The setup

The laptop sits on the left of the monitor. The tops of both line up.

Usually I want to use both monitors at the same time. However I've found it necessary to disable one of them, usually the laptop, when I want to share my screen. This means it is important that I can easily switch back and forth between dual and single monitors.

It is also important that I retain the ability to use the laptop without the external monitor.

The tricky parts

There were two aspects I found problematic:

First, the fact that the two monitors have differing DPI means each monitor needs different settings to account for that. xrandr allows setting the DPI via --dpi, but this applies globally to all monitors. I dealt with this by setting the global DPI to that of the highest DPI monitor, and then used --scale to adjust the lower DPI monitor.

Second, switching back and forth between dual monitors to one monitor lead to interesting behaviour. For example, I kept losing the scale setting on the lower DPI monitor.

I had a semi-working setup with little trouble, but it involved restarting X when I wanted to switch between dual and single monitors. This worked, but obviously was inconvenient. I wanted a solution that would work without restarts.

The solution

I came up with two xrandr commands. One configures to use both monitors, and the other configures to use only the external monitor.

Dual monitors

When I want to use both monitors, this is the command I run:

xrandr --dpi 276 --fb 7040x3960 \
    --output eDP-1 --mode 3200x1800 \
    --output DP-1-2 --scale 2x2 --pos 3200x0 --panning 3840x2160+3200+0

Here's an explanation of the options:

Single monitor (external)

If I want to use only the lower DPI monitor, then I run this command:

xrandr --dpi 276 --fb 3840x2160 \
    --output eDP-1 --off \
    --output DP-1-2 --scale 2x2 --panning 3840x2160

Most of these options are similar to the first command's. The main difference is disabling the laptop monitor with --output eDP-1 --off.

Note 3840x2160 comes from 1920*2 x 1080*2.

You might wonder why I set high DPI and scale the lower DPI monitor here. Wouldn't it be simpler to set a lower DPI and use the monitor's native resolution? That's possible, but since I've configured other things (such as my X terminal fonts) to a size suitable for a high DPI screen, I'd have to adjust settings elsewhere. Keeping the DPI the same and continuing to scale allows me to avoid that. Since I'm scaling, I need to configure a large screen and set panning for the same reasons as before.

Single monitor (laptop)

This is the default state prior to running any xrandr commands. In theory I could have another command for this, but so far I haven't needed to switch to this.

A final note: --mode vs --auto

I initially tried using --output eDP-1 --auto when I wanted to enable the laptop monitor after disabling it. However, I found this lost the --scale setting, so I had to either restart X or run the same xrandr command twice. Using --mode does not have this problem.

Resources

I did a bunch of searching figuring out this setup. The two best resources I found were these:

Tags: Linux, cli, xrandr, monitors, laptop

Comments

Posted by Felipe at
I have the same problem and pretty much the same configuration. I own a dell xps 13.3' and the only difference is the low dpi monitor with a 2560x1080 resolution. I am using DisplayLink and still I couldn't manage to make both work nicely together. My low dpi is still displaying everything brutally large. Any help or thoughts would very welcome
Posted by Will Storey at
Yeah, it sounds like a very similar setup to mine. I'm using a Dell XPS 13 too actually! What command are you trying? I'd expect something like this to work: xrandr --dpi 276 --fb 8320x3960 \ --output eDP-1 --mode 3200x1800 # assuming xps13 is 3200x1800 on eDP-1 \ --output DP-1-2 --scale 2x2 --pos 3200x0 --panning 5120x2160+3200+0 # monitor Another thought I have is whether there's anything else running trying to magically correct settings for you. I run a very basic window manager so I know that nothing interferes, but I'm not sure whether other managers touch these things or not.
Posted by Felipe at
I use a pretty slim environment (only arch + i3), so im only running xrandr actually. Are you using your USB Type C or are you usings Dell's Displaylink too? I think it is something with the DisplayLink driver and the scale function not working nice togheter
Posted by Will Storey at
Ah ok. Yeah if that's the case then that might be it :-(. I'm using the monitor through a TB16 dock connected via USB-C. I believe the monitor is connected to the mini DisplayPort output on the dock, but I'm not sure (and can't check right now as I'm out of town).
Posted by tya99 at
I also use i3 on ArchLinux with a Dell XPS 13" 9370 (native 3840x2160) and I have a Dell 30" 3000WFP (native 2560x1600) monitor. I have DisplayPort with one of those Dell DA300 mobile adaptors, connects to the XPS via USB-C. I noticed the resolution you're using is 3200x1800. I found the native resolution for the XPS is 3840x2160. Is there any reason you chose to drop the resolution down one? I also noticed your Xft.dpi is 276. How does that play with scaling GTK and Qt? Ie: GDK_DPI_SCALE. This can be useful when using scale-aware GTK+ applications together with scale-unaware applications on a high-dpi display. In that case, the font resolution can be doubled to make scale-unaware applications readable, and GDK_DPI_SCALE=0.5 can be set to compensate for that in GTK+ applications which are already scaled by setting GDK_SCALE=2. https://developer.gnome.org/gtk3/stable/gtk-x11.html If you used an odd DPI what would you make GDK_DPI_SCALE? Currently I use a DPI of 220 I think the reason I made it that was because of GDK_DPI_SCALE ie https://wiki.archlinux.org/index.php/HiDPI#Qt_5 : /etc/profile.d/hidpi.sh #!/bin/sh export GDK_SCALE=2 export GDK_DPI_SCALE=0.5 export QT_AUTO_SCREEN_SCALE_FACTOR=0 export QT_SCREEN_SCALE_FACTORS=2 export QT_QPA_PLATFORMTHEME=qt5ct I saw http://dpi.lv and it says: for a 13.3" monitor at 3200x1800 276 is the correct DPI. The formula they use is [round((sqrt(w * w + h * h)/d))] If I put in 3840x2160 I would get a DPI of 331. How would that work with GDK_DPI_SCALE? I seem to remember that only working in multiples. -------------------------------------------------------------------------------------------------- I managed to get a single external monitor working with: xrandr --dpi 220 --fb 5120x3200 \ --output eDP1 --off \ --output DP1 --scale 2x2 --panning 5120x3200 I am struggling with the panning part for my dual monitor setup. I looked at https://wiki.archlinux.org/index.php/xrandr and the man file and still couldn't figure that track x track y part out. It's not very easy to understand. --panning widthxheight[+x+y[/track_widthxtrack_height+track_x+track_y[/border_left/border_top/border_right/border_bottom]]] This option sets the panning parameters. As soon as panning is enabled, the CRTC position can change with every pointer move. The first four parameters specify the total panning area, the next four the pointer tracking area (which defaults to the same area). The last four parameters specify the border and default to 0. A width or height set to zero disables panning on the according axis. You typically have to set the screen size with --fb simultaneously. My external monitor is on the left of my laptop. This is as far as I got: xrandr --dpi 220 --fb 8960x5360 \ --output eDP1 --mode 3840x2160 \ --output DP1 --scale 2x2 --pos -2560x0 --panning 5120x3200+2560+0
Posted by tya99 at
I asked about this on arch-general here: https://lists.archlinux.org/pipermail/arch-general/2018-July/045392.html https://lists.archlinux.org/pipermail/arch-general/2018-August/045395.html One of the posters said the panning thing may have been a work around for a bug in xorg, fixed in 1.20 and to try: https://wiki.archlinux.org/index.php/HiDPI#Side_display I have found that everything works correctly except for qt5-5.11.1 with this version of Xorg using: xrandr --output eDP1 --pos 5120x0 --auto \ --output DP1 --pos 0x0 --auto --scale 2x2 ! xft fonts !---------------------------------------------------------------------- Xft.dpi: 220 Xft.autohint: 0 Xft.lcdfilter: lcddefault Xft.hintstyle: hintfull Xft.hinting: 1 Xft.antialias: 1 Xft.rgba: rgb ! urxvt !--------------------------------------------------------------------- URxvt*font: xft:DejaVu Sans Mono for Powerline:size=12: \ minspace=False:antialias=true, \ xft:Segoe UI Emoji:size=12:minspace=False:antialias=true URxvt*boldFont: xft:DejaVu Sans Mono for Powerline:size=12: \ minspace=False:antialias=true, \ xft:Segoe UI Emoji:size=12:minspace=False:antialias=true URxvt.letterSpace: -1 export GDK_SCALE=2 export GDK_DPI_SCALE=0.5 export QT_AUTO_SCREEN_SCALE_FACTOR=0 export QT_SCREEN_SCALE_FACTORS=2 The problem with Qt still seems to be that things are scaled to the HiDPI screen and when moved to the LowDPI screen they are, really small. It was suggested that this could work as the Qt docs explain https://doc.qt.io/qt-5/highdpi.html QT_SCREEN_SCALE_FACTORS [list] specifies scale factors for each screen. This will not change the size of point sized fonts. This environment variable is mainly useful for debugging, or to work around monitors with wrong EDID information(Extended Display Identification Data). The format can be either a semicolon-separated list of scale factors in the same order as QGuiApplication::screens(), or a semicolon-separated list of name=value pairs, where name is the same as QScreen::name(). So I tried using export QT_SCREEN_SCALE_FACTORS="eDP1=2;DP1=1;" Unfortunately that didn't work for me. Everything was still scaled to the HiDPI screen. I read here https://phabricator.kde.org/D12405#251159 > graesslin added a subscriber: graesslin.Apr 21 2018, 7:01 PM > I also cannot imagine this to work due to the fact how X11 works. There is just no mapping from window to screen. No window can know on which screen it is. Not even KWin knows that as the window manager (screen is not a constant property, but evaluated every time it is accessed, it's based on the distance to closest screen). Especially for overlapping windows it's very difficult to try to get to which screen it belongs. It gets even more complicated when things like panning and overlapping screens get into it. So I wouldn't trust this thing in Qt to work due to the pain we have in KWin especially with these problems. > graesslin added a comment.Apr 21 2018, 7:52 PM > I'm pointing out that I'm in general against any risky changes on X11. If users want to use this features: Wayland is there. KWin is feature frozen on X11 and I highly suggest to the Plasma community to decide the same at the sprint. We will have less maintenance issues due to it. Sometimes X shits me. A lot of things are so much simpler on Wayland. Unfortunately until https://github.com/swaywm/sway/issues/1047 https://bugzilla.mozilla.org/show_bug.cgi?id=635134 - Firefox/Thunderbird have blurry fonts on Wayland when running through XWayland, that means I am stuck with X.
Posted by tya99 at
> Second, switching back and forth between dual monitors to one monitor lead to interesting behaviour. For example, I kept losing the scale setting on the lower DPI monitor. I think that may be fixed in Xorg 1.20. I couldn't reproduce that in xf86-intel-video. So what I found when I set QT_AUTO_SCREEN_SCALE_FACTOR=1 and had my settings in .Xresources is it would doublescale giving me huge fonts. xrandr --output DP1 --scale 2x2 --auto --pos 0x0 --primary \ --output eDP1 --scale 1x1 --mode 3840x2160 --pos 5120x0 I noticed that Qt5 apps look now fine on my external screen (Low DPI) but now super huge on my laptop screen HiDPI. Single Screen Laptop: xrandr --output eDP1 --scale 1x1 --mode 3840x2160 \ --output DP1 --off All good. Single Screen External: xrandr --output eDP1 --off \ --output DP1 --scale 2x2 --mode 2560x1600 All good. Both screens! https://imgur.com/GOYZUm4 xrandr --output eDP1 --scale 1x1 --mode 3840x2160 --pos 5120x0 --primary \ --output DP1 --scale 2x2 --mode 2560x1600 --pos 0x0 Small fonts on external screen with Qt5. xrandr --output eDP1 --scale 1x1 --mode 3840x2160 --pos 5120x0 \ --output DP1 --scale 2x2 --mode 2560x1600 --pos 0x0 --primary Small fonts on laptop using Qt5. So it seems tied to Primary switch as to which screen it scales for. Fortunately when using dual monitors I basically never use virtualbox, wireshark or nvim-qt on the laptop screen. I found when no primary switch is used the eDP1 becomes primary. It looks like this might be a bug with Qt. https://bugreports.qt.io/browse/QTBUG-67928 "This will not produce correct results on X as X fakes the physicalSize by default." which probably a hack for something else, god damn I hate X11. I guess time to go back to the drawing board. Your method should be able to work around this Qt bug (I do really think it is X11's fault) if I have it configured to scale for a single factor, ie non-HiDPI and HiDPI being both 2x. I looked at this to see if I could understand panning a bit better https://www.x.org/releases/X11R7.7/doc/randrproto/randrproto.txt and I think I came up with the panning numbers. [--panning widthxheight[+x+y] ie 2560*2=5120 and 1600*2=3200 then move along by 3840 (move over by length of DP1). Which results in --panning 5120x3200+5120+0 That results in this command: xrandr --dpi 220 --fb 8960x5360 \ --output eDP1 --mode 3840x2160 --pos 5120x0 \ --output DP1 --scale 2x2 --pos 0x0 --panning 5120x3200+5120+0 --primary Everything looks right. Using scrot you're able to see the whole thing ie 8960x5360 so that's useful to debug where your screens are. https://imgur.com/d7BGPJd I still think something isn't right though because there is a lot of useless black space below my screens that isn't being used that I created with fb. Any ideas Will?
Posted by tya99 at
Solved! The Qt related issue can be solved by doing QT_SCREEN_SCALE_FACTORS="2;2" Which means you can use for your dual monitors: xrandr --output eDP1 --scale 1x1 --auto --pos 5120x0 --primary \ --output DP1 --scale 2x2 --auto --pos 0x0 and for the external: xrandr --output eDP1 --off \ --output DP1 --scale 2x2 --auto or the internal single: xrandr --output eDP1 --off \ --output DP1 --scale 2x2 --auto > I initially tried using --output eDP-1 --auto when I wanted to enable the laptop monitor after disabling it. However, I found this lost the --scale setting, so I had to either restart X or run the same xrandr command twice. Using --mode does not have this problem. That is now no longer the case. Using Xorg 1.20 I was able to use auto and the scale was kept.
Posted by Rakesh Singh at
Thank you for sharing this information. I have been fiddling with my Macbook Pro 2015 running Centos 7 for a while and could never get the external monitor working optimally, until now.
Posted by Ibrahim at
This is great for those of us who can't use Wayland too (eg. my new laptop does not work without nVidia proprietary drivers, which seem to not support Wayland). Unfortunately though, I seem to get blurry text on the external (low-DPI) screen. I guess it's better than huge text, but kind of unsettling and probably will cause eye strain or something... but anyway, thanks for the information!
Back

Comment