use crate::app::BrepApp;
use eframe::UserEvent;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use winit::application::ApplicationHandler;
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop, EventLoopProxy};
pub type Waker = Arc<dyn Fn() + Send + Sync>;
const WAKE: u64 = u64::MAX;
const SERVICE_PACE: Duration = Duration::from_millis(16);
#[derive(Clone, Default)]
pub struct Slot(Rc<RefCell<Option<(Rc<RefCell<BrepApp>>, egui::Context)>>>);
impl Slot {
pub fn install(&self, app: BrepApp, ctx: &egui::Context) -> Shell {
let app = Rc::new(RefCell::new(app));
*self.0.borrow_mut() = Some((app.clone(), ctx.clone()));
Shell(app)
}
fn get(&self) -> Option<(Rc<RefCell<BrepApp>>, egui::Context)> {
self.0.borrow().clone()
}
}
pub struct Shell(Rc<RefCell<BrepApp>>);
impl eframe::App for Shell {
fn logic(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
eframe::App::logic(&mut *self.0.borrow_mut(), ctx, frame)
}
fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
eframe::App::ui(&mut *self.0.borrow_mut(), ui, frame)
}
fn save(&mut self, storage: &mut dyn eframe::Storage) {
eframe::App::save(&mut *self.0.borrow_mut(), storage)
}
fn on_exit(&mut self) {
eframe::App::on_exit(&mut *self.0.borrow_mut())
}
fn auto_save_interval(&self) -> Duration {
eframe::App::auto_save_interval(&*self.0.borrow())
}
fn clear_color(&self, visuals: &egui::Visuals) -> [f32; 4] {
eframe::App::clear_color(&*self.0.borrow(), visuals)
}
fn persist_egui_memory(&self) -> bool {
eframe::App::persist_egui_memory(&*self.0.borrow())
}
fn raw_input_hook(&mut self, ctx: &egui::Context, raw_input: &mut egui::RawInput) {
eframe::App::raw_input_hook(&mut *self.0.borrow_mut(), ctx, raw_input)
}
}
pub fn waker(proxy: EventLoopProxy<UserEvent>) -> Waker {
let proxy = Mutex::new(proxy);
Arc::new(move || {
let event = UserEvent::RequestRepaint { viewport_id: egui::ViewportId::ROOT, when: Instant::now(), cumulative_pass_nr: WAKE };
let _ = proxy.lock().unwrap_or_else(|p| p.into_inner()).send_event(event);
})
}
fn is_wake(event: &UserEvent) -> bool {
matches!(event, UserEvent::RequestRepaint { cumulative_pass_nr: WAKE, .. })
}
pub struct DesktopLoop<H> {
inner: H,
slot: Slot,
busy: bool,
paced: Option<Instant>,
polled: bool,
redrawn: bool,
}
impl<H: ApplicationHandler<UserEvent>> DesktopLoop<H> {
pub fn new(inner: H, slot: Slot) -> Self {
DesktopLoop { inner, slot, busy: false, paced: None, polled: false, redrawn: false }
}
fn unstick_poll(&mut self, event_loop: &ActiveEventLoop) {
if event_loop.control_flow() == ControlFlow::Poll {
if self.polled && !self.redrawn {
event_loop.set_control_flow(ControlFlow::Wait);
self.polled = false;
} else {
self.polled = true;
}
} else {
self.polled = false;
}
self.redrawn = false;
}
fn service(&mut self, event_loop: &ActiveEventLoop) {
#[cfg(feature = "automation")]
self.step();
#[cfg(not(feature = "automation"))]
let _ = &self.slot;
let ours = self.paced.take().filter(|at| event_loop.control_flow() == ControlFlow::WaitUntil(*at));
if self.busy {
let next = Instant::now() + SERVICE_PACE;
match event_loop.control_flow() {
ControlFlow::Poll => {}
ControlFlow::WaitUntil(at) if at <= next && ours.is_none() => {}
_ => {
event_loop.set_control_flow(ControlFlow::WaitUntil(next));
self.paced = Some(next);
}
}
} else if ours.is_some() {
event_loop.set_control_flow(ControlFlow::Wait);
}
}
#[cfg(feature = "automation")]
fn step(&mut self) {
let Some((app, ctx)) = self.slot.get() else { return };
let Ok(mut app) = app.try_borrow_mut() else { return };
if !self.busy && !app.automation().has_frameless_work() {
return;
}
self.busy = app.service_automation(&ctx);
}
}
impl<H: ApplicationHandler<UserEvent>> ApplicationHandler<UserEvent> for DesktopLoop<H> {
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: winit::event::StartCause) {
self.inner.new_events(event_loop, cause);
}
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
self.inner.resumed(event_loop);
}
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) {
if !is_wake(&event) {
self.inner.user_event(event_loop, event);
}
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: winit::window::WindowId, event: winit::event::WindowEvent) {
self.redrawn |= matches!(event, winit::event::WindowEvent::RedrawRequested);
self.inner.window_event(event_loop, window_id, event);
}
fn device_event(&mut self, event_loop: &ActiveEventLoop, device_id: winit::event::DeviceId, event: winit::event::DeviceEvent) {
self.inner.device_event(event_loop, device_id, event);
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
self.inner.about_to_wait(event_loop);
self.unstick_poll(event_loop);
if !event_loop.exiting() {
self.service(event_loop);
}
}
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
self.inner.suspended(event_loop);
}
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
self.inner.exiting(event_loop);
}
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
self.inner.memory_warning(event_loop);
}
}
pub type Create<'a> = Box<dyn 'a + FnOnce(&eframe::CreationContext<'_>, Waker) -> Result<BrepApp, String>>;
pub fn run(app_name: &str, options: eframe::NativeOptions, create: Create<'_>) -> Result<(), String> {
let event_loop = EventLoop::<UserEvent>::with_user_event().build().map_err(|e| format!("event loop: {e}"))?;
run_on(event_loop, app_name, options, create, |eframe| eframe)
}
pub fn run_on<'a, H: ApplicationHandler<UserEvent>>(
event_loop: EventLoop<UserEvent>,
app_name: &str,
options: eframe::NativeOptions,
create: Create<'a>,
wrap: impl FnOnce(eframe::EframeWinitApplication<'a>) -> H,
) -> Result<(), String> {
let slot = Slot::default();
let failure: Rc<RefCell<Option<String>>> = Rc::default();
let waker = waker(event_loop.create_proxy());
let (installer, failed) = (slot.clone(), failure.clone());
let creator: eframe::AppCreator<'a> = Box::new(move |cc| match create(cc, waker) {
Ok(app) => Ok(Box::new(installer.install(app, &cc.egui_ctx)) as Box<dyn eframe::App>),
Err(error) => {
*failed.borrow_mut() = Some(error.clone());
Err(error.into())
}
});
let eframe = eframe::create_native(app_name, options, creator, &event_loop);
let mut handler = DesktopLoop::new(wrap(eframe), slot);
event_loop.run_app(&mut handler).map_err(|e| format!("event loop: {e}"))?;
let failed = failure.borrow_mut().take();
match failed {
Some(error) => Err(error),
None => Ok(()),
}
}