1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "js_input_monitor.h"
17 #include <cinttypes>
18 #include <cinttypes>
19 #include "define_multimodal.h"
20 #include "error_multimodal.h"
21 #include "input_manager.h"
22 #include "js_input_monitor_manager.h"
23 #include "js_input_monitor_util.h"
24
25 #define InputMgr OHOS::MMI::InputManager::GetInstance()
26
27 namespace OHOS {
28 namespace MMI {
29 namespace {
30 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, MMI_LOG_DOMAIN, "JsInputMonitor" };
31 constexpr int32_t NAPI_ERR = 3;
32 }
33
Start()34 bool InputMonitor::Start()
35 {
36 MMI_LOGD("Enter");
37 std::lock_guard<std::mutex> guard(mutex_);
38 if (monitorId_ < 0) {
39 monitorId_ = InputMgr->AddMonitor(shared_from_this());
40 return monitorId_ >= 0;
41 }
42 MMI_LOGD("Leave");
43 return true;
44 }
45
Stop()46 void InputMonitor::Stop()
47 {
48 MMI_LOGD("Enter");
49 std::lock_guard<std::mutex> guard(mutex_);
50 if (monitorId_ < 0) {
51 MMI_LOGE("Invalid values");
52 return;
53 }
54 InputMgr->RemoveMonitor(monitorId_);
55 monitorId_ = -1;
56 MMI_LOGD("Leave");
57 return;
58 }
59
SetCallback(std::function<void (std::shared_ptr<PointerEvent>)> callback)60 void InputMonitor::SetCallback(std::function<void(std::shared_ptr<PointerEvent>)> callback)
61 {
62 std::lock_guard<std::mutex> guard(mutex_);
63 callback_ = callback;
64 }
65
OnInputEvent(std::shared_ptr<PointerEvent> pointerEvent) const66 void InputMonitor::OnInputEvent(std::shared_ptr<PointerEvent> pointerEvent) const
67 {
68 MMI_LOGD("Enter");
69 CHKPV(pointerEvent);
70 if (!JSIMM.GetMonitor(id_)) {
71 MMI_LOGE("failed to process pointer event, id:%{public}d", id_);
72 return;
73 }
74 std::function<void(std::shared_ptr<PointerEvent>)> callback;
75 {
76 std::lock_guard<std::mutex> guard(mutex_);
77 if (pointerEvent->GetSourceType() == PointerEvent::SOURCE_TYPE_TOUCHSCREEN) {
78 if (pointerEvent->GetPointersIdList().size() == 1) {
79 if (pointerEvent->GetPointerAction() == PointerEvent::POINTER_ACTION_DOWN) {
80 consumed_ = false;
81 }
82 }
83 }
84 callback = callback_;
85 }
86 CHKPV(callback);
87 callback(pointerEvent);
88 MMI_LOGD("Leave");
89 }
90
SetId(int32_t id)91 void InputMonitor::SetId(int32_t id) {
92 id_ = id;
93 }
94
OnInputEvent(std::shared_ptr<KeyEvent> keyEvent) const95 void InputMonitor::OnInputEvent(std::shared_ptr<KeyEvent> keyEvent) const {}
96
OnInputEvent(std::shared_ptr<AxisEvent> axisEvent) const97 void InputMonitor::OnInputEvent(std::shared_ptr<AxisEvent> axisEvent) const {}
98
MarkConsumed(int32_t eventId)99 void InputMonitor::MarkConsumed(int32_t eventId)
100 {
101 std::lock_guard<std::mutex> guard(mutex_);
102 if (consumed_) {
103 MMI_LOGD("consumed_ is true");
104 return;
105 }
106 if (monitorId_ < 0) {
107 MMI_LOGE("Invalid values");
108 return;
109 }
110 InputMgr->MarkConsumed(monitorId_, eventId);
111 consumed_ = true;
112 }
113
JsInputMonitor(napi_env jsEnv,napi_value receiver,int32_t id)114 JsInputMonitor::JsInputMonitor(napi_env jsEnv, napi_value receiver, int32_t id)
115 : monitor_(std::make_shared<InputMonitor>()),
116 jsEnv_(jsEnv),
117 id_(id)
118 {
119 SetReceiver(receiver);
120 if (monitor_ != nullptr) {
121 monitor_->SetCallback([jsId=id](std::shared_ptr<PointerEvent> pointerEvent) {
122 auto jsMonitor = JSIMM.GetMonitor(jsId);
123 CHKPV(jsMonitor);
124 jsMonitor->OnPointerEvent(pointerEvent);
125 });
126 monitor_->SetId(id_);
127 }
128 auto status = napi_get_uv_event_loop(jsEnv_,&loop_);
129 if (status != napi_ok) {
130 MMI_LOGE("napi_get_uv_event_loop is failed");
131 return;
132 }
133 }
134
SetReceiver(napi_value receiver)135 void JsInputMonitor::SetReceiver(napi_value receiver)
136 {
137 if (receiver_ == nullptr && jsEnv_ != nullptr) {
138 uint32_t refCount = 1;
139 auto status = napi_create_reference(jsEnv_, receiver, refCount, &receiver_);
140 if (status != napi_ok) {
141 MMI_LOGE("napi_create_reference is failed");
142 return;
143 }
144 }
145 }
146
MarkConsumed(int32_t eventId)147 void JsInputMonitor::MarkConsumed(int32_t eventId)
148 {
149 CHKPV(monitor_);
150 monitor_->MarkConsumed(eventId);
151 }
152
IsMatch(napi_env jsEnv,napi_value receiver)153 int32_t JsInputMonitor::IsMatch(napi_env jsEnv, napi_value receiver)
154 {
155 CHKPR(receiver, ERROR_NULL_POINTER);
156 if (jsEnv_ == jsEnv) {
157 napi_value handlerTemp = nullptr;
158 auto status = napi_get_reference_value(jsEnv_, receiver_, &handlerTemp);
159 if (status != napi_ok) {
160 MMI_LOGE("napi_get_reference_value is failed");
161 return NAPI_ERR;
162 }
163 bool isEquals = false;
164 status = napi_strict_equals(jsEnv_, handlerTemp, receiver, &isEquals);
165 if (status != napi_ok) {
166 MMI_LOGE("napi_strict_equals is failed");
167 return NAPI_ERR;
168 }
169 if (isEquals) {
170 MMI_LOGE("isEquals:%{public}d", isEquals);
171 return RET_OK;
172 }
173 return RET_ERR;
174 }
175 return RET_ERR;
176 }
177
IsMatch(napi_env jsEnv)178 int32_t JsInputMonitor::IsMatch(napi_env jsEnv)
179 {
180 if (jsEnv_ == jsEnv) {
181 return RET_OK;
182 }
183 return RET_ERR;
184 }
185
printfPointerEvent(const std::shared_ptr<PointerEvent> pointerEvent) const186 void JsInputMonitor::printfPointerEvent(const std::shared_ptr<PointerEvent> pointerEvent) const
187 {
188 CHKPV(pointerEvent);
189 PointerEvent::PointerItem item;
190 CHK(pointerEvent->GetPointerItem(pointerEvent->GetPointerId(), item), PARAM_INPUT_FAIL);
191 MMI_LOGD("type:%{public}d,timestamp:%{public}" PRId64 ",deviceId:%{public}d,"
192 "globalX:%{public}d,globalY:%{public}d,localX:%{public}d,localY:%{public}d,"
193 "size:%{public}d,force:%{public}d", pointerEvent->GetSourceType(), item.GetDownTime(),
194 item.GetDeviceId(), item.GetGlobalX(), item.GetGlobalY(), item.GetLocalX(),
195 item.GetLocalY(), item.GetWidth()+item.GetHeight()/2, item.GetPressure());
196 }
197
GetAction(int32_t action)198 std::string JsInputMonitor::GetAction(int32_t action)
199 {
200 switch (action) {
201 case PointerEvent::POINTER_ACTION_CANCEL:
202 return "cancel";
203 case PointerEvent::POINTER_ACTION_DOWN:
204 return "down";
205 case PointerEvent::POINTER_ACTION_MOVE:
206 return "move";
207 case PointerEvent::POINTER_ACTION_UP:
208 return "up";
209 default:
210 return "";
211 }
212 }
213
TransformPointerEvent(const std::shared_ptr<PointerEvent> pointerEvent,napi_value result)214 int32_t JsInputMonitor::TransformPointerEvent(const std::shared_ptr<PointerEvent> pointerEvent, napi_value result)
215 {
216 CHKPR(pointerEvent, ERROR_NULL_POINTER);
217 CHKR(SetNameProperty(jsEnv_, result, "type", GetAction(pointerEvent->GetPointerAction())) == napi_ok,
218 CALL_NAPI_API_ERR, RET_ERR);
219
220 napi_value pointers = nullptr;
221 auto status = napi_create_array(jsEnv_, &pointers);
222 if (status != napi_ok) {
223 MMI_LOGE("napi_create_array is failed");
224 return RET_ERR;
225 }
226
227 std::vector<PointerEvent::PointerItem> pointerItems;
228 for (auto &item : pointerEvent->GetPointersIdList()) {
229 PointerEvent::PointerItem pointerItem;
230 CHKR(pointerEvent->GetPointerItem(item, pointerItem), PARAM_INPUT_FAIL, RET_ERR);
231 pointerItems.push_back(pointerItem);
232 }
233 uint32_t index = 0;
234 int32_t touchArea = 0;
235 napi_value currentPointer = nullptr;
236 int32_t currentPointerId = pointerEvent->GetPointerId();
237 for (auto &it : pointerItems) {
238 napi_value element = nullptr;
239 status = napi_create_object(jsEnv_, &element);
240 if (status != napi_ok) {
241 MMI_LOGE("napi_create_object is failed");
242 return RET_ERR;
243 }
244 if (currentPointerId == it.GetPointerId()) {
245 status = napi_create_object(jsEnv_, ¤tPointer);
246 if (status != napi_ok) {
247 MMI_LOGE("napi_create_object is failed");
248 return RET_ERR;
249 }
250 CHKR(SetNameProperty(jsEnv_, currentPointer, "globalX", it.GetGlobalX()) == napi_ok,
251 CALL_NAPI_API_ERR, RET_ERR);
252 CHKR(SetNameProperty(jsEnv_, currentPointer, "globalY", it.GetGlobalY()) == napi_ok,
253 CALL_NAPI_API_ERR, RET_ERR);
254 CHKR(SetNameProperty(jsEnv_, currentPointer, "localX", 0) == napi_ok,
255 CALL_NAPI_API_ERR, RET_ERR);
256 CHKR(SetNameProperty(jsEnv_, currentPointer, "localY", 0) == napi_ok,
257 CALL_NAPI_API_ERR, RET_ERR);
258 touchArea = (it.GetWidth() + it.GetHeight()) / 2;
259 CHKR(SetNameProperty(jsEnv_, currentPointer, "size", touchArea) == napi_ok,
260 CALL_NAPI_API_ERR, RET_ERR);
261 CHKR(SetNameProperty(jsEnv_, currentPointer, "force", it.GetPressure()) == napi_ok,
262 CALL_NAPI_API_ERR, RET_ERR);
263 CHKR(SetNameProperty(jsEnv_, result, "timestamp", pointerEvent->GetActionTime()) == napi_ok,
264 CALL_NAPI_API_ERR, RET_ERR);
265 CHKR(SetNameProperty(jsEnv_, result, "deviceId", it.GetDeviceId()) == napi_ok,
266 CALL_NAPI_API_ERR, RET_ERR);
267 }
268 CHKR(SetNameProperty(jsEnv_, element, "globalX", it.GetGlobalX()) == napi_ok,
269 CALL_NAPI_API_ERR, RET_ERR);
270 CHKR(SetNameProperty(jsEnv_, element, "globalY", it.GetGlobalY()) == napi_ok,
271 CALL_NAPI_API_ERR, RET_ERR);
272 CHKR(SetNameProperty(jsEnv_, element, "localX", 0) == napi_ok,
273 CALL_NAPI_API_ERR, RET_ERR);
274 CHKR(SetNameProperty(jsEnv_, element, "localY", 0) == napi_ok,
275 CALL_NAPI_API_ERR, RET_ERR);
276 touchArea = (it.GetWidth() + it.GetHeight()) / 2;
277 CHKR(SetNameProperty(jsEnv_, element, "size", touchArea) == napi_ok,
278 CALL_NAPI_API_ERR, RET_ERR);
279 CHKR(SetNameProperty(jsEnv_, element, "force", it.GetPressure()) == napi_ok,
280 CALL_NAPI_API_ERR, RET_ERR);
281 status = napi_set_element(jsEnv_, pointers, index, element);
282 if (status != napi_ok) {
283 MMI_LOGE("napi_set_element is failed");
284 return RET_ERR;
285 }
286 index++;
287 }
288 CHKR(SetNameProperty(jsEnv_, result, "touches", pointers) == napi_ok,
289 CALL_NAPI_API_ERR, RET_ERR);
290 CHKR(SetNameProperty(jsEnv_, result, "changedTouches", currentPointer) == napi_ok,
291 CALL_NAPI_API_ERR, RET_ERR);
292 return RET_OK;
293 }
294
Start()295 bool JsInputMonitor::Start() {
296 MMI_LOGD("Enter");
297 CHKPF(monitor_);
298 if (isMonitoring_) {
299 MMI_LOGW("js is monitoring");
300 return true;
301 }
302 if (monitor_->Start()) {
303 isMonitoring_ = true;
304 return true;
305 }
306 MMI_LOGD("Leave");
307 return false;
308 }
309
~JsInputMonitor()310 JsInputMonitor::~JsInputMonitor()
311 {
312 MMI_LOGD("Enter");
313 if (isMonitoring_) {
314 isMonitoring_ = false;
315 if (monitor_ != nullptr) {
316 monitor_->Stop();
317 }
318 }
319 uint32_t refCount = 0;
320 auto status = napi_reference_unref(jsEnv_, receiver_, &refCount);
321 if (status != napi_ok) {
322 MMI_LOGE("napi_reference_unref is failed");
323 return;
324 }
325 MMI_LOGD("Leave");
326 }
327
Stop()328 void JsInputMonitor::Stop() {
329 MMI_LOGD("Enter");
330 CHKPV(monitor_);
331 if (isMonitoring_) {
332 isMonitoring_ = false;
333 if (monitor_ != nullptr) {
334 monitor_->Stop();
335 }
336 }
337 MMI_LOGD("Leave");
338 }
339
GetId()340 int32_t JsInputMonitor::GetId()
341 {
342 return id_;
343 }
344
OnPointerEvent(std::shared_ptr<PointerEvent> pointerEvent)345 void JsInputMonitor::OnPointerEvent(std::shared_ptr<PointerEvent> pointerEvent)
346 {
347 MMI_LOGD("Enter");
348 CHKPV(monitor_);
349 CHKPV(pointerEvent);
350 int32_t num = 0;
351 {
352 std::lock_guard<std::mutex> guard(mutex_);
353 evQueue_.push(pointerEvent);
354 num = jsThreadNum_;
355 }
356 if (num < 1) {
357 int32_t *id = &id_;
358 uv_work_t *work = new uv_work_t;
359 work->data = id;
360 uv_queue_work(loop_, work, [](uv_work_t *work){}, &JsInputMonitor::JsCallback);
361 std::lock_guard<std::mutex> guard(mutex_);
362 jsThreadNum_++;
363 }
364 MMI_LOGD("Leave");
365 }
366
JsCallback(uv_work_t * work,int32_t status)367 void JsInputMonitor::JsCallback(uv_work_t *work, int32_t status)
368 {
369 MMI_LOGD("Enter");
370 CHKPV(work);
371 int32_t *id = static_cast<int32_t *>(work->data);
372 delete work;
373 work = nullptr;
374 auto jsMonitor = JSIMM.GetMonitor(*id);
375 CHKPV(jsMonitor);
376 jsMonitor->OnPointerEventInJsThread();
377 id = nullptr;
378 MMI_LOGD("Leave");
379 }
380
OnPointerEventInJsThread()381 void JsInputMonitor::OnPointerEventInJsThread()
382 {
383 MMI_LOGD("Enter");
384 if (!isMonitoring_) {
385 MMI_LOGE("js monitor stop");
386 return;
387 }
388 CHKPV(jsEnv_);
389 CHKPV(receiver_);
390 std::lock_guard<std::mutex> guard(mutex_);
391 napi_handle_scope scope = nullptr;
392 while (!evQueue_.empty()) {
393 if (!isMonitoring_) {
394 MMI_LOGE("js monitor stop handle callback");
395 break;
396 }
397 auto pointerEvent = evQueue_.front();
398 CHKPC(pointerEvent);
399 evQueue_.pop();
400 auto status = napi_open_handle_scope(jsEnv_, &scope);
401 if (status != napi_ok) {
402 break;
403 }
404 napi_value touch = nullptr;
405 status = napi_create_object(jsEnv_, &touch);
406 if (status != napi_ok) {
407 napi_close_handle_scope(jsEnv_, scope);
408 break;
409 }
410 auto ret = TransformPointerEvent(pointerEvent, touch);
411 if (ret != napi_ok) {
412 napi_close_handle_scope(jsEnv_, scope);
413 break;
414 }
415 if (touch == nullptr) {
416 napi_close_handle_scope(jsEnv_, scope);
417 break;
418 }
419 napi_value callback = nullptr;
420 status = napi_get_reference_value(jsEnv_, receiver_, &callback);
421 if (status != napi_ok) {
422 napi_close_handle_scope(jsEnv_, scope);
423 break;
424 }
425 napi_value global = nullptr;
426 status = napi_get_global(jsEnv_, &global);
427 if (status != napi_ok) {
428 napi_close_handle_scope(jsEnv_, scope);
429 break;
430 }
431 napi_value result = nullptr;
432 status = napi_call_function(jsEnv_, global, callback, 1, &touch, &result);
433 if (status != napi_ok) {
434 napi_close_handle_scope(jsEnv_, scope);
435 break;
436 }
437 bool retValue = false;
438 status = napi_get_value_bool(jsEnv_, result, &retValue);
439 if (status != napi_ok) {
440 napi_close_handle_scope(jsEnv_, scope);
441 return;
442 }
443 if (retValue) {
444 auto eventId = pointerEvent->GetId();
445 MarkConsumed(eventId);
446 }
447 napi_close_handle_scope(jsEnv_, scope);
448 }
449 --jsThreadNum_;
450 MMI_LOGD("Leave");
451 }
452 } // namespace MMI
453 } // namespace OHOS
454