Web Async Rework (#3082)
This commit is contained in:
parent
ef34692148
commit
48f6582eb4
12 changed files with 653 additions and 421 deletions
|
|
@ -1,297 +0,0 @@
|
|||
use atomic_waker::AtomicWaker;
|
||||
use std::future;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::{self, Receiver, RecvError, SendError, Sender, TryRecvError};
|
||||
use std::sync::{Arc, Condvar, Mutex, RwLock};
|
||||
use std::task::Poll;
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
|
||||
// Unsafe wrapper type that allows us to use `T` when it's not `Send` from other threads.
|
||||
// `value` **must** only be accessed on the main thread.
|
||||
pub struct MainThreadSafe<const SYNC: bool, T: 'static, E: 'static> {
|
||||
// We wrap this in an `Arc` to allow it to be safely cloned without accessing the value.
|
||||
// The `RwLock` lets us safely drop in any thread.
|
||||
// The `Option` lets us safely drop `T` only in the main thread, while letting other threads drop `None`.
|
||||
value: Arc<RwLock<Option<T>>>,
|
||||
handler: fn(&RwLock<Option<T>>, E),
|
||||
sender: AsyncSender<E>,
|
||||
// Prevent's `Send` or `Sync` to be automatically implemented.
|
||||
local: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
impl<const SYNC: bool, T, E> MainThreadSafe<SYNC, T, E> {
|
||||
thread_local! {
|
||||
static MAIN_THREAD: bool = {
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[derive(Clone)]
|
||||
type Global;
|
||||
|
||||
#[wasm_bindgen(method, getter, js_name = Window)]
|
||||
fn window(this: &Global) -> JsValue;
|
||||
}
|
||||
|
||||
let global: Global = js_sys::global().unchecked_into();
|
||||
!global.window().is_undefined()
|
||||
};
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn new(value: T, handler: fn(&RwLock<Option<T>>, E)) -> Option<Self> {
|
||||
Self::MAIN_THREAD.with(|safe| {
|
||||
if !safe {
|
||||
panic!("only callable from inside the `Window`")
|
||||
}
|
||||
});
|
||||
|
||||
let value = Arc::new(RwLock::new(Some(value)));
|
||||
|
||||
let (sender, receiver) = channel::<E>();
|
||||
|
||||
wasm_bindgen_futures::spawn_local({
|
||||
let value = Arc::clone(&value);
|
||||
async move {
|
||||
while let Ok(event) = receiver.next().await {
|
||||
handler(&value, event)
|
||||
}
|
||||
|
||||
// An error was returned because the channel was closed, which
|
||||
// happens when all senders are dropped.
|
||||
value.write().unwrap().take().unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
Some(Self {
|
||||
value,
|
||||
handler,
|
||||
sender,
|
||||
local: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send(&self, event: E) {
|
||||
Self::MAIN_THREAD.with(|is_main_thread| {
|
||||
if *is_main_thread {
|
||||
(self.handler)(&self.value, event)
|
||||
} else {
|
||||
self.sender.send(event).unwrap()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn is_main_thread(&self) -> bool {
|
||||
Self::MAIN_THREAD.with(|is_main_thread| *is_main_thread)
|
||||
}
|
||||
|
||||
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
|
||||
Self::MAIN_THREAD.with(|is_main_thread| {
|
||||
if *is_main_thread {
|
||||
Some(f(self.value.read().unwrap().as_ref().unwrap()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SYNC: bool, T, E> Clone for MainThreadSafe<SYNC, T, E> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
value: self.value.clone(),
|
||||
handler: self.handler,
|
||||
sender: self.sender.clone(),
|
||||
local: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<const SYNC: bool, T, E> Send for MainThreadSafe<SYNC, T, E> {}
|
||||
unsafe impl<T, E> Sync for MainThreadSafe<true, T, E> {}
|
||||
|
||||
fn channel<T>() -> (AsyncSender<T>, AsyncReceiver<T>) {
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let sender = Arc::new(Mutex::new(sender));
|
||||
let waker = Arc::new(AtomicWaker::new());
|
||||
let closed = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let sender = AsyncSender {
|
||||
sender,
|
||||
closed: closed.clone(),
|
||||
waker: Arc::clone(&waker),
|
||||
};
|
||||
let receiver = AsyncReceiver {
|
||||
receiver,
|
||||
closed,
|
||||
waker,
|
||||
};
|
||||
|
||||
(sender, receiver)
|
||||
}
|
||||
|
||||
struct AsyncSender<T> {
|
||||
// We need to wrap it into a `Mutex` to make it `Sync`. So the sender can't
|
||||
// be accessed on the main thread, as it could block. Additionally we need
|
||||
// to wrap it in an `Arc` to make it clonable on the main thread without
|
||||
// having to block.
|
||||
sender: Arc<Mutex<Sender<T>>>,
|
||||
closed: Arc<AtomicBool>,
|
||||
waker: Arc<AtomicWaker>,
|
||||
}
|
||||
|
||||
impl<T> AsyncSender<T> {
|
||||
pub fn send(&self, event: T) -> Result<(), SendError<T>> {
|
||||
self.sender.lock().unwrap().send(event)?;
|
||||
self.waker.wake();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for AsyncSender<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
sender: self.sender.clone(),
|
||||
waker: self.waker.clone(),
|
||||
closed: self.closed.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for AsyncSender<T> {
|
||||
fn drop(&mut self) {
|
||||
// If it's the last + the one held by the receiver make sure to wake it
|
||||
// up and tell it that all receiver have dropped.
|
||||
if Arc::strong_count(&self.closed) == 2 {
|
||||
self.closed.store(true, Ordering::Relaxed);
|
||||
self.waker.wake()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AsyncReceiver<T> {
|
||||
receiver: Receiver<T>,
|
||||
closed: Arc<AtomicBool>,
|
||||
waker: Arc<AtomicWaker>,
|
||||
}
|
||||
|
||||
impl<T> AsyncReceiver<T> {
|
||||
pub async fn next(&self) -> Result<T, RecvError> {
|
||||
future::poll_fn(|cx| match self.receiver.try_recv() {
|
||||
Ok(event) => Poll::Ready(Ok(event)),
|
||||
Err(TryRecvError::Empty) => {
|
||||
if self.closed.load(Ordering::Relaxed) {
|
||||
return Poll::Ready(Err(RecvError));
|
||||
}
|
||||
|
||||
self.waker.register(cx.waker());
|
||||
|
||||
match self.receiver.try_recv() {
|
||||
Ok(event) => Poll::Ready(Ok(event)),
|
||||
Err(TryRecvError::Empty) => {
|
||||
if self.closed.load(Ordering::Relaxed) {
|
||||
Poll::Ready(Err(RecvError))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
Err(TryRecvError::Disconnected) => Poll::Ready(Err(RecvError)),
|
||||
}
|
||||
}
|
||||
Err(TryRecvError::Disconnected) => Poll::Ready(Err(RecvError)),
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dispatcher<T: 'static>(MainThreadSafe<true, T, Closure<T>>);
|
||||
|
||||
pub struct Closure<T>(Box<dyn FnOnce(&T) + Send>);
|
||||
|
||||
impl<T> Dispatcher<T> {
|
||||
#[track_caller]
|
||||
pub fn new(value: T) -> Option<Self> {
|
||||
MainThreadSafe::new(value, |value, Closure(closure)| {
|
||||
// SAFETY: The given `Closure` here isn't really `'static`, so we shouldn't do anything
|
||||
// funny with it here. See `Self::queue()`.
|
||||
closure(value.read().unwrap().as_ref().unwrap())
|
||||
})
|
||||
.map(Self)
|
||||
}
|
||||
|
||||
pub fn dispatch(&self, f: impl 'static + FnOnce(&T) + Send) {
|
||||
if self.is_main_thread() {
|
||||
self.0.with(f).unwrap()
|
||||
} else {
|
||||
self.0.send(Closure(Box::new(f)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue<R: Send>(&self, f: impl FnOnce(&T) -> R + Send) -> R {
|
||||
if self.is_main_thread() {
|
||||
self.0.with(f).unwrap()
|
||||
} else {
|
||||
let pair = Arc::new((Mutex::new(None), Condvar::new()));
|
||||
let closure = Box::new({
|
||||
let pair = pair.clone();
|
||||
move |value: &T| {
|
||||
*pair.0.lock().unwrap() = Some(f(value));
|
||||
pair.1.notify_one();
|
||||
}
|
||||
}) as Box<dyn FnOnce(&T) + Send>;
|
||||
// SAFETY: The `transmute` is necessary because `Closure` requires `'static`. This is
|
||||
// safe because this function won't return until `f` has finished executing. See
|
||||
// `Self::new()`.
|
||||
let closure = Closure(unsafe { std::mem::transmute(closure) });
|
||||
|
||||
self.0.send(closure);
|
||||
|
||||
let mut started = pair.0.lock().unwrap();
|
||||
|
||||
while started.is_none() {
|
||||
started = pair.1.wait(started).unwrap();
|
||||
}
|
||||
|
||||
started.take().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Dispatcher<T> {
|
||||
type Target = MainThreadSafe<true, T, Closure<T>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
type ChannelValue<T, E> = MainThreadSafe<false, (T, fn(&T, E)), E>;
|
||||
|
||||
pub struct Channel<T: 'static, E: 'static>(ChannelValue<T, E>);
|
||||
|
||||
impl<T, E> Channel<T, E> {
|
||||
pub fn new(value: T, handler: fn(&T, E)) -> Option<Self> {
|
||||
MainThreadSafe::new((value, handler), |runner, event| {
|
||||
let lock = runner.read().unwrap();
|
||||
let (value, handler) = lock.as_ref().unwrap();
|
||||
handler(value, event);
|
||||
})
|
||||
.map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Clone for Channel<T, E> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Deref for Channel<T, E> {
|
||||
type Target = ChannelValue<T, E>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
115
src/platform_impl/web/async/channel.rs
Normal file
115
src/platform_impl/web/async/channel.rs
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
use atomic_waker::AtomicWaker;
|
||||
use std::future;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::{self, Receiver, RecvError, SendError, Sender, TryRecvError};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::task::Poll;
|
||||
|
||||
// NOTE: This channel doesn't wake up when all senders or receivers are
|
||||
// dropped. This is acceptable as long as it's only used in `Dispatcher`, which
|
||||
// has it's own `Drop` behavior.
|
||||
|
||||
pub fn channel<T>() -> (AsyncSender<T>, AsyncReceiver<T>) {
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let sender = Arc::new(Mutex::new(sender));
|
||||
let inner = Arc::new(Inner {
|
||||
closed: AtomicBool::new(false),
|
||||
waker: AtomicWaker::new(),
|
||||
});
|
||||
|
||||
let sender = AsyncSender {
|
||||
sender,
|
||||
inner: Arc::clone(&inner),
|
||||
};
|
||||
let receiver = AsyncReceiver {
|
||||
receiver: Rc::new(receiver),
|
||||
inner,
|
||||
};
|
||||
|
||||
(sender, receiver)
|
||||
}
|
||||
|
||||
pub struct AsyncSender<T> {
|
||||
// We need to wrap it into a `Mutex` to make it `Sync`. So the sender can't
|
||||
// be accessed on the main thread, as it could block. Additionally we need
|
||||
// to wrap it in an `Arc` to make it clonable on the main thread without
|
||||
// having to block.
|
||||
sender: Arc<Mutex<Sender<T>>>,
|
||||
inner: Arc<Inner>,
|
||||
}
|
||||
|
||||
impl<T> AsyncSender<T> {
|
||||
pub fn send(&self, event: T) -> Result<(), SendError<T>> {
|
||||
self.sender.lock().unwrap().send(event)?;
|
||||
self.inner.waker.wake();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn close(&self) {
|
||||
self.inner.closed.store(true, Ordering::Relaxed);
|
||||
self.inner.waker.wake()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for AsyncSender<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
sender: Arc::clone(&self.sender),
|
||||
inner: Arc::clone(&self.inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AsyncReceiver<T> {
|
||||
receiver: Rc<Receiver<T>>,
|
||||
inner: Arc<Inner>,
|
||||
}
|
||||
|
||||
impl<T> AsyncReceiver<T> {
|
||||
pub async fn next(&self) -> Result<T, RecvError> {
|
||||
future::poll_fn(|cx| match self.receiver.try_recv() {
|
||||
Ok(event) => Poll::Ready(Ok(event)),
|
||||
Err(TryRecvError::Empty) => {
|
||||
self.inner.waker.register(cx.waker());
|
||||
|
||||
match self.receiver.try_recv() {
|
||||
Ok(event) => Poll::Ready(Ok(event)),
|
||||
Err(TryRecvError::Empty) => {
|
||||
if self.inner.closed.load(Ordering::Relaxed) {
|
||||
Poll::Ready(Err(RecvError))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
Err(TryRecvError::Disconnected) => Poll::Ready(Err(RecvError)),
|
||||
}
|
||||
}
|
||||
Err(TryRecvError::Disconnected) => Poll::Ready(Err(RecvError)),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn try_recv(&self) -> Result<Option<T>, RecvError> {
|
||||
match self.receiver.try_recv() {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
Err(TryRecvError::Empty) => Ok(None),
|
||||
Err(TryRecvError::Disconnected) => Err(RecvError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for AsyncReceiver<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
receiver: Rc::clone(&self.receiver),
|
||||
inner: Arc::clone(&self.inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
closed: AtomicBool,
|
||||
waker: AtomicWaker,
|
||||
}
|
||||
113
src/platform_impl/web/async/dispatcher.rs
Normal file
113
src/platform_impl/web/async/dispatcher.rs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
use super::{channel, AsyncReceiver, AsyncSender, Wrapper};
|
||||
use std::{
|
||||
cell::Ref,
|
||||
sync::{Arc, Condvar, Mutex},
|
||||
};
|
||||
|
||||
pub struct Dispatcher<T: 'static>(Wrapper<true, T, AsyncSender<Closure<T>>, Closure<T>>);
|
||||
|
||||
struct Closure<T>(Box<dyn FnOnce(&T) + Send>);
|
||||
|
||||
impl<T> Dispatcher<T> {
|
||||
#[track_caller]
|
||||
pub fn new(value: T) -> Option<(Self, DispatchRunner<T>)> {
|
||||
let (sender, receiver) = channel::<Closure<T>>();
|
||||
|
||||
Wrapper::new(
|
||||
value,
|
||||
|value, Closure(closure)| {
|
||||
// SAFETY: The given `Closure` here isn't really `'static`, so we shouldn't do anything
|
||||
// funny with it here. See `Self::queue()`.
|
||||
closure(value.borrow().as_ref().unwrap())
|
||||
},
|
||||
{
|
||||
let receiver = receiver.clone();
|
||||
move |value| async move {
|
||||
while let Ok(Closure(closure)) = receiver.next().await {
|
||||
// SAFETY: The given `Closure` here isn't really `'static`, so we shouldn't do anything
|
||||
// funny with it here. See `Self::queue()`.
|
||||
closure(value.borrow().as_ref().unwrap())
|
||||
}
|
||||
}
|
||||
},
|
||||
sender,
|
||||
|sender, closure| {
|
||||
// SAFETY: The given `Closure` here isn't really `'static`, so we shouldn't do anything
|
||||
// funny with it here. See `Self::queue()`.
|
||||
sender.send(closure).unwrap()
|
||||
},
|
||||
)
|
||||
.map(|wrapper| (Self(wrapper.clone()), DispatchRunner { wrapper, receiver }))
|
||||
}
|
||||
|
||||
pub fn value(&self) -> Option<Ref<'_, T>> {
|
||||
self.0.value()
|
||||
}
|
||||
|
||||
pub fn dispatch(&self, f: impl 'static + FnOnce(&T) + Send) {
|
||||
if let Some(value) = self.0.value() {
|
||||
f(&value)
|
||||
} else {
|
||||
self.0.send(Closure(Box::new(f)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue<R: Send>(&self, f: impl FnOnce(&T) -> R + Send) -> R {
|
||||
if let Some(value) = self.0.value() {
|
||||
f(&value)
|
||||
} else {
|
||||
let pair = Arc::new((Mutex::new(None), Condvar::new()));
|
||||
let closure = Box::new({
|
||||
let pair = pair.clone();
|
||||
move |value: &T| {
|
||||
*pair.0.lock().unwrap() = Some(f(value));
|
||||
pair.1.notify_one();
|
||||
}
|
||||
}) as Box<dyn FnOnce(&T) + Send>;
|
||||
// SAFETY: The `transmute` is necessary because `Closure` requires `'static`. This is
|
||||
// safe because this function won't return until `f` has finished executing. See
|
||||
// `Self::new()`.
|
||||
let closure = Closure(unsafe { std::mem::transmute(closure) });
|
||||
|
||||
self.0.send(closure);
|
||||
|
||||
let mut started = pair.0.lock().unwrap();
|
||||
|
||||
while started.is_none() {
|
||||
started = pair.1.wait(started).unwrap();
|
||||
}
|
||||
|
||||
started.take().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Dispatcher<T> {
|
||||
fn drop(&mut self) {
|
||||
self.0.with_sender_data(|sender| sender.close())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DispatchRunner<T: 'static> {
|
||||
wrapper: Wrapper<true, T, AsyncSender<Closure<T>>, Closure<T>>,
|
||||
receiver: AsyncReceiver<Closure<T>>,
|
||||
}
|
||||
|
||||
impl<T> DispatchRunner<T> {
|
||||
pub fn run(&self) {
|
||||
while let Some(Closure(closure)) = self
|
||||
.receiver
|
||||
.try_recv()
|
||||
.expect("should only be closed when `Dispatcher` is dropped")
|
||||
{
|
||||
// SAFETY: The given `Closure` here isn't really `'static`, so we shouldn't do anything
|
||||
// funny with it here. See `Self::queue()`.
|
||||
closure(
|
||||
&self
|
||||
.wrapper
|
||||
.value()
|
||||
.expect("don't call this outside the main thread"),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/platform_impl/web/async/mod.rs
Normal file
9
src/platform_impl/web/async/mod.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
mod channel;
|
||||
mod dispatcher;
|
||||
mod waker;
|
||||
mod wrapper;
|
||||
|
||||
use self::channel::{channel, AsyncReceiver, AsyncSender};
|
||||
pub use self::dispatcher::{DispatchRunner, Dispatcher};
|
||||
pub use self::waker::{Waker, WakerSpawner};
|
||||
use self::wrapper::Wrapper;
|
||||
123
src/platform_impl/web/async/waker.rs
Normal file
123
src/platform_impl/web/async/waker.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
use super::Wrapper;
|
||||
use atomic_waker::AtomicWaker;
|
||||
use std::future;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::task::Poll;
|
||||
|
||||
pub struct WakerSpawner<T: 'static>(Wrapper<false, Handler<T>, Sender, usize>);
|
||||
|
||||
pub struct Waker<T: 'static>(Wrapper<false, Handler<T>, Sender, usize>);
|
||||
|
||||
struct Handler<T> {
|
||||
value: T,
|
||||
handler: fn(&T, usize),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Sender(Arc<Inner>);
|
||||
|
||||
impl<T> WakerSpawner<T> {
|
||||
#[track_caller]
|
||||
pub fn new(value: T, handler: fn(&T, usize)) -> Option<Self> {
|
||||
let inner = Arc::new(Inner {
|
||||
counter: AtomicUsize::new(0),
|
||||
waker: AtomicWaker::new(),
|
||||
closed: AtomicBool::new(false),
|
||||
});
|
||||
|
||||
let handler = Handler { value, handler };
|
||||
|
||||
let sender = Sender(Arc::clone(&inner));
|
||||
|
||||
let wrapper = Wrapper::new(
|
||||
handler,
|
||||
|handler, count| {
|
||||
let handler = handler.borrow();
|
||||
let handler = handler.as_ref().unwrap();
|
||||
(handler.handler)(&handler.value, count);
|
||||
},
|
||||
{
|
||||
let inner = Arc::clone(&inner);
|
||||
|
||||
move |handler| async move {
|
||||
while let Some(count) = future::poll_fn(|cx| {
|
||||
let count = inner.counter.swap(0, Ordering::Relaxed);
|
||||
|
||||
if count > 0 {
|
||||
Poll::Ready(Some(count))
|
||||
} else {
|
||||
inner.waker.register(cx.waker());
|
||||
|
||||
let count = inner.counter.swap(0, Ordering::Relaxed);
|
||||
|
||||
if count > 0 {
|
||||
Poll::Ready(Some(count))
|
||||
} else {
|
||||
if inner.closed.load(Ordering::Relaxed) {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
})
|
||||
.await
|
||||
{
|
||||
let handler = handler.borrow();
|
||||
let handler = handler.as_ref().unwrap();
|
||||
(handler.handler)(&handler.value, count);
|
||||
}
|
||||
}
|
||||
},
|
||||
sender,
|
||||
|inner, _| {
|
||||
inner.0.counter.fetch_add(1, Ordering::Relaxed);
|
||||
inner.0.waker.wake();
|
||||
},
|
||||
)?;
|
||||
|
||||
Some(Self(wrapper))
|
||||
}
|
||||
|
||||
pub fn waker(&self) -> Waker<T> {
|
||||
Waker(self.0.clone())
|
||||
}
|
||||
|
||||
pub fn fetch(&self) -> usize {
|
||||
debug_assert!(
|
||||
self.0.is_main_thread(),
|
||||
"this should only be called from the main thread"
|
||||
);
|
||||
|
||||
self.0
|
||||
.with_sender_data(|inner| inner.0.counter.swap(0, Ordering::Relaxed))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for WakerSpawner<T> {
|
||||
fn drop(&mut self) {
|
||||
self.0.with_sender_data(|inner| {
|
||||
inner.0.closed.store(true, Ordering::Relaxed);
|
||||
inner.0.waker.wake();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Waker<T> {
|
||||
pub fn wake(&self) {
|
||||
self.0.send(1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Waker<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
counter: AtomicUsize,
|
||||
waker: AtomicWaker,
|
||||
closed: AtomicBool,
|
||||
}
|
||||
131
src/platform_impl/web/async/wrapper.rs
Normal file
131
src/platform_impl/web/async/wrapper.rs
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
use std::cell::{Ref, RefCell};
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
|
||||
// Unsafe wrapper type that allows us to use `T` when it's not `Send` from other threads.
|
||||
// `value` **must** only be accessed on the main thread.
|
||||
pub struct Wrapper<const SYNC: bool, V: 'static, S: Clone + Send, E> {
|
||||
value: Value<SYNC, V>,
|
||||
handler: fn(&RefCell<Option<V>>, E),
|
||||
sender_data: S,
|
||||
sender_handler: fn(&S, E),
|
||||
}
|
||||
|
||||
struct Value<const SYNC: bool, V> {
|
||||
// SAFETY:
|
||||
// This value must not be accessed if not on the main thread.
|
||||
//
|
||||
// - We wrap this in an `Arc` to allow it to be safely cloned without
|
||||
// accessing the value.
|
||||
// - The `RefCell` lets us mutably access in the main thread but is safe to
|
||||
// drop in any thread because it has no `Drop` behavior.
|
||||
// - The `Option` lets us safely drop `T` only in the main thread.
|
||||
value: Arc<RefCell<Option<V>>>,
|
||||
// Prevent's `Send` or `Sync` to be automatically implemented.
|
||||
local: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
// SAFETY: See `Self::value`.
|
||||
unsafe impl<const SYNC: bool, V> Send for Value<SYNC, V> {}
|
||||
// SAFETY: See `Self::value`.
|
||||
unsafe impl<V> Sync for Value<true, V> {}
|
||||
|
||||
impl<const SYNC: bool, V, S: Clone + Send, E> Wrapper<SYNC, V, S, E> {
|
||||
thread_local! {
|
||||
static MAIN_THREAD: bool = {
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[derive(Clone)]
|
||||
type Global;
|
||||
|
||||
#[wasm_bindgen(method, getter, js_name = Window)]
|
||||
fn window(this: &Global) -> JsValue;
|
||||
}
|
||||
|
||||
let global: Global = js_sys::global().unchecked_into();
|
||||
!global.window().is_undefined()
|
||||
};
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn new<R: Future<Output = ()>>(
|
||||
value: V,
|
||||
handler: fn(&RefCell<Option<V>>, E),
|
||||
receiver: impl 'static + FnOnce(Arc<RefCell<Option<V>>>) -> R,
|
||||
sender_data: S,
|
||||
sender_handler: fn(&S, E),
|
||||
) -> Option<Self> {
|
||||
Self::MAIN_THREAD.with(|safe| {
|
||||
if !safe {
|
||||
panic!("only callable from inside the `Window`")
|
||||
}
|
||||
});
|
||||
|
||||
let value = Arc::new(RefCell::new(Some(value)));
|
||||
|
||||
wasm_bindgen_futures::spawn_local({
|
||||
let value = Arc::clone(&value);
|
||||
async move {
|
||||
receiver(Arc::clone(&value)).await;
|
||||
drop(value.borrow_mut().take().unwrap());
|
||||
}
|
||||
});
|
||||
|
||||
Some(Self {
|
||||
value: Value {
|
||||
value,
|
||||
local: PhantomData,
|
||||
},
|
||||
handler,
|
||||
sender_data,
|
||||
sender_handler,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send(&self, event: E) {
|
||||
Self::MAIN_THREAD.with(|is_main_thread| {
|
||||
if *is_main_thread {
|
||||
(self.handler)(&self.value.value, event)
|
||||
} else {
|
||||
(self.sender_handler)(&self.sender_data, event)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_main_thread(&self) -> bool {
|
||||
Self::MAIN_THREAD.with(|is_main_thread| *is_main_thread)
|
||||
}
|
||||
|
||||
pub fn value(&self) -> Option<Ref<'_, V>> {
|
||||
Self::MAIN_THREAD.with(|is_main_thread| {
|
||||
if *is_main_thread {
|
||||
Some(Ref::map(self.value.value.borrow(), |value| {
|
||||
value.as_ref().unwrap()
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_sender_data<T>(&self, f: impl FnOnce(&S) -> T) -> T {
|
||||
f(&self.sender_data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SYNC: bool, V, S: Clone + Send, E> Clone for Wrapper<SYNC, V, S, E> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
value: Value {
|
||||
value: self.value.value.clone(),
|
||||
local: PhantomData,
|
||||
},
|
||||
handler: self.handler,
|
||||
sender_data: self.sender_data.clone(),
|
||||
sender_handler: self.sender_handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,11 +27,12 @@ pub(crate) struct PlatformSpecificEventLoopAttributes {}
|
|||
impl<T> EventLoop<T> {
|
||||
pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> Result<Self, EventLoopError> {
|
||||
let (user_event_sender, user_event_receiver) = mpsc::channel();
|
||||
let elw = RootEventLoopWindowTarget {
|
||||
p: EventLoopWindowTarget::new(),
|
||||
_marker: PhantomData,
|
||||
};
|
||||
Ok(EventLoop {
|
||||
elw: RootEventLoopWindowTarget {
|
||||
p: EventLoopWindowTarget::new(),
|
||||
_marker: PhantomData,
|
||||
},
|
||||
elw,
|
||||
user_event_sender,
|
||||
user_event_receiver,
|
||||
})
|
||||
|
|
@ -101,7 +102,7 @@ impl<T> EventLoop<T> {
|
|||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy::new(self.elw.p.runner.clone(), self.user_event_sender.clone())
|
||||
EventLoopProxy::new(self.elw.p.waker(), self.user_event_sender.clone())
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> {
|
||||
|
|
|
|||
|
|
@ -1,30 +1,25 @@
|
|||
use std::sync::mpsc::Sender;
|
||||
use std::rc::Weak;
|
||||
use std::sync::mpsc::{SendError, Sender};
|
||||
|
||||
use super::runner;
|
||||
use crate::event::Event;
|
||||
use super::runner::Execution;
|
||||
use crate::event_loop::EventLoopClosed;
|
||||
use crate::platform_impl::platform::r#async::Channel;
|
||||
use crate::platform_impl::platform::r#async::Waker;
|
||||
|
||||
pub struct EventLoopProxy<T: 'static> {
|
||||
// used to wake the event loop handler, not to actually pass data
|
||||
runner: Channel<runner::Shared, ()>,
|
||||
runner: Waker<Weak<Execution>>,
|
||||
sender: Sender<T>,
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
pub fn new(runner: runner::Shared, sender: Sender<T>) -> Self {
|
||||
Self {
|
||||
runner: Channel::new(runner, |runner, event| {
|
||||
runner.send_event(Event::UserEvent(event))
|
||||
})
|
||||
.unwrap(),
|
||||
sender,
|
||||
}
|
||||
pub fn new(runner: Waker<Weak<Execution>>, sender: Sender<T>) -> Self {
|
||||
Self { runner, sender }
|
||||
}
|
||||
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.sender.send(event).unwrap();
|
||||
self.runner.send(());
|
||||
self.sender
|
||||
.send(event)
|
||||
.map_err(|SendError(event)| EventLoopClosed(event))?;
|
||||
self.runner.wake();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ use crate::event::{
|
|||
use crate::event_loop::{ControlFlow, DeviceEvents};
|
||||
use crate::platform::web::PollStrategy;
|
||||
use crate::platform_impl::platform::backend::EventListenerHandle;
|
||||
use crate::platform_impl::platform::r#async::{DispatchRunner, Waker, WakerSpawner};
|
||||
use crate::platform_impl::platform::window::Inner;
|
||||
use crate::window::WindowId;
|
||||
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
clone::Clone,
|
||||
|
|
@ -36,6 +37,7 @@ impl Clone for Shared {
|
|||
type OnEventHandle<T> = RefCell<Option<EventListenerHandle<dyn FnMut(T)>>>;
|
||||
|
||||
pub struct Execution {
|
||||
proxy_spawner: WakerSpawner<Weak<Self>>,
|
||||
control_flow: Cell<ControlFlow>,
|
||||
poll_strategy: Cell<PollStrategy>,
|
||||
exit: Cell<bool>,
|
||||
|
|
@ -46,7 +48,14 @@ pub struct Execution {
|
|||
id: RefCell<u32>,
|
||||
window: web_sys::Window,
|
||||
document: Document,
|
||||
all_canvases: RefCell<Vec<(WindowId, Weak<RefCell<backend::Canvas>>)>>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
all_canvases: RefCell<
|
||||
Vec<(
|
||||
WindowId,
|
||||
Weak<RefCell<backend::Canvas>>,
|
||||
DispatchRunner<Inner>,
|
||||
)>,
|
||||
>,
|
||||
redraw_pending: RefCell<HashSet<WindowId>>,
|
||||
destroy_pending: RefCell<VecDeque<WindowId>>,
|
||||
page_transition_event_handle: RefCell<Option<backend::PageTransitionEventHandle>>,
|
||||
|
|
@ -140,30 +149,40 @@ impl Shared {
|
|||
#[allow(clippy::disallowed_methods)]
|
||||
let document = window.document().expect("Failed to obtain document");
|
||||
|
||||
Shared(Rc::new(Execution {
|
||||
control_flow: Cell::new(ControlFlow::default()),
|
||||
poll_strategy: Cell::new(PollStrategy::default()),
|
||||
exit: Cell::new(false),
|
||||
runner: RefCell::new(RunnerEnum::Pending),
|
||||
suspended: Cell::new(false),
|
||||
event_loop_recreation: Cell::new(false),
|
||||
events: RefCell::new(VecDeque::new()),
|
||||
window,
|
||||
document,
|
||||
id: RefCell::new(0),
|
||||
all_canvases: RefCell::new(Vec::new()),
|
||||
redraw_pending: RefCell::new(HashSet::new()),
|
||||
destroy_pending: RefCell::new(VecDeque::new()),
|
||||
page_transition_event_handle: RefCell::new(None),
|
||||
device_events: Cell::default(),
|
||||
on_mouse_move: RefCell::new(None),
|
||||
on_wheel: RefCell::new(None),
|
||||
on_mouse_press: RefCell::new(None),
|
||||
on_mouse_release: RefCell::new(None),
|
||||
on_key_press: RefCell::new(None),
|
||||
on_key_release: RefCell::new(None),
|
||||
on_visibility_change: RefCell::new(None),
|
||||
on_touch_end: RefCell::new(None),
|
||||
Shared(Rc::<Execution>::new_cyclic(|weak| {
|
||||
let proxy_spawner = WakerSpawner::new(weak.clone(), |runner, count| {
|
||||
if let Some(runner) = runner.upgrade() {
|
||||
Shared(runner).send_events(iter::repeat(Event::UserEvent(())).take(count))
|
||||
}
|
||||
})
|
||||
.expect("`EventLoop` has to be created in the main thread");
|
||||
|
||||
Execution {
|
||||
proxy_spawner,
|
||||
control_flow: Cell::new(ControlFlow::default()),
|
||||
poll_strategy: Cell::new(PollStrategy::default()),
|
||||
exit: Cell::new(false),
|
||||
runner: RefCell::new(RunnerEnum::Pending),
|
||||
suspended: Cell::new(false),
|
||||
event_loop_recreation: Cell::new(false),
|
||||
events: RefCell::new(VecDeque::new()),
|
||||
window,
|
||||
document,
|
||||
id: RefCell::new(0),
|
||||
all_canvases: RefCell::new(Vec::new()),
|
||||
redraw_pending: RefCell::new(HashSet::new()),
|
||||
destroy_pending: RefCell::new(VecDeque::new()),
|
||||
page_transition_event_handle: RefCell::new(None),
|
||||
device_events: Cell::default(),
|
||||
on_mouse_move: RefCell::new(None),
|
||||
on_wheel: RefCell::new(None),
|
||||
on_mouse_press: RefCell::new(None),
|
||||
on_mouse_release: RefCell::new(None),
|
||||
on_key_press: RefCell::new(None),
|
||||
on_key_release: RefCell::new(None),
|
||||
on_visibility_change: RefCell::new(None),
|
||||
on_touch_end: RefCell::new(None),
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
@ -175,11 +194,13 @@ impl Shared {
|
|||
&self.0.document
|
||||
}
|
||||
|
||||
pub fn add_canvas(&self, id: WindowId, canvas: &Rc<RefCell<backend::Canvas>>) {
|
||||
self.0
|
||||
.all_canvases
|
||||
.borrow_mut()
|
||||
.push((id, Rc::downgrade(canvas)));
|
||||
pub fn add_canvas(
|
||||
&self,
|
||||
id: WindowId,
|
||||
canvas: Weak<RefCell<backend::Canvas>>,
|
||||
runner: DispatchRunner<Inner>,
|
||||
) {
|
||||
self.0.all_canvases.borrow_mut().push((id, canvas, runner));
|
||||
}
|
||||
|
||||
pub fn notify_destroy_window(&self, id: WindowId) {
|
||||
|
|
@ -411,7 +432,7 @@ impl Shared {
|
|||
"visibilitychange",
|
||||
Closure::new(move |_| {
|
||||
if !runner.0.suspended.get() {
|
||||
for (id, canvas) in &*runner.0.all_canvases.borrow() {
|
||||
for (id, canvas, _) in &*runner.0.all_canvases.borrow() {
|
||||
if let Some(canvas) = canvas.upgrade() {
|
||||
let is_visible = backend::is_visible(runner.document());
|
||||
// only fire if:
|
||||
|
|
@ -549,7 +570,7 @@ impl Shared {
|
|||
self.0
|
||||
.all_canvases
|
||||
.borrow_mut()
|
||||
.retain(|&(item_id, _)| item_id != id);
|
||||
.retain(|&(item_id, _, _)| item_id != id);
|
||||
self.handle_event(Event::WindowEvent {
|
||||
window_id: id,
|
||||
event: crate::event::WindowEvent::Destroyed,
|
||||
|
|
@ -618,9 +639,29 @@ impl Shared {
|
|||
// Don't take events out of the queue if the loop is closed or the runner doesn't exist
|
||||
// If the runner doesn't exist and this method recurses, it will recurse infinitely
|
||||
if !is_closed && self.0.runner.borrow().maybe_runner().is_some() {
|
||||
// Pre-fetch window commands to avoid having to wait until the next event loop cycle
|
||||
// and potentially block other threads in the meantime.
|
||||
for (_, window, runner) in self.0.all_canvases.borrow().iter() {
|
||||
if let Some(window) = window.upgrade() {
|
||||
runner.run();
|
||||
drop(window)
|
||||
}
|
||||
}
|
||||
|
||||
// Take an event out of the queue and handle it
|
||||
// Make sure not to let the borrow_mut live during the next handle_event
|
||||
let event = { self.0.events.borrow_mut().pop_front() };
|
||||
let event = {
|
||||
let mut events = self.0.events.borrow_mut();
|
||||
|
||||
// Pre-fetch `UserEvent`s to avoid having to wait until the next event loop cycle.
|
||||
events.extend(
|
||||
iter::repeat(Event::UserEvent(()))
|
||||
.take(self.0.proxy_spawner.fetch())
|
||||
.map(EventWrapper::from),
|
||||
);
|
||||
|
||||
events.pop_front()
|
||||
};
|
||||
if let Some(event) = event {
|
||||
self.handle_event(event);
|
||||
}
|
||||
|
|
@ -690,7 +731,7 @@ impl Shared {
|
|||
// Dropping the `Runner` drops the event handler closure, which will in
|
||||
// turn drop all `Window`s moved into the closure.
|
||||
*self.0.runner.borrow_mut() = RunnerEnum::Destroyed;
|
||||
for (_, canvas) in all_canvases {
|
||||
for (_, canvas, _) in all_canvases {
|
||||
// In case any remaining `Window`s are still not dropped, we will need
|
||||
// to explicitly remove the event handlers associated with their canvases.
|
||||
if let Some(canvas) = canvas.upgrade() {
|
||||
|
|
@ -734,23 +775,29 @@ impl Shared {
|
|||
fn device_events(&self) -> bool {
|
||||
match self.0.device_events.get() {
|
||||
DeviceEvents::Always => true,
|
||||
DeviceEvents::WhenFocused => self.0.all_canvases.borrow().iter().any(|(_, canvas)| {
|
||||
if let Some(canvas) = canvas.upgrade() {
|
||||
canvas.borrow().has_focus.load(Ordering::Relaxed)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}),
|
||||
DeviceEvents::WhenFocused => {
|
||||
self.0.all_canvases.borrow().iter().any(|(_, canvas, _)| {
|
||||
if let Some(canvas) = canvas.upgrade() {
|
||||
canvas.borrow().has_focus.get()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
DeviceEvents::Never => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn transient_activation(&self) {
|
||||
self.0.all_canvases.borrow().iter().for_each(|(_, canvas)| {
|
||||
if let Some(canvas) = canvas.upgrade() {
|
||||
canvas.borrow().transient_activation();
|
||||
}
|
||||
});
|
||||
self.0
|
||||
.all_canvases
|
||||
.borrow()
|
||||
.iter()
|
||||
.for_each(|(_, canvas, _)| {
|
||||
if let Some(canvas) = canvas.upgrade() {
|
||||
canvas.borrow().transient_activation();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn event_loop_recreation(&self, allow: bool) {
|
||||
|
|
@ -780,6 +827,10 @@ impl Shared {
|
|||
pub(crate) fn poll_strategy(&self) -> PollStrategy {
|
||||
self.0.poll_strategy.get()
|
||||
}
|
||||
|
||||
pub(crate) fn waker(&self) -> Waker<Weak<Execution>> {
|
||||
self.0.proxy_spawner.waker()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum EventWrapper {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@ use std::clone::Clone;
|
|||
use std::collections::{vec_deque::IntoIter as VecDequeIter, VecDeque};
|
||||
use std::iter;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use super::runner::EventWrapper;
|
||||
use super::runner::{EventWrapper, Execution};
|
||||
use super::{
|
||||
super::{monitor::MonitorHandle, KeyEventExtra},
|
||||
backend,
|
||||
|
|
@ -20,6 +19,7 @@ use crate::event::{
|
|||
use crate::event_loop::{ControlFlow, DeviceEvents};
|
||||
use crate::keyboard::ModifiersState;
|
||||
use crate::platform::web::PollStrategy;
|
||||
use crate::platform_impl::platform::r#async::Waker;
|
||||
use crate::window::{Theme, WindowId as RootWindowId};
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -81,7 +81,6 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
id: WindowId,
|
||||
prevent_default: bool,
|
||||
) {
|
||||
self.runner.add_canvas(RootWindowId(id), canvas);
|
||||
let canvas_clone = canvas.clone();
|
||||
let mut canvas = canvas.borrow_mut();
|
||||
canvas.set_attribute("data-raw-handle", &id.0.to_string());
|
||||
|
|
@ -92,7 +91,7 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
let has_focus = canvas.has_focus.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
canvas.on_blur(move || {
|
||||
has_focus.store(false, Ordering::Relaxed);
|
||||
has_focus.set(false);
|
||||
|
||||
let clear_modifiers = (!modifiers.get().is_empty()).then(|| {
|
||||
modifiers.set(ModifiersState::empty());
|
||||
|
|
@ -115,7 +114,7 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
let runner = self.runner.clone();
|
||||
let has_focus = canvas.has_focus.clone();
|
||||
canvas.on_focus(move || {
|
||||
if !has_focus.swap(true, Ordering::Relaxed) {
|
||||
if !has_focus.replace(true) {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::Focused(true),
|
||||
|
|
@ -204,15 +203,13 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers, pointer_id| {
|
||||
let focus = (has_focus.load(Ordering::Relaxed)
|
||||
&& modifiers.get() != active_modifiers)
|
||||
.then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
}
|
||||
});
|
||||
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
}
|
||||
});
|
||||
|
||||
let pointer = pointer_id.map(|pointer_id| Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
|
|
@ -233,15 +230,13 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers, pointer_id| {
|
||||
let focus = (has_focus.load(Ordering::Relaxed)
|
||||
&& modifiers.get() != active_modifiers)
|
||||
.then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
}
|
||||
});
|
||||
let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
}
|
||||
});
|
||||
|
||||
let pointer = pointer_id.map(|pointer_id| Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
|
|
@ -263,7 +258,7 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers| {
|
||||
if has_focus.load(Ordering::Relaxed) && modifiers.get() != active_modifiers {
|
||||
if has_focus.get() && modifiers.get() != active_modifiers {
|
||||
modifiers.set(active_modifiers);
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
|
|
@ -278,9 +273,8 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers, pointer_id, events| {
|
||||
let modifiers = (has_focus.load(Ordering::Relaxed)
|
||||
&& modifiers.get() != active_modifiers)
|
||||
.then(|| {
|
||||
let modifiers =
|
||||
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
|
|
@ -307,9 +301,8 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers, device_id, events| {
|
||||
let modifiers = (has_focus.load(Ordering::Relaxed)
|
||||
&& modifiers.get() != active_modifiers)
|
||||
.then(|| {
|
||||
let modifiers =
|
||||
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
|
|
@ -341,9 +334,8 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
position: crate::dpi::PhysicalPosition<f64>,
|
||||
buttons,
|
||||
button| {
|
||||
let modifiers = (has_focus.load(Ordering::Relaxed)
|
||||
&& modifiers.get() != active_modifiers)
|
||||
.then(|| {
|
||||
let modifiers =
|
||||
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
|
|
@ -473,7 +465,7 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers| {
|
||||
if has_focus.load(Ordering::Relaxed) && modifiers.get() != active_modifiers {
|
||||
if has_focus.get() && modifiers.get() != active_modifiers {
|
||||
modifiers.set(active_modifiers);
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
|
|
@ -488,9 +480,8 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers, pointer_id, position, button| {
|
||||
let modifiers = (has_focus.load(Ordering::Relaxed)
|
||||
&& modifiers.get() != active_modifiers)
|
||||
.then(|| {
|
||||
let modifiers =
|
||||
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
|
|
@ -528,9 +519,8 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
let modifiers = self.modifiers.clone();
|
||||
|
||||
move |active_modifiers, device_id, location, force| {
|
||||
let modifiers = (has_focus.load(Ordering::Relaxed)
|
||||
&& modifiers.get() != active_modifiers)
|
||||
.then(|| {
|
||||
let modifiers =
|
||||
(has_focus.get() && modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
|
|
@ -558,8 +548,7 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
let modifiers = self.modifiers.clone();
|
||||
canvas.on_mouse_wheel(
|
||||
move |pointer_id, delta, active_modifiers| {
|
||||
let modifiers_changed = (has_focus.load(Ordering::Relaxed)
|
||||
&& modifiers.get() != active_modifiers)
|
||||
let modifiers_changed = (has_focus.get() && modifiers.get() != active_modifiers)
|
||||
.then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
|
|
@ -713,4 +702,8 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
pub(crate) fn poll_strategy(&self) -> PollStrategy {
|
||||
self.runner.poll_strategy()
|
||||
}
|
||||
|
||||
pub(crate) fn waker(&self) -> Waker<Weak<Execution>> {
|
||||
self.runner.waker()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use std::cell::Cell;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use smol_str::SmolStr;
|
||||
|
|
@ -29,7 +28,7 @@ use super::{event, ButtonsState, ResizeScaleHandle};
|
|||
pub struct Canvas {
|
||||
common: Common,
|
||||
id: WindowId,
|
||||
pub has_focus: Arc<AtomicBool>,
|
||||
pub has_focus: Rc<Cell<bool>>,
|
||||
pub is_intersecting: Option<bool>,
|
||||
on_touch_start: Option<EventListenerHandle<dyn FnMut(Event)>>,
|
||||
on_focus: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>,
|
||||
|
|
@ -139,7 +138,7 @@ impl Canvas {
|
|||
Ok(Canvas {
|
||||
common,
|
||||
id,
|
||||
has_focus: Arc::new(AtomicBool::new(false)),
|
||||
has_focus: Rc::new(Cell::new(false)),
|
||||
is_intersecting: None,
|
||||
on_touch_start: None,
|
||||
on_blur: None,
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ use super::{backend, monitor::MonitorHandle, EventLoopWindowTarget, Fullscreen};
|
|||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Window {
|
||||
inner: Dispatcher<Inner>,
|
||||
|
|
@ -27,7 +25,6 @@ pub struct Inner {
|
|||
canvas: Rc<RefCell<backend::Canvas>>,
|
||||
previous_pointer: RefCell<&'static str>,
|
||||
destroy_fn: Option<Box<dyn FnOnce()>>,
|
||||
has_focus: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
|
@ -51,14 +48,12 @@ impl Window {
|
|||
let runner = target.runner.clone();
|
||||
let destroy_fn = Box::new(move || runner.notify_destroy_window(RootWI(id)));
|
||||
|
||||
let has_focus = canvas.borrow().has_focus.clone();
|
||||
let inner = Inner {
|
||||
id,
|
||||
window: window.clone(),
|
||||
canvas,
|
||||
previous_pointer: RefCell::new("auto"),
|
||||
destroy_fn: Some(destroy_fn),
|
||||
has_focus,
|
||||
};
|
||||
|
||||
inner.set_title(&attr.title);
|
||||
|
|
@ -66,9 +61,11 @@ impl Window {
|
|||
inner.set_visible(attr.visible);
|
||||
inner.set_window_icon(attr.window_icon);
|
||||
|
||||
Ok(Window {
|
||||
inner: Dispatcher::new(inner).unwrap(),
|
||||
})
|
||||
let canvas = Rc::downgrade(&inner.canvas);
|
||||
let (dispatcher, runner) = Dispatcher::new(inner).unwrap();
|
||||
target.runner.add_canvas(RootWI(id), canvas, runner);
|
||||
|
||||
Ok(Window { inner: dispatcher })
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Inner) + Send + 'static) {
|
||||
|
|
@ -80,7 +77,9 @@ impl Window {
|
|||
}
|
||||
|
||||
pub fn canvas(&self) -> Option<HtmlCanvasElement> {
|
||||
self.inner.with(|inner| inner.canvas.borrow().raw().clone())
|
||||
self.inner
|
||||
.value()
|
||||
.map(|inner| inner.canvas.borrow().raw().clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -414,7 +413,7 @@ impl Inner {
|
|||
|
||||
#[inline]
|
||||
pub fn has_focus(&self) -> bool {
|
||||
self.has_focus.load(Ordering::Relaxed)
|
||||
self.canvas.borrow().has_focus.get()
|
||||
}
|
||||
|
||||
pub fn title(&self) -> String {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue