Web: Implement MonitorHandle (#3801)
Requires getting permission from the user to get "detailed" support. Also enables users to go fullscreen on specific monitors. Exposes platform-specific orientation API. Most functionality depends on browser support, currently only Chromium.
This commit is contained in:
parent
2e97ab3d4f
commit
a0bc3e5dc8
21 changed files with 1493 additions and 120 deletions
|
|
@ -1,8 +1,8 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use super::{backend, device, window};
|
||||
use super::{backend, device, window, HasMonitorPermissionFuture, MonitorPermissionFuture};
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::error::{EventLoopError, NotSupportedError};
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::ActiveEventLoop as RootActiveEventLoop;
|
||||
use crate::platform::web::{ActiveEventLoopExtWeb, PollStrategy, WaitUntilStrategy};
|
||||
|
|
@ -77,6 +77,18 @@ impl EventLoop {
|
|||
pub fn wait_until_strategy(&self) -> WaitUntilStrategy {
|
||||
self.elw.wait_until_strategy()
|
||||
}
|
||||
|
||||
pub fn has_multiple_screens(&self) -> Result<bool, NotSupportedError> {
|
||||
self.elw.has_multiple_screens()
|
||||
}
|
||||
|
||||
pub(crate) fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture {
|
||||
self.elw.request_detailed_monitor_permission().0
|
||||
}
|
||||
|
||||
pub fn has_detailed_monitor_permission(&self) -> HasMonitorPermissionFuture {
|
||||
self.elw.p.runner.monitor().has_detailed_monitor_permission_async()
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event<A: ApplicationHandler>(app: &mut A, target: &RootActiveEventLoop, event: Event) {
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ use std::rc::{Rc, Weak};
|
|||
use js_sys::Function;
|
||||
use wasm_bindgen::prelude::{wasm_bindgen, Closure};
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{Document, KeyboardEvent, PageTransitionEvent, PointerEvent, WheelEvent};
|
||||
use web_sys::{Document, KeyboardEvent, Navigator, PageTransitionEvent, PointerEvent, WheelEvent};
|
||||
use web_time::{Duration, Instant};
|
||||
|
||||
use super::super::main_thread::MainThreadMarker;
|
||||
use super::super::monitor::MonitorHandler;
|
||||
use super::super::DeviceId;
|
||||
use super::backend;
|
||||
use super::state::State;
|
||||
|
|
@ -51,11 +52,13 @@ struct Execution {
|
|||
events: RefCell<VecDeque<EventWrapper>>,
|
||||
id: RefCell<u32>,
|
||||
window: web_sys::Window,
|
||||
navigator: Navigator,
|
||||
document: Document,
|
||||
#[allow(clippy::type_complexity)]
|
||||
all_canvases: RefCell<Vec<(WindowId, Weak<backend::Canvas>, DispatchRunner<Inner>)>>,
|
||||
redraw_pending: RefCell<HashSet<WindowId>>,
|
||||
destroy_pending: RefCell<VecDeque<WindowId>>,
|
||||
pub(crate) monitor: Rc<MonitorHandler>,
|
||||
page_transition_event_handle: RefCell<Option<backend::PageTransitionEventHandle>>,
|
||||
device_events: Cell<DeviceEvents>,
|
||||
on_mouse_move: OnEventHandle<PointerEvent>,
|
||||
|
|
@ -70,6 +73,8 @@ struct Execution {
|
|||
enum RunnerEnum {
|
||||
/// The `EventLoop` is created but not being run.
|
||||
Pending,
|
||||
/// The `EventLoop` is running some async initialization and is waiting to be started.
|
||||
Initializing(Runner),
|
||||
/// The `EventLoop` is being run.
|
||||
Running(Runner),
|
||||
/// The `EventLoop` is exited after being started with `EventLoop::run_app`. Since
|
||||
|
|
@ -134,6 +139,8 @@ impl Shared {
|
|||
#[allow(clippy::disallowed_methods)]
|
||||
let window = web_sys::window().expect("only callable from inside the `Window`");
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
let navigator = window.navigator();
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
let document = window.document().expect("Failed to obtain document");
|
||||
|
||||
Shared(Rc::<Execution>::new_cyclic(|weak| {
|
||||
|
|
@ -144,6 +151,13 @@ impl Shared {
|
|||
}
|
||||
});
|
||||
|
||||
let monitor = MonitorHandler::new(
|
||||
main_thread,
|
||||
window.clone(),
|
||||
&navigator,
|
||||
WeakShared(weak.clone()),
|
||||
);
|
||||
|
||||
Execution {
|
||||
main_thread,
|
||||
proxy_spawner,
|
||||
|
|
@ -156,11 +170,13 @@ impl Shared {
|
|||
event_loop_recreation: Cell::new(false),
|
||||
events: RefCell::new(VecDeque::new()),
|
||||
window,
|
||||
navigator,
|
||||
document,
|
||||
id: RefCell::new(0),
|
||||
all_canvases: RefCell::new(Vec::new()),
|
||||
redraw_pending: RefCell::new(HashSet::new()),
|
||||
destroy_pending: RefCell::new(VecDeque::new()),
|
||||
monitor: Rc::new(monitor),
|
||||
page_transition_event_handle: RefCell::new(None),
|
||||
device_events: Cell::default(),
|
||||
on_mouse_move: RefCell::new(None),
|
||||
|
|
@ -182,6 +198,10 @@ impl Shared {
|
|||
&self.0.window
|
||||
}
|
||||
|
||||
pub fn navigator(&self) -> &Navigator {
|
||||
&self.0.navigator
|
||||
}
|
||||
|
||||
pub fn document(&self) -> &Document {
|
||||
&self.0.document
|
||||
}
|
||||
|
|
@ -199,17 +219,38 @@ impl Shared {
|
|||
self.0.destroy_pending.borrow_mut().push_back(id);
|
||||
}
|
||||
|
||||
pub(crate) fn start(&self, event_handler: Box<EventHandler>) {
|
||||
let mut runner = self.0.runner.borrow_mut();
|
||||
assert!(matches!(*runner, RunnerEnum::Pending));
|
||||
if self.0.monitor.is_initializing() {
|
||||
*runner = RunnerEnum::Initializing(Runner::new(event_handler));
|
||||
} else {
|
||||
*runner = RunnerEnum::Running(Runner::new(event_handler));
|
||||
|
||||
drop(runner);
|
||||
|
||||
self.init();
|
||||
self.set_listener();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn start_delayed(&self) {
|
||||
let event_handler = match self.0.runner.replace(RunnerEnum::Pending) {
|
||||
RunnerEnum::Initializing(event_handler) => event_handler,
|
||||
// The event loop wasn't started yet.
|
||||
RunnerEnum::Pending => return,
|
||||
_ => unreachable!("event loop already started before waiting for initialization"),
|
||||
};
|
||||
*self.0.runner.borrow_mut() = RunnerEnum::Running(event_handler);
|
||||
|
||||
self.init();
|
||||
self.set_listener();
|
||||
}
|
||||
|
||||
// Set the event callback to use for the event loop runner
|
||||
// This the event callback is a fairly thin layer over the user-provided callback that closes
|
||||
// over a RootActiveEventLoop reference
|
||||
pub(crate) fn set_listener(&self, event_handler: Box<EventHandler>) {
|
||||
{
|
||||
let mut runner = self.0.runner.borrow_mut();
|
||||
assert!(matches!(*runner, RunnerEnum::Pending));
|
||||
*runner = RunnerEnum::Running(Runner::new(event_handler));
|
||||
}
|
||||
self.init();
|
||||
|
||||
fn set_listener(&self) {
|
||||
*self.0.page_transition_event_handle.borrow_mut() = Some(backend::on_page_transition(
|
||||
self.window().clone(),
|
||||
{
|
||||
|
|
@ -236,6 +277,7 @@ impl Shared {
|
|||
|
||||
let runner = self.clone();
|
||||
let window = self.window().clone();
|
||||
let navigator = self.navigator().clone();
|
||||
*self.0.on_mouse_move.borrow_mut() = Some(EventListenerHandle::new(
|
||||
self.window().clone(),
|
||||
"pointermove",
|
||||
|
|
@ -263,7 +305,7 @@ impl Shared {
|
|||
}
|
||||
|
||||
// pointer move event
|
||||
let mut delta = backend::event::MouseDelta::init(&window, &event);
|
||||
let mut delta = backend::event::MouseDelta::init(&navigator, &event);
|
||||
runner.send_events(backend::event::pointer_move_event(event).flat_map(|event| {
|
||||
let delta = delta.delta(&event).to_physical(backend::scale_factor(&window));
|
||||
|
||||
|
|
@ -419,7 +461,7 @@ impl Shared {
|
|||
self.send_events::<EventWrapper>(iter::empty());
|
||||
}
|
||||
|
||||
pub fn init(&self) {
|
||||
fn init(&self) {
|
||||
// NB: For consistency all platforms must call `can_create_surfaces` even though Web
|
||||
// applications don't themselves have a formal surface destroy/create lifecycle.
|
||||
self.run_until_cleared(
|
||||
|
|
@ -501,8 +543,8 @@ impl Shared {
|
|||
match self.0.runner.try_borrow().as_ref().map(Deref::deref) {
|
||||
// If the runner is attached but not running, we always wake it up.
|
||||
Ok(RunnerEnum::Running(_)) => (),
|
||||
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
|
||||
Ok(RunnerEnum::Pending | RunnerEnum::Initializing(_)) => {
|
||||
process_immediately = false;
|
||||
},
|
||||
// Some other code is mutating the runner, which most likely means
|
||||
|
|
@ -605,6 +647,8 @@ impl Shared {
|
|||
RunnerEnum::Pending => self.0.events.borrow_mut().push_back(event.into()),
|
||||
// If the Runner has been destroyed, there is nothing to do.
|
||||
RunnerEnum::Destroyed => return,
|
||||
// This function should never be called if we are still waiting for something.
|
||||
RunnerEnum::Initializing(_) => unreachable!(),
|
||||
}
|
||||
|
||||
let is_closed = self.exiting();
|
||||
|
|
@ -730,6 +774,8 @@ impl Shared {
|
|||
Ok(RunnerEnum::Pending) => false,
|
||||
// The event loop is closed since it has been destroyed.
|
||||
Ok(RunnerEnum::Destroyed) => true,
|
||||
// The event loop is not closed since its still waiting to be started.
|
||||
Ok(RunnerEnum::Initializing(_)) => false,
|
||||
// Some other code is mutating the runner, which most likely means
|
||||
// the event loop is running and busy.
|
||||
Err(_) => false,
|
||||
|
|
@ -795,6 +841,14 @@ impl Shared {
|
|||
pub(crate) fn waker(&self) -> Waker<WeakShared> {
|
||||
self.0.proxy_spawner.waker()
|
||||
}
|
||||
|
||||
pub(crate) fn weak(&self) -> WeakShared {
|
||||
WeakShared(Rc::downgrade(&self.0))
|
||||
}
|
||||
|
||||
pub(crate) fn monitor(&self) -> &Rc<MonitorHandler> {
|
||||
&self.0.monitor
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
use std::cell::Cell;
|
||||
use std::clone::Clone;
|
||||
use std::collections::vec_deque::IntoIter as VecDequeIter;
|
||||
use std::collections::VecDeque;
|
||||
use std::iter;
|
||||
use std::rc::Rc;
|
||||
|
||||
use web_sys::Element;
|
||||
|
||||
use super::super::monitor::MonitorHandle;
|
||||
use super::super::monitor::{MonitorHandle, MonitorPermissionFuture};
|
||||
use super::super::{lock, KeyEventExtra};
|
||||
use super::device::DeviceId;
|
||||
use super::runner::{EventWrapper, WeakShared};
|
||||
use super::window::WindowId;
|
||||
use super::{backend, runner, EventLoopProxy};
|
||||
use crate::error::NotSupportedError;
|
||||
use crate::event::{
|
||||
DeviceId as RootDeviceId, ElementState, Event, KeyEvent, Touch, TouchPhase, WindowEvent,
|
||||
};
|
||||
|
|
@ -61,7 +60,7 @@ impl ActiveEventLoop {
|
|||
event_loop_recreation: bool,
|
||||
) {
|
||||
self.runner.event_loop_recreation(event_loop_recreation);
|
||||
self.runner.set_listener(event_handler);
|
||||
self.runner.start(event_handler);
|
||||
}
|
||||
|
||||
pub fn generate_id(&self) -> WindowId {
|
||||
|
|
@ -594,12 +593,12 @@ impl ActiveEventLoop {
|
|||
canvas.on_context_menu();
|
||||
}
|
||||
|
||||
pub fn available_monitors(&self) -> VecDequeIter<MonitorHandle> {
|
||||
VecDeque::new().into_iter()
|
||||
pub fn available_monitors(&self) -> Vec<MonitorHandle> {
|
||||
self.runner.monitor().available_monitors()
|
||||
}
|
||||
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
None
|
||||
self.runner.monitor().primary_monitor()
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
|
|
@ -653,7 +652,19 @@ impl ActiveEventLoop {
|
|||
}
|
||||
|
||||
pub(crate) fn is_cursor_lock_raw(&self) -> bool {
|
||||
lock::is_cursor_lock_raw(self.runner.window(), self.runner.document())
|
||||
lock::is_cursor_lock_raw(self.runner.navigator(), self.runner.document())
|
||||
}
|
||||
|
||||
pub(crate) fn has_multiple_screens(&self) -> Result<bool, NotSupportedError> {
|
||||
self.runner.monitor().is_extended().ok_or(NotSupportedError::new())
|
||||
}
|
||||
|
||||
pub(crate) fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture {
|
||||
self.runner.monitor().request_detailed_monitor_permission(self.runner.weak())
|
||||
}
|
||||
|
||||
pub(crate) fn has_detailed_monitor_permission(&self) -> bool {
|
||||
self.runner.monitor().has_detailed_monitor_permission()
|
||||
}
|
||||
|
||||
pub(crate) fn waker(&self) -> Waker<WeakShared> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue