Web: monitor API improvements (#3847)
- Improved the documentation to point users into the right direction from all kinds of methods and types. - De-duplicated some code and added more comments. - Implement an ID system to correctly and efficiently implement `Eq`, `Hash`, `Ord`, `PartialEq` and `PartialOrd`. - Fixed screen locking support being cached thread local, ergo calling from a different thread would require to make the check again, not fulfilling its purpose as a fast-path at all.
This commit is contained in:
parent
42ba0a74e0
commit
586255ac0a
4 changed files with 296 additions and 173 deletions
|
|
@ -652,7 +652,7 @@ impl ActiveEventLoop {
|
|||
}
|
||||
|
||||
pub(crate) fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture {
|
||||
self.runner.monitor().request_detailed_monitor_permission(self.runner.weak())
|
||||
self.runner.monitor().request_detailed_monitor_permission()
|
||||
}
|
||||
|
||||
pub(crate) fn has_detailed_monitor_permission(&self) -> bool {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
use std::cell::{OnceCell, Ref, RefCell};
|
||||
use std::cmp::Ordering;
|
||||
use std::future::Future;
|
||||
use std::hash::Hash;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::iter::{self, Once};
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::pin::Pin;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::sync::OnceLock;
|
||||
use std::task::{ready, Context, Poll};
|
||||
|
||||
use dpi::LogicalSize;
|
||||
|
|
@ -28,24 +31,29 @@ use crate::platform::web::{
|
|||
MonitorPermissionError, Orientation, OrientationData, OrientationLock, OrientationLockError,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct MonitorHandle(Dispatcher<Inner>);
|
||||
#[derive(Debug, Clone, Eq)]
|
||||
pub struct MonitorHandle {
|
||||
/// [`None`] means [`web_sys::Screen`], which is always the same.
|
||||
id: Option<u64>,
|
||||
inner: Dispatcher<Inner>,
|
||||
}
|
||||
|
||||
impl MonitorHandle {
|
||||
fn new(main_thread: MainThreadMarker, inner: Inner) -> Self {
|
||||
Self(Dispatcher::new(main_thread, inner).0)
|
||||
let id = if let Screen::Detailed { id, .. } = inner.screen { Some(id) } else { None };
|
||||
Self { id, inner: Dispatcher::new(main_thread, inner).0 }
|
||||
}
|
||||
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
self.0.queue(|inner| match &inner.screen {
|
||||
self.inner.queue(|inner| match &inner.screen {
|
||||
Screen::Screen(_) => 0.,
|
||||
Screen::Detailed(screen) => screen.device_pixel_ratio(),
|
||||
Screen::Detailed { screen, .. } => screen.device_pixel_ratio(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn position(&self) -> PhysicalPosition<i32> {
|
||||
self.0.queue(|inner| {
|
||||
if let Screen::Detailed(screen) = &inner.screen {
|
||||
self.inner.queue(|inner| {
|
||||
if let Screen::Detailed { screen, .. } = &inner.screen {
|
||||
PhysicalPosition::new(screen.left(), screen.top())
|
||||
} else {
|
||||
PhysicalPosition::default()
|
||||
|
|
@ -54,8 +62,8 @@ impl MonitorHandle {
|
|||
}
|
||||
|
||||
pub fn name(&self) -> Option<String> {
|
||||
self.0.queue(|inner| {
|
||||
if let Screen::Detailed(screen) = &inner.screen {
|
||||
self.inner.queue(|inner| {
|
||||
if let Screen::Detailed { screen, .. } = &inner.screen {
|
||||
Some(screen.label())
|
||||
} else {
|
||||
None
|
||||
|
|
@ -68,7 +76,7 @@ impl MonitorHandle {
|
|||
}
|
||||
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
self.0.queue(|inner| {
|
||||
self.inner.queue(|inner| {
|
||||
let width = inner.screen.width().unwrap();
|
||||
let height = inner.screen.height().unwrap();
|
||||
|
||||
|
|
@ -86,9 +94,8 @@ impl MonitorHandle {
|
|||
}
|
||||
|
||||
pub fn orientation(&self) -> OrientationData {
|
||||
self.0.queue(|inner| {
|
||||
let orientation =
|
||||
inner.orientation.get_or_init(|| inner.screen.orientation().unchecked_into());
|
||||
self.inner.queue(|inner| {
|
||||
let orientation = inner.orientation();
|
||||
let angle = orientation.angle().unwrap();
|
||||
|
||||
match orientation.type_().unwrap() {
|
||||
|
|
@ -121,23 +128,19 @@ impl MonitorHandle {
|
|||
|
||||
pub fn request_lock(&self, orientation_lock: OrientationLock) -> OrientationLockFuture {
|
||||
// Short-circuit without blocking.
|
||||
if let Some(support) = HAS_LOCK_SUPPORT.with(|support| support.get().cloned()) {
|
||||
if let Some(support) = has_previous_lock_support() {
|
||||
if !support {
|
||||
return OrientationLockFuture::Ready(Some(Err(OrientationLockError::Unsupported)));
|
||||
}
|
||||
}
|
||||
|
||||
self.0.queue(|inner| {
|
||||
let orientation =
|
||||
inner.orientation.get_or_init(|| inner.screen.orientation().unchecked_into());
|
||||
|
||||
if !HAS_LOCK_SUPPORT
|
||||
.with(|support| *support.get_or_init(|| !orientation.has_lock().is_undefined()))
|
||||
{
|
||||
self.inner.queue(|inner| {
|
||||
if !inner.has_lock_support() {
|
||||
return OrientationLockFuture::Ready(Some(Err(OrientationLockError::Unsupported)));
|
||||
}
|
||||
|
||||
let future = JsFuture::from(orientation.lock(orientation_lock.to_js()).unwrap());
|
||||
let future =
|
||||
JsFuture::from(inner.orientation().lock(orientation_lock.to_js()).unwrap());
|
||||
let notifier = Notifier::new();
|
||||
let notified = notifier.notified();
|
||||
|
||||
|
|
@ -151,29 +154,24 @@ impl MonitorHandle {
|
|||
|
||||
pub fn unlock(&self) -> Result<(), OrientationLockError> {
|
||||
// Short-circuit without blocking.
|
||||
if let Some(support) = HAS_LOCK_SUPPORT.with(|support| support.get().cloned()) {
|
||||
if let Some(support) = has_previous_lock_support() {
|
||||
if !support {
|
||||
return Err(OrientationLockError::Unsupported);
|
||||
}
|
||||
}
|
||||
|
||||
self.0.queue(|inner| {
|
||||
let orientation =
|
||||
inner.orientation.get_or_init(|| inner.screen.orientation().unchecked_into());
|
||||
|
||||
if !HAS_LOCK_SUPPORT
|
||||
.with(|support| *support.get_or_init(|| !orientation.has_lock().is_undefined()))
|
||||
{
|
||||
self.inner.queue(|inner| {
|
||||
if !inner.has_lock_support() {
|
||||
return Err(OrientationLockError::Unsupported);
|
||||
}
|
||||
|
||||
orientation.unlock().map_err(OrientationLockError::from_js)
|
||||
inner.orientation().unlock().map_err(OrientationLockError::from_js)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_internal(&self) -> Option<bool> {
|
||||
self.0.queue(|inner| {
|
||||
if let Screen::Detailed(screen) = &inner.screen {
|
||||
self.inner.queue(|inner| {
|
||||
if let Screen::Detailed { screen, .. } = &inner.screen {
|
||||
Some(screen.is_internal())
|
||||
} else {
|
||||
None
|
||||
|
|
@ -182,19 +180,19 @@ impl MonitorHandle {
|
|||
}
|
||||
|
||||
pub fn is_detailed(&self) -> bool {
|
||||
self.0.queue(|inner| matches!(inner.screen, Screen::Detailed(_)))
|
||||
self.inner.queue(|inner| matches!(inner.screen, Screen::Detailed { .. }))
|
||||
}
|
||||
|
||||
pub(crate) fn detailed(
|
||||
&self,
|
||||
main_thread: MainThreadMarker,
|
||||
) -> Option<Ref<'_, ScreenDetailed>> {
|
||||
let inner = self.0.value(main_thread);
|
||||
let inner = self.inner.value(main_thread);
|
||||
match &inner.screen {
|
||||
Screen::Screen(_) => None,
|
||||
Screen::Detailed(_) => Some(Ref::map(inner, |inner| {
|
||||
if let Screen::Detailed(detailed) = &inner.screen {
|
||||
detailed
|
||||
Screen::Detailed { .. } => Some(Ref::map(inner, |inner| {
|
||||
if let Screen::Detailed { screen, .. } = &inner.screen {
|
||||
screen.deref()
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
|
@ -203,6 +201,30 @@ impl MonitorHandle {
|
|||
}
|
||||
}
|
||||
|
||||
impl Hash for MonitorHandle {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.id.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for MonitorHandle {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.id.cmp(&other.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for MonitorHandle {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id.eq(&other.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for MonitorHandle {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OrientationLockFuture {
|
||||
Future(Notified<Result<(), OrientationLockError>>),
|
||||
|
|
@ -269,7 +291,7 @@ impl VideoModeHandle {
|
|||
}
|
||||
|
||||
pub fn bit_depth(&self) -> u16 {
|
||||
self.0 .0.queue(|inner| inner.screen.color_depth().unwrap()).try_into().unwrap()
|
||||
self.0.inner.queue(|inner| inner.screen.color_depth().unwrap()).try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn refresh_rate_millihertz(&self) -> u32 {
|
||||
|
|
@ -292,11 +314,37 @@ impl Inner {
|
|||
fn new(window: WindowExt, engine: Option<Engine>, screen: Screen) -> Self {
|
||||
Self { window, engine, screen, orientation: OnceCell::new() }
|
||||
}
|
||||
|
||||
fn orientation(&self) -> &ScreenOrientationExt {
|
||||
self.orientation.get_or_init(|| self.screen.orientation().unchecked_into())
|
||||
}
|
||||
|
||||
fn has_lock_support(&self) -> bool {
|
||||
*HAS_LOCK_SUPPORT.get_or_init(|| !self.orientation().has_lock().is_undefined())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Inner {
|
||||
fn drop(&mut self) {
|
||||
if let Screen::Detailed { runner, id, screen } = &self.screen {
|
||||
// If this is the last screen with its ID, clean it up in the `MonitorHandler`.
|
||||
if Rc::strong_count(screen) == 1 {
|
||||
if let Some(runner) = runner.upgrade() {
|
||||
let mut state = runner.monitor().state.borrow_mut();
|
||||
let State::Detailed(detailed) = state.deref_mut() else {
|
||||
unreachable!("found a `ScreenDetailed` without being in `State::Detailed`")
|
||||
};
|
||||
|
||||
detailed.screens.retain(|(id_internal, _)| *id_internal != *id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Screen {
|
||||
Screen(ScreenExt),
|
||||
Detailed(ScreenDetailed),
|
||||
Detailed { runner: WeakShared, id: u64, screen: Rc<ScreenDetailed> },
|
||||
}
|
||||
|
||||
impl Deref for Screen {
|
||||
|
|
@ -305,12 +353,13 @@ impl Deref for Screen {
|
|||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Screen::Screen(screen) => screen,
|
||||
Screen::Detailed(screen) => screen,
|
||||
Screen::Detailed { screen, .. } => screen,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MonitorHandler {
|
||||
runner: WeakShared,
|
||||
state: RefCell<State>,
|
||||
main_thread: MainThreadMarker,
|
||||
window: WindowExt,
|
||||
|
|
@ -323,10 +372,60 @@ enum State {
|
|||
Initialize(Notified<Result<(), MonitorPermissionError>>),
|
||||
Permission { permission: PermissionStatusExt, _handle: EventListenerHandle<dyn Fn()> },
|
||||
Upgrade(Notified<Result<(), MonitorPermissionError>>),
|
||||
Detailed(ScreenDetails),
|
||||
Detailed(Detailed),
|
||||
}
|
||||
|
||||
struct Detailed {
|
||||
details: ScreenDetails,
|
||||
id_counter: u64,
|
||||
screens: Vec<(u64, Weak<ScreenDetailed>)>,
|
||||
}
|
||||
|
||||
impl Detailed {
|
||||
fn handle(
|
||||
&mut self,
|
||||
main_thread: MainThreadMarker,
|
||||
runner: WeakShared,
|
||||
window: WindowExt,
|
||||
engine: Option<Engine>,
|
||||
screen: ScreenDetailed,
|
||||
) -> MonitorHandle {
|
||||
// Before creating a new entry, see if we have an ID for this screen already.
|
||||
let found_screen = self.screens.iter().find_map(|(id, internal_screen)| {
|
||||
let internal_screen =
|
||||
internal_screen.upgrade().expect("dropped `MonitorHandle` without cleaning up");
|
||||
|
||||
if *internal_screen == screen {
|
||||
Some((*id, internal_screen))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let (id, screen) = if let Some((id, screen)) = found_screen {
|
||||
(id, screen)
|
||||
} else {
|
||||
let id = self.id_counter;
|
||||
self.id_counter += 1;
|
||||
let screen = Rc::new(screen);
|
||||
|
||||
self.screens.push((id, Rc::downgrade(&screen)));
|
||||
|
||||
(id, screen)
|
||||
};
|
||||
|
||||
MonitorHandle::new(
|
||||
main_thread,
|
||||
Inner::new(window, engine, Screen::Detailed { runner, id, screen }),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl MonitorHandler {
|
||||
/// When the [`MonitorHandler`] is created, it first checks if permission has already been
|
||||
/// granted by the user for this page, in which case it retrieves [`ScreenDetails`].
|
||||
///
|
||||
/// If not, it will listen to external changes in the permission and automatically elevate
|
||||
/// [`MonitorHandler`].
|
||||
pub fn new(
|
||||
main_thread: MainThreadMarker,
|
||||
window: Window,
|
||||
|
|
@ -338,6 +437,7 @@ impl MonitorHandler {
|
|||
let screen: ScreenExt = window.screen().unwrap().unchecked_into();
|
||||
|
||||
let state = if has_screen_details_support(&window) {
|
||||
// First try and get permissions.
|
||||
let permissions = navigator.permissions().expect(
|
||||
"expected the Permissions API to be implemented if the Window Management API is \
|
||||
as well",
|
||||
|
|
@ -346,6 +446,7 @@ impl MonitorHandler {
|
|||
descriptor.set_name("window-management");
|
||||
let future = JsFuture::from(permissions.query(&descriptor).unwrap());
|
||||
|
||||
let runner = runner.clone();
|
||||
let window = window.clone();
|
||||
let notifier = Notifier::new();
|
||||
let notified = notifier.notified();
|
||||
|
|
@ -359,17 +460,18 @@ impl MonitorHandler {
|
|||
),
|
||||
};
|
||||
|
||||
let screen_details = match permission.state() {
|
||||
let details = match permission.state() {
|
||||
// If we have permission, go ahead and get `ScreenDetails`.
|
||||
PermissionState::Granted => {
|
||||
let screen_details = match JsFuture::from(window.screen_details()).await {
|
||||
Ok(screen_details) => screen_details.unchecked_into(),
|
||||
let details = match JsFuture::from(window.screen_details()).await {
|
||||
Ok(details) => details.unchecked_into(),
|
||||
Err(error) => unreachable_error(
|
||||
&error,
|
||||
"getting screen details failed even though permission was granted",
|
||||
),
|
||||
};
|
||||
notifier.notify(Ok(()));
|
||||
Some(screen_details)
|
||||
Some(details)
|
||||
},
|
||||
PermissionState::Denied => {
|
||||
notifier.notify(Err(MonitorPermissionError::Denied));
|
||||
|
|
@ -392,17 +494,17 @@ impl MonitorHandler {
|
|||
// Notifying `Future`s is not dependant on the lifetime of the runner,
|
||||
// because they can outlive it.
|
||||
if let Some(runner) = runner.upgrade() {
|
||||
let state = if let Some(screen_details) = screen_details {
|
||||
State::Detailed(screen_details)
|
||||
if let Some(details) = details {
|
||||
runner.monitor().upgrade(details);
|
||||
} else {
|
||||
// If permission is denied we listen for changes so we can catch external
|
||||
// permission granting.
|
||||
let handle =
|
||||
Self::setup_listener(runner.weak(), window, permission.clone());
|
||||
State::Permission { permission, _handle: handle }
|
||||
*runner.monitor().state.borrow_mut() =
|
||||
State::Permission { permission, _handle: handle };
|
||||
};
|
||||
|
||||
*runner.monitor().state.borrow_mut() = state;
|
||||
runner.start_delayed();
|
||||
}
|
||||
});
|
||||
|
|
@ -412,9 +514,10 @@ impl MonitorHandler {
|
|||
State::Unsupported
|
||||
};
|
||||
|
||||
Self { state: RefCell::new(state), main_thread, window, engine, screen }
|
||||
Self { runner, state: RefCell::new(state), main_thread, window, engine, screen }
|
||||
}
|
||||
|
||||
/// Listens to external permission changes and elevates [`MonitorHandle`] automatically.
|
||||
fn setup_listener(
|
||||
runner: WeakShared,
|
||||
window: WindowExt,
|
||||
|
|
@ -429,8 +532,8 @@ impl MonitorHandler {
|
|||
|
||||
let runner = runner.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
let screen_details = match future.await {
|
||||
Ok(screen_details) => screen_details.unchecked_into(),
|
||||
let details = match future.await {
|
||||
Ok(details) => details.unchecked_into(),
|
||||
Err(error) => unreachable_error(
|
||||
&error,
|
||||
"getting screen details failed even though permission was granted",
|
||||
|
|
@ -439,9 +542,9 @@ impl MonitorHandler {
|
|||
|
||||
if let Some(runner) = runner.upgrade() {
|
||||
// We drop the event listener handle here, which
|
||||
// doesn't drop it while we are running it, because
|
||||
// doesn't drop it during its execution, because
|
||||
// we are in a `spawn_local()` context.
|
||||
*runner.monitor().state.borrow_mut() = State::Detailed(screen_details);
|
||||
runner.monitor().upgrade(details);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -449,6 +552,12 @@ impl MonitorHandler {
|
|||
)
|
||||
}
|
||||
|
||||
/// Elevate [`MonitorHandler`] to [`ScreenDetails`].
|
||||
fn upgrade(&self, details: ScreenDetails) {
|
||||
*self.state.borrow_mut() =
|
||||
State::Detailed(Detailed { details, id_counter: 0, screens: Vec::new() });
|
||||
}
|
||||
|
||||
pub fn is_extended(&self) -> Option<bool> {
|
||||
self.screen.is_extended()
|
||||
}
|
||||
|
|
@ -457,16 +566,19 @@ impl MonitorHandler {
|
|||
matches!(self.state.borrow().deref(), State::Initialize(_))
|
||||
}
|
||||
|
||||
fn handle(&self, detailed: &mut Detailed, screen: ScreenDetailed) -> MonitorHandle {
|
||||
detailed.handle(
|
||||
self.main_thread,
|
||||
self.runner.clone(),
|
||||
self.window.clone(),
|
||||
self.engine,
|
||||
screen,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn current_monitor(&self) -> MonitorHandle {
|
||||
if let State::Detailed(details) = self.state.borrow().deref() {
|
||||
MonitorHandle::new(
|
||||
self.main_thread,
|
||||
Inner::new(
|
||||
self.window.clone(),
|
||||
self.engine,
|
||||
Screen::Detailed(details.current_screen()),
|
||||
),
|
||||
)
|
||||
if let State::Detailed(detailed) = self.state.borrow_mut().deref_mut() {
|
||||
self.handle(detailed, detailed.details.current_screen())
|
||||
} else {
|
||||
MonitorHandle::new(
|
||||
self.main_thread,
|
||||
|
|
@ -477,106 +589,80 @@ impl MonitorHandler {
|
|||
|
||||
// Note: We have to return a `Vec` here because the iterator is otherwise not `Send` + `Sync`.
|
||||
pub fn available_monitors(&self) -> Vec<MonitorHandle> {
|
||||
if let State::Detailed(details) = self.state.borrow().deref() {
|
||||
details
|
||||
let mut state = self.state.borrow_mut();
|
||||
if let State::Detailed(detailed) = state.deref_mut() {
|
||||
detailed
|
||||
.details
|
||||
.screens()
|
||||
.into_iter()
|
||||
.map(move |screen| {
|
||||
MonitorHandle::new(
|
||||
self.main_thread,
|
||||
Inner::new(self.window.clone(), self.engine, Screen::Detailed(screen)),
|
||||
)
|
||||
})
|
||||
.map(move |screen| self.handle(detailed, screen))
|
||||
.collect()
|
||||
} else {
|
||||
drop(state);
|
||||
vec![self.current_monitor()]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
if let State::Detailed(details) = self.state.borrow().deref() {
|
||||
details.screens().into_iter().find_map(|screen| {
|
||||
screen.is_primary().then(|| {
|
||||
MonitorHandle::new(
|
||||
self.main_thread,
|
||||
Inner::new(self.window.clone(), self.engine, Screen::Detailed(screen)),
|
||||
)
|
||||
})
|
||||
})
|
||||
if let State::Detailed(detailed) = self.state.borrow_mut().deref_mut() {
|
||||
detailed
|
||||
.details
|
||||
.screens()
|
||||
.into_iter()
|
||||
.find_map(|screen| screen.is_primary().then(|| self.handle(detailed, screen)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn request_detailed_monitor_permission(
|
||||
&self,
|
||||
shared: WeakShared,
|
||||
) -> MonitorPermissionFuture {
|
||||
pub(crate) fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture {
|
||||
let state = self.state.borrow();
|
||||
let (notifier, notified) = match state.deref() {
|
||||
let permission = match state.deref() {
|
||||
State::Unsupported => {
|
||||
return MonitorPermissionFuture::Ready(Some(Err(
|
||||
MonitorPermissionError::Unsupported,
|
||||
)))
|
||||
},
|
||||
// If we are currently initializing, wait for initialization to finish before we do our
|
||||
// thing.
|
||||
State::Initialize(notified) => {
|
||||
return MonitorPermissionFuture::Initialize {
|
||||
runner: Dispatcher::new(self.main_thread, (shared, self.window.clone())).0,
|
||||
runner: Dispatcher::new(
|
||||
self.main_thread,
|
||||
(self.runner.clone(), self.window.clone()),
|
||||
)
|
||||
.0,
|
||||
notified: notified.clone(),
|
||||
}
|
||||
},
|
||||
State::Permission { permission, .. } => {
|
||||
match permission.state() {
|
||||
PermissionState::Granted | PermissionState::Prompt => (),
|
||||
PermissionState::Denied => {
|
||||
return MonitorPermissionFuture::Ready(Some(Err(
|
||||
MonitorPermissionError::Denied,
|
||||
)))
|
||||
},
|
||||
_ => {
|
||||
error!(
|
||||
"encountered unknown permission state: {}",
|
||||
permission.state_string()
|
||||
);
|
||||
|
||||
return MonitorPermissionFuture::Ready(Some(Err(
|
||||
MonitorPermissionError::Denied,
|
||||
)));
|
||||
},
|
||||
}
|
||||
|
||||
drop(state);
|
||||
|
||||
let notifier = Notifier::new();
|
||||
let notified = notifier.notified();
|
||||
*self.state.borrow_mut() = State::Upgrade(notified.clone());
|
||||
|
||||
(notifier, notified)
|
||||
},
|
||||
// A request is already in progress.
|
||||
// If we finished initialization we at least possess `PermissionStatus`.
|
||||
State::Permission { permission, .. } => permission,
|
||||
// A request is already in progress. Use that!
|
||||
State::Upgrade(notified) => return MonitorPermissionFuture::Upgrade(notified.clone()),
|
||||
State::Detailed(_) => return MonitorPermissionFuture::Ready(Some(Ok(()))),
|
||||
State::Detailed { .. } => return MonitorPermissionFuture::Ready(Some(Ok(()))),
|
||||
};
|
||||
|
||||
let future = JsFuture::from(self.window.screen_details());
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match future.await {
|
||||
Ok(details) => {
|
||||
// Notifying `Future`s is not dependant on the lifetime of the runner, because
|
||||
// they can outlive it.
|
||||
notifier.notify(Ok(()));
|
||||
match permission.state() {
|
||||
PermissionState::Granted | PermissionState::Prompt => (),
|
||||
PermissionState::Denied => {
|
||||
return MonitorPermissionFuture::Ready(Some(Err(MonitorPermissionError::Denied)))
|
||||
},
|
||||
_ => {
|
||||
error!("encountered unknown permission state: {}", permission.state_string());
|
||||
|
||||
if let Some(shared) = shared.upgrade() {
|
||||
*shared.monitor().state.borrow_mut() =
|
||||
State::Detailed(details.unchecked_into())
|
||||
}
|
||||
},
|
||||
Err(error) => unreachable_error(
|
||||
&error,
|
||||
"getting screen details failed even though permission was granted",
|
||||
),
|
||||
}
|
||||
});
|
||||
return MonitorPermissionFuture::Ready(Some(Err(MonitorPermissionError::Denied)));
|
||||
},
|
||||
}
|
||||
|
||||
drop(state);
|
||||
|
||||
// We are ready to explicitly ask the user for permission, lets go!
|
||||
|
||||
let notifier = Notifier::new();
|
||||
let notified = notifier.notified();
|
||||
*self.state.borrow_mut() = State::Upgrade(notified.clone());
|
||||
|
||||
MonitorPermissionFuture::upgrade_internal(self.runner.clone(), &self.window, notifier);
|
||||
|
||||
MonitorPermissionFuture::Upgrade(notified)
|
||||
}
|
||||
|
|
@ -587,7 +673,7 @@ impl MonitorHandler {
|
|||
HasMonitorPermissionFuture::Ready(Some(false))
|
||||
},
|
||||
State::Initialize(notified) => HasMonitorPermissionFuture::Future(notified.clone()),
|
||||
State::Detailed(_) => HasMonitorPermissionFuture::Ready(Some(true)),
|
||||
State::Detailed { .. } => HasMonitorPermissionFuture::Ready(Some(true)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -597,7 +683,7 @@ impl MonitorHandler {
|
|||
State::Initialize(_) => {
|
||||
unreachable!("called `has_detailed_monitor_permission()` while initializing")
|
||||
},
|
||||
State::Detailed(_) => true,
|
||||
State::Detailed { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -621,34 +707,38 @@ impl MonitorPermissionFuture {
|
|||
unreachable!()
|
||||
};
|
||||
|
||||
runner.dispatch(|(shared, window)| {
|
||||
let future = JsFuture::from(window.screen_details());
|
||||
|
||||
if let Some(shared) = shared.upgrade() {
|
||||
*shared.monitor().state.borrow_mut() = State::Upgrade(notified);
|
||||
runner.dispatch(|(runner, window)| {
|
||||
if let Some(runner) = runner.upgrade() {
|
||||
*runner.monitor().state.borrow_mut() = State::Upgrade(notified);
|
||||
}
|
||||
|
||||
let shared = shared.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match future.await {
|
||||
Ok(details) => {
|
||||
// Notifying `Future`s is not dependant on the lifetime
|
||||
// of
|
||||
// the runner, because
|
||||
// they can outlive it.
|
||||
notifier.notify(Ok(()));
|
||||
Self::upgrade_internal(runner.clone(), window, notifier);
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(shared) = shared.upgrade() {
|
||||
*shared.monitor().state.borrow_mut() =
|
||||
State::Detailed(details.unchecked_into())
|
||||
}
|
||||
},
|
||||
Err(error) => unreachable_error(
|
||||
&error,
|
||||
"getting screen details failed even though permission was granted",
|
||||
),
|
||||
}
|
||||
});
|
||||
fn upgrade_internal(
|
||||
runner: WeakShared,
|
||||
window: &WindowExt,
|
||||
notifier: Notifier<Result<(), MonitorPermissionError>>,
|
||||
) {
|
||||
let future = JsFuture::from(window.screen_details());
|
||||
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
match future.await {
|
||||
Ok(details) => {
|
||||
// Notifying `Future`s is not dependant on the lifetime of the runner, because
|
||||
// they can outlive it.
|
||||
notifier.notify(Ok(()));
|
||||
|
||||
if let Some(runner) = runner.upgrade() {
|
||||
runner.monitor().upgrade(details.unchecked_into());
|
||||
}
|
||||
},
|
||||
Err(error) => unreachable_error(
|
||||
&error,
|
||||
"getting screen details failed even though permission was granted",
|
||||
),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -714,8 +804,10 @@ fn unreachable_error(error: &JsValue, message: &str) -> ! {
|
|||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static HAS_LOCK_SUPPORT: OnceCell<bool> = const { OnceCell::new() };
|
||||
static HAS_LOCK_SUPPORT: OnceLock<bool> = OnceLock::new();
|
||||
|
||||
fn has_previous_lock_support() -> Option<bool> {
|
||||
HAS_LOCK_SUPPORT.get().cloned()
|
||||
}
|
||||
|
||||
pub fn has_screen_details_support(window: &Window) -> bool {
|
||||
|
|
@ -751,7 +843,7 @@ extern "C" {
|
|||
#[wasm_bindgen(method, getter)]
|
||||
fn screens(this: &ScreenDetails) -> Vec<ScreenDetailed>;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[wasm_bindgen(extends = web_sys::Screen)]
|
||||
pub(crate) type ScreenExt;
|
||||
|
||||
|
|
@ -767,6 +859,7 @@ extern "C" {
|
|||
#[wasm_bindgen(method, getter, js_name = lock)]
|
||||
fn has_lock(this: &ScreenOrientationExt) -> JsValue;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[wasm_bindgen(extends = ScreenExt)]
|
||||
pub(crate) type ScreenDetailed;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue