Improve macOS/iOS/Web thread safety
Co-authored-by: daxpedda <daxpedda@gmail.com>
This commit is contained in:
parent
119462795a
commit
af6c343d0e
20 changed files with 552 additions and 724 deletions
|
|
@ -96,16 +96,6 @@ impl<const SYNC: bool, T, E> MainThreadSafe<SYNC, T, E> {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn with_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Option<R> {
|
||||
Self::MAIN_THREAD.with(|is_main_thread| {
|
||||
if *is_main_thread.deref() {
|
||||
Some(f(self.value.write().unwrap().as_mut().unwrap()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SYNC: bool, T, E> Clone for MainThreadSafe<SYNC, T, E> {
|
||||
|
|
@ -219,17 +209,15 @@ impl<T> AsyncReceiver<T> {
|
|||
|
||||
pub struct Dispatcher<T: 'static>(MainThreadSafe<true, T, Closure<T>>);
|
||||
|
||||
pub enum Closure<T> {
|
||||
Ref(Box<dyn FnOnce(&T) + Send>),
|
||||
RefMut(Box<dyn FnOnce(&mut T) + Send>),
|
||||
}
|
||||
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| match closure {
|
||||
Closure::Ref(f) => f(value.read().unwrap().as_ref().unwrap()),
|
||||
Closure::RefMut(f) => f(value.write().unwrap().as_mut().unwrap()),
|
||||
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)
|
||||
}
|
||||
|
|
@ -238,30 +226,26 @@ impl<T> Dispatcher<T> {
|
|||
if self.is_main_thread() {
|
||||
self.0.with(f).unwrap()
|
||||
} else {
|
||||
self.0.send(Closure::Ref(Box::new(f)))
|
||||
self.0.send(Closure(Box::new(f)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dispatch_mut(&self, f: impl 'static + FnOnce(&mut T) + Send) {
|
||||
if self.is_main_thread() {
|
||||
self.0.with_mut(f).unwrap()
|
||||
} else {
|
||||
self.0.send(Closure::RefMut(Box::new(f)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue<R: 'static + Send>(&self, f: impl 'static + FnOnce(&T) -> R + Send) -> R {
|
||||
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 = Closure::Ref(Box::new({
|
||||
let closure = Box::new({
|
||||
let pair = pair.clone();
|
||||
move |value| {
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@ pub use self::resize_scaling::ResizeScaleHandle;
|
|||
pub use self::timeout::{IdleCallback, Timeout};
|
||||
|
||||
use crate::dpi::{LogicalPosition, LogicalSize};
|
||||
use crate::platform::web::WindowExtWebSys;
|
||||
use crate::window::Window;
|
||||
use wasm_bindgen::closure::Closure;
|
||||
use web_sys::{
|
||||
CssStyleDeclaration, Document, Element, HtmlCanvasElement, PageTransitionEvent, VisibilityState,
|
||||
|
|
@ -52,12 +50,6 @@ pub fn on_page_transition(
|
|||
}
|
||||
}
|
||||
|
||||
impl WindowExtWebSys for Window {
|
||||
fn canvas(&self) -> Option<HtmlCanvasElement> {
|
||||
self.window.canvas()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scale_factor(window: &web_sys::Window) -> f64 {
|
||||
window.device_pixel_ratio()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,24 +13,23 @@ use super::r#async::Dispatcher;
|
|||
use super::{backend, monitor::MonitorHandle, EventLoopWindowTarget, Fullscreen};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::vec_deque::IntoIter as VecDequeIter;
|
||||
use std::collections::VecDeque;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Window {
|
||||
id: WindowId,
|
||||
has_focus: Arc<AtomicBool>,
|
||||
pub inner: Dispatcher<Inner>,
|
||||
inner: Dispatcher<Inner>,
|
||||
}
|
||||
|
||||
pub struct Inner {
|
||||
id: WindowId,
|
||||
pub window: web_sys::Window,
|
||||
document: Document,
|
||||
canvas: Rc<RefCell<backend::Canvas>>,
|
||||
previous_pointer: RefCell<&'static str>,
|
||||
destroy_fn: Option<Box<dyn FnOnce()>>,
|
||||
has_focus: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
|
@ -55,41 +54,42 @@ impl Window {
|
|||
let destroy_fn = Box::new(move || runner.notify_destroy_window(RootWI(id)));
|
||||
|
||||
let has_focus = canvas.borrow().has_focus.clone();
|
||||
let window = Window {
|
||||
let inner = Inner {
|
||||
id,
|
||||
window: window.clone(),
|
||||
document: document.clone(),
|
||||
canvas,
|
||||
previous_pointer: RefCell::new("auto"),
|
||||
destroy_fn: Some(destroy_fn),
|
||||
has_focus,
|
||||
inner: Dispatcher::new(Inner {
|
||||
window: window.clone(),
|
||||
document: document.clone(),
|
||||
canvas,
|
||||
previous_pointer: RefCell::new("auto"),
|
||||
destroy_fn: Some(destroy_fn),
|
||||
})
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
window.set_title(&attr.title);
|
||||
window.set_maximized(attr.maximized);
|
||||
window.set_visible(attr.visible);
|
||||
window.set_window_icon(attr.window_icon);
|
||||
inner.set_title(&attr.title);
|
||||
inner.set_maximized(attr.maximized);
|
||||
inner.set_visible(attr.visible);
|
||||
inner.set_window_icon(attr.window_icon);
|
||||
|
||||
Ok(window)
|
||||
Ok(Window {
|
||||
inner: Dispatcher::new(inner).unwrap(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&Inner) + Send + 'static) {
|
||||
self.inner.dispatch(f)
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_wait_on_main<R: Send>(&self, f: impl FnOnce(&Inner) -> R + Send) -> R {
|
||||
self.inner.queue(f)
|
||||
}
|
||||
|
||||
pub fn canvas(&self) -> Option<HtmlCanvasElement> {
|
||||
self.inner.with(|inner| inner.canvas.borrow().raw().clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
pub fn set_title(&self, title: &str) {
|
||||
if self
|
||||
.inner
|
||||
.with(|inner| inner.canvas.borrow().set_attribute("alt", title))
|
||||
.is_none()
|
||||
{
|
||||
let title = title.to_owned();
|
||||
self.inner
|
||||
.dispatch(move |inner| inner.canvas.borrow().set_attribute("alt", &title));
|
||||
}
|
||||
self.canvas.borrow().set_attribute("alt", title)
|
||||
}
|
||||
|
||||
pub fn set_transparent(&self, _transparent: bool) {}
|
||||
|
|
@ -104,21 +104,17 @@ impl Window {
|
|||
}
|
||||
|
||||
pub fn request_redraw(&self) {
|
||||
self.inner.dispatch(move |inner| {
|
||||
inner.canvas.borrow().request_animation_frame();
|
||||
});
|
||||
self.canvas.borrow().request_animation_frame();
|
||||
}
|
||||
|
||||
pub fn pre_present_notify(&self) {}
|
||||
|
||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
self.inner.queue(|inner| {
|
||||
Ok(inner
|
||||
.canvas
|
||||
.borrow()
|
||||
.position()
|
||||
.to_physical(inner.scale_factor()))
|
||||
})
|
||||
Ok(self
|
||||
.canvas
|
||||
.borrow()
|
||||
.position()
|
||||
.to_physical(self.scale_factor()))
|
||||
}
|
||||
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
||||
|
|
@ -127,17 +123,15 @@ impl Window {
|
|||
}
|
||||
|
||||
pub fn set_outer_position(&self, position: Position) {
|
||||
self.inner.dispatch(move |inner| {
|
||||
let canvas = inner.canvas.borrow();
|
||||
let position = position.to_logical::<f64>(inner.scale_factor());
|
||||
let canvas = self.canvas.borrow();
|
||||
let position = position.to_logical::<f64>(self.scale_factor());
|
||||
|
||||
backend::set_canvas_position(canvas.document(), canvas.raw(), canvas.style(), position)
|
||||
});
|
||||
backend::set_canvas_position(canvas.document(), canvas.raw(), canvas.style(), position)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
self.inner.queue(|inner| inner.canvas.borrow().inner_size())
|
||||
self.canvas.borrow().inner_size()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -148,43 +142,24 @@ impl Window {
|
|||
|
||||
#[inline]
|
||||
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
self.inner.dispatch(move |inner| {
|
||||
let size = size.to_logical(inner.scale_factor());
|
||||
let canvas = inner.canvas.borrow();
|
||||
backend::set_canvas_size(canvas.document(), canvas.raw(), canvas.style(), size);
|
||||
});
|
||||
|
||||
let size = size.to_logical(self.scale_factor());
|
||||
let canvas = self.canvas.borrow();
|
||||
backend::set_canvas_size(canvas.document(), canvas.raw(), canvas.style(), size);
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
|
||||
self.inner.dispatch(move |inner| {
|
||||
let dimensions =
|
||||
dimensions.map(|dimensions| dimensions.to_logical(inner.scale_factor()));
|
||||
let canvas = inner.canvas.borrow();
|
||||
backend::set_canvas_min_size(
|
||||
canvas.document(),
|
||||
canvas.raw(),
|
||||
canvas.style(),
|
||||
dimensions,
|
||||
)
|
||||
})
|
||||
let dimensions = dimensions.map(|dimensions| dimensions.to_logical(self.scale_factor()));
|
||||
let canvas = self.canvas.borrow();
|
||||
backend::set_canvas_min_size(canvas.document(), canvas.raw(), canvas.style(), dimensions)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
|
||||
self.inner.dispatch(move |inner| {
|
||||
let dimensions =
|
||||
dimensions.map(|dimensions| dimensions.to_logical(inner.scale_factor()));
|
||||
let canvas = inner.canvas.borrow();
|
||||
backend::set_canvas_max_size(
|
||||
canvas.document(),
|
||||
canvas.raw(),
|
||||
canvas.style(),
|
||||
dimensions,
|
||||
)
|
||||
})
|
||||
let dimensions = dimensions.map(|dimensions| dimensions.to_logical(self.scale_factor()));
|
||||
let canvas = self.canvas.borrow();
|
||||
backend::set_canvas_max_size(canvas.document(), canvas.raw(), canvas.style(), dimensions)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -216,19 +191,13 @@ impl Window {
|
|||
|
||||
#[inline]
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
self.inner.queue(|inner| inner.scale_factor())
|
||||
super::backend::scale_factor(&self.window)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
|
||||
self.inner.dispatch(move |inner| {
|
||||
*inner.previous_pointer.borrow_mut() = cursor.name();
|
||||
backend::set_canvas_style_property(
|
||||
inner.canvas.borrow().raw(),
|
||||
"cursor",
|
||||
cursor.name(),
|
||||
);
|
||||
});
|
||||
*self.previous_pointer.borrow_mut() = cursor.name();
|
||||
backend::set_canvas_style_property(self.canvas.borrow().raw(), "cursor", cursor.name());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -238,36 +207,31 @@ impl Window {
|
|||
|
||||
#[inline]
|
||||
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
||||
self.inner.queue(move |inner| {
|
||||
let lock = match mode {
|
||||
CursorGrabMode::None => false,
|
||||
CursorGrabMode::Locked => true,
|
||||
CursorGrabMode::Confined => {
|
||||
return Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
};
|
||||
let lock = match mode {
|
||||
CursorGrabMode::None => false,
|
||||
CursorGrabMode::Locked => true,
|
||||
CursorGrabMode::Confined => {
|
||||
return Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
};
|
||||
|
||||
inner
|
||||
.canvas
|
||||
.borrow()
|
||||
.set_cursor_lock(lock)
|
||||
.map_err(ExternalError::Os)
|
||||
})
|
||||
self.canvas
|
||||
.borrow()
|
||||
.set_cursor_lock(lock)
|
||||
.map_err(ExternalError::Os)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_visible(&self, visible: bool) {
|
||||
self.inner.dispatch(move |inner| {
|
||||
if !visible {
|
||||
backend::set_canvas_style_property(inner.canvas.borrow().raw(), "cursor", "none");
|
||||
} else {
|
||||
backend::set_canvas_style_property(
|
||||
inner.canvas.borrow().raw(),
|
||||
"cursor",
|
||||
&inner.previous_pointer.borrow(),
|
||||
);
|
||||
}
|
||||
});
|
||||
if !visible {
|
||||
backend::set_canvas_style_property(self.canvas.borrow().raw(), "cursor", "none");
|
||||
} else {
|
||||
backend::set_canvas_style_property(
|
||||
self.canvas.borrow().raw(),
|
||||
"cursor",
|
||||
&self.previous_pointer.borrow(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -309,24 +273,20 @@ impl Window {
|
|||
|
||||
#[inline]
|
||||
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
self.inner.queue(|inner| {
|
||||
if inner.canvas.borrow().is_fullscreen() {
|
||||
Some(Fullscreen::Borderless(Some(MonitorHandle)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
if self.canvas.borrow().is_fullscreen() {
|
||||
Some(Fullscreen::Borderless(Some(MonitorHandle)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
||||
self.inner.dispatch(move |inner| {
|
||||
if fullscreen.is_some() {
|
||||
inner.canvas.borrow().request_fullscreen();
|
||||
} else if inner.canvas.borrow().is_fullscreen() {
|
||||
backend::exit_fullscreen(&inner.document);
|
||||
}
|
||||
});
|
||||
if fullscreen.is_some() {
|
||||
self.canvas.borrow().request_fullscreen();
|
||||
} else if self.canvas.borrow().is_fullscreen() {
|
||||
backend::exit_fullscreen(&self.document);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -365,9 +325,7 @@ impl Window {
|
|||
|
||||
#[inline]
|
||||
pub fn focus_window(&self) {
|
||||
self.inner.dispatch(|inner| {
|
||||
let _ = inner.canvas.borrow().raw().focus();
|
||||
})
|
||||
let _ = self.canvas.borrow().raw().focus();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -381,8 +339,8 @@ impl Window {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn available_monitors(&self) -> VecDequeIter<MonitorHandle> {
|
||||
VecDeque::new().into_iter()
|
||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||
VecDeque::new()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -412,14 +370,12 @@ impl Window {
|
|||
|
||||
#[inline]
|
||||
pub fn theme(&self) -> Option<Theme> {
|
||||
self.inner.queue(|inner| {
|
||||
backend::is_dark_mode(&inner.window).map(|is_dark_mode| {
|
||||
if is_dark_mode {
|
||||
Theme::Dark
|
||||
} else {
|
||||
Theme::Light
|
||||
}
|
||||
})
|
||||
backend::is_dark_mode(&self.window).map(|is_dark_mode| {
|
||||
if is_dark_mode {
|
||||
Theme::Dark
|
||||
} else {
|
||||
Theme::Light
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -437,23 +393,13 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
impl Drop for Inner {
|
||||
fn drop(&mut self) {
|
||||
self.inner.dispatch_mut(|inner| {
|
||||
if let Some(destroy_fn) = inner.destroy_fn.take() {
|
||||
destroy_fn();
|
||||
}
|
||||
});
|
||||
if let Some(destroy_fn) = self.destroy_fn.take() {
|
||||
destroy_fn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
#[inline]
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
super::backend::scale_factor(&self.window)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(pub(crate) u32);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue