use std::cell::Cell;
use crate::overlay::OverlayKey;
use super::ctx::EventResult;
use super::input::{InputEvent, OverlayEvent, OverlayEventKind};
use super::pointer::PointerId;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct ContactPassState {
open: bool,
pointer_id: PointerId,
secondary: bool,
contacts_requested: bool,
capture_released: bool,
}
impl ContactPassState {
const IDLE: ContactPassState = ContactPassState {
open: false,
pointer_id: PointerId::MOUSE,
secondary: false,
contacts_requested: false,
capture_released: false,
};
}
thread_local! {
static CONTACT_PASS: Cell<ContactPassState> = const { Cell::new(ContactPassState::IDLE) };
}
pub(crate) struct ContactPass {
saved: ContactPassState,
}
impl ContactPass {
pub(crate) fn enter(pointer_id: PointerId, secondary: bool) -> Self {
let saved = CONTACT_PASS.with(|slot| {
slot.replace(ContactPassState {
open: true,
pointer_id,
secondary,
contacts_requested: false,
capture_released: false,
})
});
Self { saved }
}
pub(crate) fn contacts_requested(&self) -> bool {
CONTACT_PASS.with(|slot| slot.get().contacts_requested)
}
pub(crate) fn capture_released(&self) -> bool {
CONTACT_PASS.with(|slot| slot.get().capture_released)
}
}
impl Drop for ContactPass {
fn drop(&mut self) {
CONTACT_PASS.with(|slot| slot.set(self.saved));
}
}
pub(crate) fn current_pointer_id() -> PointerId {
CONTACT_PASS.with(|slot| slot.get().pointer_id)
}
pub(crate) fn in_secondary_contact_pass() -> bool {
CONTACT_PASS.with(|slot| slot.get().secondary)
}
pub(super) fn note_contacts_requested() {
CONTACT_PASS.with(|slot| {
let mut state = slot.get();
if state.open {
state.contacts_requested = true;
slot.set(state);
}
});
CONTACT_FRAME.with(|slot| {
let mut frame = slot.get();
frame.opted_in = true;
slot.set(frame);
});
}
pub(super) fn note_capture_released() {
CONTACT_PASS.with(|slot| {
let mut state = slot.get();
if state.open {
state.capture_released = true;
state.contacts_requested = false;
slot.set(state);
}
});
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
struct ContactFrameState {
opted_in: bool,
opted_in_below: bool,
under_captor: bool,
}
thread_local! {
static CONTACT_FRAME: Cell<ContactFrameState> =
const { Cell::new(ContactFrameState { opted_in: false, opted_in_below: false, under_captor: false }) };
}
pub(crate) struct ContactFrame {
saved: ContactFrameState,
}
impl ContactFrame {
pub(crate) fn enter(captor: bool) -> Self {
let saved = CONTACT_FRAME.with(|slot| {
let saved = slot.get();
slot.set(ContactFrameState {
opted_in: false,
opted_in_below: false,
under_captor: saved.under_captor || captor,
});
saved
});
Self { saved }
}
pub(crate) fn close(self) -> (bool, bool) {
let frame = CONTACT_FRAME.with(|slot| slot.get());
(frame.opted_in, frame.opted_in || frame.opted_in_below)
}
}
impl Drop for ContactFrame {
fn drop(&mut self) {
CONTACT_FRAME.with(|slot| {
let frame = slot.get();
let mut restored = self.saved;
restored.opted_in_below |= frame.opted_in || frame.opted_in_below;
slot.set(restored);
});
}
}
pub(super) fn under_contact_captor() -> bool {
CONTACT_FRAME.with(|slot| slot.get().under_captor)
}
struct SecondaryWalkFrame {
event: InputEvent,
delivered: Option<EventResult>,
}
thread_local! {
static SECONDARY_WALK: std::cell::RefCell<Option<SecondaryWalkFrame>> =
const { std::cell::RefCell::new(None) };
static SECONDARY_WALK_KEY: OverlayKey = OverlayKey::next();
}
pub(crate) fn secondary_walk_carrier() -> InputEvent {
InputEvent::Overlay(OverlayEvent {
key: SECONDARY_WALK_KEY.with(|key| *key),
kind: OverlayEventKind::OutsideDown,
})
}
fn is_secondary_walk_carrier(event: &InputEvent) -> bool {
matches!(event, InputEvent::Overlay(overlay)
if overlay.key == SECONDARY_WALK_KEY.with(|key| *key))
}
pub(crate) fn secondary_walk_event(event: &InputEvent) -> Option<InputEvent> {
if !is_secondary_walk_carrier(event) {
return None;
}
SECONDARY_WALK.with(|slot| slot.borrow().as_ref().map(|frame| frame.event.clone()))
}
struct SecondaryWalkRestore(Option<SecondaryWalkFrame>);
impl Drop for SecondaryWalkRestore {
fn drop(&mut self) {
let saved = self.0.take();
SECONDARY_WALK.with(|slot| *slot.borrow_mut() = saved);
}
}
pub(crate) fn run_secondary_walk<R>(
event: InputEvent,
f: impl FnOnce() -> R,
) -> (R, Option<EventResult>) {
let saved = SECONDARY_WALK.with(|slot| {
slot.borrow_mut().replace(SecondaryWalkFrame {
event,
delivered: None,
})
});
let restore = SecondaryWalkRestore(saved);
let result = f();
let delivered =
SECONDARY_WALK.with(|slot| slot.borrow().as_ref().and_then(|frame| frame.delivered));
drop(restore);
(result, delivered)
}
pub(crate) fn without_secondary_walk<R>(f: impl FnOnce() -> R) -> R {
let saved = SECONDARY_WALK.with(|slot| slot.borrow_mut().take());
let _restore = SecondaryWalkRestore(saved);
f()
}
pub(crate) fn note_secondary_delivered(result: EventResult) {
SECONDARY_WALK.with(|slot| {
if let Some(frame) = slot.borrow_mut().as_mut() {
frame.delivered = Some(result);
}
});
}