-
Notifications
You must be signed in to change notification settings - Fork 927
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
Handle hidpi scaling. #105
Comments
I believe this needs some discussion, as DPI is not handled the same way on all platforms. The wayland way is:
|
From what I gathered, on Win32 each thread has a HiDPI-awareness level:
Notably, the third option is only available on Windows 8.1+. I couldn't find whether moving a window from a thread to another, or changing the HiDPI awareness level during the execution, modifies the existing windows. The HiDPI awareness is supposed to be set once at initialization. The older way to deal with that was per application, and you could only set it once (all further calls would fail). Maybe this is also the case for threads. |
Merge from upstream to get OSMesa changes <!-- Reviewable:start --> This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/glutin/105) <!-- Reviewable:end -->
We need to support 3 things to support high DPI:
On Windows situation is as follows: Notifying OS that we are high DPI aware
Quote from some unofficial documentation:
Getting scaling factor of the window
Receiving notifications from the OS about change of the scale factor
|
@kryptan For older Windows you can get the DPI by calling |
Even on Windows 10 Anniversary Update it is still necessary to call PR should be ready soon. |
It works but requires user to log out after changing scaling. |
Yes, because older Windows only supports the notion of system DPI, not per monitor DPI, and changes to the system DPI require relogging. Regardless, that is the correct API to use for those older systems. |
Currently For my Windows patch I will probably just follow the existing logic despite the rounding errors that it causes. |
Aside from implementation details I'd like to raise the questions of what winit should assume from its users:
Thing is, as in wayland the user can bascially draw at any dpi they want, we need to be sure to advertize the proper used dpi factor to the server, other wise the sizes won't match. |
Should it be per-program or per-window? My current implementation for Windows is here. Windows has application global DPI-awareness. Latest Windows 10 has mechanism for per-window DPI-awareness but I hasn't been able to make it work. Setting awareness for window different from the global one causes weird issues, so my current implementation has one global function - |
To move forward I think we need to agree on the cross-platform interface and then implement it in backends. This is kind of an RFC for the proposed API changes to winit. Detailed design
All backends must ensure that for each window, either one of the following invariants holds:
AlternativesDon't create Unresolved questionsShould we return What should be the default value for Thoughts? |
Is all the The rest of this sounds good to me--both the MonitorId improvements and the additional event to the WindowEvent enum. |
If you don't care about it you just don't call it and assume that hidpi factor is 1.0. Whether it can be a builder method I think I addressed in the Alternatives section. By the time you create your window you already need to know hidpi factor to specify window size but to get it from |
I may be wrong, but for me HiDPI awareness is just a thing that exists to preserve backward compatibility. |
@tomaka I agree with this but on Windows we can't just make process hidpi aware without consent of the programmer. Winit may be used as part of a larger application, e.g. in dll, where there are windows created by other, non-winit, code. Enabling high-dpi awareness will mess this up. Therefore, I think |
I have no concerns regarding wayland implementation, as wayland allows very fine control over dpi-awareness, we should be able to implement this API without much trouble. However, I must say, I find it weird to have both a global |
But this reasoning would also mean that these DLLs are forbidden to enable HiDPI-awareness as well, as they would mess up the rest of the application. What I mean, is that more generally it's not possible to isolate a code that creates a window in its own little module or its own little library. Any code that creates a window (whether it is from winit or not) should specify what it does when it comes to HiDPI-awareness. Therefore I'd be in favour of making HiDPI-awareness mandatory for winit. |
I have the following use case in mind. There is an existing C++ Windows application which has a GUI and which can use DLL plugins. These DLL plugins may need to display their own GUI to show user some settings. This is not just a theory, see e.g. #159. If the original application is not HiDPI aware then plugin written in Rust should not attempt to change that. This issue was previously raised here. |
As an alternative we can provide an option to opt-out of the DPI-awareness in which case winit will not attempt to change any process-global settings. |
I think I found a reasonable solution. The use case that I mentioned above is very niche while probably 99% of winit users on Windows are writing standalone executables where changing process-global settings is not an issue. I propose the following: don't introduce |
* Implement public API for high-DPI #105 * Recover get_inner_size_points and get_inner_size_pixels and change their implementation assuming get_inner_size() returns size in pixels * Update changelog for high-DPI changes
Tbh I'm not quite sure. The risk of storing the DPI factor in it would be a user keeping an old Keeping it out would make clear that the DPI factor is some context value that may change and needs to be tracked, I guess. On the other hand, integrating the DPI factor in the struct is certainly more convenient if we assume that there are supposed to always be short-lived anyway. |
That risk sounds like it's largely mitigated by the existence of |
Yeah, but in that case I think we need to make it very clear that the |
I think I'm still in favor of including it, since when possible users should favor responding to events over directly querying things, and I think they're more likely to follow that recommendation if the DPI is included. (I'm assuming you agree with that recommendation. It's partially motivated by our inability to guarantee that queries will always have a low cost.) |
Wouldn't that be a strong argument to not build an API that forces the backends to make such queries on every event with a coordinate in it? I'd think the queries could be made cheap just by caching the latest value for each window, though. There's also the downside that anyone who is going to be passing around or storing lots of logical coordinates (say, for a GUI impl) will need to come up with their own type and do a lot of converting. |
@Ralith true, the performance issue was a weak argument on my part. Why would including the DPI factor in the struct force application developers to make their own type and do lots of converting, though? |
Because if the DPI factor is embedded, the struct is larger than necessary for performing computations purely in logical space, which is undesirable. People could just ignore the cost, but it's an incentive against using it all the same. |
I'm not sure I share that sentiment, but since embedding the DPI factor would be less transparent/explicit anyway, I think I've changed my mind about it. @vberger's struct looks good as-is. |
How do you handle the coordinate object having an embedded dpi factor when the window is overlapping multiple displays with different dpi factors on each? |
The idea I was thinking of regarding named types instead of tuples was something more like this:
With the primary purpose of not mixing them up, if we need both in the API. I'm unsure if the DPI factor should be bundled with the coordinates or not. |
@anderejd sounds good to me. It's simple, composable, clearly communicates the intentions, and provides us with more hope for getting this done before this issue hits 100 comments. |
For the record: my suggestion was voluntarily going farther than yours @anderejd , I was just thinking that in APIs where there is no obvious "better choice" between them (which may be many), offering an abstraction over both could be useful. Nevertheless, this can easily be done with your suggestion as well, by adding appropriates So, from my point of view both are pretty equivalent, I don't have any strong preference between any of the two approaches. |
So, which functions should take/return logical pixels, besides |
That seems reasonable based on what has been written earlier in this issue, @vberger ? |
I agree that Now, taking the rest of winit API, there are the places where coordinates or dimensions are used:
So. From this I mostly see two possibilities for a roughly general and coherent API:
I would tend to favor the second case, because it matches the fact that DPI awareness really only matters for drawing, most of the time. But both options seem viable to me, so it's not an hill I'll die on. |
Option 2 makes more sense to me. Making drawing special probably makes more sense to the average user than making window creation special. Either way, besides API documentation, I think we really ought to have an example program for this. I'm not sure where it should live (since without a drawing surface there's nothing to demonstrate) but I'm confident it would improve the chances of people actually supporting HiDPI. |
If we end up going with option 2, I don't think we should have winit tell the OS it's DPI-aware by default. That way, the worst-case-scenario with the program not handling DPI scaling is that the OS scales the entire window up, and everything looks blocky or a bit blurry. However, if we're telling the OS that we're handling DPI scaling, but the program isn't, and is also using pixel-based coordinates for drawing, we end up with apps that looks like this: Which is broken to the point of being unusable. |
@Osspial AFAIK "DPI awareness" the way you refer to it only exists on Windows. |
I believe it was stated a few times that winit would explicitly not support non-dpi aware apps... Furthermore, using the logical/physical pixels wrapping structs should make it rather difficult to accidentally be non-dpi aware, wouldn't it? |
Alright, PR is up: #548 |
The wording here is intentionally vague because I don't know very well what the possibilities are and what makes sens for winit. It is safe to say that it would be good for winit to expose some way to indicate whether the application is "dpi-aware" and avoid having the window upscaled by the window manager for apps that don't want this behavior.
On Fedora 25 with wayland (now enabled by default), winit windows are all upscaled on hidpi screens.
cc @vberger
The text was updated successfully, but these errors were encountered: