Skip to content

Handle screen DPI changes on Windows #127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

lampysprites
Copy link

@lampysprites lampysprites commented Jan 11, 2025

Issue aseprite/aseprite#4606, also possibly avoids having aseprite/aseprite#2740, and I think fixes aseprite/aseprite#4495.

On several displays with diffferent scale dragging the window from one screen to another makes everything either tiny or huge, depending on the direction it's dragged. This can be an issue when the screen with higher resolution is smaller in size, like a laptop with external monitor, or a pc with a screen tablet.

I tested these changes on 100%/200% and 150%/300% screen combination, windowed/maximized window, menu and preview windows, moving window with mouse and shift+wid+arrow key, and changing screen scale in settings w/ app running. As far as I can see it behaves same as other applications (namely, notepad) now. Except, rounding of 150% scale and 1x scale setting to 1x resolution feelt a bit confusing (personally) so i rounded it up.

PR aseprite/aseprite#4934 is needed to handle these changes properly.

Window decorations

  • Laf's WinAPI constructor (laf/os/win/winapi.cpp) tries to set process' dpi awareness to PER_MONITOR_AWARE_V2, and then and to PER_MONITOR_AWARE if first one fails. In Aseprite however both actually fail with "access denied", because it can only be set once, and src/main/settings.manifest already set it to V1.
  • In addition to enabling DPI awareness V1, window needs to call EnableNonClientDpiScaling() in create event to make titlebar resize.
  • GetSystemMetric and AdjustWindowRectEx need to be replaced with dpi-aware variants when available.

Corresponding commit to Aseprite repo changes manifest file to enable V2 dpi awareness, although when it's not available V1 works identical to it. Also theorethically this can be applied without the next commit if it's too messy.

Window content

I think it's important that window content is intact when changing screen. Think of moving a preview window - doing additional zoom/pan would be annoying. It means we need to:

  • Scale window borders proportional to dpi change.
  • Adjust display scale factor, not UI scale.
  • Not clamp scale factor at 4x when drawing.

The easiest wai I think is to scale pixel coordinates in laf window as scale = baseScale * displayDPI / defaultDPI, where baseScale is now the scaling chosen by user in the settings. Scale is used for input and painting same as before, while baseScale replaces it in window settings and initial values. This however breaks backwards compatibility, and I can't think of a simple way to keep it (e.g. need to handle window sizing outside window class for that).

There's also a few ways to sprovie API for this - i picked one with fewer changes. And maybe Window::setScale should now be setBaseScale but I don't think i got naming right anyway.

Corresponding commit to Aseprite repo replaces some scale() calls with baseScale() when needed.


I agree that my contributions are licensed under the MIT License.
You can find a copy of this license at https://opensource.org/licenses/MIT

@dacap
Copy link
Member

dacap commented Jan 23, 2025

Thanks a lot @lampysprites for the dedication to see this issue. I'll try to review this soon, not for the next release but probably for the other, and I'll try to merge this on beta branch (so we can release a preview of the fix to a small number of users first).

@dacap dacap assigned martincapello and unassigned dacap Mar 12, 2025
Copy link
Member

@martincapello martincapello left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works great! I've just suggested changing the use of std::max by std::ceil in three places, because I think it is a nice use case for it.

Copy link
Member

@martincapello martincapello left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While reviewing the Aseprite changes that make use of this changes I've found this issue: aseprite/aseprite#4934 (review)

Which is something that should be fixed in laf. The issue can be reproduced modifying the hello_laf example a bit, by just setting the window as floating. Then while this modified version of the hello_laf example is running, try to change the display scale from Windows configuration and you will see that the window is not updated until it is moved/resized.

Will try to create a bare minimum WinAPI app similar to hello_laf to see what happens in this case.

@martincapello
Copy link
Member

This is basically a default Windows Desktop Application generated by Microsoft Visual Studio Community 2022 with some modifications:

  • Removal of some unneeded things (like the about dialog, and some code that is not needed to prove the issue).
  • It sets the SetProcessDpiAwarenessContext to DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2.
  • Replace the use of CreateWindowW by CreateWindowEx, to set the WS_EX_TOOLWINDOW style of the window.

dpiaware_issue.zip

This solution demonstrates that the WM_DPICHANGED message is not arriving when the user changes the display's DPI and the window has the WS_EX_TOOLWINDOW style set. If this style is not set, it works as expected.

Will try to send this to Microsoft and see what happens...

@martincapello martincapello requested a review from dacap March 28, 2025 20:25
@martincapello martincapello assigned dacap and unassigned martincapello Mar 28, 2025
lampysprites and others added 3 commits March 30, 2025 08:24
Co-authored-by: Martín Capello <m.a.capello@gmail.com>
Co-authored-by: Martín Capello <m.a.capello@gmail.com>
Co-authored-by: Martín Capello <m.a.capello@gmail.com>
@lampysprites
Copy link
Author

Thanks a lot for spending time on this! I'm really sorry it turned into this kind of troubleshooting🥲
I've applied your changes with std::ceil and updated laf hash in the aseprite pr with that, is there something else?

@martincapello
Copy link
Member

Thanks a lot for spending time on this! I'm really sorry it turned into this kind of troubleshooting🥲

No worries! Thanks to you actually for bringing this new feature.

I've applied your changes with std::ceil and updated laf hash in the aseprite pr with that, is there something else?

Nothing else by me, maybe after David's review he could point out some changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Native title bar is smaller on 4k display
3 participants