Skip to content

Setting DPI Scaling in WSL on High-DPI Monitor

The video driver in WSL (2024-08-17) is not sending the real monitor size information to Xrandr.

This is a problem for any GUI application.

You can check a tool I made based on this post at github: https://github.com/A-Ribeiro/wsl-hidpi-suggestion .

Context

These days I was programming a tool that uses Xrandr to query the display size to render graphic components with the correct monitor resolution.

The native code on Windows and Linux works correctly.

In WSL we have this issue.

It seems that the video driver in WSL is not passing the correct information about the size of the physical monitors to applications inside WSL.

That's why I created these scripts that configure the DPI scaling in the WSL environment variables in '~/.bashrc'.

Configuration

1) Enter wsl and create a file named '~/powershell_get_dim.ps1' and fill it with the content below:

Add-Type @"
using System;
using System.Runtime.InteropServices;
public class DCOps {
    [DllImport("user32.dll")] public static extern IntPtr GetDC(IntPtr hwnd);
    [DllImport("user32.dll")] public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);
    [DllImport("gdi32.dll")] public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);

    public static int HORZSIZE = 4;
    public static int VERTSIZE = 6;
    public static int DESKTOPVERTRES = 117;
    public static int DESKTOPHORZRES = 118;
}
"@

$hdc = [DCOps]::GetDC([IntPtr]::Zero)

if ($hdc -eq [IntPtr]::Zero){
    echo "1,1,1,1"
    exit
}

$w=[DCOps]::GetDeviceCaps($hdc, [DCOps]::DESKTOPHORZRES)
$h=[DCOps]::GetDeviceCaps($hdc, [DCOps]::DESKTOPVERTRES)

$wm=[DCOps]::GetDeviceCaps($hdc, [DCOps]::HORZSIZE)
$hm=[DCOps]::GetDeviceCaps($hdc, [DCOps]::VERTSIZE)

$rc=[DCOps]::ReleaseDC([IntPtr]::Zero, $hdc)

echo "$w,$h,$wm,$hm"

This script takes the default Windows monitor and returns the current resolution in pixels and millimeters.

2) Now create a file named '~/_new_set_dpi_scale_from_win32.sh' with the content below:

#!/bin/sh

f_dpi_scale() {

    local dpi_scale powershell_script MainDisplayInfo dpi_100_percent

    # the notation ${<item1>:-<item2>}
    #    is similar to <item1> or <item2>, if <item1> is not defined
    dpi_scale="${GDK_DPI_SCALE:-${QT_SCALE_FACTOR:-${GTK_SCALE:-${GDK_SCALE:-}}}}"

    if [[ -z "${dpi_scale:-}" ]] ; then
        powershell_script=`cat ~/powershell_get_dim.ps1`
        MainDisplayInfo=$("/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe" "$powershell_script")
        # set -f # avoid globbing (expansion of *).
        # array=(${MainDisplayInfo//,/ })
        # array=(`echo $MainDisplayInfo | sed 's/,/\n/g'`)
        array=(`echo $MainDisplayInfo | tr ',' '\n'`)
        w=${array[0]} # width in px
        h=${array[1]} # height in px
        wm=${array[2]} # width in mm
        hm=${array[3]} # height in mm

        dpi_100_percent=$(bc <<<"scale=6; $w / ($wm / 25.4)")
        dpi_scale=$(bc <<<"scale=6; ($w * 25.4) / ($wm * 96) ")

        export GDK_SCALE=${GDK_SCALE:-$dpi_scale}
        export GDK_DPI_SCALE=${GDK_DPI_SCALE:-$dpi_scale}
        export GTK_SCALE=${GTK_SCALE:-$dpi_scale}

        # https://doc.qt.io/qt-5/highdpi.html
        # export QT_AUTO_SCREEN_SCALE_FACTOR=${QT_AUTO_SCREEN_SCALE_FACTOR:-1}
        # export QT_ENABLE_HIGHDPI_SCALING=${QT_ENABLE_HIGHDPI_SCALING:-1}
        export QT_SCALE_FACTOR=${QT_SCALE_FACTOR:-$dpi_scale}

        echo "DPI scale info:"
        echo " - Main size"
        echo "     (px): ${w}x${h}"
        echo "     (mm): ${wm}x${hm}"
        echo "     (DPI-100%): ${dpi_100_percent} DPI"
        echo " - Scale set to: $dpi_scale"

    fi
}

# if dir runtime-dir exists, then ...
[ -d /mnt/wslg/runtime-dir ] && f_dpi_scale

This script runs our powershell script. At the end it sets the variables in the system: GDK_SCALE, GDK_DPI_SCALE, GTK_SCALE and QT_SCALE_FACTOR.

3) Finally, open your '~/.bashrc' file and add the following line of code:

# ...

. ~/_new_set_dpi_scale_from_win32.sh

And Thats It

Now just open WSL and test any GUI application.

Note: This configuration only affects applications that use one of the environment variables we just configured.

Hope you liked the content.

best regards,

Alessandro Ribeiro

Leave a Reply

Your email address will not be published. Required fields are marked *