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

[wayland] hangs on poll_events and run_forever #884

Closed
SiebenCorgie opened this issue May 14, 2017 · 8 comments
Closed

[wayland] hangs on poll_events and run_forever #884

SiebenCorgie opened this issue May 14, 2017 · 8 comments

Comments

@SiebenCorgie
Copy link

Hi,
I have a problem with the new glutin 0.8 release. The following symptoms:
If you call events_loop.run_forever or 'poll_events' in your application on wayland, the window gets registered (I can see it in my gnome-panel) but the window doesn't appear. The application does NOT crash. It just stops. When debugging inside the application I can see that the application never ends the events_loop.run_forever / poll_events.

To find the deeper problem it was suggested to me that I should clone a local version of winit, use this a dependency and debug the events_loop.run_forever and the events_loop.poll_events function.

I modified the functions with some println!() like this:

run_forever

    pub fn run_forever<F>(&self, callback: F)
        where F: FnMut(::Event)
    {
        // send pending requests to the server...
        self.ctxt.flush();
        println!("flushed");
        // first of all, get exclusive access to this event queue
        let mut evq_guard = self.evq.lock().unwrap();
        println!("got access");
        // set the callback into the sink
        // we extend the lifetime of the closure to 'static to be able to put it in
        // the sink, but we'll explicitly drop it at the end of this function, so it's fine
        let static_cb = unsafe { ::std::mem::transmute(Box::new(callback) as Box<FnMut(_)>) };
        let old_cb = unsafe { self.sink.lock().unwrap().set_callback(static_cb) };
        println!("unsaves 1");

        while !self.interrupted.load(::std::sync::atomic::Ordering::Relaxed) {
            println!("is != interrupt");
            self.ctxt.dispatch();
            println!("dispatched");
            evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost");
            println!("dispatch pending");
            let ids_guard = self.decorated_ids.lock().unwrap();
            println!("locked");
            self.sink.lock().unwrap().with_callback(
                |cb| Self::process_resize(&mut evq_guard, &ids_guard, cb)
            );
            println!("finshed 'with callback'");
            self.ctxt.flush();
            println!("flushed");
            if self.cleanup_needed.swap(false, ::std::sync::atomic::Ordering::Relaxed) {
                println!("cleanup needed");
                self.prune_dead_windows()
            }
        }
        println!("while is over");
        // replace the old noop callback
        unsafe { self.sink.lock().unwrap().set_callback(old_cb) };
        println!("end");
    }

When using it with the window example of glutin, I get the following output:

Pixel format of the window: PixelFormat { hardware_accelerated: true, color_bits: 24, alpha_bits: 8, depth_bits: 24, stencil_bits: 8, stereoscopy: false, double_buffer: true, multisampling: None, srgb: false }
OpenGL version 4.5 (Core Profile) Mesa 17.0.5
flushed
got access
unsaves 1
is != interrupt
dispatched
dispatch pending
locked
finshed 'with callback'
flushed
is != interrupt
dispatched
dispatch pending
locked
finshed 'with callback'
flushed
is != interrupt

As you can see problem occurs here of the original file (self.ctxt.dispatch();)
The strange thing is, that it occurs after the third time repeating the loop. I tested it, its always the third one.

poll_events

When doing the same procedure for poll_events which looks like this: ...

    pub fn poll_events<F>(&self, callback: F)
        where F: FnMut(::Event)
    {
        println!("Polling events");
        // send pending requests to the server...
        self.ctxt.flush();
        println!("Flushed");

        // first of all, get exclusive access to this event queue
        let mut evq_guard = self.evq.lock().unwrap();
        println!("got access");

        // read some events from the socket if some are waiting & queue is empty
        if let Some(guard) = evq_guard.prepare_read() {
            guard.read_events().expect("Wayland connection unexpectedly lost");
        }
        println!("read events");

        // set the callback into the sink
        // we extend the lifetime of the closure to 'static to be able to put it in
        // the sink, but we'll explicitly drop it at the end of this function, so it's fine
        let static_cb = unsafe { ::std::mem::transmute(Box::new(callback) as Box<FnMut(_)>) };
        let old_cb = unsafe { self.sink.lock().unwrap().set_callback(static_cb) };
        println!("adter unsaves");

        // then do the actual dispatching
        self.ctxt.dispatch_pending();
        evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost");
        println!("dispatched");

        let mut sink_guard = self.sink.lock().unwrap();
        println!("sink");

        // events where probably dispatched, process resize
        let ids_guard = self.decorated_ids.lock().unwrap();
        sink_guard.with_callback(
            |cb| Self::process_resize(&mut evq_guard, &ids_guard, cb)
        );
        println!("processed resize");


        // replace the old noop callback
        unsafe { self.sink.lock().unwrap().set_callback(old_cb) };
        println!("after noop callback");

        if self.cleanup_needed.swap(false, ::std::sync::atomic::Ordering::Relaxed) {
            self.prune_dead_windows()
        }
        println!("cleaned up");

    }

... Glutin/winit gets stuck with this output:

Pixel format of the window: PixelFormat { hardware_accelerated: true, color_bits: 24, alpha_bits: 8, depth_bits: 24, stencil_bits: 8, stereoscopy: false, double_buffer: true, multisampling: None, srgb: false }
OpenGL version 4.5 (Core Profile) Mesa 17.0.5
Starting loop
Polling events
Flushed
got access
read events
adter unsaves
dispatched
sink
processed resize

So the problem is in this line of the original file.

Thanks in advance for having a look at it!
-Siebencorgie

@elinorbgr
Copy link
Contributor

This looks really like the issue already encountered with the various examples.

TL;DR: you need to draw something on the window at least once before entering the event loop when using run_forever.

Long story: on wayland, the window do not appear until its contents have been defined at least once (as it's the contents that define the window characteristics such as the dimensions). As long as the window is not visible, it cannot receive any events from the user, and thus the event loop cannot make progress to reach the point where it draws something.

Regarding poll_events, looks like a plain old deadlock, I'll correct it right now.

@mitchmindtree
Copy link
Contributor

@vberger seeing as this issue seems to pop up so regularly, perhaps it's worth seeing if there is some way that we can have the window automatically show for glutin (and ultimately winit, but I imagine that might be trickier)?

glutin and winit aim to be a cross-platform crates and as such users are going to expect to be able to get the same behaviour on different platforms with the same code. Currently it can be frustrating to have to add extra drawing code before the loop starts because of this special case in wayland. For many users, the point of using a cross-platform crate like glutin or winit is often to avoid having to write this kind of special case code in the first place.

Is it possible for us to do some sort of check within the EventsLoop so that if run_forever or poll_events is called before anything is drawn to the windows, we manually "activate" the window perhaps somehow by drawing something like a transparent background? I realise this kind of thing might not be wayland's policy, however I think it could be worth it in the case of glutin and winit for consistency across backends.

Alternatively, rather than manually activating the glutin window like this, we could instead panic! if either run_forever or poll_events is called before anything is drawn to the screen. The panic! message could explain that windows must be drawn before they can receive events.

Either way, I think it would be nice if we could address this as the current behaviour is not consistent across platforms.

@elinorbgr
Copy link
Contributor

Yes I agree this you, I'm just not sure what would the proper course be. A few months ago, while talking about this with @tomaka, he didn't seem thrilled by the idea of automatically drawing something without the user knowing...

Certainly the easiest would be to just blitz a black content and call swap_buffers() once, at the creation of the window.

Otherwise, I'm not sure there is an easy way to check wether the window has been draw on at least once... ?

@mitchmindtree
Copy link
Contributor

Otherwise, I'm not sure there is an easy way to check wether the window has been draw on at least once... ?

@vberger come to think of it, perhaps we could do this when the WindowBuilder::build(&events_loop) method is called, as we know that the user can't have drawn to the window yet by that stage?

the idea of automatically drawing something without the user knowing...

I personally would be more concerned that the windows behave differently across each platform. From what I remember macOS draws a gray window by default and I think Windows draws a blank window too (though I could be wrong, it's been a while!) so I think your suggestion is perfectly reasonable in terms of consistency, and at the very least would be less surprising than the current behaviour.

@tomaka thoughts? If you give it the go ahead I could probably get around to implementing this at some point.

@tomaka
Copy link
Contributor

tomaka commented May 22, 2017

Drawing at initialization would solve the problem for glutin, but not for winit though.

@elinorbgr
Copy link
Contributor

The "initialization drawing" could be done in winit, by software-drawing a black frame and sending it to the wayland server. It would not add a lot of logic neither any dependency, as this is already what the wayland backend does to draw borders. And it would not impact the creation of an EGL/vulkan context afterwards.

Would this be ok, @tomaka ?

@tomaka
Copy link
Contributor

tomaka commented May 22, 2017

I guess so, yeah.

@mitchmindtree
Copy link
Contributor

I believe this has been addressed in rust-windowing/winit#188, rust-windowing/winit#190 and rust-windowing/winit#181 and can now be closed.

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

No branches or pull requests

4 participants