Opened 4 years ago

Closed 9 months ago

#17721 closed enhancement (fixed)

Support per-monitor DIP (Device-Independent Pixel, i.e. for High-DPI) on wxMSW

Reported by: laro Owned by:
Priority: normal Milestone: future
Component: wxMSW Version: 3.1.0
Keywords: DIP, Device-Independent Pixel, HiDPI, Retina, per-monitor DIP Cc: pbfordev@…
Blocked By: Blocking: #18474
Patch: no


To support automatic change of monitor-DPI on wxMSW (or of moving the window from a High-DPI monitor to another one with 96 DPI) we could call wxWindow::Layout() on every WM_DPICHANGED, effectively resizing most any GUI element. But then we should have the DIP-based sizes stored in the widgets (to be rearranged by a sizer), not the "raw"/"native" DPI-based ones that were fitted to the DPI of a specific monitor.

So I would suggest to switch pixel-based sizes in all wxMSW to DIP (Device-Independent Pixel), as it is done on macOS (OS X) and GTK+ 3 already. (And despite the fact that the Win32-API does not work this way.)

In addition we should offer a way (i.e. member functions) to access the raw pixel coordinates, as that will be handy/necessary sometime, i.e. when drawing directly to screen.

Change History (10)

comment:1 Changed 4 years ago by MaartenB

Calling only Layout() will have no effect. The actual widget sizes and font sizes need to be changed. I recently created this PR to add support for Per-Monitor DPI awareness.

There is still some functionality missing in the Win32 API (DPI support in comctl32 for checkboxes, radioboxes, etc.) See e.g. this recent blog. Once this is added, Per-Monitor DPI awareness can be supported.

comment:2 Changed 4 years ago by laro

Ok, you are right, there needs to be an additional rescale step.

I noticed that the spacing in between menu items and within wxComboBox seems too small. But I can not see any "big" problems with checkboxes (nor other GUI elements).

In your task list for PR 334 you have checked off the point "Checkboxes and RadioButtons are not scaled." Did you do that because it is "Not possible with the current Windows API" or because it is fixed now?

I am using

How does your code handle rounding errors?
Something like:
7 pixel at 120 DPI (125%)
-> 5.6, rounded to 6 pixel at 96 DPI (100%)
-> 7.5, rounded to 8 pixels at 120 DPI (125%)

comment:3 Changed 4 years ago by MaartenB

Menu's and other non-client areas are scaled by Windows (for top-level windows), child-windows are not yet supported by the API. I don't remember issues with wxComboBox, but I will recheck.

Native checkboxes are not scaled at all. E.g. if your default* DPI is 175%. And you change it to 100%, checkboxes will remain huge. Similar, when switching from 100% to 175%, checkboxes stay small.
(* default DPI is DPI of main window after signing in to Windows)

I checked it off because it is not (yet) possible.

Rounding uses ceil when scaling factor is < 1, otherwise floor:

7 x (96 / 120) = 5.6 -> 6
6 x (120 / 96) = 7.5 -> 7

comment:4 Changed 4 years ago by laro

Ok, probably you are right again.
I did not really check moving a window from High-DPI to 96 DPI (or reverse), I was checking the plain system-DPI case only. Now I see what you meant.

But anyway: My observations are that on a High-DPI monitor the spacing in between menu items as well as the inner spacing of wxComboBox seems too dense. But that are minor glitches, and they seem to be a problem of ComCtl32.dll anyway, as I couldn't find a Win32-API function to adapt that spacings.

So, do you think converting sizes based on "newDPI/oldDPI" will be fine, even in the long run? Wouldn't switching to DPI-independent pixels (for positions and sizes) be the better solution? It would avoid many calls of FromDIP() in user code and would be more in line with macOS and GTk+ 3. I have no experience with those, BTW, it just seems to be the more straightforward solution for the problem. And a GUI framework like wxWidgets is IMHO the right place for those kinds of adaption.

comment:5 Changed 4 years ago by MaartenB

You always need to convert to get the correct values for the Windows API. DPI-independent pixels wouldn't help here.

I don't have experience with DPI-independent pixels either, but would the only advantage be that a developer doesn't have to use FromDIP() when specifying a fixed size/point?

This could be a nice addition, but I would like to hear the opinion of the main developers.
DPI-independent pixels is not limited to per-monitor dpi-aware, it is also relevant for 'normal' dpi-aware.

comment:6 Changed 4 years ago by laro

would the only advantage be that a developer doesn't have to use FromDIP() when specifying a fixed size/point?

That would be the main advantage.
And then we would have the same behaviour on Windows, Mac, and Linux, as it is desirable for a cross-platform framework. (But I admit, that programs using FromDIP() will work fine on Mac/Linux, too.)

Besides, I was of the opinion that storing the DPI-independent points in the widgets (i.e. spacers) would be the only clean solution to support per-monitor DIP.

Now I see that your solution is fine, too. Just not quite as elegant in user code IMHO.

On the other hand: Using DPI-independent points (DIPs) would result in a lot of changes in library code. Using DIPs for GUI spacing and pixels when drawing on a wxDC, there needs to be an adjustment somewhere in between. And often it will be difficult to decide what should be used.

AppKit on Mac seems to use float values for coordinates -- that is a bit too much IMHO, but may be necessary for perfect positioning.

Last edited 4 years ago by laro (previous) (diff)

comment:7 Changed 3 years ago by vadz

  • Keywords HiDPI added; High-DPI removed

I'm not sure what exactly is this ticket about and whether it's useful to keep it open or if having PR 334 is enough?

comment:8 Changed 12 months ago by vadz

  • Blocking 18474 added

comment:9 Changed 9 months ago by pb101

  • Cc pbfordev@… added

I think this ticket can be closed now.

comment:10 Changed 9 months ago by vadz

  • Resolution set to fixed
  • Status changed from new to closed

This is indeed implemented now, and new tickets should be opened for any specific problems with 3.1.3 or later.

Note: See TracTickets for help on using tickets.