`ControlFlow` is ignored while window is being resized on Windows
The ControlFlow set on the EventLoopWindowTarget is currently ignored on Windows while a window is being resized.
As a result, the EventLoop is not resumed when it should during the resizing process when WaitUntil or Poll are set as the control flow.
Other platforms are unaffected and work as expected.
I generally don't see what could be causing this, like the WM_SIZE handling is no different than anything else.
Would suggest to step with debugger or something like that before it tries to go back to polling.
This seems to be happening when interacting with the window buttons as well.
https://github.com/rust-windowing/winit/assets/29180043/0d5b74c1-5e80-4a62-8336-702f3287ecdc
https://github.com/rust-windowing/winit/assets/29180043/e74b29eb-d0bc-4fca-beac-6fcd9a8efdf2
I mean, the only thing that is helpful would be debugging the wait_for_msg inside the windows backend, given that I generally don't have a way to debug due to hard to access windows hardware, it could take time until someone on windows try to look into it.
Some more info:
dispatch_peeked_messages never returns after the DispatchMessageW(msg = WM_NCLMOUSEDOWN) until after the user releases the mouse.
WM_PAINT events continuously flow while it's paused there.
Some (long running) actions in Windows start modal event loops (e.g as mentioned resizing and non-client area interactions) on top of the current running event loop iteration. Before the event loop changes the Windows backend internally checked the control flow on WM_PAINT events to as sync points to handle control flow changes (feeding in NewEvent events and so on), but added quite some complexity. Redraws are now more decoupled from the normal event flow and don't act as these sync points anymore. I'm not sure what is the desired behavior to be fair.
Continous presentation should be possible by requesting redraws inside the redraw event to periodically schedule new paint events. (IIRC non-client area didn't work in the past and might need some internal handling to correctly trigger redraws)
@msiglreith I think the issue is that there's no resize at all now and the events are not sent to user? Though, it's hard to get that based on video (where you must hunt for log and could easily miss) and report saying that it's just broken.
Could you look into that when you have time?
Yes, this is the issue. It will repaint because the window itself is receiving paint events but the dispatcher is still stuck waiting for the resize event to stop. No other events will flow back to the user at this time, which makes it impossible to do other things besides repaint while resizing, but normally you'd be able to (and other platforms work properly).
Posting an update on this as I tried a few things so far but couldn't come close to a proper fix so far. I looked into the non-client control part primarily as this appears to be more strict compared to the resize border:
As mentioned, the root cause is that DefWindowProc starts to block on WM_NCLBUTTONDOWN and won't return unless user input happens:
Trying to wakeup this modal loop by manually scheduling a WM_PAINT, a timer to trigger WM_TIMER, or a custom event via PostMessageW doesn't work, the message may only be received afterwards.
Not calling DefWindowProc to manually, for example, call the SC_CLOSE syscommand allows to re-implement the close button behavior similar to the case where the client draws the system controls. But this is still not free of issues as the button hover rendering is incorrect then. SetCapture preserves the pressed-hover rendering once moving the mouse inside the non-client area but re-entering will break the rendering again..
@msiglreith Do you know why winit uses a hidden window on win32 to receive events? I believe this issue would also be solved by forwarding window events in the wndproc of each window separately rather than using a separate window to receive the events.
I've played with this design in my own rusty-windows testbed and I don't see why the current design was chosen.
@dtzxporter There is basically one eventloop window which kind of ties things together: it handles 'global' events (like raw input) but also as interface for the user to handle custom events or calls, which should execute in the same thread/execution context as the event loop.
Can't windows just reside on the main thread and requests basically being called via threaded_executor or something? And maybe some stuff via side channels to remove the delays, like for Window::request_redraw?
Right, but why is that logic in an event loop window instead of literally in the event loop before:
TranslateMessage -> DispatchMessageW
You can handle raw input directly in that loop before TranslateMessage fires.
Custom events can be registered that also make it to the 'thread' that is running the dispatch loop.
Then, each window's wndproc handles it's specific events and forwards them to the winit event loop.
Adding a window and using it as an event loop seems like an unnecessary workaround for just processing in the main event loop. (And I believe this is the root cause of the issue because we can't wake up the other windows event loop while another is resizing, however, the main loop's dispatch loop will always fire)
Yes, it probably could be replaced with another solution - raw input could be associated to no window and the OS will then pick the one currently in focus, but currently I don't see how this would help with the current issue. For resizing, in comparison to the control buttons, it's possible to poke the message queue to execute our callback but the OS will still block until the current action finishes.
What I mean is that the window 'stuck' in the resize loop's wndproc can be used to continue to flow winit events?
SetTimer can also be used to wake up that loop provided you specify the hwnd of the window currently in the resize loop:
unsafe { SetTimer(window, 1337, 5000, None) };
WM_TIMER => {
println!("timer!");
}