Android: Implement EventLoopExtPumpEvents and EventLoopExtRunOnDemand

This commit is contained in:
Robert Bragg 2023-06-18 11:10:13 +01:00 committed by Kirill Chibisov
parent f40b5f0dad
commit f5e73b0af4
2 changed files with 220 additions and 155 deletions

View file

@ -9,8 +9,10 @@
//! - `windows` //! - `windows`
//! - `web` //! - `web`
//! //!
//! And the following platform-specific module: //! And the following platform-specific modules:
//! //!
//! - `run_ondemand` (available on `android`)
//! - `pump_events` (available on `android`)
//! - `run_return` (available on `windows`, `unix`, `macos`, and `android`) //! - `run_return` (available on `windows`, `unix`, `macos`, and `android`)
//! //!
//! However only the module corresponding to the platform you're compiling to will be available. //! However only the module corresponding to the platform you're compiling to will be available.
@ -34,7 +36,12 @@ pub mod windows;
#[cfg(x11_platform)] #[cfg(x11_platform)]
pub mod x11; pub mod x11;
pub mod modifier_supplement; #[cfg(any(android_platform))]
pub mod run_ondemand;
#[cfg(any(android_platform,))]
pub mod pump_events;
#[cfg(any( #[cfg(any(
windows_platform, windows_platform,
macos_platform, macos_platform,
@ -44,4 +51,6 @@ pub mod modifier_supplement;
orbital_platform orbital_platform
))] ))]
pub mod run_return; pub mod run_return;
pub mod modifier_supplement;
pub mod scancode; pub mod scancode;

View file

@ -19,22 +19,32 @@ use raw_window_handle::{
AndroidDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, AndroidDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle,
}; };
use crate::platform_impl::Fullscreen;
use crate::{ use crate::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size}, dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error, error,
event::{self, StartCause}, event::{self, StartCause},
event_loop::{self, ControlFlow, EventLoopWindowTarget as RootELW}, event_loop::{self, ControlFlow, EventLoopWindowTarget as RootELW},
keyboard::NativeKey, keyboard::NativeKey,
platform::pump_events::PumpStatus,
window::{ window::{
self, CursorGrabMode, ImePurpose, ResizeDirection, Theme, WindowButtons, WindowLevel, self, CursorGrabMode, ImePurpose, ResizeDirection, Theme, WindowButtons, WindowLevel,
}, },
}; };
use crate::{error::RunLoopError, platform_impl::Fullscreen};
mod keycodes; mod keycodes;
static HAS_FOCUS: Lazy<RwLock<bool>> = Lazy::new(|| RwLock::new(true)); static HAS_FOCUS: Lazy<RwLock<bool>> = Lazy::new(|| RwLock::new(true));
/// Returns the minimum `Option<Duration>`, taking into account that `None`
/// equates to an infinite timeout, not a zero timeout (so can't just use
/// `Option::min`)
fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
a.map_or(b, |a_timeout| {
b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout)))
})
}
struct PeekableReceiver<T> { struct PeekableReceiver<T> {
recv: mpsc::Receiver<T>, recv: mpsc::Receiver<T>,
first: Option<T>, first: Option<T>,
@ -135,7 +145,11 @@ pub struct EventLoop<T: 'static> {
redraw_flag: SharedFlag, redraw_flag: SharedFlag,
user_events_sender: mpsc::Sender<T>, user_events_sender: mpsc::Sender<T>,
user_events_receiver: PeekableReceiver<T>, //must wake looper whenever something gets sent user_events_receiver: PeekableReceiver<T>, //must wake looper whenever something gets sent
loop_running: bool, // Dispatched `NewEvents<Init>`
running: bool, running: bool,
pending_redraw: bool,
control_flow: ControlFlow,
cause: StartCause,
ignore_volume_keys: bool, ignore_volume_keys: bool,
} }
@ -171,12 +185,6 @@ fn sticky_exit_callback<T, F>(
} }
} }
struct IterationResult {
deadline: Option<Instant>,
timeout: Option<Duration>,
wait_start: Instant,
}
impl<T: 'static> EventLoop<T> { impl<T: 'static> EventLoop<T> {
pub(crate) fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Self { pub(crate) fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Self {
let (user_events_sender, user_events_receiver) = mpsc::channel(); let (user_events_sender, user_events_receiver) = mpsc::channel();
@ -200,33 +208,33 @@ impl<T: 'static> EventLoop<T> {
redraw_flag, redraw_flag,
user_events_sender, user_events_sender,
user_events_receiver: PeekableReceiver::from_recv(user_events_receiver), user_events_receiver: PeekableReceiver::from_recv(user_events_receiver),
loop_running: false,
running: false, running: false,
pending_redraw: false,
control_flow: Default::default(),
cause: StartCause::Init,
ignore_volume_keys: attributes.ignore_volume_keys, ignore_volume_keys: attributes.ignore_volume_keys,
} }
} }
fn single_iteration<F>( fn single_iteration<F>(&mut self, main_event: Option<MainEvent<'_>>, callback: &mut F)
&mut self,
control_flow: &mut ControlFlow,
main_event: Option<MainEvent<'_>>,
pending_redraw: &mut bool,
cause: &mut StartCause,
callback: &mut F,
) -> IterationResult
where where
F: FnMut(event::Event<'_, T>, &RootELW<T>, &mut ControlFlow), F: FnMut(event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{ {
trace!("Mainloop iteration"); trace!("Mainloop iteration");
let cause = self.cause;
let mut control_flow = self.control_flow;
let mut pending_redraw = self.pending_redraw;
let mut resized = false;
sticky_exit_callback( sticky_exit_callback(
event::Event::NewEvents(*cause), event::Event::NewEvents(cause),
self.window_target(), self.window_target(),
control_flow, &mut control_flow,
callback, callback,
); );
let mut resized = false;
if let Some(event) = main_event { if let Some(event) = main_event {
trace!("Handling main event {:?}", event); trace!("Handling main event {:?}", event);
@ -235,7 +243,7 @@ impl<T: 'static> EventLoop<T> {
sticky_exit_callback( sticky_exit_callback(
event::Event::Resumed, event::Event::Resumed,
self.window_target(), self.window_target(),
control_flow, &mut control_flow,
callback, callback,
); );
} }
@ -243,12 +251,12 @@ impl<T: 'static> EventLoop<T> {
sticky_exit_callback( sticky_exit_callback(
event::Event::Suspended, event::Event::Suspended,
self.window_target(), self.window_target(),
control_flow, &mut control_flow,
callback, callback,
); );
} }
MainEvent::WindowResized { .. } => resized = true, MainEvent::WindowResized { .. } => resized = true,
MainEvent::RedrawNeeded { .. } => *pending_redraw = true, MainEvent::RedrawNeeded { .. } => pending_redraw = true,
MainEvent::ContentRectChanged { .. } => { MainEvent::ContentRectChanged { .. } => {
warn!("TODO: find a way to notify application of content rect change"); warn!("TODO: find a way to notify application of content rect change");
} }
@ -260,7 +268,7 @@ impl<T: 'static> EventLoop<T> {
event: event::WindowEvent::Focused(true), event: event::WindowEvent::Focused(true),
}, },
self.window_target(), self.window_target(),
control_flow, &mut control_flow,
callback, callback,
); );
} }
@ -272,7 +280,7 @@ impl<T: 'static> EventLoop<T> {
event: event::WindowEvent::Focused(false), event: event::WindowEvent::Focused(false),
}, },
self.window_target(), self.window_target(),
control_flow, &mut control_flow,
callback, callback,
); );
} }
@ -289,7 +297,12 @@ impl<T: 'static> EventLoop<T> {
scale_factor, scale_factor,
}, },
}; };
sticky_exit_callback(event, self.window_target(), control_flow, callback); sticky_exit_callback(
event,
self.window_target(),
&mut control_flow,
callback,
);
} }
} }
MainEvent::LowMemory => { MainEvent::LowMemory => {
@ -398,7 +411,7 @@ impl<T: 'static> EventLoop<T> {
sticky_exit_callback( sticky_exit_callback(
event, event,
self.window_target(), self.window_target(),
control_flow, &mut control_flow,
callback callback
); );
} }
@ -446,7 +459,7 @@ impl<T: 'static> EventLoop<T> {
sticky_exit_callback( sticky_exit_callback(
event, event,
self.window_target(), self.window_target(),
control_flow, &mut control_flow,
callback, callback,
); );
} }
@ -465,7 +478,7 @@ impl<T: 'static> EventLoop<T> {
sticky_exit_callback( sticky_exit_callback(
crate::event::Event::UserEvent(event), crate::event::Event::UserEvent(event),
self.window_target(), self.window_target(),
control_flow, &mut control_flow,
callback, callback,
); );
} }
@ -474,7 +487,7 @@ impl<T: 'static> EventLoop<T> {
sticky_exit_callback( sticky_exit_callback(
event::Event::MainEventsCleared, event::Event::MainEventsCleared,
self.window_target(), self.window_target(),
control_flow, &mut control_flow,
callback, callback,
); );
@ -491,64 +504,26 @@ impl<T: 'static> EventLoop<T> {
window_id: window::WindowId(WindowId), window_id: window::WindowId(WindowId),
event: event::WindowEvent::Resized(size), event: event::WindowEvent::Resized(size),
}; };
sticky_exit_callback(event, self.window_target(), control_flow, callback); sticky_exit_callback(event, self.window_target(), &mut control_flow, callback);
} }
*pending_redraw |= self.redraw_flag.get_and_reset(); pending_redraw |= self.redraw_flag.get_and_reset();
if *pending_redraw { if pending_redraw {
*pending_redraw = false; pending_redraw = false;
let event = event::Event::RedrawRequested(window::WindowId(WindowId)); let event = event::Event::RedrawRequested(window::WindowId(WindowId));
sticky_exit_callback(event, self.window_target(), control_flow, callback); sticky_exit_callback(event, self.window_target(), &mut control_flow, callback);
} }
} }
sticky_exit_callback( sticky_exit_callback(
event::Event::RedrawEventsCleared, event::Event::RedrawEventsCleared,
self.window_target(), self.window_target(),
control_flow, &mut control_flow,
callback, callback,
); );
let start = Instant::now(); self.control_flow = control_flow;
let (deadline, timeout); self.pending_redraw = pending_redraw;
match control_flow {
ControlFlow::ExitWithCode(_) => {
deadline = None;
timeout = None;
}
ControlFlow::Poll => {
*cause = StartCause::Poll;
deadline = None;
timeout = Some(Duration::from_millis(0));
}
ControlFlow::Wait => {
*cause = StartCause::WaitCancelled {
start,
requested_resume: None,
};
deadline = None;
timeout = None;
}
ControlFlow::WaitUntil(wait_deadline) => {
*cause = StartCause::ResumeTimeReached {
start,
requested_resume: *wait_deadline,
};
timeout = if *wait_deadline > start {
Some(*wait_deadline - start)
} else {
Some(Duration::from_millis(0))
};
deadline = Some(*wait_deadline);
}
}
IterationResult {
wait_start: start,
deadline,
timeout,
}
} }
pub fn run<F>(mut self, event_handler: F) -> ! pub fn run<F>(mut self, event_handler: F) -> !
@ -556,39 +531,124 @@ impl<T: 'static> EventLoop<T> {
F: 'static F: 'static
+ FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow), + FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
{ {
let exit_code = self.run_return(event_handler); let exit_code = match self.run_ondemand(event_handler) {
Err(RunLoopError::ExitFailure(code)) => code,
Err(_err) => 1,
Ok(_) => 0,
};
::std::process::exit(exit_code); ::std::process::exit(exit_code);
} }
pub fn run_return<F>(&mut self, mut callback: F) -> i32 pub fn run_return<F>(&mut self, callback: F) -> i32
where where
F: FnMut(event::Event<'_, T>, &RootELW<T>, &mut ControlFlow), F: FnMut(event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{ {
let mut control_flow = ControlFlow::default(); match self.run_ondemand(callback) {
let mut cause = StartCause::Init; Err(RunLoopError::ExitFailure(code)) => code,
let mut pending_redraw = false; Err(_err) => 1,
Ok(_) => 0,
}
}
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), RunLoopError>
where
F: FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
{
if self.loop_running {
return Err(RunLoopError::AlreadyRunning);
}
loop {
match self.pump_events_with_timeout(None, &mut event_handler) {
PumpStatus::Exit(0) => {
break Ok(());
}
PumpStatus::Exit(code) => {
break Err(RunLoopError::ExitFailure(code));
}
_ => {
continue;
}
}
}
}
pub fn pump_events<F>(&mut self, event_handler: F) -> PumpStatus
where
F: FnMut(event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
self.pump_events_with_timeout(Some(Duration::ZERO), event_handler)
}
fn pump_events_with_timeout<F>(
&mut self,
timeout: Option<Duration>,
mut callback: F,
) -> PumpStatus
where
F: FnMut(event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
if !self.loop_running {
self.loop_running = true;
// Reset the internal state for the loop as we start running to
// ensure consistent behaviour in case the loop runs and exits more
// than once
self.pending_redraw = false;
self.cause = StartCause::Init;
self.control_flow = ControlFlow::Poll;
// run the initial loop iteration // run the initial loop iteration
let mut iter_result = self.single_iteration( self.single_iteration(None, &mut callback);
&mut control_flow, }
None,
&mut pending_redraw, // Consider the possibility that the `StartCause::Init` iteration could
&mut cause, // request to Exit
if !matches!(self.control_flow, ControlFlow::ExitWithCode(_)) {
self.poll_events_with_timeout(timeout, &mut callback);
}
if let ControlFlow::ExitWithCode(code) = self.control_flow {
self.loop_running = false;
let mut dummy = self.control_flow;
sticky_exit_callback(
event::Event::LoopDestroyed,
self.window_target(),
&mut dummy,
&mut callback, &mut callback,
); );
let exit_code = loop { PumpStatus::Exit(code)
if let ControlFlow::ExitWithCode(code) = control_flow { } else {
break code; PumpStatus::Continue
}
} }
let mut timeout = iter_result.timeout; fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
where
F: FnMut(event::Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
let start = Instant::now();
// If we already have work to do then we don't want to block on the next poll... self.pending_redraw |= self.redraw_flag.get_and_reset();
pending_redraw |= self.redraw_flag.get_and_reset();
if self.running && (pending_redraw || self.user_events_receiver.has_incoming()) { timeout =
timeout = Some(Duration::from_millis(0)) if self.running && (self.pending_redraw || self.user_events_receiver.has_incoming()) {
// If we already have work to do then we don't want to block on the next poll
Some(Duration::ZERO)
} else {
let control_flow_timeout = match self.control_flow {
ControlFlow::Wait => None,
ControlFlow::Poll => Some(Duration::ZERO),
ControlFlow::WaitUntil(wait_deadline) => {
Some(wait_deadline.saturating_duration_since(start))
} }
// `ExitWithCode()` will be reset to `Poll` before polling
ControlFlow::ExitWithCode(_code) => unreachable!(),
};
min_timeout(control_flow_timeout, timeout)
};
let app = self.android_app.clone(); // Don't borrow self as part of poll expression let app = self.android_app.clone(); // Don't borrow self as part of poll expression
app.poll_events(timeout, |poll_event| { app.poll_events(timeout, |poll_event| {
@ -603,9 +663,9 @@ impl<T: 'static> EventLoop<T> {
// For now, user_events and redraw_requests are the only reasons to expect // For now, user_events and redraw_requests are the only reasons to expect
// a wake up here so we can ignore the wake up if there are no events/requests. // a wake up here so we can ignore the wake up if there are no events/requests.
// We also ignore wake ups while suspended. // We also ignore wake ups while suspended.
pending_redraw |= self.redraw_flag.get_and_reset(); self.pending_redraw |= self.redraw_flag.get_and_reset();
if !self.running if !self.running
|| (!pending_redraw && !self.user_events_receiver.has_incoming()) || (!self.pending_redraw && !self.user_events_receiver.has_incoming())
{ {
return; return;
} }
@ -619,35 +679,31 @@ impl<T: 'static> EventLoop<T> {
} }
} }
let wait_cancelled = iter_result self.cause = match self.control_flow {
.deadline ControlFlow::Poll => StartCause::Poll,
.map_or(false, |deadline| Instant::now() < deadline); ControlFlow::Wait => StartCause::WaitCancelled {
start,
if wait_cancelled { requested_resume: None,
cause = StartCause::WaitCancelled { },
start: iter_result.wait_start, ControlFlow::WaitUntil(deadline) => {
requested_resume: iter_result.deadline, if Instant::now() < deadline {
}; StartCause::WaitCancelled {
start,
requested_resume: Some(deadline),
} }
} else {
iter_result = self.single_iteration( StartCause::ResumeTimeReached {
&mut control_flow, start,
main_event, requested_resume: deadline,
&mut pending_redraw, }
&mut cause, }
&mut callback, }
); // `ExitWithCode()` will be reset to `Poll` before polling
}); ControlFlow::ExitWithCode(_code) => unreachable!(),
}; };
sticky_exit_callback( self.single_iteration(main_event, &mut callback);
event::Event::LoopDestroyed, });
self.window_target(),
&mut control_flow,
&mut callback,
);
exit_code
} }
pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget<T> { pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget<T> {