winit/winit-web/src/lock.rs
2025-10-20 00:01:36 +09:00

82 lines
2.8 KiB
Rust

use std::cell::OnceCell;
use js_sys::{Object, Promise};
use tracing::error;
use wasm_bindgen::closure::Closure;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{Document, DomException, Element, Navigator, console};
pub(crate) fn is_cursor_lock_raw(navigator: &Navigator, document: &Document) -> bool {
thread_local! {
static IS_CURSOR_LOCK_RAW: OnceCell<bool> = const { OnceCell::new() };
}
IS_CURSOR_LOCK_RAW.with(|cell| {
*cell.get_or_init(|| {
// TODO: Remove when Chrome can better advertise that they don't support unaccelerated
// movement on Linux.
// See <https://issues.chromium.org/issues/40833850>.
if super::web_sys::chrome_linux(navigator) {
return false;
}
let element: ElementExt = document.create_element("div").unwrap().unchecked_into();
let promise = element.request_pointer_lock();
if promise.is_undefined() {
false
} else {
thread_local! {
static REJECT_HANDLER: Closure<dyn FnMut(JsValue)> = Closure::new(|_| ());
}
let promise: Promise = promise.unchecked_into();
let _ = REJECT_HANDLER.with(|handler| promise.catch(handler));
true
}
})
})
}
pub(crate) fn request_pointer_lock(navigator: &Navigator, document: &Document, element: &Element) {
if is_cursor_lock_raw(navigator, document) {
thread_local! {
static REJECT_HANDLER: Closure<dyn FnMut(JsValue)> = Closure::new(|error: JsValue| {
if let Some(error) = error.dyn_ref::<DomException>() {
error!("Failed to lock pointer. {}: {}", error.name(), error.message());
} else {
console::error_1(&error);
error!("Failed to lock pointer");
}
});
}
let element: &ElementExt = element.unchecked_ref();
let options: PointerLockOptions = Object::new().unchecked_into();
options.set_unadjusted_movement(true);
let _ = REJECT_HANDLER
.with(|handler| element.request_pointer_lock_with_options(&options).catch(handler));
} else {
element.request_pointer_lock();
}
}
#[wasm_bindgen]
extern "C" {
type ElementExt;
#[wasm_bindgen(method, js_name = requestPointerLock)]
fn request_pointer_lock(this: &ElementExt) -> JsValue;
#[wasm_bindgen(method, js_name = requestPointerLock)]
fn request_pointer_lock_with_options(
this: &ElementExt,
options: &PointerLockOptions,
) -> Promise;
type PointerLockOptions;
#[wasm_bindgen(method, setter, js_name = unadjustedMovement)]
fn set_unadjusted_movement(this: &PointerLockOptions, value: bool);
}