Skip to content

Commit

Permalink
Fix run_on_demand exiting on consequent call
Browse files Browse the repository at this point in the history
Fixes #3284.
  • Loading branch information
kchibisov committed Dec 24, 2023
1 parent 5ca810b commit 11d1b7a
Show file tree
Hide file tree
Showing 13 changed files with 141 additions and 69 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Unreleased` header.

- On Web, fix context menu not being disabled by `with_prevent_default(true)`.
- On Wayland, fix `WindowEvent::Destroyed` not being delivered after destroying window.
- Fix `EventLoopExtRunOnDemand::run_on_demand` not working for consequent invocation

# 0.29.5

Expand Down
115 changes: 71 additions & 44 deletions examples/util/fill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,30 @@
//! The `softbuffer` crate is used, largely because of its ease of use. `glutin` or `wgpu` could
//! also be used to fill the window buffer, but they are more complicated to use.

use winit::window::Window;
#[allow(unused_imports)]
pub use platform::cleanup_window;
pub use platform::fill_window;

#[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))]
pub(super) fn fill_window(window: &Window) {
use softbuffer::{Context, Surface};
mod platform {
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem::ManuallyDrop;
use std::num::NonZeroU32;

use softbuffer::{Context, Surface};
use winit::window::Window;
use winit::window::WindowId;

thread_local! {
// NOTE: You should never do things like that, create context and drop it before
// you drop the event loop. We do this for brevity to not blow up examples. We use
// ManuallyDrop to prevent destructors from running.
//
// A static, thread-local map of graphics contexts to open windows.
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = ManuallyDrop::new(RefCell::new(None));
}

/// The graphics context used to draw to a window.
struct GraphicsContext {
/// The global softbuffer context.
Expand All @@ -35,55 +48,69 @@ pub(super) fn fill_window(window: &Window) {
}
}

fn surface(&mut self, w: &Window) -> &mut Surface {
self.surfaces.entry(w.id()).or_insert_with(|| {
unsafe { Surface::new(&self.context, w) }
fn create_surface(&mut self, window: &Window) -> &mut Surface {
self.surfaces.entry(window.id()).or_insert_with(|| {
unsafe { Surface::new(&self.context, window) }
.expect("Failed to create a softbuffer surface")
})
}

fn destroy_surface(&mut self, window: &Window) {
self.surfaces.remove(&window.id());
}
}

thread_local! {
// NOTE: You should never do things like that, create context and drop it before
// you drop the event loop. We do this for brevity to not blow up examples. We use
// ManuallyDrop to prevent destructors from running.
//
// A static, thread-local map of graphics contexts to open windows.
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = ManuallyDrop::new(RefCell::new(None));
pub fn fill_window(window: &Window) {
GC.with(|gc| {
let size = window.inner_size();
let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
else {
return;
};

// Either get the last context used or create a new one.
let mut gc = gc.borrow_mut();
let surface = gc
.get_or_insert_with(|| GraphicsContext::new(window))
.create_surface(window);

// Fill a buffer with a solid color.
const DARK_GRAY: u32 = 0xFF181818;

surface
.resize(width, height)
.expect("Failed to resize the softbuffer surface");

let mut buffer = surface
.buffer_mut()
.expect("Failed to get the softbuffer buffer");
buffer.fill(DARK_GRAY);
buffer
.present()
.expect("Failed to present the softbuffer buffer");
})
}

GC.with(|gc| {
let size = window.inner_size();
let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
else {
return;
};

// Either get the last context used or create a new one.
let mut gc = gc.borrow_mut();
let surface = gc
.get_or_insert_with(|| GraphicsContext::new(window))
.surface(window);

// Fill a buffer with a solid color.
const DARK_GRAY: u32 = 0xFF181818;

surface
.resize(width, height)
.expect("Failed to resize the softbuffer surface");

let mut buffer = surface
.buffer_mut()
.expect("Failed to get the softbuffer buffer");
buffer.fill(DARK_GRAY);
buffer
.present()
.expect("Failed to present the softbuffer buffer");
})
#[allow(dead_code)]
pub fn cleanup_window(window: &Window) {
GC.with(|gc| {
let mut gc = gc.borrow_mut();
if let Some(context) = gc.as_mut() {
context.destroy_surface(window);
}
});
}
}

#[cfg(not(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios")))))]
pub(super) fn fill_window(_window: &Window) {
// No-op on mobile platforms.
mod platform {
pub fn fill_window(_window: &winit::window::Window) {
// No-op on mobile platforms.
}

#[allow(dead_code)]
pub fn cleanup_window(_window: &winit::window::Window) {
// No-op on mobile platforms.
}
}
1 change: 1 addition & 0 deletions examples/window_on_demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ fn main() -> Result<(), impl std::error::Error> {
window_id,
} if window.id() == window_id => {
println!("--------------------------------------------------------- Window {idx} CloseRequested");
fill::cleanup_window(window);
app.window = None;
}
Event::AboutToWait => window.request_redraw(),
Expand Down
8 changes: 8 additions & 0 deletions src/platform/run_on_demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
{
self.event_loop.window_target().clear_exit();
self.event_loop.run_on_demand(event_handler)
}
}

impl<T> EventLoopWindowTarget<T> {
/// Clear exit status.
pub(crate) fn clear_exit(&self) {
self.p.clear_exit()
}
}
4 changes: 4 additions & 0 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,10 @@ impl<T: 'static> EventLoopWindowTarget<T> {
self.exit.set(true)
}

pub(crate) fn clear_exit(&self) {
self.exit.set(false)
}

pub(crate) fn exiting(&self) -> bool {
self.exit.get()
}
Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,10 @@ impl<T> EventLoopWindowTarget<T> {
x11_or_wayland!(match self; Self(evlp) => evlp.control_flow())
}

pub(crate) fn clear_exit(&self) {
x11_or_wayland!(match self; Self(evlp) => evlp.clear_exit())
}

pub(crate) fn exit(&self) {
x11_or_wayland!(match self; Self(evlp) => evlp.exit())
}
Expand Down
28 changes: 28 additions & 0 deletions src/platform_impl/linux/wayland/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,34 @@ pub struct EventLoopWindowTarget<T> {
}

impl<T> EventLoopWindowTarget<T> {
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
self.control_flow.set(control_flow)
}

pub(crate) fn control_flow(&self) -> ControlFlow {
self.control_flow.get()
}

pub(crate) fn exit(&self) {
self.exit.set(Some(0))
}

pub(crate) fn clear_exit(&self) {
self.exit.set(None)
}

pub(crate) fn exiting(&self) -> bool {
self.exit.get().is_some()
}

pub(crate) fn set_exit_code(&self, code: i32) {
self.exit.set(Some(code))
}

pub(crate) fn exit_code(&self) -> Option<i32> {
self.exit.get()
}

#[inline]
pub fn listen_device_events(&self, _allowed: DeviceEvents) {}

Expand Down
25 changes: 0 additions & 25 deletions src/platform_impl/linux/wayland/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use sctk::reexports::client::Proxy;
use sctk::output::OutputData;

use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use crate::event_loop::ControlFlow;
use crate::platform_impl::platform::VideoMode as PlatformVideoMode;

use super::event_loop::EventLoopWindowTarget;
Expand All @@ -24,30 +23,6 @@ impl<T> EventLoopWindowTarget<T> {
// There's no primary monitor on Wayland.
None
}

pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
self.control_flow.set(control_flow)
}

pub(crate) fn control_flow(&self) -> ControlFlow {
self.control_flow.get()
}

pub(crate) fn exit(&self) {
self.exit.set(Some(0))
}

pub(crate) fn exiting(&self) -> bool {
self.exit.get().is_some()
}

pub(crate) fn set_exit_code(&self, code: i32) {
self.exit.set(Some(code))
}

pub(crate) fn exit_code(&self) -> Option<i32> {
self.exit.get()
}
}

#[derive(Clone, Debug)]
Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/linux/x11/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,10 @@ impl<T> EventLoopWindowTarget<T> {
self.exit.set(Some(0))
}

pub(crate) fn clear_exit(&self) {
self.exit.set(None)
}

pub(crate) fn exiting(&self) -> bool {
self.exit.get().is_some()
}
Expand Down
8 changes: 8 additions & 0 deletions src/platform_impl/macos/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ impl Handler {
self.exit.store(true, Ordering::Relaxed)
}

pub fn clear_exit(&self) {
self.exit.store(false, Ordering::Relaxed)
}

pub fn exiting(&self) -> bool {
self.exit.load(Ordering::Relaxed)
}
Expand Down Expand Up @@ -434,6 +438,10 @@ impl AppState {
HANDLER.exit()
}

pub fn clear_exit() {
HANDLER.clear_exit()
}

pub fn exiting() -> bool {
HANDLER.exiting()
}
Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/macos/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ impl<T: 'static> EventLoopWindowTarget<T> {
AppState::exit()
}

pub(crate) fn clear_exit(&self) {
AppState::clear_exit()
}

pub(crate) fn exiting(&self) -> bool {
AppState::exiting()
}
Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/windows/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,10 @@ impl<T> EventLoopWindowTarget<T> {
self.runner_shared.exit_code().is_some()
}

pub(crate) fn clear_exit(&self) {
self.runner_shared.clear_exit();
}

fn exit_code(&self) -> Option<i32> {
self.runner_shared.exit_code()
}
Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/windows/event_loop/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ impl<T> EventLoopRunner<T> {
self.exit.get()
}

pub fn clear_exit(&self) {
self.exit.set(None);
}

pub fn should_buffer(&self) -> bool {
let handler = self.event_handler.take();
let should_buffer = handler.is_none();
Expand Down

0 comments on commit 11d1b7a

Please sign in to comment.