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

examples/winit: Implement proper resumed() semantics #127

Merged
merged 2 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading