mirror of
https://github.com/rust-windowing/winit.git
synced 2026-09-02 06:40:06 -04:00
Web: queue EventLoopProxy::send_event() to microtask
This commit is contained in:
@@ -40,6 +40,16 @@ changelog entry.
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- On Web, let events wake up event loop immediately when using
|
||||||
|
`ControlFlow::Poll`.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- On Web, fix `EventLoopProxy::send_event()` triggering event loop immediately
|
||||||
|
when not called from inside the event loop. Now queues a microtask instead.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- Remove `EventLoop::run`.
|
- Remove `EventLoop::run`.
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::future;
|
use std::future;
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
@@ -6,13 +7,13 @@ use std::task::Poll;
|
|||||||
use super::super::main_thread::MainThreadMarker;
|
use super::super::main_thread::MainThreadMarker;
|
||||||
use super::{AtomicWaker, Wrapper};
|
use super::{AtomicWaker, Wrapper};
|
||||||
|
|
||||||
pub struct WakerSpawner<T: 'static>(Wrapper<Handler<T>, Sender, usize>);
|
pub struct WakerSpawner<T: 'static>(Wrapper<Handler<T>, Sender, NonZeroUsize>);
|
||||||
|
|
||||||
pub struct Waker<T: 'static>(Wrapper<Handler<T>, Sender, usize>);
|
pub struct Waker<T: 'static>(Wrapper<Handler<T>, Sender, NonZeroUsize>);
|
||||||
|
|
||||||
struct Handler<T> {
|
struct Handler<T> {
|
||||||
value: T,
|
value: T,
|
||||||
handler: fn(&T, usize),
|
handler: fn(&T, NonZeroUsize, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -20,7 +21,11 @@ struct Sender(Arc<Inner>);
|
|||||||
|
|
||||||
impl<T> WakerSpawner<T> {
|
impl<T> WakerSpawner<T> {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn new(main_thread: MainThreadMarker, value: T, handler: fn(&T, usize)) -> Option<Self> {
|
pub fn new(
|
||||||
|
main_thread: MainThreadMarker,
|
||||||
|
value: T,
|
||||||
|
handler: fn(&T, NonZeroUsize, bool),
|
||||||
|
) -> Option<Self> {
|
||||||
let inner = Arc::new(Inner {
|
let inner = Arc::new(Inner {
|
||||||
counter: AtomicUsize::new(0),
|
counter: AtomicUsize::new(0),
|
||||||
waker: AtomicWaker::new(),
|
waker: AtomicWaker::new(),
|
||||||
@@ -37,7 +42,7 @@ impl<T> WakerSpawner<T> {
|
|||||||
|handler, count| {
|
|handler, count| {
|
||||||
let handler = handler.borrow();
|
let handler = handler.borrow();
|
||||||
let handler = handler.as_ref().unwrap();
|
let handler = handler.as_ref().unwrap();
|
||||||
(handler.handler)(&handler.value, count);
|
(handler.handler)(&handler.value, count, true);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
let inner = Arc::clone(&inner);
|
let inner = Arc::clone(&inner);
|
||||||
@@ -46,29 +51,31 @@ impl<T> WakerSpawner<T> {
|
|||||||
while let Some(count) = future::poll_fn(|cx| {
|
while let Some(count) = future::poll_fn(|cx| {
|
||||||
let count = inner.counter.swap(0, Ordering::Relaxed);
|
let count = inner.counter.swap(0, Ordering::Relaxed);
|
||||||
|
|
||||||
if count > 0 {
|
match NonZeroUsize::new(count) {
|
||||||
Poll::Ready(Some(count))
|
Some(count) => Poll::Ready(Some(count)),
|
||||||
} else {
|
None => {
|
||||||
inner.waker.register(cx.waker());
|
inner.waker.register(cx.waker());
|
||||||
|
|
||||||
let count = inner.counter.swap(0, Ordering::Relaxed);
|
let count = inner.counter.swap(0, Ordering::Relaxed);
|
||||||
|
|
||||||
if count > 0 {
|
match NonZeroUsize::new(count) {
|
||||||
Poll::Ready(Some(count))
|
Some(count) => Poll::Ready(Some(count)),
|
||||||
} else {
|
None => {
|
||||||
if inner.closed.load(Ordering::Relaxed) {
|
if inner.closed.load(Ordering::Relaxed) {
|
||||||
return Poll::Ready(None);
|
return Poll::Ready(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
},
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
let handler = handler.borrow();
|
let handler = handler.borrow();
|
||||||
let handler = handler.as_ref().unwrap();
|
let handler = handler.as_ref().unwrap();
|
||||||
(handler.handler)(&handler.value, count);
|
(handler.handler)(&handler.value, count, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -107,7 +114,7 @@ impl<T> Drop for WakerSpawner<T> {
|
|||||||
|
|
||||||
impl<T> Waker<T> {
|
impl<T> Waker<T> {
|
||||||
pub fn wake(&self) {
|
pub fn wake(&self) {
|
||||||
self.0.send(1)
|
self.0.send(NonZeroUsize::MIN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,15 @@ use crate::platform_impl::platform::r#async::{DispatchRunner, Waker, WakerSpawne
|
|||||||
use crate::platform_impl::platform::window::Inner;
|
use crate::platform_impl::platform::window::Inner;
|
||||||
use crate::window::WindowId;
|
use crate::window::WindowId;
|
||||||
|
|
||||||
|
use js_sys::Function;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::{HashSet, VecDeque};
|
use std::collections::{HashSet, VecDeque};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
use wasm_bindgen::prelude::Closure;
|
use wasm_bindgen::prelude::{wasm_bindgen, Closure};
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
use web_sys::{Document, KeyboardEvent, PageTransitionEvent, PointerEvent, WheelEvent};
|
use web_sys::{Document, KeyboardEvent, PageTransitionEvent, PointerEvent, WheelEvent};
|
||||||
use web_time::{Duration, Instant};
|
use web_time::{Duration, Instant};
|
||||||
|
|
||||||
@@ -133,12 +136,13 @@ impl Shared {
|
|||||||
let document = window.document().expect("Failed to obtain document");
|
let document = window.document().expect("Failed to obtain document");
|
||||||
|
|
||||||
Shared(Rc::<Execution>::new_cyclic(|weak| {
|
Shared(Rc::<Execution>::new_cyclic(|weak| {
|
||||||
let proxy_spawner = WakerSpawner::new(main_thread, weak.clone(), |runner, count| {
|
let proxy_spawner =
|
||||||
if let Some(runner) = runner.upgrade() {
|
WakerSpawner::new(main_thread, weak.clone(), |runner, count, local| {
|
||||||
Shared(runner).send_events(iter::repeat(Event::UserEvent(())).take(count))
|
if let Some(runner) = runner.upgrade() {
|
||||||
}
|
Shared(runner).send_user_events(count, local)
|
||||||
})
|
}
|
||||||
.expect("`EventLoop` has to be created in the main thread");
|
})
|
||||||
|
.expect("`EventLoop` has to be created in the main thread");
|
||||||
|
|
||||||
Execution {
|
Execution {
|
||||||
main_thread,
|
main_thread,
|
||||||
@@ -460,6 +464,48 @@ impl Shared {
|
|||||||
self.send_events(iter::once(event));
|
self.send_events(iter::once(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add a series of user events to the event loop runner
|
||||||
|
//
|
||||||
|
// This will schedule the event loop to wake up instead of waking it up immediately if its not
|
||||||
|
// running.
|
||||||
|
pub(crate) fn send_user_events(&self, count: NonZeroUsize, local: bool) {
|
||||||
|
// If the event loop is closed, it should discard any new events
|
||||||
|
if self.is_closed() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if local {
|
||||||
|
// If the loop is not running and triggered locally, queue on next microtick.
|
||||||
|
if let Ok(RunnerEnum::Running(_)) =
|
||||||
|
self.0.runner.try_borrow().as_ref().map(Deref::deref)
|
||||||
|
{
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_name = queueMicrotask)]
|
||||||
|
fn queue_microtask(task: Function);
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_microtask(
|
||||||
|
Closure::once_into_js({
|
||||||
|
let this = Rc::downgrade(&self.0);
|
||||||
|
move || {
|
||||||
|
if let Some(shared) = this.upgrade() {
|
||||||
|
Shared(shared).send_events(
|
||||||
|
iter::repeat(Event::UserEvent(())).take(count.get()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unchecked_into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.send_events(iter::repeat(Event::UserEvent(())).take(count.get()))
|
||||||
|
}
|
||||||
|
|
||||||
// Add a series of events to the event loop runner
|
// Add a series of events to the event loop runner
|
||||||
//
|
//
|
||||||
// It will determine if the event should be immediately sent to the user or buffered for later
|
// It will determine if the event should be immediately sent to the user or buffered for later
|
||||||
@@ -471,13 +517,8 @@ impl Shared {
|
|||||||
// If we can run the event processing right now, or need to queue this and wait for later
|
// If we can run the event processing right now, or need to queue this and wait for later
|
||||||
let mut process_immediately = true;
|
let mut process_immediately = true;
|
||||||
match self.0.runner.try_borrow().as_ref().map(Deref::deref) {
|
match self.0.runner.try_borrow().as_ref().map(Deref::deref) {
|
||||||
Ok(RunnerEnum::Running(ref runner)) => {
|
// If the runner is attached but not running, we always wake it up.
|
||||||
// If we're currently polling, queue this and wait for the poll() method to be
|
Ok(RunnerEnum::Running(_)) => (),
|
||||||
// called
|
|
||||||
if let State::Poll { .. } = runner.state {
|
|
||||||
process_immediately = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Ok(RunnerEnum::Pending) => {
|
Ok(RunnerEnum::Pending) => {
|
||||||
// The runner still hasn't been attached: queue this event and wait for it to be
|
// The runner still hasn't been attached: queue this event and wait for it to be
|
||||||
process_immediately = false;
|
process_immediately = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user