afritz1/OpenTESArena

Add DPI-aware support

Closed this issue · 3 comments

In order to have the application be properly sized on a high DPI display, we need to set some platform-specific DPI-awareness values. Otherwise we might have cases where the game window only renders at something like 1440p on a 2160p monitor.

On Windows, this is done with a manifest. Apparently SDL_WINDOW_ALLOW_HIGHDPI is optional and only affects querying/changing DPI at runtime. I googled around and wasn't able to find a clear solution to this in CMake besides the following rough idea:

IF (MSVC)
    ADD_CUSTOM_COMMAND(
        TARGET TESArena
        POST_BUILD
        COMMAND "mt.exe" -manifest \"${CMAKE_CURRENT_SOURCE_DIR}/../windows/dpi.manifest\" -outputresource:\"TESArena.exe\"\;\#1
    )
ENDIF()

The DPI manifest is an XML document with some dpiAware and dpiAwareness elements. Not entirely sure what all is needed in it but the values should be ones that enable per-monitor DPI awareness, and can be verified in Visual Studio > Project Properties > Manifest Tool > Input and Output > DPI Awareness.

It looks like the equivalent on macOS is NSHighResolutionCapable but it is not clear how we could do that in CMake.

I don't know how any of this applies to Linux.

To get basic DPI awareness, you only need the following in your manifest:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
	<application xmlns="urn:schemas-microsoft-com:asm.v3">
		<windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
			<dpiAware>True</dpiAware>
		</windowsSettings>
	</application>
</assembly>

This enables System level DPI awareness and Windows won't scale the window unless it's on a monitor with a different DPI to the system setting. The dpiAwareness element was added in Win8 to advertise multi-monitor DPI support.

This document explains the values you can add to the manifest: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/mt846517(v%3Dvs.85)

And this one has more info on what the various DPI modes do: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows#dpi-awareness-mode

It goes without saying that you need to code this game to take advantage of higher DPI settings.

As far as I'm aware, Linux doesn't do any auto DPI scaling but relies on the programs to do this themselves.

Yes, those links are more or less what I found last night. The main blocker is knowing how to do this in CMake, because I tried multiple combinations of the example code in my first comment and none of them result in the Visual Studio project settings changing.

I think we want to support the per-monitor V2 mode, and that requires grabbing some low level info from the SDL window. I think it's SDL_SysWMInfo, and then we get the window handle from that and then register a callback in the application, because SDL doesn't handle it itself.

I think commit 4707cd6 fixes this. My ReleaseGeneric window is 2160p again when maximized instead of 1234p.