Add Window.requestIdleCallback() support (#3084)
This commit is contained in:
parent
b99403b1b9
commit
83950acd5a
5 changed files with 162 additions and 28 deletions
|
|
@ -6,6 +6,7 @@ use crate::event::{
|
|||
WindowEvent,
|
||||
};
|
||||
use crate::event_loop::{ControlFlow, DeviceEvents};
|
||||
use crate::platform::web::PollType;
|
||||
use crate::platform_impl::platform::backend::EventListenerHandle;
|
||||
use crate::window::WindowId;
|
||||
|
||||
|
|
@ -36,6 +37,7 @@ type OnEventHandle<T> = RefCell<Option<EventListenerHandle<dyn FnMut(T)>>>;
|
|||
|
||||
pub struct Execution {
|
||||
control_flow: Cell<ControlFlow>,
|
||||
poll_type: Cell<PollType>,
|
||||
exit: Cell<bool>,
|
||||
runner: RefCell<RunnerEnum>,
|
||||
suspended: Cell<bool>,
|
||||
|
|
@ -140,6 +142,7 @@ impl Shared {
|
|||
|
||||
Shared(Rc::new(Execution {
|
||||
control_flow: Cell::new(ControlFlow::default()),
|
||||
poll_type: Cell::new(PollType::default()),
|
||||
exit: Cell::new(false),
|
||||
runner: RefCell::new(RunnerEnum::Pending),
|
||||
suspended: Cell::new(false),
|
||||
|
|
@ -635,9 +638,9 @@ impl Shared {
|
|||
let cloned = self.clone();
|
||||
State::Poll {
|
||||
request: backend::Schedule::new(
|
||||
self.window().clone(),
|
||||
self.poll_type(),
|
||||
self.window(),
|
||||
move || cloned.poll(),
|
||||
None,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
@ -658,10 +661,10 @@ impl Shared {
|
|||
State::WaitUntil {
|
||||
start,
|
||||
end,
|
||||
timeout: backend::Schedule::new(
|
||||
self.window().clone(),
|
||||
timeout: backend::Schedule::new_with_duration(
|
||||
self.window(),
|
||||
move || cloned.resume_time_reached(start, end),
|
||||
Some(delay),
|
||||
delay,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
@ -769,6 +772,14 @@ impl Shared {
|
|||
pub(crate) fn exiting(&self) -> bool {
|
||||
self.0.exit.get()
|
||||
}
|
||||
|
||||
pub(crate) fn set_poll_type(&self, poll_type: PollType) {
|
||||
self.0.poll_type.set(poll_type)
|
||||
}
|
||||
|
||||
pub(crate) fn poll_type(&self) -> PollType {
|
||||
self.0.poll_type.get()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum EventWrapper {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use crate::event::{
|
|||
};
|
||||
use crate::event_loop::{ControlFlow, DeviceEvents};
|
||||
use crate::keyboard::ModifiersState;
|
||||
use crate::platform::web::PollType;
|
||||
use crate::window::{Theme, WindowId as RootWindowId};
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -694,4 +695,12 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
pub(crate) fn exiting(&self) -> bool {
|
||||
self.runner.exiting()
|
||||
}
|
||||
|
||||
pub(crate) fn set_poll_type(&self, poll_type: PollType) {
|
||||
self.runner.set_poll_type(poll_type)
|
||||
}
|
||||
|
||||
pub(crate) fn poll_type(&self) -> PollType {
|
||||
self.runner.poll_type()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,41 +6,61 @@ use wasm_bindgen::prelude::wasm_bindgen;
|
|||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use web_sys::{AbortController, AbortSignal, MessageChannel, MessagePort};
|
||||
|
||||
use crate::platform::web::PollType;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Schedule(Inner);
|
||||
pub struct Schedule {
|
||||
_closure: Closure<dyn FnMut()>,
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Inner {
|
||||
Scheduler {
|
||||
controller: AbortController,
|
||||
_closure: Closure<dyn FnMut()>,
|
||||
},
|
||||
IdleCallback {
|
||||
window: web_sys::Window,
|
||||
handle: u32,
|
||||
},
|
||||
Timeout {
|
||||
window: web_sys::Window,
|
||||
handle: i32,
|
||||
port: MessagePort,
|
||||
_message_closure: Closure<dyn FnMut()>,
|
||||
_timeout_closure: Closure<dyn FnMut()>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Schedule {
|
||||
pub fn new<F>(window: web_sys::Window, f: F, duration: Option<Duration>) -> Schedule
|
||||
pub fn new<F>(poll_type: PollType, window: &web_sys::Window, f: F) -> Schedule
|
||||
where
|
||||
F: 'static + FnMut(),
|
||||
{
|
||||
if has_scheduler_support(&window) {
|
||||
Self::new_scheduler(window, f, duration)
|
||||
if poll_type == PollType::Scheduler && has_scheduler_support(window) {
|
||||
Self::new_scheduler(window, f, None)
|
||||
} else if poll_type == PollType::IdleCallback && has_idle_callback_support(window) {
|
||||
Self::new_idle_callback(window.clone(), f)
|
||||
} else {
|
||||
Self::new_timeout(window, f, duration)
|
||||
Self::new_timeout(window.clone(), f, None)
|
||||
}
|
||||
}
|
||||
|
||||
fn new_scheduler<F>(window: web_sys::Window, f: F, duration: Option<Duration>) -> Schedule
|
||||
pub fn new_with_duration<F>(window: &web_sys::Window, f: F, duration: Duration) -> Schedule
|
||||
where
|
||||
F: 'static + FnMut(),
|
||||
{
|
||||
let window: WindowSupportExt = window.unchecked_into();
|
||||
if has_scheduler_support(window) {
|
||||
Self::new_scheduler(window, f, Some(duration))
|
||||
} else {
|
||||
Self::new_timeout(window.clone(), f, Some(duration))
|
||||
}
|
||||
}
|
||||
|
||||
fn new_scheduler<F>(window: &web_sys::Window, f: F, duration: Option<Duration>) -> Schedule
|
||||
where
|
||||
F: 'static + FnMut(),
|
||||
{
|
||||
let window: &WindowSupportExt = window.unchecked_ref();
|
||||
let scheduler = window.scheduler();
|
||||
|
||||
let closure = Closure::new(f);
|
||||
|
|
@ -61,10 +81,25 @@ impl Schedule {
|
|||
.catch(handler);
|
||||
});
|
||||
|
||||
Schedule(Inner::Scheduler {
|
||||
controller,
|
||||
Schedule {
|
||||
_closure: closure,
|
||||
})
|
||||
inner: Inner::Scheduler { controller },
|
||||
}
|
||||
}
|
||||
|
||||
fn new_idle_callback<F>(window: web_sys::Window, f: F) -> Schedule
|
||||
where
|
||||
F: 'static + FnMut(),
|
||||
{
|
||||
let closure = Closure::new(f);
|
||||
let handle = window
|
||||
.request_idle_callback(closure.as_ref().unchecked_ref())
|
||||
.expect("Failed to request idle callback");
|
||||
|
||||
Schedule {
|
||||
_closure: closure,
|
||||
inner: Inner::IdleCallback { window, handle },
|
||||
}
|
||||
}
|
||||
|
||||
fn new_timeout<F>(window: web_sys::Window, f: F, duration: Option<Duration>) -> Schedule
|
||||
|
|
@ -72,10 +107,10 @@ impl Schedule {
|
|||
F: 'static + FnMut(),
|
||||
{
|
||||
let channel = MessageChannel::new().unwrap();
|
||||
let message_closure = Closure::new(f);
|
||||
let closure = Closure::new(f);
|
||||
let port_1 = channel.port1();
|
||||
port_1
|
||||
.add_event_listener_with_callback("message", message_closure.as_ref().unchecked_ref())
|
||||
.add_event_listener_with_callback("message", closure.as_ref().unchecked_ref())
|
||||
.expect("Failed to set message handler");
|
||||
port_1.start();
|
||||
|
||||
|
|
@ -95,20 +130,23 @@ impl Schedule {
|
|||
}
|
||||
.expect("Failed to set timeout");
|
||||
|
||||
Schedule(Inner::Timeout {
|
||||
window,
|
||||
handle,
|
||||
port: port_1,
|
||||
_message_closure: message_closure,
|
||||
_timeout_closure: timeout_closure,
|
||||
})
|
||||
Schedule {
|
||||
_closure: closure,
|
||||
inner: Inner::Timeout {
|
||||
window,
|
||||
handle,
|
||||
port: port_1,
|
||||
_timeout_closure: timeout_closure,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Schedule {
|
||||
fn drop(&mut self) {
|
||||
match &self.0 {
|
||||
match &self.inner {
|
||||
Inner::Scheduler { controller, .. } => controller.abort(),
|
||||
Inner::IdleCallback { window, handle, .. } => window.cancel_idle_callback(*handle),
|
||||
Inner::Timeout {
|
||||
window,
|
||||
handle,
|
||||
|
|
@ -144,6 +182,27 @@ fn has_scheduler_support(window: &web_sys::Window) -> bool {
|
|||
})
|
||||
}
|
||||
|
||||
fn has_idle_callback_support(window: &web_sys::Window) -> bool {
|
||||
thread_local! {
|
||||
static IDLE_CALLBACK_SUPPORT: OnceCell<bool> = OnceCell::new();
|
||||
}
|
||||
|
||||
IDLE_CALLBACK_SUPPORT.with(|support| {
|
||||
*support.get_or_init(|| {
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
type IdleCallbackSupport;
|
||||
|
||||
#[wasm_bindgen(method, getter, js_name = requestIdleCallback)]
|
||||
fn has_request_idle_callback(this: &IdleCallbackSupport) -> JsValue;
|
||||
}
|
||||
|
||||
let support: &IdleCallbackSupport = window.unchecked_ref();
|
||||
!support.has_request_idle_callback().is_undefined()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
type WindowSupportExt;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue