• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_, &currentPointer);
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