Skip to content

No more hanging up when the viewport it resized#2521

Merged
hoffstadt merged 1 commit intohoffstadt:masterfrom
v-ein:bugfix/2401-resize-hangup
Jun 23, 2025
Merged

No more hanging up when the viewport it resized#2521
hoffstadt merged 1 commit intohoffstadt:masterfrom
v-ein:bugfix/2401-resize-hangup

Conversation

@v-ein
Copy link
Collaborator

@v-ein v-ein commented Jun 20, 2025


name: Pull Request
about: Create a pull request to help us improve
title: No more hanging up when the viewport it resized
assignees: ''


Description:
The main issue this PR addresses is #2401. It is caused by WM_SIZING branch in the window procedure, which is handled by the same code as WM_SIZE. That branch uses LPARAM to get the new viewport size and to appropriately resize DirectX buffers. Works fine on WM_SIZE, but WM_SIZING gets a different type of data in LPARAM (a LPRECT pointer), and therefore feeds wrong sizes to DX and spoils its swap chain. Moreover, WM_SIZING is not needed and can be safely removed. It's exactly what I did to fix the hangup issue. This resolves #2401.

In addition to that, I've modified the message loop so that it processes all pending messages rather than one message per frame. This fixes #1571 and fixes #2357.

Yet another change is to actually render frames on WM_TIMER while the user is resizing or moving the window. This makes DPG behave the same way as on Linux, where resizing does not block rendering. This is actually a bit of a double-edged change because render_dearpygui_frame() will still hang up when the user starts resizing, and DPG will start an internal rendering loop to compensate for that. Will work fine for most DPG users (those who simply call start_dearpygui()) but might give some surprising side effects to those who use a custom rendering loop (e.g. asyncio-based apps). In future we might want to add an option to opt-out of this behavior.

Anyway, rendering on WM_TIMER seems to work quite well, and also helps with calling viewport_resize_callback properly. I've also corrected the code calling SetTimer (no need to call it twice). This fixes #1896 and fixes #2217.

Note: I've completely removed WM_PAINT and let DefWindowProc handle it. This is perfectly normal since we're rendering frames all the time anyway (either via render_dearpygui_frame or on WM_TIMER).

Concerning Areas:
None.

… WM_SIZING as it is not needed and was processed incorrectly (hoffstadt#2401).

Reworked the message loop so that it processes all pending messages rather than one message per frame (hoffstadt#544, hoffstadt#1571, hoffstadt#2357).

Continue rendering when the viewport is being moved or resized by the user. Sending resize events, too (hoffstadt#1896, hoffstadt#2217).

Removed unused width/height fields from mvViewport.
@v-ein
Copy link
Collaborator Author

v-ein commented Jun 22, 2025

Yet another issue this PR is going to fix is when a drag handler misbehaves after set_viewport_pos if an input_text is active (yes, that complicated). The issue was reported a couple of times on Discord: post 1, post 2. The latter post contains some analysis of the issue that I'd like to preserve independently of Discord, so below is a copy of my explanation from Discord.


On Windows, DPG needs to process Win32 messages and feed some of them to Dear ImGui so that ImGui can get user input (keyboard/mouse events). There are different ways a message can be retrieved and processed. User input comes via the message queue. DPG calls PeekMessage to retrieve messages from the queue, which is a perfectly good way of doing it. What is not good, however, is that DPG only calls PeekMessage once per frame. Yes, it fetches one message per frame. To confuse you a bit more 🤪 - this only affects queued messages; messages that are sent to the window can still be processed normally, multiple messages per frame.

Okay, DPG handles one queued message per frame. So what? Even within the queue, there are different categories of messages. Some of them are low-priority and generated on-the-fly when the queue is empty. Here's a good StackOverflow discussion on the topics - check both answers:
https://stackoverflow.com/questions/7216161/determine-priority-of-a-window-message

When you simply drag the window, DPG fetches WM_MOUSEMOVE messages from the queue (once per frame!) and feeds into ImGui, then ImGui updates mouse position in its internal IO structure, and finally the drag handler in DPG receives "drag delta" from that updated position. Every time you move the mouse while dragging the window, mouse position within ImGui is updated every frame and reflects the actual position relative to the viewport origin, which follows the mouse thanks to set_viewport_pos.

To put it simple: you shift the mouse, the drag handler sees a nonzero delta, calls the callback, the callback moves the viewport by that delta, and next frame ImGui updates mouse position so that the delta gets back to zero.

Now, you might wonder how the hell could an input widget mess things up here. When you click the input widget, Windows posts a WM_IME_NOTIFY to the message queue. Yes, even though the doc states the message is sent (i.e. not queued), Windows can actually post it, too, with an undocumented wParam/command value of 0x13 (it does send a couple of WM_IME_NOTIFY's as well).

As soon as you drag the mouse, WM_IME_NOTIFY get posted to the queue and now PeekMessage handles a single WM_IME_NOTIFY every frame. For some reason, it always retrieves WM_IME_NOTIFY and they never end - probably the window move triggers another WM_IME_NOTIFY and it all starts going in circles. Anyway, the WM_MOUSEMOVE that we so desperately need to update the drag delta doesn't get a chance to be retrieved; I believe it's one of those generated messages that get the lowest priority. GetQueueStatus indicates that there's a pending WM_MOUSEMOVE but it just doesn't get through. As a result, the drag handler always reports the same drag delta, effectively moving the viewport every frame and kicking another loop iteration with WM_IME_NOTIFY.

@hoffstadt hoffstadt merged commit 034d0cf into hoffstadt:master Jun 23, 2025
4 checks passed
v-ein added a commit to v-ein/DearPyGui-fixes that referenced this pull request Jun 23, 2025
hoffstadt pushed a commit that referenced this pull request Jun 23, 2025
@v-ein
Copy link
Collaborator Author

v-ein commented Jun 29, 2025

One of the problems this PR solves is a "visual hang-up" that occurs on Windows when the user is dragging or resizing the window - DPG does not render frames while the mouse is held down.

This is not related to hang-up after the mouse is released, and the cause is that Windows starts a modal message loop for the duration of move or resize. The PR solves it by doing extra rendering on WM_TIMER within that loop. There are other alternatives to this solution, all nicely listed by @axeldavy in DearCyGui/DearCyGui#8 along with a good explanation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants