mirror of
https://github.com/danbulant/appit
synced 2026-06-17 13:21:20 +00:00
Window panics are now handled
Prior to this commit, if a window panic happened, the window would be left on the screen frozen. Now, the panic is caught and the window is closed before resuming the panic. The app event loop has a separate message it receives when a window panics, and if it's the last window, the process exits with a non-zero exit code. This places the burden of handling a panicking window into the developer's hands: ultimately if the window has a way to communicate with it, the behavior is being dropped as part of the panic handling, which ensures any channels the window had will be dropped too.
This commit is contained in:
parent
3f099070c2
commit
8f49442f28
3 changed files with 34 additions and 19 deletions
|
|
@ -6,6 +6,8 @@
|
|||
mod private;
|
||||
mod window;
|
||||
|
||||
pub use winit;
|
||||
|
||||
use raw_window_handle::HasRawWindowHandle;
|
||||
pub use window::{RunningWindow, Window, WindowBehavior, WindowBuilder};
|
||||
|
||||
|
|
@ -70,6 +72,11 @@ impl PendingApp {
|
|||
*control_flow = ControlFlow::ExitWithCode(0);
|
||||
}
|
||||
}
|
||||
AppMessage::WindowPanic(window_id) => {
|
||||
if self.running.windows.close(window_id) {
|
||||
*control_flow = ControlFlow::ExitWithCode(1);
|
||||
}
|
||||
}
|
||||
AppMessage::OpenWindow {
|
||||
attrs,
|
||||
sender,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ pub enum AppMessage {
|
|||
open_sender: mpsc::SyncSender<Result<Arc<winit::window::Window>, OsError>>,
|
||||
},
|
||||
CloseWindow(WindowId),
|
||||
WindowPanic(WindowId),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::collections::HashSet;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::panic::{AssertUnwindSafe, UnwindSafe};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{mpsc, Arc};
|
||||
use std::thread;
|
||||
|
|
@ -151,7 +152,7 @@ where
|
|||
// avoid a "frozen" window causing massive memory allocations, we'll use
|
||||
// a fixed-size channel and be cautious to not block the main event loop
|
||||
// by always using try_send.
|
||||
let (sender, receiver) = mpsc::sync_channel(128);
|
||||
let (sender, receiver) = mpsc::sync_channel(1024);
|
||||
let Some(winit) = self.owner.open(self.attributes, sender)? else {
|
||||
return Ok(None)
|
||||
};
|
||||
|
|
@ -159,7 +160,7 @@ where
|
|||
id: winit.id(),
|
||||
app: self.owner.app(),
|
||||
};
|
||||
let mut running_window = RunningWindow {
|
||||
let running_window = RunningWindow {
|
||||
messages: receiver,
|
||||
app: self.owner.app(),
|
||||
occluded: winit.is_visible().unwrap_or(false),
|
||||
|
|
@ -297,25 +298,31 @@ impl RunningWindow {
|
|||
self.modifiers
|
||||
}
|
||||
|
||||
fn run_with<Behavior>(&mut self, context: Behavior::Context)
|
||||
fn run_with<Behavior>(mut self, context: Behavior::Context)
|
||||
where
|
||||
Behavior: self::WindowBehavior,
|
||||
{
|
||||
let mut behavior = Behavior::initialize(self, context);
|
||||
while !self.close && self.process_messages_until_redraw(&mut behavior) {
|
||||
self.next_redraw_target = None;
|
||||
behavior.redraw(self);
|
||||
}
|
||||
drop(behavior);
|
||||
let proxy = self.app.proxy.clone();
|
||||
let window_id = self.window.id();
|
||||
let possible_panic = std::panic::catch_unwind(AssertUnwindSafe(move || {
|
||||
let mut behavior = Behavior::initialize(&mut self, context);
|
||||
while !self.close && self.process_messages_until_redraw(&mut behavior) {
|
||||
self.next_redraw_target = None;
|
||||
behavior.redraw(&mut self);
|
||||
}
|
||||
// Do not notify the main thread to close the window until after the
|
||||
// behavior is dropped. This upholds the requirement for RawWindowHandle
|
||||
// by making sure that any resources required by the behavior have had a
|
||||
// chance to be freed.
|
||||
}));
|
||||
|
||||
// Do not notify the main thread to close the window until after the
|
||||
// behavior is dropped. This upholds the requirement for RawWindowHandle
|
||||
// by making sure that any resources required by the behavior have had a
|
||||
// chance to be freed.
|
||||
let _result = self
|
||||
.app
|
||||
.proxy
|
||||
.send_event(AppMessage::CloseWindow(self.window.id()));
|
||||
//
|
||||
if let Err(panic) = possible_panic {
|
||||
let _result = proxy.send_event(AppMessage::WindowPanic(window_id));
|
||||
std::panic::resume_unwind(panic)
|
||||
} else {
|
||||
let _result = proxy.send_event(AppMessage::CloseWindow(window_id));
|
||||
}
|
||||
}
|
||||
|
||||
fn process_messages_until_redraw<Behavior>(&mut self, behavior: &mut Behavior) -> bool
|
||||
|
|
@ -616,12 +623,12 @@ enum TimeUntilRedraw {
|
|||
/// consumers of the libraries. This trait provides functions for each of the
|
||||
/// events a window may receive, enabling the type to react and update its
|
||||
/// state.
|
||||
pub trait WindowBehavior: Sized + 'static {
|
||||
pub trait WindowBehavior: UnwindSafe + Sized + 'static {
|
||||
/// A type that is passed to [`initialize()`](Self::initialize).
|
||||
///
|
||||
/// This allows providing data to the window from the thread that is opening
|
||||
/// the window without requiring that `WindowBehavior` also be `Send`.
|
||||
type Context: Send;
|
||||
type Context: Send + UnwindSafe;
|
||||
|
||||
/// Returns a new window builder for this behavior. When the window is
|
||||
/// initialized, a default [`Context`](Self::Context) will be passed.
|
||||
|
|
|
|||
Loading…
Reference in a new issue