What is the correct way to response to the wxEVT_DPI_CHANGED event?

84 views
Skip to first unread message

AsmWarrior

unread,
Mar 30, 2023, 1:52:12 AM3/30/23
to wx-users
Hi, when the application receive a wxEVT_DPI_CHANGED event, how can it response to the event?

I looked at the wxWidgets' sample code "display", it looks like it does nothing, but just show some text in the status bar:

void MyFrame::OnDPIChanged(wxDPIChangedEvent& event)
{
    wxLogStatus(this, "DPI changed: was %d*%d, now %d*%d",
                event.GetOldDPI().x, event.GetOldDPI().y,
                event.GetNewDPI().x, event.GetNewDPI().y);

    event.Skip();
}

But if we need to layout the UIs, do we need to call the ResetMode() function of the wxDisplay class?

I just search for the "ResetMode" in wxWidgets' github site, I don't see much usage of this function.

In the "display" sample code, this function is called when a wxButton get clicked.

I even did some test about the DPI changing, and I think if ResetMode() is not called, the layout is wrong, you can see the discussion and image shots here in the:

How does the FromDIP function work under different DPI scaling? — https://forums.wxwidgets.org/viewtopic.php?p=218156#p218156

Any suggestions? Thanks!

Asmwarrior

Vadim Zeitlin

unread,
Mar 30, 2023, 8:07:35 AM3/30/23
to wx-u...@googlegroups.com
On Wed, 29 Mar 2023 22:52:11 -0700 (PDT) AsmWarrior wrote:

A> Hi, when the application receive a wxEVT_DPI_CHANGED event, how can it
A> response to the event?

This depends on where you're handling it. If you handle it for some custom
widget, you would typically recompute any cached DPI-dependent metrics in
this handler. If you handle it for the TLW, you could redo the layout
taking the new DPI into account if the automatic layout update (basically,
scaling everything by the new-to-old DPI ratio) doesn't produce the best
results.

A> I looked at the wxWidgets' sample code "display", it looks like it does
A> nothing, but just show some text in the status bar:
A>
A> void MyFrame::OnDPIChanged(wxDPIChangedEvent& event)
A> {
A> wxLogStatus(this, "DPI changed: was %d*%d, now %d*%d",
A> event.GetOldDPI().x, event.GetOldDPI().y,
A> event.GetNewDPI().x, event.GetNewDPI().y);
A>
A> event.Skip();
A> }
A>
A> But if we need to layout the UIs, do we need to call the ResetMode()
A> function of the wxDisplay class?

No, you never need to call this function nor ChangeMode(). This is about
changing the display resolution and is something you basically never do.

A> I even did some test about the DPI changing, and I think if ResetMode() is
A> not called, the layout is wrong, you can see the discussion and image shots
A> here in the:
A>
A> How does the FromDIP function work under different DPI scaling? —
A> https://forums.wxwidgets.org/viewtopic.php?p=218156#p218156
A>
A> Any suggestions? Thanks!

I don't really understand what the problem is after looking at the
screenshots in the post above. It seems perfectly normal that a 250*50
button becomes of 375*75 size at 150% DPI scaling.

Have you read https://docs.wxwidgets.org/latest/overview_high_dpi.html ?
I've tried explaining high DPI support in wxWidgets as well as I could
there.

Regards,
VZ

--
TT-Solutions: wxWidgets consultancy and technical support
http://www.tt-solutions.com/

AsmWarrior

unread,
Mar 30, 2023, 8:51:50 PM3/30/23
to wx-users
Hi, VZ, thanks for your time and help.

On Thursday, March 30, 2023 at 8:07:35 PM UTC+8 Vadim Zeitlin wrote:
On Wed, 29 Mar 2023 22:52:11 -0700 (PDT) AsmWarrior wrote:

A> Hi, when the application receive a wxEVT_DPI_CHANGED event, how can it
A> response to the event?

This depends on where you're handling it. If you handle it for some custom
widget, you would typically recompute any cached DPI-dependent metrics in
this handler. If you handle it for the TLW, you could redo the layout
taking the new DPI into account if the automatic layout update (basically,
scaling everything by the new-to-old DPI ratio) doesn't produce the best
results.

OK, I see, it looks like in many cases, I don't even do anything to handle the wxEVT_DPI_CHANGED even, just like currently the sample "display" does.

 

A> I looked at the wxWidgets' sample code "display", it looks like it does
A> nothing, but just show some text in the status bar:
A>
A> void MyFrame::OnDPIChanged(wxDPIChangedEvent& event)
A> {
A> wxLogStatus(this, "DPI changed: was %d*%d, now %d*%d",
A> event.GetOldDPI().x, event.GetOldDPI().y,
A> event.GetNewDPI().x, event.GetNewDPI().y);
A>
A> event.Skip();
A> }
A>
A> But if we need to layout the UIs, do we need to call the ResetMode()
A> function of the wxDisplay class?

No, you never need to call this function nor ChangeMode(). This is about
changing the display resolution and is something you basically never do.

OK. So, it looks like the ResetMode() function is just for debug only? Maybe some document(like your above explanation) is need to mention this.
 

A> I even did some test about the DPI changing, and I think if ResetMode() is
A> not called, the layout is wrong, you can see the discussion and image shots
A> here in the:
A>
A> How does the FromDIP function work under different DPI scaling? —
A> https://forums.wxwidgets.org/viewtopic.php?p=218156#p218156
A>
A> Any suggestions? Thanks!

I don't really understand what the problem is after looking at the
screenshots in the post above. It seems perfectly normal that a 250*50
button becomes of 375*75 size at 150% DPI scaling.

OK, I think you are correct, thanks.
 

Have you read https://docs.wxwidgets.org/latest/overview_high_dpi.html ?
I've tried explaining high DPI support in wxWidgets as well as I could
there.

I have read this document for at least two or three times, but I think some of knowledge are still hard to understand.
That's why I try to run the sample "display" to test it.

I would suggest that a minimal sample code should be put in this document, so that people(like me) can understand quickly.

Vadim Zeitlin

unread,
Mar 30, 2023, 8:55:53 PM3/30/23
to wx-u...@googlegroups.com
On Thu, 30 Mar 2023 17:51:50 -0700 (PDT) AsmWarrior wrote:

A> OK. So, it looks like the ResetMode() function is just for debug only?

No, but it's just almost completely unrelated to high DPI support. The
"modes" shown by the display sample are different video modes supported by
the display, but you typically don't care about those, and definitely never
change them (except for demonstration purposes, like this sample does).

A> I would suggest that a minimal sample code should be put in this document,
A> so that people(like me) can understand quickly.

It's not really clear what should such sample code demonstrate. As you
wrote, in most cases you don't need to explicitly handle the DPI change at
all, and when you do need to handle it, it means something relatively
complicated (and hence difficult to show in a sample) is going on.

AsmWarrior

unread,
Mar 31, 2023, 6:42:30 AM3/31/23
to wx-users
On Friday, March 31, 2023 at 8:55:53 AM UTC+8 Vadim Zeitlin wrote:
On Thu, 30 Mar 2023 17:51:50 -0700 (PDT) AsmWarrior wrote:

A> OK. So, it looks like the ResetMode() function is just for debug only?

No, but it's just almost completely unrelated to high DPI support. The
"modes" shown by the display sample are different video modes supported by
the display, but you typically don't care about those, and definitely never
change them (except for demonstration purposes, like this sample does).

OK, I see, thanks!
 

A> I would suggest that a minimal sample code should be put in this document,
A> so that people(like me) can understand quickly.

It's not really clear what should such sample code demonstrate. As you
wrote, in most cases you don't need to explicitly handle the DPI change at
all, and when you do need to handle it, it means something relatively
complicated (and hence difficult to show in a sample) is going on.

OK, let's keep the document unchanged right now. Thanks.

Asmwarrior

 

Maarten Bent

unread,
Mar 31, 2023, 4:19:16 PM3/31/23
to wx-u...@googlegroups.com
Hi,

In most cases you shouldn't have to do anything. The scaling is done internally.
If your own code has stuff that depend on the DPI, you can handle the event and update things accordingly.

FromDIP converts a device-independent size to the size applicable for the display.
So if your display has scaling 100% it converts 250x50 to 250x50 physical pixels
If your display has 200% scaling, it converts 250x50 to 500x100
physical pixels.
This ensures that it will look the same on both displays.

In your example on the forum you use the static wxWindow::FromDIP() function. This is incorrect. It will always use the DPI of the main window when creating the control.
This is why you see the wrong button size when you reset the resolution and the button is recreated.
You should use member function FromDIP() (without
wxWindow::), so it will use the DPI of the display that the window is shown on.

wxDisplay::OnResetMode is definitely not needed for DPI changes.

Maarten
--
Please read https://www.wxwidgets.org/support/mlhowto.htm before posting.
---
You received this message because you are subscribed to the Google Groups "wx-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wx-users+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wx-users/af6073ad-2ba0-43f8-94d2-d2c156fa6bcfn%40googlegroups.com.

Maarten Bent

unread,
Mar 31, 2023, 4:19:16 PM3/31/23
to wx-u...@googlegroups.com
Actually my comment about wxWindow::FromDIP is invalid. It shouldn't matter if you use wxWindow:: prefix.
I am not sure why using the Reset mode button results in a button with the wrong size. I cannot seem to reproduce this.

Maarten

AsmWarrior

unread,
Mar 31, 2023, 7:42:28 PM3/31/23
to wx-users
Hi, thanks for the reply. See my comments below.

On Saturday, April 1, 2023 at 4:19:16 AM UTC+8 Maarten Bent wrote:
Actually my comment about wxWindow::FromDIP is invalid. It shouldn't matter if you use wxWindow:: prefix.
I am not sure why using the Reset mode button results in a button with the wrong size. I cannot seem to reproduce this.

Can you tell me what is your environment? I'm testing under Win10 64bit and the compiler is GCC(from msys2), and the library is wx3.2.2.1 prebuild from msys2(installed by pacman command).

My guess is that the From::DIP function is from the constructor of the MyFrame::MyFrame, when the ResetMode function get called, the MyFrame get re-constructed, but the old scaling 1.0 value(which is used inside the From::DIP) is used for my wxButton?

Asmwarrior
 

AsmWarrior

unread,
Apr 1, 2023, 9:52:12 AM4/1/23
to wx-users
Hi, Vadim Zeitlin and Maarten Bent. I think I found a mistake.

In my test code, it has such line:

sizer->Add(new wxButton(page, wxID_ANY, _T("Hello World"), wxDefaultPosition, wxWindow::FromDIP(wxSize(250,50), 0)));

Which is totally wrong here, because I have used the wrong argument for the FromDIP( wxSize(250,50), 0 ), the second argument 0 is totally wrong.

Now, I have removed the second argument, and the result is: when I click the ResetMode button, I don't have the reduced button size. (Which shows in the third screen shot of the forum post here:
How does the FromDIP function work under different DPI scaling? - wxWidgets Discussion Forum — https://forums.wxwidgets.org/viewtopic.php?f=1&t=50267)

I'm sorry about that, I think it was a copy and paste error. Also, when we pass the second 0 argument, can the function report something, because it will call a function below:

static wxSize wxWindow::FromDIP ( const wxSize &   sz,
const wxWindow *   w
)

Where the w pointer is 0.

Asmwarrior


Maarten Bent

unread,
Apr 2, 2023, 8:06:24 AM4/2/23
to wx-u...@googlegroups.com
Hi,

Using 0 as the window parameter will indeed cause it to use the DPI of the main display.
I think I couldn't reproduce because I moved FromDIP(wxSize(250,50)); (without the 0) to a separate line so I could debug the return values.

You are allowed to call FromDIP/ToDIP with a nullptr window, wxWidgets internally does it too.
So I'm not sure if something like wxLogDebug("Determining DPI without a valid wxWindow"); can be added.

Maarten
--
Please read https://www.wxwidgets.org/support/mlhowto.htm before posting.
---
You received this message because you are subscribed to the Google Groups "wx-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wx-users+u...@googlegroups.com.

AsmWarrior

unread,
Apr 3, 2023, 4:01:18 AM4/3/23
to wx-users
On Sunday, April 2, 2023 at 8:06:24 PM UTC+8 Maarten Bent wrote:
Hi,

Using 0 as the window parameter will indeed cause it to use the DPI of the main display.
I think I couldn't reproduce because I moved FromDIP(wxSize(250,50)); (without the 0) to a separate line so I could debug the return values.

You are allowed to call FromDIP/ToDIP with a nullptr window, wxWidgets internally does it too.
So I'm not sure if something like wxLogDebug("Determining DPI without a valid wxWindow"); can be added.

Maarten


Hi,  Maarten, thanks for the reply.
Indeed, a C++ statement has too many braces may cause errors, so it is more safe to changed some sub-function calls to a separate line.
Also, I think the wxLogDebug message is OK, and it could avoid some kinds of error like I did.

Asmwarrior

AsmWarrior

unread,
Apr 3, 2023, 8:41:59 AM4/3/23
to wx-users
Hi, everyone, I still have question about the DPI change handling.

Here is the patch I made to the "display" sample code:

diff --git a/samples/display/display.cpp b/samples/display/display.cpp
index 58f18de..fb028ef 100644
--- a/samples/display/display.cpp
+++ b/samples/display/display.cpp
@@ -356,6 +356,10 @@ void MyFrame::PopuplateWithDisplayInfo()
         sizer->Add(new wxStaticText(page, wxID_ANY, "Current: "));
         sizer->Add(new wxStaticText(page, Display_CurrentMode, currentMode));
 
+        wxSize dpi = GetDPI();
+        wxSize newSize = wxWindow::FromDIP(wxSize(250,50));
+        sizer->Add(new wxButton(page, wxID_ANY, _T("Hello World"), wxDefaultPosition, newSize));
+
         sizerTop->Add(new wxButton(page, Display_ResetMode, "&Reset mode"),
                       wxSizerFlags().Centre().Border());
 #endif // wxUSE_DISPLAY



And now I try to set two breakpoints. The breakpoint1 in the above code, in the line of Add() function call. The breakpoint2 is set inside the function:

void MyFrame::OnDPIChanged(wxDPIChangedEvent& event)
{
    wxLogStatus(this, "DPI changed: was %d*%d, now %d*%d",
                event.GetOldDPI().x, event.GetOldDPI().y,
                event.GetNewDPI().x, event.GetNewDPI().y);

    event.Skip();
}
Now, I try to run the executable when the initial DPI scaling is 100%, I first got breakpoint1 hit, and I see the "newSize" is 250*50. Now, I try to change the DPI scaling to 150%, and after that, I got breakpoint1 hit again, I see the value of "newSize" is still 250*50. After that I got the breakpoint2 hit.

I expect that when the second time the breakpoint1 got hit, the newSize value should be 375*75, because I have already changed the DPI scaling.
Also, I expect that the breakpoint2 should be hit before the breakpoint1 after the DPI changes?

Any one can explain this?

Thanks.
Asmwarrior

Maarten Bent

unread,
Apr 4, 2023, 5:05:11 PM4/4/23
to wx-u...@googlegroups.com
Hi,

When I move the application between displays with different DPI, I only get breakpoint2 (OnDPIChanged).
When I have the application on a display, and change the DPI of that display in the Windows settings, it hits both breakpoints.
I get breakpoint2 first (
OnDPIChanged) and then breakpoint1 (via OnDisplayChanged). So opposite to what you get.
(Windows 10.0.19045, latest Visual Studio 2022).
Even without breakpoints I can see this behavior, because it shows 'Display resolution was changed'  in the status bar, and not ''DPI changed: ...'.

These events are direct responses to Windows messages (WM_DPICHANGED and WM_DISPLAYCHANGE).
We don't have any influence on the order of these events.

Maarten
--
Please read https://www.wxwidgets.org/support/mlhowto.htm before posting.
---
You received this message because you are subscribed to the Google Groups "wx-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wx-users+u...@googlegroups.com.

AsmWarrior

unread,
Apr 5, 2023, 10:05:18 PM4/5/23
to wx-users
On Wednesday, April 5, 2023 at 5:05:11 AM UTC+8 Maarten Bent wrote:
Hi,

When I move the application between displays with different DPI, I only get breakpoint2 (OnDPIChanged).
When I have the application on a display, and change the DPI of that display in the Windows settings, it hits both breakpoints.
I get breakpoint2 first (
OnDPIChanged) and then breakpoint1 (via OnDisplayChanged). So opposite to what you get.
(Windows 10.0.19045, latest Visual Studio 2022).
Even without breakpoints I can see this behavior, because it shows 'Display resolution was changed'  in the status bar, and not ''DPI changed: ...'.

These events are direct responses to Windows messages (WM_DPICHANGED and WM_DISPLAYCHANGE).
We don't have any influence on the order of these events.



Hi, Maarten, thanks for the test.

I have did some more tests, and I found that the the event sequence is depend on whether the "Display wxWidgets sample" application window is active when I change the DPI scaling.

Test case 1: If I change the DPI scaling while the sample executable window is shown on the desktop, then the OnDPIChanged event arrived first, after that the wxFrame get reconstructed. in this case, the wxWindow::FromDIP(wxSize(250,50)); is 375*75, which is correct.

Test case 2: If I first minimize the sample window, and change the DPI scaling, after this operation the wxFrame get reconstructed, but no DPI change event arrived.
The DPI change event only arrived after I restore the sample window again. In this case, when the wxFrame get reconstructed, the wxWindow::FromDIP(wxSize(250,50)); is still  250,50 , which sounds correct, because at this time, the DPI change event is not arrived yet. But the bad thing is: I don't see the wxFrame get reconstructed again after the  OnDPIChanged function call. This looks like if a user has some customized window which depends on the FromDIP function call, it may got the wrong size values.

Luckily, in both of the above two cases, the final window size is correct!
I'm not sure my guess is correct, but from my point of view, the DPI change event should always arrive before the wxFrame reconstruction if I change the DPI scaling, in both of the above cases.

Asmwarrior
Reply all
Reply to author
Forward
0 new messages