oxygengine_input_device_web/
touch.rs1use backend::closure::WebClosure;
2use core::{ecs::Universe, Scalar};
3use input::device::InputDevice;
4use std::{
5 any::Any,
6 cell::{Ref, RefCell},
7 collections::{HashMap, HashSet},
8 rc::Rc,
9};
10use wasm_bindgen::{prelude::*, JsCast};
11use web_sys::*;
12
13pub struct WebTouchPointer {
14 pub x: Scalar,
15 pub y: Scalar,
16 pub force: Scalar,
17 pub radius_x: Scalar,
18 pub radius_y: Scalar,
19 pub angle: Scalar,
20}
21
22pub struct WebTouchInputDevice {
23 element: EventTarget,
24 pointers: Rc<RefCell<HashMap<i32, WebTouchPointer>>>,
25 started: HashSet<i32>,
26 ended: HashSet<i32>,
27 cached: HashSet<i32>,
28 touch_start_closure: WebClosure,
29 touch_end_closure: WebClosure,
30 touch_move_closure: WebClosure,
31 touch_cancel_closure: WebClosure,
32}
33
34unsafe impl Send for WebTouchInputDevice {}
35unsafe impl Sync for WebTouchInputDevice {}
36
37impl WebTouchInputDevice {
38 pub fn new(element: EventTarget) -> Self {
39 Self {
40 element,
41 pointers: Default::default(),
42 cached: Default::default(),
43 started: Default::default(),
44 ended: Default::default(),
45 touch_start_closure: Default::default(),
46 touch_end_closure: Default::default(),
47 touch_move_closure: Default::default(),
48 touch_cancel_closure: Default::default(),
49 }
50 }
51
52 pub fn touches(&self) -> Ref<HashMap<i32, WebTouchPointer>> {
53 self.pointers.borrow()
54 }
55
56 pub fn active(&self) -> impl Iterator<Item = i32> + '_ {
57 self.cached.iter().copied()
58 }
59
60 pub fn started(&self) -> impl Iterator<Item = i32> + '_ {
61 self.started.iter().copied()
62 }
63
64 pub fn ended(&self) -> impl Iterator<Item = i32> + '_ {
65 self.ended.iter().copied()
66 }
67}
68
69impl InputDevice for WebTouchInputDevice {
70 fn name(&self) -> &str {
71 "touch"
72 }
73
74 fn on_register(&mut self) {
75 macro_rules! impl_callback {
76 ($name:expr) => {{
77 let pointers = self.pointers.clone();
78 let closure = Closure::wrap(Box::new(move |event: TouchEvent| {
79 let list = event.target_touches();
80 let count = list.length();
81 let mut pointers = pointers.borrow_mut();
82 pointers.clear();
83 pointers.reserve(count as _);
84 for i in 0..count {
85 let item = list.item(i).unwrap();
86 pointers.insert(
87 item.identifier(),
88 WebTouchPointer {
89 x: item.client_x() as _,
90 y: item.client_y() as _,
91 force: item.force() as _,
92 radius_x: item.radius_x() as _,
93 radius_y: item.radius_y() as _,
94 angle: item.rotation_angle() as _,
95 },
96 );
97 }
98 }) as Box<dyn FnMut(_)>);
99 self.element
100 .add_event_listener_with_callback($name, closure.as_ref().unchecked_ref())
101 .unwrap();
102 WebClosure::acquire(closure)
103 }};
104 }
105
106 self.touch_start_closure = impl_callback!("touchstart");
107 self.touch_end_closure = impl_callback!("touchend");
108 self.touch_move_closure = impl_callback!("touchmove");
109 self.touch_cancel_closure = impl_callback!("touchcancel");
110 }
111
112 fn on_unregister(&mut self) {
113 self.touch_start_closure.release();
114 self.touch_end_closure.release();
115 self.touch_move_closure.release();
116 self.touch_cancel_closure.release();
117 }
118
119 fn process(&mut self, _: &mut Universe) {
120 let pointers = self.pointers.borrow();
121 self.started.clear();
122 self.started.reserve(pointers.len());
123 for id in pointers.keys() {
124 if self.cached.contains(id) {
125 self.started.insert(*id);
126 }
127 }
128 self.ended.clear();
129 self.ended.reserve(pointers.len());
130 for id in &self.cached {
131 if !pointers.contains_key(id) {
132 self.ended.insert(*id);
133 }
134 }
135 self.cached.clear();
136 self.cached.reserve(pointers.len());
137 self.cached.extend(pointers.keys().copied());
138 }
139
140 fn query_axis(&self, name: &str) -> Option<Scalar> {
141 match name {
142 "x" => self
143 .pointers
144 .borrow()
145 .values()
146 .next()
147 .map(|pointer| pointer.x),
148 "y" => self
149 .pointers
150 .borrow()
151 .values()
152 .next()
153 .map(|pointer| pointer.y),
154 "force" => self
155 .pointers
156 .borrow()
157 .values()
158 .next()
159 .map(|pointer| pointer.force),
160 "radius-x" => self
161 .pointers
162 .borrow()
163 .values()
164 .next()
165 .map(|pointer| pointer.radius_x),
166 "radius-y" => self
167 .pointers
168 .borrow()
169 .values()
170 .next()
171 .map(|pointer| pointer.radius_y),
172 "angle" => self
173 .pointers
174 .borrow()
175 .values()
176 .next()
177 .map(|pointer| pointer.angle),
178 _ => None,
179 }
180 }
181
182 fn query_trigger(&self, name: &str) -> Option<bool> {
183 match name {
184 "touch" => Some(!self.pointers.borrow().is_empty()),
185 _ => None,
186 }
187 }
188
189 fn query_text(&self) -> Option<String> {
190 None
191 }
192
193 fn as_any(&self) -> &dyn Any {
194 self
195 }
196}