Windows System DPI: GetDeviceCaps(hdc, LOGPIXELSX/Y) vs GetDpiForSystem() - winapi

Recently, I read this excellent page about DPI on Win32:
DPI and device-independent pixels
However, I am confused about GetDeviceCaps(hdc, LOGPIXELSX/Y) vs GetDpiForSystem(). On systems where I tested, all three values always match.
Questions:
Is it possible for GetDeviceCaps(hdc, LOGPIXELSX) and GetDeviceCaps(hdc, LOGPIXELSY) to return different values? I assume that LOGPIXELS means DPI. (Please correct me if wrong!)
If the previous answer is yes, then is GetDeviceCaps(GetDC(NULL), LOGPIXELSX/Y) the same as GetDpiForSystem()?
When possible, I should be using GetDpiForWindow(hWnd), but I want to clarify my understanding about "system" DPI in my questions above.

As far as I'm concerned, GetDeviceCaps(hdc, LOGPIXELSX/Y) is not a match with GetDpiForSystem().
LOGPIXELSX:Number of pixels per logical inch along the screen width.
In a system with multiple display monitors, this value is the same for
all monitors.
LOGPIXELSY:Number of pixels per logical inch along the screen height.
In a system with multiple display monitors, this value is the same for
all monitors.
This function is not able to return a per-monitor DPI. For that, you should use GetDpiForWindow().
GetDpiForWindow() also returns a different value based on the DPI_AWARENESS value.
GetDpiForSystem() returns the system DPI. The return value will be dependent based upon the calling context. If the current thread has a DPI_AWARENESS value of DPI_AWARENESS_UNAWARE, the return value will be 96.

Is it possible for GetDeviceCaps(hdc, LOGPIXELSX) != GetDeviceCaps(hdc, LOGPIXELSY)? I assume that "LOGPIXELS" means DPI. (Please correct me if wrong!)
For monitors, I believe LOGPIXELSX == LOGPIXELSY even if your display has non-square pixels (which is extremely rare nowadays). There are still many printers out there that have different horizontal and vertical dot densities. In some cases, the printer driver may hide that, giving the illusion of square pixels. Among ones that don't, you not only have to be careful to use the right value for the context, but you should be aware the some drivers forget to swap the horizontal and vertical values when you change the page orientation to landscape.
LOGPIXELSX and LOGPIXELSY refer to the number of pixels per logical inch, an idea that's been buried in recent versions of Windows. You used to be able to tell Windows that, when a program wants to display something that's 1-inch long, use my logical inch value rather than the actual DPI. (In the olden days of CRT monitors, it was usually impossible for Windows to discover the actual DPI anyway.)
Common values for logical inches were 96 and 120. If you had a really high-end monitor, you might've used 144. If you were into WYSIWYG applications or desktop publishing, you'd usually choose a value that was about 20% larger than an actual inch on your screen. I've heard various rationales for this, but I usually chose a higher value for easier reading.
When possible, I should be using GetDpiForWindow(hWnd)
That's fine. I use LOGPIXELSX and LOGPIXELSY because I'm old school and have always written high-DPI aware code.
but I want to clarify my understanding about "system" DPI in my questions above.
I believe the system DPI is the scaled DPI of the primary monitor. The scaling gives the programmer the same functionality as a logical inch, but it's presented to the user in a different way conceptually.
On systems where I tested, all three values always match.
Yes, it's very common for all of the methods to report the same DPI.
If your program is not high-DPI aware, you'll get 96 regardless of how you ask.
If it is aware, the system DPI is the DPI of the primary monitor. (Well, it's the possibly scaled native DPI of the monitor, but that's the same value you'll be told for the DPI of monitor.)
That covers a lot of common cases. To truly and thoroughly test, you'd need a second monitor with a different native DPI than the primary.
A couple points to keep in mind.
GetDeviceCaps approach is specific to the device context you reference with the HDC parameter. Remember that you can have printer DCs, enhanced metafile DCs, and memory DCs in addition to screen DCs. For screens, the return value will depend on the DPI awareness.
DPI awareness comes into play for screens (not printers). Your program's UI thread can be:
DPI unaware, in which all methods will return 96 dots per inch and actual differences will/might be handled by the system scaling things on the backend.
DPI aware, in which case most will return the system DPI. I believe the system DPI is the (scaled?) DPI of the primary monitor, but I'm not sure about that.
per-monitor DPI aware, in which case the GetDeviceCaps and GetDpiForWindow will return the actual DPI of the monitor that's referenced by the DC. I don't recall what it returns if the DC spans multiple monitors.
It might by the actual DPI if the monitors spanned have the same DPI, or it might be the system DPI, or it might be the DPI of one of the spanned monitors.
GetDpiForMonitor ignores DPI awareness altogether. I assume it remains for backward compatibility.

Related

How do GUI developers deal with variable pixel densities?

Todays displays have a quite huge range in size and resolution. For example, my 34.5cm × 19.5cm display (resulting in a diagonal of 39.6cm or 15.6") has 1366 × 768 pixels, whereas the MacBook Pro (3rd generation) with a 15" diagonal has 2880×1800 pixels.
Multiple people complained that everything is too small with such high resolution displays (see example). That is simple to explain when developers use pixels to define their GUI. For "traditional displays", this is not a big problem as the pixels might have about the same size on most monitors. But on the new monitors with much higher pixel density the pixels are simply smaller.
So how can / should user interface developers deal with that problem? Is it possible to get the physical size of the screen? Is it possible to set physical sizes instead of pixel-based ones? Is that still a problem (it's been a while since I last read about it) or was that fixed meanwhile?
(While css seems to support cm, when I try here it, it is not the set size).
how can / should user interface developers deal with that problem?
Use a toolkit or framework that support resolution independence. WPF is built from the ground up to be resolution-independent, but even old framework like Windows Forms can learn new tricks. OSX/iOS and Windows (or browser if we're talking about web) itself may try to take care the problem by automatic scaling, but if there's bitmap graphic involved, developers might need to provide different bitmaps such in Android (which face most varying resolution and densities compared to other OS)
Is it possible to get the physical size of the screen?
No, and developers shouldn't care about it. Developers should only care about the class of the device (say, different UI for tablet and smartphone), and perhaps the DPI to decide which bitmap resource to use. Vector resource and font should be scaled by the framework.
Is that still a problem (it's been a while since I last read about it) or was that fixed meanwhile?
Depend on when you last read about it. Windows support is still spotty, even for the internal apps itself, and while anyone developing in WPF or UWP have it easy, don't expect major third party apps to join soon. OSX display scaling seems to work a bit better, while modern mobile OS are either running on limited range of resolution (iOS and Windows Phone) or handle every resolution imaginable quite nicely (Android)
There are a few ways to deal with different screen sizes, for example when I make mobile apps in java, I either use DIP(Density Independent Pixels; They stay at a fixed size) or make objects occupy a percentage of the screen with simple math. As for web development, you can use VW and VH (Viewport Width and Viewport Height), by adding these to the end of a value instead of px, the objects take up a percentage of the viewport. For example 100vh takes 100% of the viewport height. Then what I think is the best way to do it, but time consuming, is to use a library like Bootstrap that automatically resizes elements, even when the window is resized. W3Schools has a good tutorial on bootstrap and more detailed explainations on any of these options can be looked up with an easy google search.
The design of the GUI in today display diversity era is real challenge. I would suggest several hints, mainly about the GUI applications design:
Never set or expect constant pixel size of the text - the user can change it from the system settings of the OS. Use some real-world measures for the text and check its pixel size when drawing. Provide some way to put the random size text in the boundaries of the window.
Never set or expect constant pixel size of the GUI widgets. Try to position them on the window in some adaptive way - according to the size of the window. Most GUI widget toolkits today have such instruments.
Never set or expect constant pixel size dialog windows. Let the OS to choose the size for you and then use what you get (X). Or, if you need to set some size and position (Windows), define it as a percent of the screen size.
If possible use scalable image formats for the icons. SVG is great for icons actually. Using sets of bitmap icons with different sizes is acceptable, but highly non-optimal as memory use and still will not provide perfect scaling in most cases.

Wrong screen size for Retina display in Matlab

I use Matlab on my MacBook Pro with Retina display.
Using get(0,'ScreenSize'), we obtain
ans =
1 1 1440 900
instead of 1 1 2880 1800. Is there any way to work with right sizes?
No, 1440-by-900 is likely the correct effective value for your screen's resolution. This is the value that the OS tells applications and is not the the same as the number of pixels (sometimes referred to as the "native resolution"). However, applications also need to check if a display supports HiDPI mode (a.k.a. Retina) as well. In your case, each "retina pixel" is made up of a 2-by-2 set of raw pixels (which, in turn, each have RGB sub-pixels). Applications that are "Retina-aware" can then render certain graphics (e.g., images and video) at the the full native resolution within regions of the screen. Some more details – probably more accurately stated – can be found in this article.
There are 3rd-party solutions to run OS X at the native resolution (e.g., SwitchResX and the methods discussed here), but this of course makes everything, UI included, ridiculously tiny. If you're running one of these, Matlab should report your resolution as 2880-by-1800.
I am not aware of any Matlab options, properties, or functions that allow one to actually take advantage of advantage of a Retina display. This means that, for example, when you display an image, each of it's pixels are rendered as 2-by-2 retina pixels.

Windows device coordinates vs virtual coordinates

I've tried to find an answer for this on MSDN, but I'm not getting a clear picture of how this is intended to work. All of my work is on Windows 8.1.
Here is my issue. I am working on a Laptop with a high resolution monitor, 3200x1800. I've been using EnumDisplayMonitors to get the bounding rectangle of my screen.
This seems to work fine if my display settings are default. But I've noticed that when I change the Window display settings to provide larger text, the resolution returned by EnumDisplayMonitor changes. Rather than getting 3200x1800 I will get 2133x1200.
I'm guessing since I asked for larger text, Windows chooses to represent the screen as a smaller resolution.
It seems that if I look at the virtual screen properties, everything is represented in the actual coordinates of my screen, i.e. 3200x1800. But the APIs for getting the window and monitor rectangles seem to operate on this "other" coordinate space.
Is there any documentation/Windows APIs to handle the conversion between these "other coordinates" and the "virtual coordinates"? i.e. if I want EnumDisplayMonitor or GetMonitorInfo to give me the true screen coordinates, how could I convert 2133x1200 to 3200x1800?
You have increased the DPI of the video adapter to 150% (144 dots per inch) to keep text readable and avoid having windows the size of a postage stamp. Quite necessary on such high resolution displays. But you haven't told Windows that your program knows how to deal with it.
So it assumes your program is an old one that was never designed to run on such monitors. It helps and lies to you. It gets your program to render its output to a memory buffer, then takes that output, rescales it by 150% and copies it to the video adapter. This is something you can see, text looks fuzzier if you put your program's output next to a program that doesn't ask for this kind of scaling, like Notepad.
And of course, it lies to you when you ask for the size of the screen. It tells you that it is 150% smaller than it really is. So that, after rescaling, a window you create will fill the screen.
Which is all just fine but of course not ideal, your program doesn't look as good as it should. You have to tell Windows that you know how to deal with the higher resolution. Do beware that this looks easier than it is in practice. Getting text to look crisp is trivial, it is bitmaps that are problematic. And in general a fertile source of bugs, even the big companies can get this wrong.
Before I start with an answer, let me ask: what are you really trying to do ? Or more specific - why do you need to know the monitor resolution ? The standard way to do this is to call GetWindowRect(GetDesktopWindow(), &rect) I'm not sure if the screen coordinates change based on DPI settings - but you should try that instead of GetMonitorInfo as the latter is for more advanced stuff. And if GetWindowRect still returns back a scaled rect, just call DPtoLP, LPtoDP or other mapping coordinate function as appropriate.
When you adjust the display settings as you described, you are actually changing the DPI settings of the screen. As such, certain APIs go into compatibility mode so that they allow the app to create larger elements and windows without knowing anything about this setting.
Why do you need to know the actual screen resolution since most of the windowing APIs will behave accordingly when the DPI scaling changes?
I suspect you could call SetProcessDPIAware or the manifest file equivalent. But do read this MSDN article first to understand DPI scaling.

How I can get the "pointer resolution" (or mouse DPI) on Windows?

experts.
How I can query the mouse DPI (pointer resolution) on Windows?
I read the article Pointer Ballistics for Windows XP. It says "the typical pointer resolution is 400 mickey/inch". But how I can query the exact value used by various kinds of mice?
It would be great if you could also point me to documents related to this topic.
Thanks!
It's impossible to tell. The mouse DPI is simply the number of times the mouse reports a change in location when it's moved by one inch. On the other side of the mouse cord all you know is that you periodically get a change in location, and you simply move the pointer on the screen every time.
One thing you can do if this is critically important for some special application is to have the user move his/her mouse exactly one inch and count the changes in location. If you're doing this in some professional environment then it's probably worth your while to issue special equipment- give your users the same model of high-end mouse with a particular known DPI. I say high-end because for most mice the approximate DPI number will be ludicrously inaccurate.

What's the difference between WindowFromPhysicalPoint and WindowFromPoint?

WindowFromPhysicalPoint is new with Vista. Its documentation is almost identical to WindowFromPoint. What's the difference? Both seem to take an absolute point (offset from screen origin) and return the topmost (Z order) HWND that contains the point.
http://msdn.microsoft.com/en-us/library/ms633533(VS.85).aspx
Windows Vista introduces the concept of physical coordinates. Desktop Window Manager (DWM) scales non-dots per inch (dpi) aware windows when the display is high dpi. The window seen on the screen corresponds to the physical coordinates. The application continues to work in logical space. Therefore, the application's view of the window is different from that which appears on the screen. For scaled windows, logical and physical coordinates are different.
WindowFromPhysicalPoint operates in physical screen coordinates, while WindowFromPoint works with logical ones. To understand the different read this page.
TL;DR; version would be:
Suppose you design a dialog box with a button at coordinates (100,
48). When this dialog box is displayed at the default 96 dpi, the
button is located at physical coordinates of (100, 48). At 120 dpi, it
is located at physical coordinates of (125, 60). But the logical
coordinates are the same at any dpi setting: (100, 48).
So unless you design your app to be DPI aware I would stick with logical coordinates, since most APIs and window messages operate in logical space. Another reason to use logical coordinates is to make your app backward compatible with Windows XP.

Resources