Skip to content

Commit

Permalink
winit: use new ApplicationHandler to run
Browse files Browse the repository at this point in the history
  • Loading branch information
dhardy committed May 3, 2024
1 parent 4d87266 commit 19cc240
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 114 deletions.
4 changes: 2 additions & 2 deletions crates/kas-core/src/app/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ where
/// Run the main loop.
#[inline]
pub fn run(self) -> Result<()> {
let mut el = super::EventLoop::new(self.windows, self.state);
self.el.run(move |event, elwt| el.handle(event, elwt))?;
let mut l = super::Loop::new(self.windows, self.state);
self.el.run_app(&mut l)?;
Ok(())
}
}
Expand Down
232 changes: 121 additions & 111 deletions crates/kas-core/src/app/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ use crate::theme::Theme;
use crate::{Action, WindowId};
use std::collections::HashMap;
use std::time::Instant;
use winit::event::{Event, StartCause};
use winit::application::ApplicationHandler;
use winit::event::StartCause;
use winit::event_loop::{ActiveEventLoop, ControlFlow};
use winit::window as ww;

Expand All @@ -33,139 +34,148 @@ where
resumes: Vec<(Instant, WindowId)>,
}

impl<A: AppData, G: AppGraphicsBuilder, T: Theme<G::Shared>> Loop<A, G, T>
impl<A: AppData, G, T> ApplicationHandler<ProxyAction> for Loop<A, G, T>
where
G: AppGraphicsBuilder,
T: Theme<G::Shared>,
T::Window: kas::theme::Window,
{
pub(super) fn new(mut windows: Vec<Box<Window<A, G, T>>>, state: AppState<A, G, T>) -> Self {
Loop {
suspended: true,
windows: windows.drain(..).map(|w| (w.window_id, w)).collect(),
popups: Default::default(),
id_map: Default::default(),
state,
resumes: vec![],
fn new_events(&mut self, el: &ActiveEventLoop, cause: StartCause) {
// MainEventsCleared will reset control_flow (but not when it is Poll)
el.set_control_flow(ControlFlow::Wait);

match cause {
StartCause::ResumeTimeReached {
requested_resume, ..
} => {
let item = self
.resumes
.first()
.cloned()
.unwrap_or_else(|| panic!("timer wakeup without resume"));
assert_eq!(item.0, requested_resume);
log::trace!("Wakeup: timer (window={:?})", item.1);

let resume = if let Some(w) = self.windows.get_mut(&item.1) {
w.update_timer(&mut self.state)
} else {
// presumably, some window with active timers was removed
None
};

if let Some(instant) = resume {
self.resumes[0].0 = instant;
} else {
self.resumes.remove(0);
}
}
StartCause::WaitCancelled { .. } => {
// This event serves no purpose?
// log::debug!("Wakeup: WaitCancelled (ignoring)");
}
StartCause::Poll => (),
StartCause::Init => (),
}
}

pub(super) fn handle(&mut self, event: Event<ProxyAction>, el: &ActiveEventLoop) {
fn user_event(&mut self, _: &ActiveEventLoop, event: ProxyAction) {
match event {
Event::NewEvents(cause) => {
// MainEventsCleared will reset control_flow (but not when it is Poll)
el.set_control_flow(ControlFlow::Wait);

match cause {
StartCause::ResumeTimeReached {
requested_resume, ..
} => {
let item = self
.resumes
.first()
.cloned()
.unwrap_or_else(|| panic!("timer wakeup without resume"));
assert_eq!(item.0, requested_resume);
log::trace!("Wakeup: timer (window={:?})", item.1);

let resume = if let Some(w) = self.windows.get_mut(&item.1) {
w.update_timer(&mut self.state)
} else {
// presumably, some window with active timers was removed
None
};

if let Some(instant) = resume {
self.resumes[0].0 = instant;
} else {
self.resumes.remove(0);
}
}
StartCause::WaitCancelled { .. } => {
// This event serves no purpose?
// log::debug!("Wakeup: WaitCancelled (ignoring)");
}
StartCause::Poll => (),
StartCause::Init => (),
ProxyAction::Close(id) => {
if let Some(window) = self.windows.get_mut(&id) {
window.send_action(Action::CLOSE);
}
}

Event::WindowEvent { window_id, event } => {
self.flush_pending(el);

if let Some(id) = self.id_map.get(&window_id) {
if let Some(window) = self.windows.get_mut(id) {
if window.handle_event(&mut self.state, event) {
el.set_control_flow(ControlFlow::Poll);
}
}
ProxyAction::CloseAll => {
for window in self.windows.values_mut() {
window.send_action(Action::CLOSE);
}
}
Event::DeviceEvent { .. } => {
// windows handle local input; we do not handle global input
ProxyAction::Message(msg) => {
let mut stack = crate::messages::MessageStack::new();
stack.push_erased(msg.into_erased());
self.state.handle_messages(&mut stack);
}
Event::UserEvent(action) => match action {
ProxyAction::Close(id) => {
if let Some(window) = self.windows.get_mut(&id) {
window.send_action(Action::CLOSE);
ProxyAction::WakeAsync => {
// We don't need to do anything: MainEventsCleared will
// automatically be called after, which automatically calls
// window.update(..), which calls EventState::Update.
}
}
}

fn resumed(&mut self, el: &ActiveEventLoop) {
if self.suspended {
for window in self.windows.values_mut() {
match window.resume(&mut self.state, el) {
Ok(winit_id) => {
self.id_map.insert(winit_id, window.window_id);
}
}
ProxyAction::CloseAll => {
for window in self.windows.values_mut() {
window.send_action(Action::CLOSE);
Err(e) => {
log::error!("Unable to create window: {}", e);
}
}
ProxyAction::Message(msg) => {
let mut stack = crate::messages::MessageStack::new();
stack.push_erased(msg.into_erased());
self.state.handle_messages(&mut stack);
}
ProxyAction::WakeAsync => {
// We don't need to do anything: MainEventsCleared will
// automatically be called after, which automatically calls
// window.update(..), which calls EventState::Update.
}
},

Event::Suspended if !self.suspended => {
for window in self.windows.values_mut() {
window.suspend();
}
self.suspended = true;
}
Event::Suspended => (),
Event::Resumed if self.suspended => {
for window in self.windows.values_mut() {
match window.resume(&mut self.state, el) {
Ok(winit_id) => {
self.id_map.insert(winit_id, window.window_id);
}
Err(e) => {
log::error!("Unable to create window: {}", e);
}
}
self.suspended = false;
}
}

fn window_event(
&mut self,
el: &ActiveEventLoop,
window_id: ww::WindowId,
event: winit::event::WindowEvent,
) {
self.flush_pending(el);

if let Some(id) = self.id_map.get(&window_id) {
if let Some(window) = self.windows.get_mut(id) {
if window.handle_event(&mut self.state, event) {
el.set_control_flow(ControlFlow::Poll);
}
self.suspended = false;
}
Event::Resumed => (),
}
}

Event::AboutToWait => {
self.flush_pending(el);
self.resumes.sort_by_key(|item| item.0);
fn about_to_wait(&mut self, el: &ActiveEventLoop) {
self.flush_pending(el);
self.resumes.sort_by_key(|item| item.0);

if self.windows.is_empty() {
el.exit();
} else if matches!(el.control_flow(), ControlFlow::Poll) {
} else if let Some((instant, _)) = self.resumes.first() {
el.set_control_flow(ControlFlow::WaitUntil(*instant));
} else {
el.set_control_flow(ControlFlow::Wait);
};
}
if self.windows.is_empty() {
el.exit();
} else if matches!(el.control_flow(), ControlFlow::Poll) {
} else if let Some((instant, _)) = self.resumes.first() {
el.set_control_flow(ControlFlow::WaitUntil(*instant));
} else {
el.set_control_flow(ControlFlow::Wait);
};
}

Event::LoopExiting => {
self.state.on_exit();
fn suspended(&mut self, _: &ActiveEventLoop) {
if !self.suspended {
for window in self.windows.values_mut() {
window.suspend();
}
self.suspended = true;
}
}

fn exiting(&mut self, _: &ActiveEventLoop) {
self.state.on_exit();
}
}

Event::MemoryWarning => (), // TODO ?
impl<A: AppData, G: AppGraphicsBuilder, T: Theme<G::Shared>> Loop<A, G, T>
where
T::Window: kas::theme::Window,
{
pub(super) fn new(mut windows: Vec<Box<Window<A, G, T>>>, state: AppState<A, G, T>) -> Self {
Loop {
suspended: true,
windows: windows.drain(..).map(|w| (w.window_id, w)).collect(),
popups: Default::default(),
id_map: Default::default(),
state,
resumes: vec![],
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ mod common;
use crate::messages::MessageStack;
#[cfg(winit)] use crate::WindowId;
#[cfg(winit)] use app::PlatformWrapper;
#[cfg(winit)] use event_loop::Loop as EventLoop;
#[cfg(winit)] use event_loop::Loop;
#[cfg(winit)] pub(crate) use shared::{AppShared, AppState};
#[cfg(winit)]
pub(crate) use window::{Window, WindowDataErased};
Expand Down

0 comments on commit 19cc240

Please sign in to comment.