static SEEN: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
#[cfg(target_arch = "wasm32")]
static INSTALLED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
#[cfg(target_arch = "wasm32")]
static CALLBACK: std::sync::Mutex<Option<Box<dyn Fn() + Send + Sync>>> =
std::sync::Mutex::new(None);
pub fn on_first_user_gesture(f: impl Fn() + Send + Sync + 'static) {
#[cfg(target_arch = "wasm32")]
{
let mut slot = CALLBACK.lock().unwrap_or_else(|e| e.into_inner());
if slot.is_none() {
*slot = Some(Box::new(f));
}
install();
}
#[cfg(not(target_arch = "wasm32"))]
let _ = f;
}
#[cfg(not(target_arch = "wasm32"))]
fn install() {}
pub fn user_gesture_seen() -> bool {
install();
SEEN.load(std::sync::atomic::Ordering::SeqCst)
}
#[cfg(target_arch = "wasm32")]
fn install() {
use std::sync::atomic::Ordering;
use wasm_bindgen::JsCast;
use wasm_bindgen::closure::Closure;
if INSTALLED.swap(true, Ordering::SeqCst) {
return;
}
let global = js_sys::global();
let Ok(add) = js_sys::Reflect::get(
&global,
&wasm_bindgen::JsValue::from_str("addEventListener"),
) else {
return;
};
let Some(add) = add.dyn_ref::<js_sys::Function>() else {
return;
};
let handler = Closure::wrap(Box::new(|| {
if !SEEN.swap(true, Ordering::SeqCst)
&& let Some(f) = CALLBACK.lock().unwrap_or_else(|e| e.into_inner()).take()
{
f();
}
}) as Box<dyn FnMut()>)
.into_js_value();
for event in ["pointerdown", "touchstart", "keydown"] {
let _ = add.call2(&global, &wasm_bindgen::JsValue::from_str(event), &handler);
}
}