[Game Client] Cursor Locking Doesn't Behave Properly With High DPI Settings
Cursor Locking is getting confused in certain cases when DPI is > 100% on Window 7.
For instance, imagine this setup
- DPI at 135%
- Monitor 1 (main) at 0, 0 with resolution 3840 x 2160
- Monitor 2 (extended) at 3840, 0 (so, to the right of the main screen) with resolution 3840 x 2160
In this case, the cursor is NOT locked. It can leave the screen to the right. It does get locked after it's gone 1344 pixels off to the right. That's 35% of the second screen. This is because my DPI is 135%, and the "Locking Rect" is being scaled by Windows 7's DPI scaling, so the locking rect is 135% of what it should be.
Let's take it further. Imagine this setup
- DPI at 135%
- Monitor 1 (main) at 0, 0 with resolution 3840 x 2160
- Monitor 2 (extended) at -3840, 0 (so, to the left of the main screen) with resolution 3840 x 2160
The problem does not happen here, because the locking rect starts at 0, 0, and wont wrap into the negative space.
Here's a trickier scenario
- DPI at 135%
- Monitor 1 (main) at 0, 0 with resolution 3840 x 2160
- Monitor 2 (extended) at 3840, 0 (so, to the right of the main screen) with resolution 1920 x 1080
Does the problem happen here? No. Because the height of the secondary screen is smaller than that of the primary screen, it seems as if the locking rect is squished so it fits-to-size of the main screen.
I've tested this problem extensively, and have gone as far as counting the pixels to ensure I'm correct. You can also reproduce this with the secondary screen below the main screen, but never above. This validates my theory further. If you right-click on the executable, go to Properties, select the Compatibility tab, and check "Disable display scaling on high DPI settings", the problem vanishes.
This is likely due to a mismatch in DPI awareness levels. It seems like the DirectX rendering engine is "System-DPI aware", where it handles everything on it's own. Conversely, it seems like the locking rect is "Not DPI–aware", causing it to be scaled (called virtualized) by the Windows kernel.
The proper fix would be to size the locking rect appropriately:
DPI = somehowGetDPIAsPercentage(); dpiScale = (DPI / 100); rect.width = floor(screen.width / (dpiScale)); rect.height = floor(screen.height / (dpiScale));
Note the flooring is important, otherwise we can end up clicking off the screen by just a pixel and tabbing ourselves out.
It may be easier than that, though. You might be able to add this to the application manifest to always disable the virtualized DPI
<asmv3:windowsSettings
xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
This is a problem a lot of people have but don't even know how to express because they're simply not technically inclined enough to debug. When Googling this issue, I saw dozens of posts where posters didn't get an answer, and resorted to things like unplugging their monitors or using third-party software. For the sake of those hopefully you guys can turn around a quick fix, I've done my best to give you everything you need.