Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ControlFlow is ignored while window is being resized on Windows #3272

Open
hecrj opened this issue Dec 19, 2023 · 14 comments
Open

ControlFlow is ignored while window is being resized on Windows #3272

hecrj opened this issue Dec 19, 2023 · 14 comments
Assignees
Labels
B - bug Dang, that shouldn't have happened DS - windows

Comments

@hecrj
Copy link
Contributor

hecrj commented Dec 19, 2023

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.

@hecrj hecrj changed the title ControlFlow ignored while window is resized on Windows ControlFlow is ignored while window is resized on Windows Dec 19, 2023
@hecrj hecrj changed the title ControlFlow is ignored while window is resized on Windows ControlFlow is ignored while window is being resized on Windows Dec 19, 2023
@kchibisov kchibisov added B - bug Dang, that shouldn't have happened DS - windows labels Dec 19, 2023
@kchibisov
Copy link
Member

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.

@AustinMReppert
Copy link

This seems to be happening when interacting with the window buttons as well.

Recording.2023-12-20.092743.-.Trim.mp4
Recording.2023-12-20.055544.mp4

@kchibisov
Copy link
Member

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.

@dtzxporter
Copy link
Contributor

dtzxporter commented Dec 20, 2023

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.

@msiglreith
Copy link
Member

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)

@kchibisov
Copy link
Member

@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?

@dtzxporter
Copy link
Contributor

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).

@msiglreith msiglreith self-assigned this Dec 22, 2023
@simonask simonask mentioned this issue Dec 30, 2023
17 tasks
@kchibisov kchibisov added this to the Version 0.30.0 milestone Jan 15, 2024
@msiglreith
Copy link
Member

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:
nclbuttondown

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..

@dtzxporter
Copy link
Contributor

@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.

@msiglreith
Copy link
Member

@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.

@kchibisov
Copy link
Member

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?

@dtzxporter
Copy link
Contributor

dtzxporter commented Jan 23, 2024

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)

@msiglreith
Copy link
Member

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.

@dtzxporter
Copy link
Contributor

dtzxporter commented Jan 25, 2024

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!");
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
B - bug Dang, that shouldn't have happened DS - windows
Development

No branches or pull requests

5 participants