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 Jul 21, 2024
1 parent 701026d commit eb6be27
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 108 deletions.
28 changes: 17 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,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
28 changes: 18 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,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;
};
if let (Some(width), Some(height)) = {
let size = window.inner_size();
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
Expand Down
26 changes: 16 additions & 10 deletions examples/fruit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,31 @@ 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 surface = softbuffer::Surface::new(&context, window.clone()).unwrap();
let context = softbuffer::Context::new(window.clone()).unwrap();

(window, surface)
})
.with_event_handler(move |state, event, elwt| {
let (window, surface) = state;
(window, context)
},
|_elwt, (window, context)| softbuffer::Surface::new(context, window.clone()).unwrap(),
)
.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;
};
surface
.resize(
NonZeroU32::new(fruit.width()).unwrap(),
Expand Down
30 changes: 19 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,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
if let (Some(width), Some(height)) = {
let size = window.inner_size();
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,
);
}
}
}
21 changes: 13 additions & 8 deletions examples/winit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,28 @@ mod winit_app;
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);
let app = winit_app::WinitAppBuilder::with_init(
|elwt| {
let window = winit_app::make_window(elwt, |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();

(window, surface)
})
.with_event_handler(|state, event, elwt| {
let (window, surface) = state;
(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;
};
if let (Some(width), Some(height)) = {
let size = window.inner_size();
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
Expand Down
Loading

0 comments on commit eb6be27

Please sign in to comment.