Skip to content
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ And please only add new entries to the top of this list, right below the `# Unre

# Unreleased

- The `EventLoopWindowTarget` passed to the `EventLoop::run` callback now has a lifetime of `'static`.
- The `EventLoopWindowTarget` passed to the `EventLoop::run_return` callback now lives for as long as the reference to the event loop.
- Build docs on `docs.rs` for iOS and Android as well.
- **Breaking:** Removed the `WindowAttributes` struct, since all its functionality is accessible from `WindowBuilder`.
- Added `WindowBuilder::transparent` getter to check if the user set `transparent` attribute.
Expand Down
2 changes: 1 addition & 1 deletion src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ impl<T> EventLoop<T> {
#[inline]
pub fn run<F>(self, event_handler: F) -> !
where
F: 'static + FnMut(Event<'_, T>, &EventLoopWindowTarget<T>, &mut ControlFlow),
F: 'static + FnMut(Event<'_, T>, &'static EventLoopWindowTarget<T>, &mut ControlFlow),
{
self.event_loop.run(event_handler)
}
Expand Down
36 changes: 32 additions & 4 deletions src/platform/run_return.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
target_os = "openbsd"
))]

use std::{
panic::{catch_unwind, AssertUnwindSafe},
process,
};

use crate::{
event::Event,
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
Expand Down Expand Up @@ -39,26 +44,49 @@ pub trait EventLoopExtRunReturn {
///
/// - **Unix-alikes** (**X11** or **Wayland**): This function returns `1` upon disconnection from
/// the display server.
fn run_return<F>(&mut self, event_handler: F) -> i32
fn run_return<'a, F>(&'a mut self, event_handler: F) -> i32
where
F: FnMut(
Event<'_, Self::UserEvent>,
&EventLoopWindowTarget<Self::UserEvent>,
&'a EventLoopWindowTarget<Self::UserEvent>,
&mut ControlFlow,
);
}

impl<T> EventLoopExtRunReturn for EventLoop<T> {
type UserEvent = T;

fn run_return<F>(&mut self, event_handler: F) -> i32
fn run_return<'a, F>(&'a mut self, event_handler: F) -> i32
where
F: FnMut(
Event<'_, Self::UserEvent>,
&EventLoopWindowTarget<Self::UserEvent>,
&'a EventLoopWindowTarget<Self::UserEvent>,
&mut ControlFlow,
),
{
self.event_loop.run_return(event_handler)
}
}

impl<T> crate::platform_impl::EventLoop<T> {
pub fn run<F>(mut self, callback: F) -> !
where
F: 'static + FnMut(Event<'_, T>, &'static EventLoopWindowTarget<T>, &mut ControlFlow),
{
// SAFETY: `process::exit` will terminate the entire program before `self` is
// dropped, and `catch_unwind` prevents control from from exiting this function
// by panicking, therefore it will live for the rest of the program ('static).
//
// I believe this pointer casting is the correct way to do it because that's how
// `Box::leak` is implemented (https://doc.rust-lang.org/1.60.0/src/alloc/boxed.rs.html#1147-1152)
let this: &'static mut Self = unsafe { &mut *(&mut self as *mut Self) };
// Note: we don't touch `callback` again if this unwinds, so it doesn't matter
// if it's unwind safe.
let exit_code = catch_unwind(AssertUnwindSafe(|| this.run_return(callback)))
// 101 seems to be the status code Rust uses for panics.
// Note: the panic message gets printed before unwinding, so we don't have to print it
// ourselves.
.unwrap_or(101);
process::exit(exit_code);
}
}
39 changes: 15 additions & 24 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,25 +271,16 @@ impl<T: 'static> EventLoop<T> {
}
}

pub fn run<F>(mut self, event_handler: F) -> !
pub fn run_return<'a, F>(&'a mut self, mut event_handler: F) -> i32
where
F: 'static
+ FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
{
let exit_code = self.run_return(event_handler);
::std::process::exit(exit_code);
}

pub fn run_return<F>(&mut self, mut event_handler: F) -> i32
where
F: FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
F: FnMut(event::Event<'_, T>, &'a event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
{
let mut control_flow = ControlFlow::default();

'event_loop: loop {
call_event_handler!(
event_handler,
self.window_target(),
&self.window_target,
control_flow,
event::Event::NewEvents(self.start_cause)
);
Expand All @@ -302,7 +293,7 @@ impl<T: 'static> EventLoop<T> {
Event::WindowCreated => {
call_event_handler!(
event_handler,
self.window_target(),
&self.window_target,
control_flow,
event::Event::Resumed
);
Expand All @@ -312,7 +303,7 @@ impl<T: 'static> EventLoop<T> {
Event::WindowDestroyed => {
call_event_handler!(
event_handler,
self.window_target(),
&self.window_target,
control_flow,
event::Event::Suspended
);
Expand All @@ -337,7 +328,7 @@ impl<T: 'static> EventLoop<T> {
};
call_event_handler!(
event_handler,
self.window_target(),
&self.window_target,
control_flow,
event
);
Expand All @@ -346,7 +337,7 @@ impl<T: 'static> EventLoop<T> {
Event::WindowHasFocus => {
call_event_handler!(
event_handler,
self.window_target(),
&self.window_target,
control_flow,
event::Event::WindowEvent {
window_id: window::WindowId(WindowId),
Expand All @@ -357,7 +348,7 @@ impl<T: 'static> EventLoop<T> {
Event::WindowLostFocus => {
call_event_handler!(
event_handler,
self.window_target(),
&self.window_target,
control_flow,
event::Event::WindowEvent {
window_id: window::WindowId(WindowId),
Expand Down Expand Up @@ -427,7 +418,7 @@ impl<T: 'static> EventLoop<T> {
};
call_event_handler!(
event_handler,
self.window_target(),
&self.window_target,
control_flow,
event
);
Expand Down Expand Up @@ -458,7 +449,7 @@ impl<T: 'static> EventLoop<T> {
};
call_event_handler!(
event_handler,
self.window_target(),
&self.window_target,
control_flow,
event
);
Expand All @@ -474,7 +465,7 @@ impl<T: 'static> EventLoop<T> {
while let Some(event) = user_queue.pop_front() {
call_event_handler!(
event_handler,
self.window_target(),
&self.window_target,
control_flow,
event::Event::UserEvent(event)
);
Expand All @@ -488,7 +479,7 @@ impl<T: 'static> EventLoop<T> {

call_event_handler!(
event_handler,
self.window_target(),
&self.window_target,
control_flow,
event::Event::MainEventsCleared
);
Expand All @@ -499,17 +490,17 @@ impl<T: 'static> EventLoop<T> {
window_id: window::WindowId(WindowId),
event: event::WindowEvent::Resized(size),
};
call_event_handler!(event_handler, self.window_target(), control_flow, event);
call_event_handler!(event_handler, &self.window_target, control_flow, event);
}

if redraw && self.running {
let event = event::Event::RedrawRequested(window::WindowId(WindowId));
call_event_handler!(event_handler, self.window_target(), control_flow, event);
call_event_handler!(event_handler, &self.window_target, control_flow, event);
}

call_event_handler!(
event_handler,
self.window_target(),
&self.window_target,
control_flow,
event::Event::RedrawEventsCleared
);
Expand Down
19 changes: 13 additions & 6 deletions src/platform_impl/ios/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ impl<T: 'static> EventLoop<T> {

pub fn run<F>(self, event_handler: F) -> !
where
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
F: 'static + FnMut(Event<'_, T>, &'static RootEventLoopWindowTarget<T>, &mut ControlFlow),
{
unsafe {
let application: *mut c_void = msg_send![class!(UIApplication), sharedApplication];
Expand All @@ -115,9 +115,16 @@ impl<T: 'static> EventLoop<T> {
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
Note: `EventLoop::run` calls `UIApplicationMain` on iOS"
);
// SAFETY: `UIApplicationMain` will never return, meaning that this function
// will never return, and so `self.window_target` will never be dropped and live
// for `'static`.
//
// I believe this pointer casting is the correct way to do it because that's how
// `Box::leak` is implemented (https://doc.rust-lang.org/1.60.0/src/alloc/boxed.rs.html#1147-1152)
let window_target: &'static _ = &*(&self.window_target as *const _);
app_state::will_launch(Box::new(EventLoopHandler {
f: event_handler,
event_loop: self.window_target,
event_loop: window_target,
}));

UIApplicationMain(
Expand Down Expand Up @@ -308,7 +315,7 @@ pub trait EventHandler: Debug {

struct EventLoopHandler<F, T: 'static> {
f: F,
event_loop: RootEventLoopWindowTarget<T>,
event_loop: &'static RootEventLoopWindowTarget<T>,
}

impl<F, T: 'static> Debug for EventLoopHandler<F, T> {
Expand All @@ -321,20 +328,20 @@ impl<F, T: 'static> Debug for EventLoopHandler<F, T> {

impl<F, T> EventHandler for EventLoopHandler<F, T>
where
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
F: 'static + FnMut(Event<'_, T>, &'static RootEventLoopWindowTarget<T>, &mut ControlFlow),
T: 'static,
{
fn handle_nonuser_event(&mut self, event: Event<'_, Never>, control_flow: &mut ControlFlow) {
(self.f)(
event.map_nonuser_event().unwrap(),
&self.event_loop,
self.event_loop,
control_flow,
);
}

fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
for event in self.event_loop.p.receiver.try_iter() {
(self.f)(Event::UserEvent(event), &self.event_loop, control_flow);
(self.f)(Event::UserEvent(event), self.event_loop, control_flow);
}
}
}
Expand Down
17 changes: 5 additions & 12 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -710,20 +710,13 @@ impl<T: 'static> EventLoop<T> {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.create_proxy(); as EventLoopProxy)
}

pub fn run_return<F>(&mut self, callback: F) -> i32
pub fn run_return<'a, F>(&'a mut self, callback: F) -> i32
where
F: FnMut(crate::event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
F: FnMut(crate::event::Event<'_, T>, &'a RootELW<T>, &mut ControlFlow),
{
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_return(callback))
}

pub fn run<F>(self, callback: F) -> !
where
F: 'static + FnMut(crate::event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run(callback))
}

pub fn window_target(&self) -> &crate::event_loop::EventLoopWindowTarget<T> {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.window_target())
}
Expand Down Expand Up @@ -798,13 +791,13 @@ impl<T> EventLoopWindowTarget<T> {
}
}

fn sticky_exit_callback<T, F>(
fn sticky_exit_callback<'a, T, F>(
evt: Event<'_, T>,
target: &RootELW<T>,
target: &'a RootELW<T>,
control_flow: &mut ControlFlow,
callback: &mut F,
) where
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
F: FnMut(Event<'_, T>, &'a RootELW<T>, &mut ControlFlow),
{
// make ControlFlow::ExitWithCode sticky by providing a dummy
// control flow reference if it is already ExitWithCode.
Expand Down
Loading