ID2D1RenderTarget::GetSize returing physical pixels instead of DIP - winapi

I'm currently getting started with Win32 and Direct2D and reached the chapter on DPI and DIP. At the very bottom it says ID2D1RenderTarget::GetSize returns size as DIP and ID2D1RenderTarget::GetPixelSize as physical pixels. Their individual documentation confirms that.
However I cannot observe that ID2D1RenderTarget::GetSize actually returns DIP.
I tested it by
setting the scale of one of my two otherwise identical displays to 175%,
adding <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> to my application manifest,
obtaining
D2D1_SIZE_U sizeP = pRenderTarget->GetPixelSize();
D2D1_SIZE_F size = pRenderTarget->GetSize();
in method MainWindow::CalculateLayout from this example (and printing the values),
and moving the window from one screen to the other, and arbitrarily resizing it.
I can see the window-border changing size when moving from one display to another. However, the values in both sizeP and size (besides being int and float) are always identical and correspond to the physical size of the ID2D1HwndRenderTarget.
Since I do not expect the documentation to be flawed, I wonder what I am missing to actually get the DIP of the window of the ID2D1HwndRenderTarget pRenderTarget.

The size is only relative to the DPI of the render target, set using ID2D1RenderTarget::SetDpi. This value is not automatically connected to the value provided by the display system, which can be queried using ID2D1Factory::GetDesktopDpi or GetDpiForMonitor.

Related

CGDisplayCopyAllDisplayModes leaves out one valid mode

When programmatically working with display modes in OS X (documentation), I've found that the CGDisplayCopyAllDisplayModes leaves out the rightmost option that is presented in System Preferences.
A simple utility that prints the size of the current display mode and all available display mode sizes outputs this
current size: 1920x1200
available sizes:
2880x1800
1440x900
2560x1600
2048x1280
1024x768
800x600
640x480
1680x1050
1280x800
1920x1200 is a valid option
All of the other options that System Preferences gives are represented in the list. Does anyone have any idea why 1920x1200 may not be included? I have tried changing to another one of the pre-defined values in system prefs, but it did not cause 1920x1200 to be included.
Edit (the accepted answer is much better than these shenanigans, but I'm leaving this info just in case)
The "scaled" display modes can be found by referencing a private API.
You can create a header file that makes the private methods available: see this gist that I borrowed from this project.
Then you can see all modes, including the scaled ones like this
print("Private modes:\n")
var numDisplayModes: Int32 = 0
CGSGetNumberOfDisplayModes(mainDisplayID, &numDisplayModes)
print("Num modes \(numDisplayModes)")
for i in 0...(numDisplayModes-1) {
var pmode: CGPrivDisplayMode = CGPrivDisplayMode()
CGSGetDisplayModeDescriptionOfLength(mainDisplayID, CInt(i), &pmode, CInt(sizeof(CGPrivDisplayMode)))
print("\t\(pmode.modeNumber): \(pmode.width)x\(pmode.height) -- \(pmode.density) \n")
}
There's public API that's only documented in the header. CGDisplayCopyAllDisplayModes() takes an options parameter, which is a dictionary. The docs (and even the headers) say that it's unused and you must pass NULL, but you can pass a dictionary with the key kCGDisplayShowDuplicateLowResolutionModes and value kCFBooleanTrue.
The option name is not really clear. It includes a bunch of extra modes.
Also, you may need to use CGDisplayModeGetPixelWidth() and CGDisplayModeGetPixelHeight() to distinguish between the point size and the pixel size of the backing store. (CGDisplayModeGetWidth() and CGDisplayModeGetHeight() return point size. By comparing those values, you can determine if the mode is scaled.)

DPI awareness: could I be told when I need to recalculate my text height so I don't have to do it all the time? And SM_CYSMICON/checkbox heights too?

A frequent operation in my Windows Table control, which I am reworking and moving into a DLL, is to get the height of a row. This is the maximum of
the height of text in the current font, in pixels
the current small icon height, in pixels (GetSystemMetrics(SM_CYSMICON))
the height of checkboxes, in pixels (determined on a WM_THEMECHANGED, when checkbox information is recalculated)
Calculating the text height, as far as I know, requires getting a DC, selecting the font in (and getting the SYSTEM_FONT if that's NULL), getting the text metrics, selecting the font out, and releasing the DC, all of which can error out/fail/etc. This means that virtually every function in my control can fail.
I can avoid this by storing the text height somewhere else, only calculating it when it changes. I know that text height is a property related to the DPI of the DC that GetDC(hwnd) returns. I would like my control to be DPI-agnostic because DPI awareness is per-process, not per-DLL/per-window.
At the same time, knowing when GetSystemMetrics(SM_CYSMICON) changes would also be useful.
So my questions are simple:
Is there a message that I can look for that will tell me that my DPI has changed and that I need to recalculate my text height?
Is there a message that will tell me that SM_CYSMICON has changed and that I need to recalculate everything? Is it the same one? (I know there is no reliable way to detect a GetSystemMetrics() failure (since 0 is a valid return and it does not set the last error code), so I am assuming it cannot fail with a valid parameter and am simply calling it each time I need to calculate the row height; this is just so I can queue a redraw when the value does change.) Would it also work for SM_CXSMICON?
In addition, looking back at my code, GetThemePartSize() takes a DC as well; do theme items like checkbox images scale with DPI? And if so, what messages do I look for in that case? The same one?
Alternative: is there a non-failing way to get the text height that I don't know about, given only a HWND and HFONT?
I will be happy to take a solution that was introduced in either Windows XP or Windows Vista; if there's a solution that was introduced in a newer version of Windows, then knowing about it could also be beneficial.
Thanks.

Restoring modified size of wxFrame in wxWidgets

I am using wxWidgets to design GUI in windows. The requirements is, if the user has modified the frame size then I have to store the modified size and use the modified size for next session. I am able to store the size, but still I am getting older size not the modified size in next session. My window has several children(check, text, label). These controls are put in panel using sizers. Every time the best size is queried and recalculated and SetClientSize(size) is called. Is this the reason why the modified size is not reflected?
First, don't save and restore the frame size yourself, use wxPersistentTLW which does it for you instead, see the overview for more information and the "widgets" sample for an example of using it to preserve the frame geometry.
Second, the layout mechanism in wxWidgets is totally deterministic, so restoring the same frame size as during the last run should definitely result in the same positions and sizes being used for the children. If this isn't the case (I'm not really sure about it, you don't actually say what the problem is), most likely explanation is that your size saving/restoring code doesn't work correctly -- and that simply getting rid of it and using the built-in support for this should fix the problem (whatever it is).

Resolution 1024x611, why is it not 1024x600? (Or why does SM_CYFULLSCREEN report a slightly too large height=)

I am posting here on the off-chance that someone knows why my screen size is reported as 611 instead of 600?
GetSystemMetrics(SM_CYFULLSCREEN)
returns 611. My netbook is 1024x600, so I expected 600, not 611.
Googling for 1024x611 turns up a surprising number of results, also.
from other forum threads, it seems that GetSystemMetrics(SM_CYFULLSCREEN) does not always return the exptected result. it seems to be adjusted, and may not be the value you want.
try using GetSystemMetrics(SM_CYSCREEN), which will return the size of your primary monitor.
(also, note that some people use multiple monitors: take care of not constraining your application to a single monitor)
SM_CXFULLSCREEN and SM_CYFULLSCREEN returns the size of a full screen window, so would usually be less than your screen size due to the taskbar:
The width of the client area for a full-screen window on the primary
display monitor, in pixels. To get the coordinates of the portion of
the screen that is not obscured by the system taskbar or by
application desktop toolbars, call the SystemParametersInfo function
with the SPI_GETWORKAREA value.
Compare it to what SPI_GETWORKAREA in SystemParametersInfo returns for your display? Or what is your display set to in the control panel??
A 1024x640 screen would be a true 16:10 ratio and would amount to ~ 611 y available desktop size.

How do I set a minimum window size in wxWidgets?

This is the hierarchy of widgets I've got:
Frame > wxBoxSizer > wxPanel > wxBoxSizer > wxFlexGridSizer (2 columns, 9 rows), with assorted form fields and buttons inside.
The first BoxSizer is to place the panel in, and the second is to make a border around the FlexGrid.
Everything looks nice, and the frame can expand properly if the window is enlarged, but it also can be resized to almost nothing, hiding all form elements.
How do I force the minimum size for the window to the one suggested by the FlexGridSizer by default (all form elements visible and having their minimal possible sizes)?
Probably much too late but the answers given here are needlessly complicated, you just need to call SetSizerAndFit(sizer) to both associate the sizer with the frame, set its initial size and also set this size as minimal acceptable size.
Using the SetMinSize() method on your Frame to set a minimum size will set a limit on the smallest area of the frame (just tested it). Once set, wxWidgets will not allow the frame to be sized smaller than the value specified.
I'd set the value like this. In the constructor, set up all the elements of the frame. At the end, after you call the Layout() method to setup all the sizers and such, call the GetSize(int *w, int *h) method to get the x and y size of your frame at the default layout. Use those values to call the SetMinSize() method to set that default size as the minimum for your Frame. This will take into account all the various padding and borders and such set up by the frame and the contained elements.
In wxPerl, dagorym's answer can be written concisely as
$self->SetMinSize($self->GetSize()); after a call to Layout().
I would try calling wxFrame->SetMinSize(wxSize) with whatever wxFlexGridSizer->GetMinSize() returns, should work, but not tested. You will need to note what GetMinSize says about coverting to window size before passing it I expect.

Resources