Add initialization closure and drop on exit

This commit is contained in:
Mads Marquart
2024-12-03 12:17:58 +01:00
parent 164bf85b5b
commit fbc6fdc30c
25 changed files with 380 additions and 322 deletions

View File

@@ -13,26 +13,12 @@ fn main() -> Result<(), impl std::error::Error> {
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
#[derive(Default)]
struct Application { struct Application {
parent_window_id: Option<WindowId>, parent_window_id: WindowId,
windows: HashMap<WindowId, Box<dyn Window>>, windows: HashMap<WindowId, Box<dyn Window>>,
} }
impl ApplicationHandler for Application { impl ApplicationHandler for Application {
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let attributes = WindowAttributes::default()
.with_title("parent window")
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_surface_size(LogicalSize::new(640.0f32, 480.0f32));
let window = event_loop.create_window(attributes).unwrap();
println!("Parent window id: {:?})", window.id());
self.parent_window_id = Some(window.id());
self.windows.insert(window.id(), window);
}
fn window_event( fn window_event(
&mut self, &mut self,
event_loop: &dyn ActiveEventLoop, event_loop: &dyn ActiveEventLoop,
@@ -56,7 +42,7 @@ fn main() -> Result<(), impl std::error::Error> {
event: KeyEvent { state: ElementState::Pressed, .. }, event: KeyEvent { state: ElementState::Pressed, .. },
.. ..
} => { } => {
let parent_window = self.windows.get(&self.parent_window_id.unwrap()).unwrap(); let parent_window = self.windows.get(&self.parent_window_id).unwrap();
let child_window = spawn_child_window(parent_window.as_ref(), event_loop); let child_window = spawn_child_window(parent_window.as_ref(), event_loop);
let child_id = child_window.id(); let child_id = child_window.id();
println!("Child window created with id: {child_id:?}"); println!("Child window created with id: {child_id:?}");
@@ -72,6 +58,20 @@ fn main() -> Result<(), impl std::error::Error> {
} }
} }
fn init(event_loop: &dyn ActiveEventLoop) -> Application {
let attributes = WindowAttributes::default()
.with_title("parent window")
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_surface_size(LogicalSize::new(640.0f32, 480.0f32));
let window = event_loop.create_window(attributes).unwrap();
println!("Parent window id: {:?})", window.id());
let parent_window_id = window.id();
let windows = HashMap::from([(window.id(), window)]);
Application { parent_window_id, windows }
}
fn spawn_child_window( fn spawn_child_window(
parent: &dyn Window, parent: &dyn Window,
event_loop: &dyn ActiveEventLoop, event_loop: &dyn ActiveEventLoop,
@@ -89,7 +89,7 @@ fn main() -> Result<(), impl std::error::Error> {
} }
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
event_loop.run_app(Application::default()) event_loop.run(init)
} }
#[cfg(not(any(x11_platform, macos_platform, windows_platform)))] #[cfg(not(any(x11_platform, macos_platform, windows_platform)))]

View File

@@ -43,16 +43,31 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
event_loop.run_app(ControlFlowDemo::default()) event_loop.run(ControlFlowDemo::new)
} }
#[derive(Default)]
struct ControlFlowDemo { struct ControlFlowDemo {
mode: Mode, mode: Mode,
request_redraw: bool, request_redraw: bool,
wait_cancelled: bool, wait_cancelled: bool,
close_requested: bool, close_requested: bool,
window: Option<Box<dyn Window>>, window: Box<dyn Window>,
}
impl ControlFlowDemo {
fn new(event_loop: &dyn ActiveEventLoop) -> Self {
let window_attributes = WindowAttributes::default().with_title(
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
);
let window = event_loop.create_window(window_attributes).unwrap();
Self {
mode: Mode::default(),
request_redraw: false,
wait_cancelled: false,
close_requested: false,
window,
}
}
} }
impl ApplicationHandler for ControlFlowDemo { impl ApplicationHandler for ControlFlowDemo {
@@ -65,13 +80,6 @@ impl ApplicationHandler for ControlFlowDemo {
} }
} }
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let window_attributes = WindowAttributes::default().with_title(
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
);
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
fn window_event( fn window_event(
&mut self, &mut self,
_event_loop: &dyn ActiveEventLoop, _event_loop: &dyn ActiveEventLoop,
@@ -112,9 +120,8 @@ impl ApplicationHandler for ControlFlowDemo {
_ => (), _ => (),
}, },
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
let window = self.window.as_ref().unwrap(); self.window.pre_present_notify();
window.pre_present_notify(); fill::fill_window(&*self.window);
fill::fill_window(window.as_ref());
}, },
_ => (), _ => (),
} }
@@ -122,7 +129,7 @@ impl ApplicationHandler for ControlFlowDemo {
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) { fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
if self.request_redraw && !self.wait_cancelled && !self.close_requested { if self.request_redraw && !self.wait_cancelled && !self.close_requested {
self.window.as_ref().unwrap().request_redraw(); self.window.request_redraw();
} }
match self.mode { match self.mode {

View File

@@ -8,7 +8,7 @@ fn main() -> std::process::ExitCode {
use std::time::Duration; use std::time::Duration;
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::event::WindowEvent; use winit::event::{StartCause, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::platform::pump_events::{EventLoopExtPumpEvents, PumpStatus}; use winit::platform::pump_events::{EventLoopExtPumpEvents, PumpStatus};
use winit::window::{Window, WindowAttributes, WindowId}; use winit::window::{Window, WindowAttributes, WindowId};
@@ -22,9 +22,12 @@ fn main() -> std::process::ExitCode {
} }
impl ApplicationHandler for PumpDemo { impl ApplicationHandler for PumpDemo {
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { fn new_events(&mut self, event_loop: &dyn ActiveEventLoop, cause: StartCause) {
let window_attributes = WindowAttributes::default().with_title("A fantastic window!"); if matches!(cause, StartCause::Init) && self.window.is_none() {
self.window = Some(event_loop.create_window(window_attributes).unwrap()); let window_attributes =
WindowAttributes::default().with_title("A fantastic window!");
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
} }
fn window_event( fn window_event(

View File

@@ -21,14 +21,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
window: Option<Box<dyn Window>>, window: Option<Box<dyn Window>>,
} }
impl ApplicationHandler for App { impl App {
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) { fn init_window(&mut self, event_loop: &dyn ActiveEventLoop) {
if let Some(window) = self.window.as_ref() {
window.request_redraw();
}
}
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let window_attributes = WindowAttributes::default() let window_attributes = WindowAttributes::default()
.with_title("Fantastic window number one!") .with_title("Fantastic window number one!")
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0)); .with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0));
@@ -36,6 +30,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
self.window_id = Some(window.id()); self.window_id = Some(window.id());
self.window = Some(window); self.window = Some(window);
} }
}
impl ApplicationHandler for App {
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
if let Some(window) = self.window.as_ref() {
window.request_redraw();
}
}
fn window_event( fn window_event(
&mut self, &mut self,
@@ -81,14 +83,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut event_loop = EventLoop::new().unwrap(); let mut event_loop = EventLoop::new().unwrap();
let mut app = App { idx: 1, ..Default::default() }; let mut app = App { idx: 1, ..Default::default() };
event_loop.run_app_on_demand(&mut app)?; event_loop.run_on_demand(|event_loop| {
app.init_window(event_loop);
&mut app
})?;
println!("--------------------------------------------------------- Finished first loop"); println!("--------------------------------------------------------- Finished first loop");
println!("--------------------------------------------------------- Waiting 5 seconds"); println!("--------------------------------------------------------- Waiting 5 seconds");
std::thread::sleep(Duration::from_secs(5)); std::thread::sleep(Duration::from_secs(5));
app.idx += 1; app.idx += 1;
event_loop.run_app_on_demand(&mut app)?; event_loop.run_on_demand(|event_loop| {
app.init_window(event_loop);
&mut app
})?;
println!("--------------------------------------------------------- Finished second loop"); println!("--------------------------------------------------------- Finished second loop");
Ok(()) Ok(())
} }

View File

@@ -70,8 +70,7 @@ fn main() -> Result<(), Box<dyn Error>> {
}); });
} }
let app = Application::new(&event_loop, receiver, sender); Ok(event_loop.run(|event_loop| Application::new(event_loop, receiver, sender))?)
Ok(event_loop.run_app(app)?)
} }
/// Application state and event handling. /// Application state and event handling.
@@ -88,21 +87,24 @@ struct Application {
/// ///
/// With OpenGL it could be EGLDisplay. /// With OpenGL it could be EGLDisplay.
#[cfg(not(android_platform))] #[cfg(not(android_platform))]
context: Option<Context<DisplayHandle<'static>>>, context: Context<DisplayHandle<'static>>,
} }
impl Application { impl Application {
fn new(event_loop: &EventLoop, receiver: Receiver<Action>, sender: Sender<Action>) -> Self { fn new(
// SAFETY: we drop the context right before the event loop is stopped, thus making it safe. event_loop: &dyn ActiveEventLoop,
receiver: Receiver<Action>,
sender: Sender<Action>,
) -> Self {
// SAFETY: The context is stored in the application, which is dropped right before the event
// loop is stopped, thus making it safe.
#[cfg(not(android_platform))] #[cfg(not(android_platform))]
let context = Some( let context = Context::new(unsafe {
Context::new(unsafe { std::mem::transmute::<DisplayHandle<'_>, DisplayHandle<'static>>(
std::mem::transmute::<DisplayHandle<'_>, DisplayHandle<'static>>( event_loop.display_handle().unwrap(),
event_loop.display_handle().unwrap(), )
) })
}) .unwrap();
.unwrap(),
);
// You'll have to choose an icon size at your own discretion. On X11, the desired size // You'll have to choose an icon size at your own discretion. On X11, the desired size
// varies by WM, and on Windows, you still have to account for screen scaling. Here // varies by WM, and on Windows, you still have to account for screen scaling. Here
@@ -120,7 +122,7 @@ impl Application {
.into_iter() .into_iter()
.collect(); .collect();
Self { let mut app = Self {
receiver, receiver,
sender, sender,
#[cfg(not(android_platform))] #[cfg(not(android_platform))]
@@ -128,7 +130,16 @@ impl Application {
custom_cursors, custom_cursors,
icon, icon,
windows: Default::default(), windows: Default::default(),
} };
app.dump_monitors(event_loop);
// Create initial window.
app.create_window(event_loop, None).expect("failed to create initial window");
app.print_help();
app
} }
fn create_window( fn create_window(
@@ -560,14 +571,12 @@ impl ApplicationHandler for Application {
info!("Device {device_id:?} event: {event:?}"); info!("Device {device_id:?} event: {event:?}");
} }
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { #[cfg(not(android_platform))]
info!("Ready to create surfaces"); fn can_create_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) {
self.dump_monitors(event_loop); for window in self.windows.values_mut() {
window.surface = Some(Surface::new(&self.context, Arc::clone(&window.window)).unwrap());
// Create initial window. window.resize(window.window.surface_size());
self.create_window(event_loop, None).expect("failed to create initial window"); }
self.print_help();
} }
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) { fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
@@ -578,9 +587,10 @@ impl ApplicationHandler for Application {
} }
#[cfg(not(android_platform))] #[cfg(not(android_platform))]
fn exiting(&mut self, _event_loop: &dyn ActiveEventLoop) { fn destroy_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) {
// We must drop the context here. for window in self.windows.values_mut() {
self.context = None; window.surface = None;
}
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@@ -607,9 +617,9 @@ struct WindowState {
ime: bool, ime: bool,
/// Render surface. /// Render surface.
/// ///
/// NOTE: This surface must be dropped before the `Window`. /// `None` when not between `can_create_surfaces` and `destroy_surfaces`.
#[cfg(not(android_platform))] #[cfg(not(android_platform))]
surface: Surface<DisplayHandle<'static>, Arc<dyn Window>>, surface: Option<Surface<DisplayHandle<'static>, Arc<dyn Window>>>,
/// The actual winit Window. /// The actual winit Window.
window: Arc<dyn Window>, window: Arc<dyn Window>,
/// The window theme we're drawing with. /// The window theme we're drawing with.
@@ -642,11 +652,6 @@ impl WindowState {
fn new(app: &Application, window: Box<dyn Window>) -> Result<Self, Box<dyn Error>> { fn new(app: &Application, window: Box<dyn Window>) -> Result<Self, Box<dyn Error>> {
let window: Arc<dyn Window> = Arc::from(window); let window: Arc<dyn Window> = Arc::from(window);
// SAFETY: the surface is dropped before the `window` which provided it with handle, thus
// it doesn't outlive it.
#[cfg(not(android_platform))]
let surface = Surface::new(app.context.as_ref().unwrap(), Arc::clone(&window))?;
let theme = window.theme().unwrap_or(Theme::Dark); let theme = window.theme().unwrap_or(Theme::Dark);
info!("Theme: {theme:?}"); info!("Theme: {theme:?}");
let named_idx = 0; let named_idx = 0;
@@ -656,15 +661,14 @@ impl WindowState {
let ime = true; let ime = true;
window.set_ime_allowed(ime); window.set_ime_allowed(ime);
let size = window.surface_size(); Ok(Self {
let mut state = Self {
#[cfg(macos_platform)] #[cfg(macos_platform)]
option_as_alt: window.option_as_alt(), option_as_alt: window.option_as_alt(),
custom_idx: app.custom_cursors.as_ref().map(Vec::len).unwrap_or(1) - 1, custom_idx: app.custom_cursors.as_ref().map(Vec::len).unwrap_or(1) - 1,
cursor_grab: CursorGrabMode::None, cursor_grab: CursorGrabMode::None,
named_idx, named_idx,
#[cfg(not(android_platform))] #[cfg(not(android_platform))]
surface, surface: None,
window, window,
theme, theme,
ime, ime,
@@ -675,10 +679,7 @@ impl WindowState {
rotated: Default::default(), rotated: Default::default(),
panned: Default::default(), panned: Default::default(),
zoom: Default::default(), zoom: Default::default(),
}; })
state.resize(size);
Ok(state)
} }
pub fn toggle_ime(&mut self) { pub fn toggle_ime(&mut self) {
@@ -852,7 +853,11 @@ impl WindowState {
(Some(width), Some(height)) => (width, height), (Some(width), Some(height)) => (width, height),
_ => return, _ => return,
}; };
self.surface.resize(width, height).expect("failed to resize inner buffer"); self.surface
.as_mut()
.unwrap()
.resize(width, height)
.expect("failed to resize inner buffer");
} }
self.window.request_redraw(); self.window.request_redraw();
} }
@@ -945,7 +950,7 @@ impl WindowState {
return Ok(()); return Ok(());
} }
let mut buffer = self.surface.buffer_mut()?; let mut buffer = self.surface.as_mut().unwrap().buffer_mut()?;
// Draw a different color inside the safe area // Draw a different color inside the safe area
let surface_size = self.window.surface_size(); let surface_size = self.window.surface_size();

View File

@@ -13,39 +13,28 @@ fn main() -> Result<(), Box<dyn Error>> {
mod fill; mod fill;
pub struct XEmbedDemo { pub struct XEmbedDemo {
parent_window_id: u32, window: Box<dyn Window>,
window: Option<Box<dyn Window>>,
} }
impl ApplicationHandler for XEmbedDemo { impl ApplicationHandler for XEmbedDemo {
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let window_attributes = WindowAttributes::default()
.with_title("An embedded window!")
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.with_embed_parent_window(self.parent_window_id);
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
fn window_event( fn window_event(
&mut self, &mut self,
event_loop: &dyn ActiveEventLoop, event_loop: &dyn ActiveEventLoop,
_window_id: WindowId, _window_id: WindowId,
event: WindowEvent, event: WindowEvent,
) { ) {
let window = self.window.as_ref().unwrap();
match event { match event {
WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
window.pre_present_notify(); self.window.pre_present_notify();
fill::fill_window(window.as_ref()); fill::fill_window(&*self.window);
}, },
_ => (), _ => (),
} }
} }
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) { fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
self.window.as_ref().unwrap().request_redraw(); self.window.request_redraw();
} }
} }
@@ -58,7 +47,15 @@ fn main() -> Result<(), Box<dyn Error>> {
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
let event_loop = EventLoop::new()?; let event_loop = EventLoop::new()?;
Ok(event_loop.run_app(XEmbedDemo { parent_window_id, window: None })?) Ok(event_loop.run(|event_loop| {
let window_attributes = WindowAttributes::default()
.with_title("An embedded window!")
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.with_embed_parent_window(parent_window_id);
let window = event_loop.create_window(window_attributes).unwrap();
XEmbedDemo { window }
})?)
} }
#[cfg(not(x11_platform))] #[cfg(not(x11_platform))]

View File

@@ -7,6 +7,8 @@ use crate::platform::macos::ApplicationHandlerExtMacOS;
use crate::window::WindowId; use crate::window::WindowId;
/// The handler of the application events. /// The handler of the application events.
///
/// This is [dropped][std::ops::Drop] when the event loop is being shut down.
pub trait ApplicationHandler { pub trait ApplicationHandler {
/// Emitted when new events arrive from the OS to be processed. /// Emitted when new events arrive from the OS to be processed.
/// ///
@@ -101,7 +103,9 @@ pub trait ApplicationHandler {
/// ///
/// [`can_create_surfaces()`]: Self::can_create_surfaces() /// [`can_create_surfaces()`]: Self::can_create_surfaces()
/// [`destroy_surfaces()`]: Self::destroy_surfaces() /// [`destroy_surfaces()`]: Self::destroy_surfaces()
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop); fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop;
}
/// Called after a wake up is requested using [`EventLoopProxy::wake_up()`]. /// Called after a wake up is requested using [`EventLoopProxy::wake_up()`].
/// ///
@@ -142,8 +146,6 @@ pub trait ApplicationHandler {
/// # ) { /// # ) {
/// # } /// # }
/// # /// #
/// # fn can_create_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) {}
/// #
/// fn proxy_wake_up(&mut self, _event_loop: &dyn ActiveEventLoop) { /// fn proxy_wake_up(&mut self, _event_loop: &dyn ActiveEventLoop) {
/// // Iterate current events, since wake-ups may have been merged. /// // Iterate current events, since wake-ups may have been merged.
/// // /// //
@@ -162,8 +164,6 @@ pub trait ApplicationHandler {
/// ///
/// let (sender, receiver) = mpsc::channel(); /// let (sender, receiver) = mpsc::channel();
/// ///
/// let mut app = MyApp { receiver };
///
/// // Send an event in a loop /// // Send an event in a loop
/// let proxy = event_loop.create_proxy(); /// let proxy = event_loop.create_proxy();
/// let background_thread = thread::spawn(move || { /// let background_thread = thread::spawn(move || {
@@ -182,9 +182,8 @@ pub trait ApplicationHandler {
/// } /// }
/// }); /// });
/// ///
/// event_loop.run_app(&mut app)?; /// event_loop.run(|_event_loop| MyApp { receiver })?;
/// ///
/// drop(app);
/// background_thread.join().unwrap(); /// background_thread.join().unwrap();
/// ///
/// Ok(()) /// Ok(())
@@ -286,13 +285,13 @@ pub trait ApplicationHandler {
/// with the [`onStop`] lifecycle event which typically results in the surface to be destroyed /// with the [`onStop`] lifecycle event which typically results in the surface to be destroyed
/// after the app becomes invisible. /// after the app becomes invisible.
/// ///
/// Applications that need to run on Android should assume their [`NativeWindow`] has been /// Applications that need to run on Android must be able to handle their underlying
/// destroyed, which indirectly invalidates any existing render surfaces that may have been /// [`SurfaceView`] being destroyed, which in turn indirectly invalidates any existing
/// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]). /// render surfaces that may have been created outside of Winit (such as an `EGLSurface`,
/// [`VkSurfaceKHR`] or [`wgpu::Surface`]).
/// ///
/// When receiving [`destroy_surfaces()`] Android applications should drop all render surfaces /// This means that in this method, you must drop all render surfaces before the event callback
/// before the event callback completes, which may be re-created when the application next /// completes, and only re-create them in or after [`can_create_surfaces()`] is next recieved.
/// receives [`can_create_surfaces()`].
/// ///
/// [`NativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window /// [`NativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window
/// [`Surface`]: https://developer.android.com/reference/android/view/Surface /// [`Surface`]: https://developer.android.com/reference/android/view/Surface
@@ -310,14 +309,6 @@ pub trait ApplicationHandler {
let _ = event_loop; let _ = event_loop;
} }
/// Emitted when the event loop is being shut down.
///
/// This is irreversible - if this method is called, it is guaranteed that the event loop
/// will exit right after.
fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) {
let _ = event_loop;
}
/// Emitted when the application has received a memory warning. /// Emitted when the application has received a memory warning.
/// ///
/// ## Platform-specific /// ## Platform-specific
@@ -413,11 +404,6 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
(**self).destroy_surfaces(event_loop); (**self).destroy_surfaces(event_loop);
} }
#[inline]
fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).exiting(event_loop);
}
#[inline] #[inline]
fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) { fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).memory_warning(event_loop); (**self).memory_warning(event_loop);
@@ -487,11 +473,6 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
(**self).destroy_surfaces(event_loop); (**self).destroy_surfaces(event_loop);
} }
#[inline]
fn exiting(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).exiting(event_loop);
}
#[inline] #[inline]
fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) { fn memory_warning(&mut self, event_loop: &dyn ActiveEventLoop) {
(**self).memory_warning(event_loop); (**self).memory_warning(event_loop);

View File

@@ -1,39 +1,4 @@
//! The event enums and assorted supporting types. //! The event enums and assorted supporting types.
//!
//! These are sent to the closure given to [`EventLoop::run_app(...)`], where they get
//! processed and used to modify the program state. For more details, see the root-level
//! documentation.
//!
//! Some of these events represent different "parts" of a traditional event-handling loop. You could
//! approximate the basic ordering loop of [`EventLoop::run_app(...)`] like this:
//!
//! ```rust,ignore
//! let mut start_cause = StartCause::Init;
//!
//! while !elwt.exiting() {
//! app.new_events(event_loop, start_cause);
//!
//! for event in (window events, user events, device events) {
//! // This will pick the right method on the application based on the event.
//! app.handle_event(event_loop, event);
//! }
//!
//! for window_id in (redraw windows) {
//! app.window_event(event_loop, window_id, RedrawRequested);
//! }
//!
//! app.about_to_wait(event_loop);
//! start_cause = wait_if_necessary();
//! }
//!
//! app.exiting(event_loop);
//! ```
//!
//! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully
//! describes what happens in what order.
//!
//! [`EventLoop::run_app(...)`]: crate::event_loop::EventLoop::run_app
//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{Mutex, Weak}; use std::sync::{Mutex, Weak};
#[cfg(not(web_platform))] #[cfg(not(web_platform))]
@@ -85,11 +50,6 @@ pub(crate) enum Event {
/// [`ApplicationHandler::suspended()`]: crate::application::ApplicationHandler::suspended() /// [`ApplicationHandler::suspended()`]: crate::application::ApplicationHandler::suspended()
Suspended, Suspended,
/// See [`ApplicationHandler::can_create_surfaces()`] for details.
///
/// [`ApplicationHandler::can_create_surfaces()`]: crate::application::ApplicationHandler::can_create_surfaces()
CreateSurfaces,
/// See [`ApplicationHandler::resumed()`] for details. /// See [`ApplicationHandler::resumed()`] for details.
/// ///
/// [`ApplicationHandler::resumed()`]: crate::application::ApplicationHandler::resumed() /// [`ApplicationHandler::resumed()`]: crate::application::ApplicationHandler::resumed()
@@ -100,11 +60,6 @@ pub(crate) enum Event {
/// [`ApplicationHandler::about_to_wait()`]: crate::application::ApplicationHandler::about_to_wait() /// [`ApplicationHandler::about_to_wait()`]: crate::application::ApplicationHandler::about_to_wait()
AboutToWait, AboutToWait,
/// See [`ApplicationHandler::exiting()`] for details.
///
/// [`ApplicationHandler::exiting()`]: crate::application::ApplicationHandler::exiting()
LoopExiting,
/// See [`ApplicationHandler::memory_warning()`] for details. /// See [`ApplicationHandler::memory_warning()`] for details.
/// ///
/// [`ApplicationHandler::memory_warning()`]: crate::application::ApplicationHandler::memory_warning() /// [`ApplicationHandler::memory_warning()`]: crate::application::ApplicationHandler::memory_warning()
@@ -1195,7 +1150,6 @@ mod tests {
let wid = WindowId::from_raw(0); let wid = WindowId::from_raw(0);
x(NewEvents(event::StartCause::Init)); x(NewEvents(event::StartCause::Init));
x(AboutToWait); x(AboutToWait);
x(LoopExiting);
x(Suspended); x(Suspended);
x(Resumed); x(Resumed);

View File

@@ -195,10 +195,48 @@ impl EventLoop {
} }
impl EventLoop { impl EventLoop {
/// Run the application with the event loop on the calling thread. /// Run the event loop on the current thread.
///
/// You pass in a closure that returns your application state. This closure has access to the
/// currently running event loop, allowing you to initialize your windows and surfaces in here.
/// ///
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior. /// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
/// ///
/// ## Event loop flow
///
/// This function internally handles the different parts of a traditional event-handling loop.
/// You could imagine this method being implemented like this:
///
/// ```rust,ignore
/// // Initialize.
/// let mut app = init_closure(event_loop);
/// let mut start_cause = StartCause::Init;
///
/// // Run loop.
/// while !elwt.exiting() {
/// // Wake up.
/// app.new_events(event_loop, start_cause);
///
/// // Handle events by the user.
/// for (device_id, event) in incoming_device_events {
/// app.device_event(event_loop, device_id, event);
/// }
/// for (window_id, event) in incoming_window_events {
/// app.window_event(event_loop, window_id, event);
/// }
///
/// // Done handling events, wait until we're woken up again.
/// app.about_to_wait(event_loop);
/// start_cause = wait_if_necessary();
/// }
///
/// // Finished running, drop application state.
/// drop(app);
/// ```
///
/// This is of course a very coarse-grained overview, and leaves out timing details like
/// [`ControlFlow::WaitUntil`] and life-cycle methods like [`ApplicationHandler::resumed`].
///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS:** Will never return to the caller and so values not passed to this function will /// - **iOS:** Will never return to the caller and so values not passed to this function will
@@ -213,7 +251,7 @@ impl EventLoop {
doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]" doc = " [`EventLoopExtWeb::spawn_app()`][crate::platform::web::EventLoopExtWeb::spawn_app()]"
)] )]
#[cfg_attr(not(any(web_platform, docsrs)), doc = " `EventLoopExtWeb::spawn_app()`")] #[cfg_attr(not(any(web_platform, docsrs)), doc = " `EventLoopExtWeb::spawn_app()`")]
/// [^1] instead of [`run_app()`] to avoid the need for the Javascript exception trick, and to /// [^1] instead of [`run()`] to avoid the need for the Javascript exception trick, and to
/// make it clearer that the event loop runs asynchronously (via the browser's own, /// make it clearer that the event loop runs asynchronously (via the browser's own,
/// internal, event loop) and doesn't block the current thread of execution like it does /// internal, event loop) and doesn't block the current thread of execution like it does
/// on other platforms. /// on other platforms.
@@ -223,11 +261,20 @@ impl EventLoop {
/// [^1]: `spawn_app()` is only available on the Web platform. /// [^1]: `spawn_app()` is only available on the Web platform.
/// ///
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
/// [`run_app()`]: Self::run_app() /// [`run()`]: Self::run()
#[inline] #[inline]
#[cfg(not(all(web_platform, target_feature = "exception-handling")))] #[cfg(not(all(web_platform, target_feature = "exception-handling")))]
pub fn run<A: ApplicationHandler>(
self,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.event_loop.run(init_closure)
}
/// Run the event loop with the given application state.
#[deprecated = "less flexible version of `run`"]
pub fn run_app<A: ApplicationHandler>(self, app: A) -> Result<(), EventLoopError> { pub fn run_app<A: ApplicationHandler>(self, app: A) -> Result<(), EventLoopError> {
self.event_loop.run_app(app) self.run(|_event_loop| app)
} }
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events /// Creates an [`EventLoopProxy`] that can be used to dispatch user events
@@ -392,9 +439,7 @@ pub trait ActiveEventLoop: AsAny {
/// Gets the current [`ControlFlow`]. /// Gets the current [`ControlFlow`].
fn control_flow(&self) -> ControlFlow; fn control_flow(&self) -> ControlFlow;
/// This exits the event loop. /// This stops the event loop.
///
/// See [`exiting`][crate::application::ApplicationHandler::exiting].
fn exit(&self); fn exit(&self);
/// Returns if the [`EventLoop`] is about to stop. /// Returns if the [`EventLoop`] is about to stop.

View File

@@ -1,24 +1,14 @@
//! Winit is a cross-platform window creation and event loop management library. //! Winit is a cross-platform window creation and event loop management library.
//! //!
//! # Building windows
//!
//! Before you can create a [`Window`], you first need to build an [`EventLoop`]. This is done with
//! the [`EventLoop::new()`] function.
//!
//! ```no_run
//! use winit::event_loop::EventLoop;
//!
//! # // Intentionally use `fn main` for clarity
//! fn main() {
//! let event_loop = EventLoop::new().unwrap();
//! // ...
//! }
//! ```
//!
//! Then you create a [`Window`] with [`create_window`].
//!
//! # Event handling //! # Event handling
//! //!
//! Basically all of the functionality that Winit exposes requires an [`ActiveEventLoop`], which you
//! can get access to by running an [`EventLoop`] using [`EventLoop::new()`] and
//! [`EventLoop::run()`].
//!
//! Once it's running, you can create your [`Window`]s with [`ActiveEventLoop::create_window()`] by
//! passing in the desired [`WindowAttributes`].
//!
//! Once a [`Window`] has been created, it will generate different *events*. A [`Window`] object can //! Once a [`Window`] has been created, it will generate different *events*. A [`Window`] object can
//! generate [`WindowEvent`]s when certain input events occur, such as a cursor moving over the //! generate [`WindowEvent`]s when certain input events occur, such as a cursor moving over the
//! window or a key getting pressed while the window is focused. Devices can generate //! window or a key getting pressed while the window is focused. Devices can generate
@@ -26,9 +16,10 @@
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a //! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
//! [`DeviceEvent`]. //! [`DeviceEvent`].
//! //!
//! You can retrieve events by calling [`EventLoop::run_app()`]. This function will //! You retrieve by implementing [`ApplicationHandler`] for a new type, which will be the state of
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and //! your application. The methods in this trait will continuously receive events until
//! will run until [`exit()`] is used, at which point [`exiting()`] is called. //! [`ActiveEventLoop::exit()`] is used, at which point your application state will be dropped, and
//! the application shuts down.
//! //!
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop //! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
//! model, since that can't be implemented properly on some platforms (e.g Web, iOS) and works //! model, since that can't be implemented properly on some platforms (e.g Web, iOS) and works
@@ -49,19 +40,20 @@
//! use winit::application::ApplicationHandler; //! use winit::application::ApplicationHandler;
//! use winit::event::WindowEvent; //! use winit::event::WindowEvent;
//! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; //! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
//! use winit::window::{Window, WindowId, WindowAttributes}; //! use winit::window::{Window, WindowId};
//! //!
//! #[derive(Default)]
//! struct App { //! struct App {
//! window: Option<Box<dyn Window>>, //! window: Box<dyn Window>,
//! } //! }
//! //!
//! impl ApplicationHandler for App { //! impl ApplicationHandler for App {
//! fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { //! fn window_event(
//! self.window = Some(event_loop.create_window(WindowAttributes::default()).unwrap()); //! &mut self,
//! } //! event_loop: &dyn ActiveEventLoop,
//! //! id: WindowId,
//! fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, id: WindowId, event: WindowEvent) { //! event: WindowEvent,
//! ) {
//! // Called by `EventLoop::run` when a new event happens on the window.
//! match event { //! match event {
//! WindowEvent::CloseRequested => { //! WindowEvent::CloseRequested => {
//! println!("The close button was pressed; stopping"); //! println!("The close button was pressed; stopping");
@@ -81,16 +73,19 @@
//! // You only need to call this if you've determined that you need to redraw in //! // You only need to call this if you've determined that you need to redraw in
//! // applications which do not always need to. Applications that redraw continuously //! // applications which do not always need to. Applications that redraw continuously
//! // can render here instead. //! // can render here instead.
//! self.window.as_ref().unwrap().request_redraw(); //! self.window.request_redraw();
//! } //! },
//! _ => (), //! _ => (),
//! } //! }
//! } //! }
//! } //! }
//! //!
//! # // Intentionally use `fn main` for clarity //! # // Intentionally use `fn main` for clarity
//! fn main() { //! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let event_loop = EventLoop::new().unwrap(); //! // Create a new event loop.
//! let event_loop = EventLoop::new()?;
//!
//! // Configure settings before launching.
//! //!
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't //! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
//! // dispatched any events. This is ideal for games and similar applications. //! // dispatched any events. This is ideal for games and similar applications.
@@ -101,8 +96,21 @@
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll. //! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
//! event_loop.set_control_flow(ControlFlow::Wait); //! event_loop.set_control_flow(ControlFlow::Wait);
//! //!
//! let mut app = App::default(); //! // Launch and begin running our event loop.
//! event_loop.run_app(&mut app); //! event_loop.run(|event_loop| {
//! // The event loop has launched, and we can initialize our UI state in this closure.
//!
//! // Create a simple window with default attributes.
//! let window = event_loop
//! .create_window(Window::default_attributes())
//! .expect("failed creating window");
//!
//! // Give our newly created application state to Winit, which will, when necessary, call
//! // the `ApplicationHandler` methods configured above.
//! App { window }
//! })?;
//!
//! Ok(())
//! } //! }
//! ``` //! ```
//! //!
@@ -250,20 +258,21 @@
//! |32-bit ARM Android |`arm-linux-androideabi` |Android | //! |32-bit ARM Android |`arm-linux-androideabi` |Android |
//! |64-bit SPARC Linux with glibc |`sparc64-unknown-linux-gnu` |X11, Wayland | //! |64-bit SPARC Linux with glibc |`sparc64-unknown-linux-gnu` |X11, Wayland |
//! //!
//! [`ActiveEventLoop`]: event_loop::ActiveEventLoop
//! [`EventLoop`]: event_loop::EventLoop //! [`EventLoop`]: event_loop::EventLoop
//! [`EventLoop::new()`]: event_loop::EventLoop::new //! [`EventLoop::new()`]: event_loop::EventLoop::new
//! [`EventLoop::run_app()`]: event_loop::EventLoop::run_app //! [`EventLoop::run()`]: event_loop::EventLoop::run
//! [`exit()`]: event_loop::ActiveEventLoop::exit //! [`ActiveEventLoop::exit()`]: event_loop::ActiveEventLoop::exit
//! [`Window`]: window::Window //! [`Window`]: window::Window
//! [`WindowId`]: window::WindowId //! [`WindowId`]: window::WindowId
//! [`WindowAttributes`]: window::WindowAttributes //! [`WindowAttributes`]: window::WindowAttributes
//! [window_new]: window::Window::new //! [window_new]: window::Window::new
//! [`create_window`]: event_loop::ActiveEventLoop::create_window //! [`ActiveEventLoop::create_window()`]: event_loop::ActiveEventLoop::create_window
//! [`Window::id()`]: window::Window::id //! [`Window::id()`]: window::Window::id
//! [`WindowEvent`]: event::WindowEvent //! [`WindowEvent`]: event::WindowEvent
//! [`DeviceEvent`]: event::DeviceEvent //! [`DeviceEvent`]: event::DeviceEvent
//! [`ApplicationHandler`]: application::ApplicationHandler
//! [`Event::UserEvent`]: event::Event::UserEvent //! [`Event::UserEvent`]: event::Event::UserEvent
//! [`exiting()`]: crate::application::ApplicationHandler::exiting
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle //! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle //! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
//! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland. //! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland.

View File

@@ -65,7 +65,7 @@
//! let app = NSApplication::sharedApplication(mtm); //! let app = NSApplication::sharedApplication(mtm);
//! app.setDelegate(Some(ProtocolObject::from_ref(&*delegate))); //! app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
//! //!
//! // event_loop.run_app(&mut my_app); //! // event_loop.run(|event_loop| { ... })?;
//! Ok(()) //! Ok(())
//! } //! }
//! ``` //! ```

View File

@@ -1,16 +1,14 @@
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::error::EventLoopError; use crate::error::EventLoopError;
use crate::event_loop::EventLoop; use crate::event_loop::{ActiveEventLoop, EventLoop};
#[cfg(doc)] #[cfg(doc)]
use crate::{ use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window};
event_loop::ActiveEventLoop, platform::pump_events::EventLoopExtPumpEvents, window::Window,
};
/// Additional methods on [`EventLoop`] to return control flow to the caller. /// Additional methods on [`EventLoop`] to return control flow to the caller.
pub trait EventLoopExtRunOnDemand { pub trait EventLoopExtRunOnDemand {
/// Run the application with the event loop on the calling thread. /// Run the application with the event loop on the calling thread.
/// ///
/// Unlike [`EventLoop::run_app`], this function accepts non-`'static` (i.e. non-`move`) /// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`)
/// closures and it is possible to return control back to the caller without /// closures and it is possible to return control back to the caller without
/// consuming the `EventLoop` (by using [`exit()`]) and /// consuming the `EventLoop` (by using [`exit()`]) and
/// so the event loop can be re-run after it has exit. /// so the event loop can be re-run after it has exit.
@@ -23,7 +21,7 @@ pub trait EventLoopExtRunOnDemand {
/// to while maintaining the full state of your application. (If you need something like this /// to while maintaining the full state of your application. (If you need something like this
/// you can look at the [`EventLoopExtPumpEvents::pump_app_events()`] API) /// you can look at the [`EventLoopExtPumpEvents::pump_app_events()`] API)
/// ///
/// Each time `run_app_on_demand` is called the startup sequence of `init`, followed by /// Each time `run_on_demand` is called the startup sequence of `init`, followed by
/// `resume` is being preserved. /// `resume` is being preserved.
/// ///
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior. /// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
@@ -40,7 +38,7 @@ pub trait EventLoopExtRunOnDemand {
/// [^1] more than once instead). /// [^1] more than once instead).
/// - No [`Window`] state can be carried between separate runs of the event loop. /// - No [`Window`] state can be carried between separate runs of the event loop.
/// ///
/// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you /// You are strongly encouraged to use [`EventLoop::run()`] for portability, unless you
/// specifically need the ability to re-run a single event loop more than once /// specifically need the ability to re-run a single event loop more than once
/// ///
/// # Supported Platforms /// # Supported Platforms
@@ -60,12 +58,18 @@ pub trait EventLoopExtRunOnDemand {
/// ///
/// [`exit()`]: ActiveEventLoop::exit() /// [`exit()`]: ActiveEventLoop::exit()
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError>; fn run_on_demand<A: ApplicationHandler>(
&mut self,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
) -> Result<(), EventLoopError>;
} }
impl EventLoopExtRunOnDemand for EventLoop { impl EventLoopExtRunOnDemand for EventLoop {
fn run_app_on_demand<A: ApplicationHandler>(&mut self, app: A) -> Result<(), EventLoopError> { fn run_on_demand<A: ApplicationHandler>(
self.event_loop.run_app_on_demand(app) &mut self,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.event_loop.run_on_demand(init_closure)
} }
} }

View File

@@ -190,10 +190,10 @@ pub trait EventLoopExtWeb {
/// Initializes the winit event loop. /// Initializes the winit event loop.
/// ///
/// Unlike /// Unlike
#[cfg_attr(all(web_platform, target_feature = "exception-handling"), doc = "`run_app()`")] #[cfg_attr(all(web_platform, target_feature = "exception-handling"), doc = "`run()`")]
#[cfg_attr( #[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")), not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run_app()`]" doc = "[`run()`]"
)] )]
/// [^1], this returns immediately, and doesn't throw an exception in order to /// [^1], this returns immediately, and doesn't throw an exception in order to
/// satisfy its [`!`] return type. /// satisfy its [`!`] return type.
@@ -206,9 +206,9 @@ pub trait EventLoopExtWeb {
/// ///
#[cfg_attr( #[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")), not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run_app()`]: EventLoop::run_app()" doc = "[`run()`]: EventLoop::run()"
)] )]
/// [^1]: `run_app()` is _not_ available on Wasm when the target supports `exception-handling`. /// [^1]: `run()` is _not_ available on Wasm when the target supports `exception-handling`.
fn spawn_app<A: ApplicationHandler + 'static>(self, app: A); fn spawn_app<A: ApplicationHandler + 'static>(self, app: A);
/// Sets the strategy for [`ControlFlow::Poll`]. /// Sets the strategy for [`ControlFlow::Poll`].

View File

@@ -496,15 +496,21 @@ impl EventLoop {
input_status input_status
} }
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> { pub fn run<A: ApplicationHandler>(
self.run_app_on_demand(app) mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.run_on_demand(init_closure)
} }
pub fn run_app_on_demand<A: ApplicationHandler>( pub fn run_on_demand<A: ApplicationHandler>(
&mut self, &mut self,
mut app: A, init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> { ) -> Result<(), EventLoopError> {
self.window_target.clear_exit(); self.window_target.clear_exit();
let mut app = init_closure(&self.window_target);
loop { loop {
match self.pump_app_events(None, &mut app) { match self.pump_app_events(None, &mut app) {
PumpStatus::Exit(0) => { PumpStatus::Exit(0) => {
@@ -546,8 +552,6 @@ impl EventLoop {
if self.exiting() { if self.exiting() {
self.loop_running = false; self.loop_running = false;
app.exiting(&self.window_target);
PumpStatus::Exit(0) PumpStatus::Exit(0)
} else { } else {
PumpStatus::Continue PumpStatus::Continue

View File

@@ -9,12 +9,12 @@ use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSRunningAppli
use objc2_foundation::{MainThreadMarker, NSNotification}; use objc2_foundation::{MainThreadMarker, NSNotification};
use super::super::event_handler::EventHandler; use super::super::event_handler::EventHandler;
use super::event_loop::{stop_app_immediately, ActiveEventLoop, EventLoopProxy, PanicInfo}; use super::event_loop::{stop_app_immediately, EventLoopProxy, PanicInfo};
use super::menu; use super::menu;
use super::observer::{EventLoopWaker, RunLoop}; use super::observer::{EventLoopWaker, RunLoop};
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::event::{StartCause, WindowEvent}; use crate::event::{StartCause, WindowEvent};
use crate::event_loop::ControlFlow; use crate::event_loop::{ActiveEventLoop, ControlFlow};
use crate::window::WindowId; use crate::window::WindowId;
#[derive(Debug)] #[derive(Debug)]
@@ -163,17 +163,25 @@ impl AppState {
pub fn will_terminate(self: &Rc<Self>, _notification: &NSNotification) { pub fn will_terminate(self: &Rc<Self>, _notification: &NSNotification) {
trace_scope!("NSApplicationWillTerminateNotification"); trace_scope!("NSApplicationWillTerminateNotification");
// TODO: Notify every window that it will be destroyed, like done in iOS? // TODO: Notify every window that it will be destroyed, like done in iOS?
self.with_handler(|app, event_loop| {
app.exiting(event_loop);
});
self.internal_exit(); self.internal_exit();
} }
/// Place the event handler in the application state for the duration /// Place the event handler in the application state for the duration
/// of the given closure. /// of the given closure.
pub fn set_event_handler<R>( pub fn set_init_closure<A: ApplicationHandler, R>(
&self, &self,
handler: &mut dyn ApplicationHandler, init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
closure: impl FnOnce() -> R, closure: impl FnOnce() -> R,
) -> R { ) -> R {
self.event_handler.set(handler, closure) let init_closure = Box::new(
|active_event_loop: &'_ dyn ActiveEventLoop| -> Box<dyn ApplicationHandler + '_> {
Box::new(init_closure(active_event_loop))
},
);
self.event_handler.set(init_closure, closure)
} }
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> { pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
@@ -208,10 +216,6 @@ impl AppState {
/// NOTE: that if the `NSApplication` has been launched then that state is preserved, /// NOTE: that if the `NSApplication` has been launched then that state is preserved,
/// and we won't need to re-launch the app if subsequent EventLoops are run. /// and we won't need to re-launch the app if subsequent EventLoops are run.
pub fn internal_exit(self: &Rc<Self>) { pub fn internal_exit(self: &Rc<Self>) {
self.with_handler(|app, event_loop| {
app.exiting(event_loop);
});
self.set_is_running(false); self.set_is_running(false);
self.set_stop_on_redraw(false); self.set_stop_on_redraw(false);
self.set_stop_before_wait(false); self.set_stop_before_wait(false);

View File

@@ -274,20 +274,23 @@ impl EventLoop {
&self.window_target &self.window_target
} }
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> { pub fn run<A: ApplicationHandler>(
self.run_app_on_demand(app) mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.run_on_demand(init_closure)
} }
// NB: we don't base this on `pump_events` because for `MacOs` we can't support // NB: we don't base this on `pump_events` because for `MacOs` we can't support
// `pump_events` elegantly (we just ask to run the loop for a "short" amount of // `pump_events` elegantly (we just ask to run the loop for a "short" amount of
// time and so a layered implementation would end up using a lot of CPU due to // time and so a layered implementation would end up using a lot of CPU due to
// redundant wake ups. // redundant wake ups.
pub fn run_app_on_demand<A: ApplicationHandler>( pub fn run_on_demand<A: ApplicationHandler>(
&mut self, &mut self,
mut app: A, init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> { ) -> Result<(), EventLoopError> {
self.app_state.clear_exit(); self.app_state.clear_exit();
self.app_state.set_event_handler(&mut app, || { self.app_state.set_init_closure(init_closure, || {
autoreleasepool(|_| { autoreleasepool(|_| {
// clear / normalize pump_events state // clear / normalize pump_events state
self.app_state.set_wait_timeout(None); self.app_state.set_wait_timeout(None);

View File

@@ -1,33 +1,55 @@
use std::cell::RefCell; use std::cell::Cell;
use std::{fmt, mem}; use std::{fmt, mem};
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::event_loop::ActiveEventLoop;
/// A helper type for storing a reference to `ApplicationHandler`, allowing interior mutable access /// A helper type for storing a reference to `ApplicationHandler`, allowing interior mutable access
/// to it within the execution of a closure. /// to it within the execution of a closure.
#[derive(Default)] #[derive(Default)]
pub(crate) struct EventHandler { pub(crate) struct EventHandler {
/// This can be in the following states: state: Cell<State>,
/// - Not registered by the event loop (None). }
/// - Present (Some(handler)).
/// - Currently executing the handler / in use (RefCell borrowed). type InitClosure<'handler> =
inner: RefCell<Option<&'static mut dyn ApplicationHandler>>, Box<dyn FnOnce(&dyn ActiveEventLoop) -> Box<dyn ApplicationHandler + 'handler> + 'handler>;
#[derive(Default)]
enum State {
/// Not registered by the event loop.
#[default]
NotRegistered,
/// The event is registered by the event loop.
Registered(InitClosure<'static>),
/// The application has been initialized, and we're ready to handle events.
Ready(Box<dyn ApplicationHandler + 'static>),
/// Currently executing the handler.
CurrentlyExecuting,
/// The application has been terminated.
Terminated,
// TODO: Invalid state?
} }
impl fmt::Debug for EventHandler { impl fmt::Debug for EventHandler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let state = match self.inner.try_borrow().as_deref() { let state = self.state.replace(State::CurrentlyExecuting);
Ok(Some(_)) => "<available>", // NOTE: We're very careful not to panic inside the "critial" section here.
Ok(None) => "<not set>", let string = match &state {
Err(_) => "<in use>", State::NotRegistered => "<not registered>",
State::Registered(_) => "<registered>",
State::Ready(_) => "<ready>",
State::CurrentlyExecuting => "<currently executing>",
State::Terminated => "<terminated>",
}; };
f.debug_struct("EventHandler").field("state", &state).finish_non_exhaustive() self.state.set(state);
f.debug_struct("EventHandler").field("state", &string).finish_non_exhaustive()
} }
} }
impl EventHandler { impl EventHandler {
pub(crate) fn new() -> Self { pub(crate) const fn new() -> Self {
Self { inner: RefCell::new(None) } Self { state: Cell::new(State::NotRegistered) }
} }
/// Set the event loop handler for the duration of the given closure. /// Set the event loop handler for the duration of the given closure.
@@ -37,7 +59,7 @@ impl EventHandler {
/// from within the closure. /// from within the closure.
pub(crate) fn set<'handler, R>( pub(crate) fn set<'handler, R>(
&self, &self,
app: &'handler mut dyn ApplicationHandler, init_closure: InitClosure<'handler>,
closure: impl FnOnce() -> R, closure: impl FnOnce() -> R,
) -> R { ) -> R {
// SAFETY: We extend the lifetime of the handler here so that we can // SAFETY: We extend the lifetime of the handler here so that we can
@@ -46,14 +68,9 @@ impl EventHandler {
// This is sound, since we make sure to unset the handler again at the // This is sound, since we make sure to unset the handler again at the
// end of this function, and as such the lifetime isn't actually // end of this function, and as such the lifetime isn't actually
// extended beyond `'handler`. // extended beyond `'handler`.
let handler = unsafe { let handler = unsafe { mem::transmute::<InitClosure<'handler>, InitClosure<'static>>(app) };
mem::transmute::<
&'handler mut dyn ApplicationHandler,
&'static mut dyn ApplicationHandler,
>(app)
};
match self.inner.try_borrow_mut().as_deref_mut() { match self.state.try_borrow_mut().as_deref_mut() {
Ok(Some(_)) => { Ok(Some(_)) => {
unreachable!("tried to set handler while another was already set"); unreachable!("tried to set handler while another was already set");
}, },
@@ -69,7 +86,7 @@ impl EventHandler {
impl Drop for ClearOnDrop<'_> { impl Drop for ClearOnDrop<'_> {
fn drop(&mut self) { fn drop(&mut self) {
match self.0.inner.try_borrow_mut().as_deref_mut() { match self.0.state.try_borrow_mut().as_deref_mut() {
Ok(data @ Some(_)) => { Ok(data @ Some(_)) => {
*data = None; *data = None;
}, },
@@ -101,6 +118,10 @@ impl EventHandler {
// soundness. // soundness.
} }
fn init(&self) {}
fn terminate(&self) {}
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub(crate) fn in_use(&self) -> bool { pub(crate) fn in_use(&self) -> bool {
self.inner.try_borrow().is_err() self.inner.try_borrow().is_err()
@@ -120,7 +141,7 @@ impl EventHandler {
// //
// If the handler unwinds, the `RefMut` will ensure that the // If the handler unwinds, the `RefMut` will ensure that the
// handler is no longer borrowed. // handler is no longer borrowed.
callback(*user_app); callback(user_app);
}, },
Ok(None) => { Ok(None) => {
// `NSApplication`, our app state and this handler are all // `NSApplication`, our app state and this handler are all

View File

@@ -360,15 +360,18 @@ impl EventLoop {
} }
} }
pub fn run_app<A: ApplicationHandler>(self, app: A) -> Result<(), EventLoopError> { pub fn run<A: ApplicationHandler>(
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app(app)) self,
init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run(init_closure))
} }
pub fn run_app_on_demand<A: ApplicationHandler>( pub fn run_on_demand<A: ApplicationHandler>(
&mut self, &mut self,
app: A, init_closure: impl FnOnce(&dyn ActiveEventLoop) -> A,
) -> Result<(), EventLoopError> { ) -> Result<(), EventLoopError> {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app_on_demand(app)) x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_on_demand(init_closure))
} }
pub fn pump_app_events<A: ApplicationHandler>( pub fn pump_app_events<A: ApplicationHandler>(

View File

@@ -146,15 +146,21 @@ impl EventLoop {
Ok(event_loop) Ok(event_loop)
} }
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> { pub fn run<A: ApplicationHandler>(
self.run_app_on_demand(app) mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.run_on_demand(init_closure)
} }
pub fn run_app_on_demand<A: ApplicationHandler>( pub fn run_on_demand<A: ApplicationHandler>(
&mut self, &mut self,
mut app: A, init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> { ) -> Result<(), EventLoopError> {
self.active_event_loop.clear_exit(); self.active_event_loop.clear_exit();
let mut app = init_closure(&self.active_event_loop);
let exit = loop { let exit = loop {
match self.pump_app_events(None, &mut app) { match self.pump_app_events(None, &mut app) {
PumpStatus::Exit(0) => { PumpStatus::Exit(0) => {
@@ -198,8 +204,6 @@ impl EventLoop {
if let Some(code) = self.exit_code() { if let Some(code) = self.exit_code() {
self.loop_running = false; self.loop_running = false;
app.exiting(&self.active_event_loop);
PumpStatus::Exit(code) PumpStatus::Exit(code)
} else { } else {
PumpStatus::Continue PumpStatus::Continue

View File

@@ -373,15 +373,21 @@ impl EventLoop {
&self.event_processor.target &self.event_processor.target
} }
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> { pub fn run<A: ApplicationHandler>(
self.run_app_on_demand(app) mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.run_on_demand(init_closure)
} }
pub fn run_app_on_demand<A: ApplicationHandler>( pub fn run_on_demand<A: ApplicationHandler>(
&mut self, &mut self,
mut app: A, init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> { ) -> Result<(), EventLoopError> {
self.event_processor.target.clear_exit(); self.event_processor.target.clear_exit();
let mut app = init_closure(&self.event_processor.target);
let exit = loop { let exit = loop {
match self.pump_app_events(None, &mut app) { match self.pump_app_events(None, &mut app) {
PumpStatus::Exit(0) => { PumpStatus::Exit(0) => {
@@ -429,8 +435,6 @@ impl EventLoop {
if let Some(code) = self.exit_code() { if let Some(code) = self.exit_code() {
self.loop_running = false; self.loop_running = false;
app.exiting(self.window_target());
PumpStatus::Exit(code) PumpStatus::Exit(code)
} else { } else {
PumpStatus::Continue PumpStatus::Continue

View File

@@ -483,7 +483,11 @@ impl EventLoop {
} }
} }
pub fn run_app<A: ApplicationHandler>(mut self, mut app: A) -> Result<(), EventLoopError> { pub fn run<A: ApplicationHandler>(
mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
let mut app = init_closure(&self.window_target);
let mut start_cause = StartCause::Init; let mut start_cause = StartCause::Init;
loop { loop {
app.new_events(&self.window_target, start_cause); app.new_events(&self.window_target, start_cause);
@@ -654,8 +658,6 @@ impl EventLoop {
} }
} }
app.exiting(&self.window_target);
Ok(()) Ok(())
} }

View File

@@ -96,7 +96,6 @@ fn handle_event<A: ApplicationHandler>(app: &mut A, target: &ActiveEventLoop, ev
Event::Resumed => app.resumed(target), Event::Resumed => app.resumed(target),
Event::CreateSurfaces => app.can_create_surfaces(target), Event::CreateSurfaces => app.can_create_surfaces(target),
Event::AboutToWait => app.about_to_wait(target), Event::AboutToWait => app.about_to_wait(target),
Event::LoopExiting => app.exiting(target),
Event::MemoryWarning => app.memory_warning(target), Event::MemoryWarning => app.memory_warning(target),
} }
} }

View File

@@ -609,7 +609,7 @@ impl Shared {
self.apply_control_flow(); self.apply_control_flow();
// We don't call `handle_loop_destroyed` here because we don't need to // We don't call `handle_loop_destroyed` here because we don't need to
// perform cleanup when the Web browser is going to destroy the page. // perform cleanup when the Web browser is going to destroy the page.
self.handle_event(Event::LoopExiting); todo!("drop the application handler");
} }
// handle_event takes in events and either queues them or applies a callback // handle_event takes in events and either queues them or applies a callback
@@ -713,7 +713,7 @@ impl Shared {
} }
fn handle_loop_destroyed(&self) { fn handle_loop_destroyed(&self) {
self.handle_event(Event::LoopExiting); todo!("drop the application handler");
let all_canvases = std::mem::take(&mut *self.0.all_canvases.borrow_mut()); let all_canvases = std::mem::take(&mut *self.0.all_canvases.borrow_mut());
*self.0.page_transition_event_handle.borrow_mut() = None; *self.0.page_transition_event_handle.borrow_mut() = None;
*self.0.on_mouse_move.borrow_mut() = None; *self.0.on_mouse_move.borrow_mut() = None;

View File

@@ -220,15 +220,21 @@ impl EventLoop {
&self.window_target &self.window_target
} }
pub fn run_app<A: ApplicationHandler>(mut self, app: A) -> Result<(), EventLoopError> { pub fn run<A: ApplicationHandler>(
self.run_app_on_demand(app) mut self,
init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> {
self.run_on_demand(init_closure)
} }
pub fn run_app_on_demand<A: ApplicationHandler>( pub fn run_on_demand<A: ApplicationHandler>(
&mut self, &mut self,
mut app: A, init_closure: impl FnOnce(&dyn RootActiveEventLoop) -> A,
) -> Result<(), EventLoopError> { ) -> Result<(), EventLoopError> {
self.window_target.clear_exit(); self.window_target.clear_exit();
let mut app = init_closure(&self.window_target);
{ {
let runner = &self.window_target.runner_shared; let runner = &self.window_target.runner_shared;
@@ -250,7 +256,6 @@ impl EventLoop {
Event::Resumed => app.resumed(event_loop_windows_ref), Event::Resumed => app.resumed(event_loop_windows_ref),
Event::CreateSurfaces => app.can_create_surfaces(event_loop_windows_ref), Event::CreateSurfaces => app.can_create_surfaces(event_loop_windows_ref),
Event::AboutToWait => app.about_to_wait(event_loop_windows_ref), Event::AboutToWait => app.about_to_wait(event_loop_windows_ref),
Event::LoopExiting => app.exiting(event_loop_windows_ref),
Event::MemoryWarning => app.memory_warning(event_loop_windows_ref), Event::MemoryWarning => app.memory_warning(event_loop_windows_ref),
}); });
} }
@@ -317,7 +322,6 @@ impl EventLoop {
Event::Resumed => app.resumed(event_loop_windows_ref), Event::Resumed => app.resumed(event_loop_windows_ref),
Event::CreateSurfaces => app.can_create_surfaces(event_loop_windows_ref), Event::CreateSurfaces => app.can_create_surfaces(event_loop_windows_ref),
Event::AboutToWait => app.about_to_wait(event_loop_windows_ref), Event::AboutToWait => app.about_to_wait(event_loop_windows_ref),
Event::LoopExiting => app.exiting(event_loop_windows_ref),
Event::MemoryWarning => app.memory_warning(event_loop_windows_ref), Event::MemoryWarning => app.memory_warning(event_loop_windows_ref),
}); });
@@ -348,8 +352,10 @@ impl EventLoop {
PumpStatus::Continue PumpStatus::Continue
}; };
// We wait until we've checked for an exit status before clearing the // We wait until we've checked for an exit status before clearing the application callback,
// application callback, in case we need to dispatch a LoopExiting event // in case any of the methods above ends up triggering an event.
//
// This drops the user's `ApplicationHandler`.
// //
// # Safety // # Safety
// This pairs up with our call to `runner.set_event_handler` and ensures // This pairs up with our call to `runner.set_event_handler` and ensures

View File

@@ -248,9 +248,8 @@ impl EventLoopRunner {
} }
} }
/// Dispatch control flow events (`NewEvents`, `AboutToWait`, and /// Dispatch control flow events (`new_events`, `about_to_wait`) as necessary to bring the
/// `LoopExiting`) as necessary to bring the internal `RunnerState` to the /// internal `RunnerState` to the new runner state.
/// new runner state.
/// ///
/// The state transitions are defined as follows: /// The state transitions are defined as follows:
/// ///
@@ -294,7 +293,6 @@ impl EventLoopRunner {
self.call_new_events(true); self.call_new_events(true);
self.call_event_handler(Event::AboutToWait); self.call_event_handler(Event::AboutToWait);
self.last_events_cleared.set(Instant::now()); self.last_events_cleared.set(Instant::now());
self.call_event_handler(Event::LoopExiting);
}, },
(_, Uninitialized) => panic!("cannot move state to Uninitialized"), (_, Uninitialized) => panic!("cannot move state to Uninitialized"),
@@ -302,9 +300,7 @@ impl EventLoopRunner {
(Idle, HandlingMainEvents) => { (Idle, HandlingMainEvents) => {
self.call_new_events(false); self.call_new_events(false);
}, },
(Idle, Destroyed) => { (Idle, Destroyed) => {},
self.call_event_handler(Event::LoopExiting);
},
(HandlingMainEvents, Idle) => { (HandlingMainEvents, Idle) => {
// This is always the last event we dispatch before waiting for new events // This is always the last event we dispatch before waiting for new events
@@ -314,7 +310,6 @@ impl EventLoopRunner {
(HandlingMainEvents, Destroyed) => { (HandlingMainEvents, Destroyed) => {
self.call_event_handler(Event::AboutToWait); self.call_event_handler(Event::AboutToWait);
self.last_events_cleared.set(Instant::now()); self.last_events_cleared.set(Instant::now());
self.call_event_handler(Event::LoopExiting);
}, },
(Destroyed, _) => panic!("cannot move state from Destroyed"), (Destroyed, _) => panic!("cannot move state from Destroyed"),