Skip to content

Commit

Permalink
examples: Implement proper Event::Resumed semantics
Browse files Browse the repository at this point in the history
On Android the backing buffer (`NativeWindow`) disappears when the
application is not focussed and/or the screen is locked.  Winit handles
this by requiring apps to create their `raw_window_handle()` consumers
_after_ `Event::Resumed` and to clean it up _before_ returning from
`Event::Suspended`.  For consistency Winit also sends `Resumed` on all
other platforms during init.
  • Loading branch information
MarijnS95 committed Dec 16, 2024
1 parent 94f6d7d commit 9f6a9cf
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 126 deletions.
28 changes: 17 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,27 @@ mod winit_app;
fn main() {
let event_loop = EventLoop::new().unwrap();
let mut app = winit_app::WinitAppBuilder::with_init(|elwt| {
let window = {
let window = elwt.create_window(Window::default_attributes());
Rc::new(window.unwrap())
};
let context = softbuffer::Context::new(window.clone()).unwrap();
let surface = softbuffer::Surface::new(&context, window.clone()).unwrap();
(window, surface)
}).with_event_handler(|state, event, elwt| {
let (window, surface) = state;
let mut app = winit_app::WinitAppBuilder::with_init(
|elwt| {
let window = {
let window = elwt.create_window(Window::default_attributes());
Rc::new(window.unwrap())
};
let context = softbuffer::Context::new(window.clone()).unwrap();
(window, context)
},
|_elwt, (window, context)| softbuffer::Surface::new(context, window.clone()).unwrap(),
)
.with_event_handler(|(window, _context), surface, event, elwt| {
elwt.set_control_flow(ControlFlow::Wait);
match event {
Event::WindowEvent { window_id, event: WindowEvent::RedrawRequested } if window_id == window.id() => {
let Some(surface) = surface else {
eprintln!("RedrawRequested fired before Resumed or after Suspended");
return;
};
let (width, height) = {
let size = window.inner_size();
(size.width, size.height)
Expand Down
34 changes: 24 additions & 10 deletions examples/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,23 @@ fn main() {
let event_loop = EventLoop::new().unwrap();
let start = Instant::now();

let app = winit_app::WinitAppBuilder::with_init(|event_loop| {
let window = winit_app::make_window(event_loop, |w| w);
let app = winit_app::WinitAppBuilder::with_init(
|event_loop| {
let window = winit_app::make_window(event_loop, |w| w);

let context = softbuffer::Context::new(window.clone()).unwrap();
let surface = softbuffer::Surface::new(&context, window.clone()).unwrap();
let context = softbuffer::Context::new(window.clone()).unwrap();

let old_size = (0, 0);
let frames = pre_render_frames(0, 0);
let old_size = (0, 0);
let frames = pre_render_frames(0, 0);

(window, surface, old_size, frames)
})
.with_event_handler(move |state, event, elwt| {
let (window, surface, old_size, frames) = state;
(window, context, old_size, frames)
},
|_elwft, (window, context, _old_size, _frames)| {
softbuffer::Surface::new(context, window.clone()).unwrap()
},
)
.with_event_handler(move |state, surface, event, elwt| {
let (window, _context, old_size, frames) = state;

elwt.set_control_flow(ControlFlow::Poll);

Expand All @@ -35,6 +39,11 @@ fn main() {
window_id,
event: WindowEvent::Resized(size),
} if window_id == window.id() => {
let Some(surface) = surface else {
eprintln!("Resized fired before Resumed or after Suspended");
return;
};

if let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
{
Expand All @@ -45,6 +54,11 @@ fn main() {
window_id,
event: WindowEvent::RedrawRequested,
} if window_id == window.id() => {
let Some(surface) = surface else {
eprintln!("RedrawRequested fired before Resumed or after Suspended");
return;
};

let size = window.inner_size();
if let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
Expand Down
49 changes: 29 additions & 20 deletions examples/fruit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,44 @@ fn main() {

let event_loop = EventLoop::new().unwrap();

let app = winit_app::WinitAppBuilder::with_init(move |elwt| {
let window = winit_app::make_window(elwt, |w| {
w.with_inner_size(winit::dpi::PhysicalSize::new(width, height))
});
let app = winit_app::WinitAppBuilder::with_init(
move |elwt| {
let window = winit_app::make_window(elwt, |w| {
w.with_inner_size(winit::dpi::PhysicalSize::new(width, height))
});

let context = softbuffer::Context::new(window.clone()).unwrap();
let mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap();
let context = softbuffer::Context::new(window.clone()).unwrap();

// Intentionally only set the size of the surface once, at creation.
// This is needed if the window chooses to ignore the size we passed in above, and for the
// platforms softbuffer supports that don't yet extract the size from the window.
surface
.resize(
NonZeroU32::new(width).unwrap(),
NonZeroU32::new(height).unwrap(),
)
.unwrap();

(window, surface)
})
.with_event_handler(move |state, event, elwt| {
let (window, surface) = state;
(window, context)
},
move |_elwt, (window, context)| {
let mut surface = softbuffer::Surface::new(context, window.clone()).unwrap();
// Intentionally only set the size of the surface once, at creation.
// This is needed if the window chooses to ignore the size we passed in above, and for the
// platforms softbuffer supports that don't yet extract the size from the window.
surface
.resize(
NonZeroU32::new(width).unwrap(),
NonZeroU32::new(height).unwrap(),
)
.unwrap();
surface
},
)
.with_event_handler(move |state, surface, event, elwt| {
let (window, _context) = state;
elwt.set_control_flow(ControlFlow::Wait);

match event {
Event::WindowEvent {
window_id,
event: WindowEvent::RedrawRequested,
} if window_id == window.id() => {
let Some(surface) = surface else {
eprintln!("RedrawRequested fired before Resumed or after Suspended");
return;
};

let mut buffer = surface.buffer_mut().unwrap();
let width = fruit.width() as usize;
for (x, y, pixel) in fruit.pixels() {
Expand Down
35 changes: 24 additions & 11 deletions examples/rectangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,24 @@ fn redraw(buffer: &mut [u32], width: usize, height: usize, flag: bool) {
fn main() {
let event_loop = EventLoop::new().unwrap();

let app = winit_app::WinitAppBuilder::with_init(|elwt| {
let window = winit_app::make_window(elwt, |w| {
w.with_title("Press space to show/hide a rectangle")
});
let app = winit_app::WinitAppBuilder::with_init(
|elwt| {
let window = winit_app::make_window(elwt, |w| {
w.with_title("Press space to show/hide a rectangle")
});

let context = softbuffer::Context::new(window.clone()).unwrap();
let surface = softbuffer::Surface::new(&context, window.clone()).unwrap();
let context = softbuffer::Context::new(window.clone()).unwrap();

let flag = false;
let flag = false;

(window, surface, flag)
})
.with_event_handler(|state, event, elwt| {
let (window, surface, flag) = state;
(window, context, flag)
},
|_elwt, (window, context, _flag)| {
softbuffer::Surface::new(context, window.clone()).unwrap()
},
)
.with_event_handler(|state, surface, event, elwt| {
let (window, _context, flag) = state;

elwt.set_control_flow(ControlFlow::Wait);

Expand All @@ -47,6 +51,11 @@ fn main() {
window_id,
event: WindowEvent::Resized(size),
} if window_id == window.id() => {
let Some(surface) = surface else {
eprintln!("Resized fired before Resumed or after Suspended");
return;
};

if let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
{
Expand All @@ -58,6 +67,10 @@ fn main() {
window_id,
event: WindowEvent::RedrawRequested,
} if window_id == window.id() => {
let Some(surface) = surface else {
eprintln!("RedrawRequested fired before Resumed or after Suspended");
return;
};
// Grab the window's client area dimensions, and ensure they're valid
let size = window.inner_size();
if let (Some(width), Some(height)) =
Expand Down
71 changes: 50 additions & 21 deletions examples/utils/winit_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,76 +31,94 @@ pub(crate) fn make_window(
}

/// Easily constructable winit application.
pub(crate) struct WinitApp<T, Init, Handler> {
/// Closure to initialize state.
pub(crate) struct WinitApp<T, S, Init, InitSurface, Handler> {
/// Closure to initialize `state`.
init: Init,

/// Closure to initialize `surface_state`.
init_surface: InitSurface,

/// Closure to run on window events.
event: Handler,

/// Contained state.
state: Option<T>,

/// Contained surface state.
surface_state: Option<S>,
}

/// Builder that makes it so we don't have to name `T`.
pub(crate) struct WinitAppBuilder<T, Init> {
/// Closure to initialize state.
pub(crate) struct WinitAppBuilder<T, S, Init, InitSurface> {
/// Closure to initialize `state`.
init: Init,

/// Closure to initialize `surface_state`.
init_surface: InitSurface,

/// Eat the type parameter.
_marker: PhantomData<Option<T>>,
_marker: PhantomData<(Option<T>, Option<S>)>,
}

impl<T, Init> WinitAppBuilder<T, Init>
impl<T, S, Init, InitSurface> WinitAppBuilder<T, S, Init, InitSurface>
where
Init: FnMut(&ActiveEventLoop) -> T,
InitSurface: FnMut(&ActiveEventLoop, &mut T) -> S,
{
/// Create with an "init" closure.
pub(crate) fn with_init(init: Init) -> Self {
pub(crate) fn with_init(init: Init, init_surface: InitSurface) -> Self {
Self {
init,
init_surface,
_marker: PhantomData,
}
}

/// Build a new application.
pub(crate) fn with_event_handler<F>(self, handler: F) -> WinitApp<T, Init, F>
pub(crate) fn with_event_handler<F>(self, handler: F) -> WinitApp<T, S, Init, InitSurface, F>
where
F: FnMut(&mut T, Event<()>, &ActiveEventLoop),
F: FnMut(&mut T, Option<&mut S>, Event<()>, &ActiveEventLoop),
{
WinitApp::new(self.init, handler)
WinitApp::new(self.init, self.init_surface, handler)
}
}

impl<T, Init, Handler> WinitApp<T, Init, Handler>
impl<T, S, Init, InitSurface, Handler> WinitApp<T, S, Init, InitSurface, Handler>
where
Init: FnMut(&ActiveEventLoop) -> T,
Handler: FnMut(&mut T, Event<()>, &ActiveEventLoop),
InitSurface: FnMut(&ActiveEventLoop, &mut T) -> S,
Handler: FnMut(&mut T, Option<&mut S>, Event<()>, &ActiveEventLoop),
{
/// Create a new application.
pub(crate) fn new(init: Init, event: Handler) -> Self {
pub(crate) fn new(init: Init, init_surface: InitSurface, event: Handler) -> Self {
Self {
init,
init_surface,
event,
state: None,
surface_state: None,
}
}
}

impl<T, Init, Handler> ApplicationHandler for WinitApp<T, Init, Handler>
impl<T, S, Init, InitSurface, Handler> ApplicationHandler
for WinitApp<T, S, Init, InitSurface, Handler>
where
Init: FnMut(&ActiveEventLoop) -> T,
Handler: FnMut(&mut T, Event<()>, &ActiveEventLoop),
InitSurface: FnMut(&ActiveEventLoop, &mut T) -> S,
Handler: FnMut(&mut T, Option<&mut S>, Event<()>, &ActiveEventLoop),
{
fn resumed(&mut self, el: &ActiveEventLoop) {
debug_assert!(self.state.is_none());
self.state = Some((self.init)(el));
let mut state = (self.init)(el);
self.surface_state = Some((self.init_surface)(el, &mut state));
self.state = Some(state);
}

fn suspended(&mut self, _event_loop: &ActiveEventLoop) {
let state = self.state.take();
debug_assert!(state.is_some());
drop(state);
let surface_state = self.surface_state.take();
debug_assert!(surface_state.is_some());
drop(surface_state);
}

fn window_event(
Expand All @@ -110,12 +128,23 @@ where
event: WindowEvent,
) {
let state = self.state.as_mut().unwrap();
(self.event)(state, Event::WindowEvent { window_id, event }, event_loop);
let surface_state = self.surface_state.as_mut();
(self.event)(
state,
surface_state,
Event::WindowEvent { window_id, event },
event_loop,
);
}

fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
if let Some(state) = self.state.as_mut() {
(self.event)(state, Event::AboutToWait, event_loop);
(self.event)(
state,
self.surface_state.as_mut(),
Event::AboutToWait,
event_loop,
);
}
}
}
Loading

0 comments on commit 9f6a9cf

Please sign in to comment.