From 1c68be0631e9614e2c9c5ff0d3f032b60487a30f Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Fri, 11 Mar 2022 18:15:33 +0300 Subject: [PATCH] On Wayland, fix consecutive run_return not polling If you try to use `EventLoop::run_return` API in a way that you do on demand polling of events it won't actually poll, since in such strategy the `ControlFlow::Exit` is sent right before Wayland backend starts to poll. This was observed with smithay compositor Anvil which was doing this particular thing leading to GNOME thinking that app isn't responding, due to connection not being polled. --- CHANGELOG.md | 1 + .../linux/wayland/event_loop/mod.rs | 206 +++++++++--------- 2 files changed, 102 insertions(+), 105 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf112028..0ea561ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ And please only add new entries to the top of this list, right below the `# Unre - **Breaking:** The platform specific extensions for Windows `winit::platform::windows` have changed. All `HANDLE`-like types e.g. `HWND` and `HMENU` were converted from winapi types or `*mut c_void` to `isize`. This was done to be consistent with the type definitions in windows-sys and to not expose internal dependencies. - The internal bindings to the [Windows API](https://docs.microsoft.com/en-us/windows/) were changed from the unofficial [winapi](https://github.com/retep998/winapi-rs) bindings to the official Microsoft [windows-sys](https://github.com/microsoft/windows-rs) bindings. - On Wayland, fix resize and scale factor changes not being propagated properly. +- On Wayland, fix polling during consecutive `EventLoop::run_return` invocations. # 0.26.1 (2022-01-05) diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index 9e97225f..0a69365b 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -214,11 +214,7 @@ impl EventLoop { where F: FnMut(Event<'_, T>, &RootEventLoopWindowTarget, &mut ControlFlow), { - // Send pending events to the server. - let _ = self.display.flush(); - - let mut control_flow = ControlFlow::default(); - + let mut control_flow = ControlFlow::Poll; let pending_user_events = self.pending_user_events.clone(); callback( @@ -237,6 +233,106 @@ impl EventLoop { // with an API to do that via some event. // Still, we set the exit code to the error's OS error code, or to 1 if not possible. let exit_code = loop { + // Send pending events to the server. + let _ = self.display.flush(); + + // During the run of the user callback, some other code monitoring and reading the + // Wayland socket may have been run (mesa for example does this with vsync), if that + // is the case, some events may have been enqueued in our event queue. + // + // If some messages are there, the event loop needs to behave as if it was instantly + // woken up by messages arriving from the Wayland socket, to avoid delaying the + // dispatch of these events until we're woken up again. + let instant_wakeup = { + let mut wayland_source = self.wayland_dispatcher.as_source_mut(); + let queue = wayland_source.queue(); + let state = match &mut self.window_target.p { + PlatformEventLoopWindowTarget::Wayland(window_target) => { + window_target.state.get_mut() + } + #[cfg(feature = "x11")] + _ => unreachable!(), + }; + + match queue.dispatch_pending(state, |_, _, _| unimplemented!()) { + Ok(dispatched) => dispatched > 0, + Err(error) => break error.raw_os_error().unwrap_or(1), + } + }; + + match control_flow { + ControlFlow::ExitWithCode(code) => break code, + ControlFlow::Poll => { + // Non-blocking dispatch. + let timeout = Duration::from_millis(0); + if let Err(error) = self.loop_dispatch(Some(timeout)) { + break error.raw_os_error().unwrap_or(1); + } + + callback( + Event::NewEvents(StartCause::Poll), + &self.window_target, + &mut control_flow, + ); + } + ControlFlow::Wait => { + let timeout = if instant_wakeup { + Some(Duration::from_millis(0)) + } else { + None + }; + + if let Err(error) = self.loop_dispatch(timeout) { + break error.raw_os_error().unwrap_or(1); + } + + callback( + Event::NewEvents(StartCause::WaitCancelled { + start: Instant::now(), + requested_resume: None, + }), + &self.window_target, + &mut control_flow, + ); + } + ControlFlow::WaitUntil(deadline) => { + let start = Instant::now(); + + // Compute the amount of time we'll block for. + let duration = if deadline > start && !instant_wakeup { + deadline - start + } else { + Duration::from_millis(0) + }; + + if let Err(error) = self.loop_dispatch(Some(duration)) { + break error.raw_os_error().unwrap_or(1); + } + + let now = Instant::now(); + + if now < deadline { + callback( + Event::NewEvents(StartCause::WaitCancelled { + start, + requested_resume: Some(deadline), + }), + &self.window_target, + &mut control_flow, + ) + } else { + callback( + Event::NewEvents(StartCause::ResumeTimeReached { + start, + requested_resume: deadline, + }), + &self.window_target, + &mut control_flow, + ) + } + } + } + // Handle pending user events. We don't need back buffer, since we can't dispatch // user events indirectly via callback to the user. for user_event in pending_user_events.borrow_mut().drain(..) { @@ -424,106 +520,6 @@ impl EventLoop { &mut control_flow, &mut callback, ); - - // Send pending events to the server. - let _ = self.display.flush(); - - // During the run of the user callback, some other code monitoring and reading the - // Wayland socket may have been run (mesa for example does this with vsync), if that - // is the case, some events may have been enqueued in our event queue. - // - // If some messages are there, the event loop needs to behave as if it was instantly - // woken up by messages arriving from the Wayland socket, to avoid delaying the - // dispatch of these events until we're woken up again. - let instant_wakeup = { - let mut wayland_source = self.wayland_dispatcher.as_source_mut(); - let queue = wayland_source.queue(); - let state = match &mut self.window_target.p { - PlatformEventLoopWindowTarget::Wayland(window_target) => { - window_target.state.get_mut() - } - #[cfg(feature = "x11")] - _ => unreachable!(), - }; - - match queue.dispatch_pending(state, |_, _, _| unimplemented!()) { - Ok(dispatched) => dispatched > 0, - Err(error) => break error.raw_os_error().unwrap_or(1), - } - }; - - match control_flow { - ControlFlow::ExitWithCode(code) => break code, - ControlFlow::Poll => { - // Non-blocking dispatch. - let timeout = Duration::from_millis(0); - if let Err(error) = self.loop_dispatch(Some(timeout)) { - break error.raw_os_error().unwrap_or(1); - } - - callback( - Event::NewEvents(StartCause::Poll), - &self.window_target, - &mut control_flow, - ); - } - ControlFlow::Wait => { - let timeout = if instant_wakeup { - Some(Duration::from_millis(0)) - } else { - None - }; - - if let Err(error) = self.loop_dispatch(timeout) { - break error.raw_os_error().unwrap_or(1); - } - - callback( - Event::NewEvents(StartCause::WaitCancelled { - start: Instant::now(), - requested_resume: None, - }), - &self.window_target, - &mut control_flow, - ); - } - ControlFlow::WaitUntil(deadline) => { - let start = Instant::now(); - - // Compute the amount of time we'll block for. - let duration = if deadline > start && !instant_wakeup { - deadline - start - } else { - Duration::from_millis(0) - }; - - if let Err(error) = self.loop_dispatch(Some(duration)) { - break error.raw_os_error().unwrap_or(1); - } - - let now = Instant::now(); - - if now < deadline { - callback( - Event::NewEvents(StartCause::WaitCancelled { - start, - requested_resume: Some(deadline), - }), - &self.window_target, - &mut control_flow, - ) - } else { - callback( - Event::NewEvents(StartCause::ResumeTimeReached { - start, - requested_resume: deadline, - }), - &self.window_target, - &mut control_flow, - ) - } - } - } }; callback(Event::LoopDestroyed, &self.window_target, &mut control_flow);