rust-windowing / winit

Window handling library in pure Rust
https://docs.rs/winit/
Apache License 2.0
4.76k stars 891 forks source link

MacOS ControlFlow::Wait doesn't work properly when pointer leaves window #3934

Open ocheret opened 3 hours ago

ocheret commented 3 hours ago

Description

When implementing ApplicationHandler I implemented my own about_to_wait() method. When control flow is set to ControlFlow::Wait and the pointer remains within the window, everything works as expected - the thread goes to sleep when nothing is happening. However, when the pointer leaves the window, the about_to_wait() method is called non-stop as if control flow was set to ControlFlow::Poll. When the pointer reenters the window, things work as expected again.

Super simple code example:

use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::window::{Window, WindowAttributes, WindowId};

#[derive(Debug, Default)]
struct App {
    window: Option<Window>,
}

impl ApplicationHandler for App {
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
        if self.window.is_none() {
            let window = event_loop
                .create_window(WindowAttributes::default())
                .expect("Failed to create window");
            self.window = Some(window);
        }
        println!("resumed");
    }

    fn window_event(
        &mut self,
        event_loop: &ActiveEventLoop,
        window_id: WindowId,
        event: WindowEvent,
    ) {
        println!("window_event: {:?}", event);
    }

    // When the pointer leaves the window, this is called incessantly!
    fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
        println!("about_to_wait");
    }
}

fn main() {
    let event_loop = EventLoop::new().expect("Failed to create event loop");
    event_loop.set_control_flow(ControlFlow::Wait);

    let mut app = App::default();
    event_loop.run_app(&mut app).expect("Failed to run app");
}

macOS version

ProductName:            macOS
ProductVersion:         14.6.1
BuildVersion:           23G93

Winit version

0.30.5

kchibisov commented 3 hours ago

Does it happen with the window example?

ocheret commented 3 hours ago

Hard to say. That example doesn't seem to open a window (I can't locate it on a screen anyway) and it also doesn't appear to call set_control_flow. Or maybe I'm missing something.

kchibisov commented 1 hour ago

Are you sure that it doesn't open anything at all? It should open a window and lists you some key bindings to press.

The default control flow is Wait, so it's what it's using, you don't have to explicitly set anything.

ocheret commented 1 hour ago

Ok, had to get a desktop view - the window shows up behind the terminal I was using. I added an info! call to the about_to_wait call in there (it wasn't logging anything normally). I do not see the behavior there. :-(

My code snippet is quite short. Is there anything obvious that I'm doing wrong in there? I removed my set_control_flow call (to get the default behavior) and I see the same result. Is there some other option I need to set?

kchibisov commented 1 hour ago

maybe not drawing anything ends up like that, because macOS is confused. Also if you don't set ControlFlow to anything does it change behavior (it shouldn't, but just in case)?

ocheret commented 1 hour ago

Not setting ControlFlow results in the same behavior as explicitly setting it to Wait (as you said it would). Basically, I'm trying to come up with bare-bones examples and build up slowly for a tutorial. Ultimately, I'm trying to get a good set of wgpu examples working with the latest winit (everything I can find out that uses older versions) and also illustrate integration with Tokio (among other things). Today was my first deep dive into 0.30.* and the docs are not super helpful. I'm happy to help improve them and provide more general tutorials if I can get it all figured out.

ocheret commented 1 hour ago

Even GitHub Copilot begs me to use an older version. ;-)

kchibisov commented 1 hour ago

I mean, your provided code should generally work and I think it does. If macOS wakes you up, it's mostly up to it and given that our example also works, the issue could you not doing any drawing, so I'd suggest to draw something(even with softbuffer) and problem will likely go away.

0.30 and 0.29 don't differ much other than big callback vs trait, the rest is entirely the same, so wgpu examples for 0.29 will work fine. You can also look into glutin examples, it's using 0.30.

kchibisov commented 1 hour ago

@madsmtm do you know what the issue could be on macOS? I'd guess macOS compositor tries to ask for drawing and no drawing ever done so it kind of loops?