Replies: 9 comments 5 replies
-
Where to put the frontier original/scaled coordinates for PER_MONITOR_AWARE_V2 ?nana was adapted to SYSTEM_AWARE and partially to PER_MONITOR_AWARE. We need now PER_MONITOR_AWARE_V2. I don't think we need to use SetThreadDpiAwarenessContext and GetThreadDpiAwarenessContext Nana public DPI API?I afraid nana is in the same difficult situation the Windows developers were (are): how transparent we need to be? The big deal is to decide where to put the frontiers between the 'original' coordinates and the scaled coordinates, in both directions: the user setting geometries, and the system reporting actions, like mouse events that the user need to map back to geometrical elements in the GUI. The DPI change can be dynamic, at any time: to recalculate we need to know all the time the 'original' coordinates or the previous DPI. Recalculating the scaling my led to incongruousness due to different rounding - which may be important when summing a large number of elements sizes to determine global geometric of complex windows or dialogs.
This can be translated into:
Let try a pros/contra table:
Strict vs. relaxed (predefined mixing) aproach:
Nana seems to be following 2. But this is a lot of work, and error prone. |
Beta Was this translation helpful? Give feedback.
-
Some functions we need to search for in nana code to replace (or add...) with DPI aware versions:
Why 72? The font size is an imprecise value. It can generally be determined by measuring the distance from the bottom of a lowercase g to the top of an adjacent uppercase M, as shown in the following illustration. |
Beta Was this translation helpful? Give feedback.
-
Reacting to DPI changesTo react on the fly to changes in DPI: handle the windows messages related to DPI changes:
PMv1 applications may also call EnableNonClientDpiScaling during WM_NCCREATE to request that Windows correctly scale the window's non-client area.
Others: (High DPI Reference)
|
Beta Was this translation helpful? Give feedback.
-
Well, I'm already making my first big experiment, and I decided to go with 1.system side in branch WindowsAll of them seems to be in a few functions in
|
Beta Was this translation helpful? Give feedback.
-
To Do:as we did with
GraphicsIn
Fonts
|
Beta Was this translation helpful? Give feedback.
-
Whiile implementing 'system-side' dpi scaling I will need to 'revert' some partial implemented 'user-side' scaling:
where dm is defined in place_parts.hpp
in line 529: double get_value(int ref_percent, const display_metrics& dm, bool to_system_px = true) const noexcept uses it: screen.cpp: combox.cpp in
menu.cpp
This is an example of a complex interaction of 'user' pos & sizes , the graph drawing, font and dpi scaling with potential accumulation of rounding errors. As we see, there is still a tendency to dpi-scale immediately before calling graph/drawing functions: this could be made on the other side much more simply.
std::size_t _m_get_index_by_pos(int x, int y) const menubar.cpp
struct font::impl_type in graphics.cpp |
Beta Was this translation helpful? Give feedback.
-
The big problem here is the accumulation of rounding error due to integer arithmetic. |
Beta Was this translation helpful? Give feedback.
-
Some win32 call:
|
Beta Was this translation helpful? Give feedback.
-
Code organizationnana-windows native interface
These 3 functions are used only by:
These basic functions are also used in the implementation of other functions in
/// \todo: generalize dpi to v2 awareness
unsigned screen_dpi(bool x_requested)
{
return ::nana::platform_abstraction::screen_dpi(x_requested);
}
std::size_t window_dpi(window wd)
{
internal_scope_guard lock;
if (is_window(wd))
return interface_type::window_dpi(wd->root);
return 0;
} Font
In platform_abstraction.cpp we have: Graphics
Image, icons::DrawIconEx(graph.handle()->context, r.x, r.y, reinterpret_cast(native_handle_), r.width, r.height, 0, 0, DI_NORMAL);
|
Beta Was this translation helpful? Give feedback.
-
Making nana DPI Awareness v2 (per monitor dpi changing dynamically)
(see #677, #697, #692, #640, #690 and 'fix' #699, discussions #701, #702 )
The real reasons we have this problem are:
If the problem is new to you see : Why is Windows Display Scaling So Bad?
Lets try to explain what it means:
Windows was unprepared for high DPI monitors
OMG, and for multiple monitors was unprepared too... and still it is: basically all Windows sees is an unique virtual big 'system' monitor with a FIXED, arbitrary and fake DPI = 96, independently of the real DPI, the size in pixels ('resolution') and of the physical size of the rel monitors. It is the job of the 'external' graphics system to show the correct size of the drawings. All that this fake 96 DPI means is that there is no scaling (100% or 1).
To compensate, there are actions or free (changing) parameters: the size of that whole 'system' monitor in pixels and the size and position inside that of each of the recognized monitors (these are king of rectangles inside of the first with the size in pixel adjusted to the size in pixels of the real monitors, not DPI.... which is set to 96 anyway!!!).
Consequently there is 'no way' for Windows (and your App) to know the 'real' size (DPI or PPI) of the drawings, only number of pixels. This led to weird things. The Escape from all of this that Windows offer is very weird too: the user may use a Windows setting to set an scaling factor, typically 100%, 125%, 150%, 175%, 200%,..etc. This is weird for two reasons: only some functions or APIs will recognize this, and, it just introduce an scaling ('virtual' coordinates) reporting that your ('fake') DPI changed: now for 200% it report you monitor has a (fake) DPI of 96*2 = 192 !! Independently of the 'real' DPI !! Now, Windows, and some time you (the App) will need to (re)calculate were a point in that scaling will be in the big 'system' monitor which still has the fixed fake DPI of 96.
Of course, if you really want, you can use some special Windows API to get the 'real' properties of the monitors: EnumDisplayMonitors, GetMonitorInfo, GetDpiForMonitor and EnumDisplaySettings.
partial solutions were added step by step
How a win32 app will be treated by windows
It depends on the type of DPI awareness the app self chose and set for herself.:
Compatibility
/DPI properties (at the bottom)/Override high DPI scaling
(at the bottom) and in the combox select the 3rd and last option -System (Enhanced)
. See: Fixing High DPI Issues in Windows 10If the DPI awareness level is not set, the default value is DPI_AWARENESS_CONTEXT_UNAWARE.
I was in an error: nana is manipulating the sizes/positions of some elements not because nana wanted to amplify it to make it more "readable", no. All nana and not-nana GUI uses the DPI/coordenates of the monitor... and no problem - just maybe veeery small. it is Windows self that offer the opportunity to the user to set an scaling factor: 125, 150, 200, etc, %. But windows does that messing with 'virtual' and 'real' coordinates.
Major sources:
Prior to the Windows 10 Anniversary Update (1607), the DPI awareness mode of a process was a process-wide property. Beginning in the Windows 10 Anniversary Update, this property can now be set per top-level window. (Child windows must continue to match the scaling size of their parent.) A top-level window is defined as a window with no parent. This is typically a "regular" window with minimize, maximize, and close buttons. The scenario that sub-process DPI awareness is intended for is to have secondary UI scaled by Windows (bitmap stretched) while you focus your time and resources on updating your primary UI.
To enable sub-process DPI awareness, call SetThreadDpiAwarenessContext before and after any window creation calls. The window that is created will be associated with the DPI awareness that you set via SetThreadDpiAwarenessContext. Use the second call to restore the current thread s DPI awareness.
While using sub-process DPI scaling enables you to rely on Windows to do some of the DPI scaling for your application, it can increase the complexity of your application. It is important that you understand the drawbacks of this approach and nature of the complexities that it introduces. For more information about sub-process DPI awareness, see Mixed-Mode DPI Scaling and DPI-aware APIs.
Many Windows APIs do not have an DPI context:
Many legacy Windows APIs do not include a DPI or HWND context as part of their interface. As a result, developers often have to do additional work to handle the scaling of any DPI-sensitive information, such as sizes, points, or icons. As an example, developers using LoadIcon must either bitmap stretch loaded icons or use alternate APIs to load correctly-sized icons for the appropriate DPI, such as LoadImage.
EnumDisplayMonitors
GetMonitorInfo
,GetDpiForMonitor
, andEnumDisplaySettings
AdjustWindowRect
and its sister API,AdjustWindowRectEx
, that calculate the required size of the window rectangle based on the desired size of the client rectangle should be replaced withAdjustWindowsRectExForDPI
, just passing a last, additional, parameter with the current (window) dpi (function from user32.dll).CreateFont
:int height = -MulDiv(orig_pointSize, ::GetDpiForWindow(hWnd), 72);
WPARAM wParam, LPARAM lParam
,::SetWindowPos
,::EnumChildWindows
, etc.Beta Was this translation helpful? Give feedback.
All reactions