1 /*
2  * Copyright (c) 2023 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_screen_utils.h"
17 
18 #include <js_runtime_utils.h>
19 #include <event_handler.h>
20 #include "process_options.h"
21 
22 #include "dm_common.h"
23 #include "window_manager_hilog.h"
24 
25 namespace OHOS::Rosen {
26 using namespace AbilityRuntime;
27 namespace {
28 constexpr HiviewDFX::HiLogLabel LABEL = { LOG_CORE, HILOG_DOMAIN_WINDOW, "JsScreenUtils" };
29 }
30 
NapiGetUndefined(napi_env env)31 napi_value NapiGetUndefined(napi_env env)
32 {
33     napi_value result = nullptr;
34     napi_get_undefined(env, &result);
35     return result;
36 }
37 
NapiIsCallable(napi_env env,napi_value value)38 bool NapiIsCallable(napi_env env, napi_value value)
39 {
40     bool result = false;
41     napi_is_callable(env, value, &result);
42     return result;
43 }
44 
GetType(napi_env env,napi_value value)45 napi_valuetype GetType(napi_env env, napi_value value)
46 {
47     napi_valuetype res = napi_undefined;
48     napi_typeof(env, value, &res);
49     return res;
50 }
51 
CreateJsScreenProperty(napi_env env,const ScreenProperty & screenProperty)52 napi_value JsScreenUtils::CreateJsScreenProperty(napi_env env, const ScreenProperty& screenProperty)
53 {
54     napi_value objValue = nullptr;
55     napi_create_object(env, &objValue);
56     if (objValue == nullptr) {
57         WLOGFE("Failed to create object!");
58         return NapiGetUndefined(env);
59     }
60 
61     napi_set_named_property(env, objValue, "propertyChangeReason",
62         CreateJsValue(env, screenProperty.GetPropertyChangeReason()));
63     napi_set_named_property(env, objValue, "rotation", CreateJsValue(env, screenProperty.GetRotation()));
64     napi_set_named_property(env, objValue, "bounds", CreateJsRRect(env, screenProperty.GetBounds()));
65     return objValue;
66 }
67 
CreateJsRRect(napi_env env,const RRect & rrect)68 napi_value JsScreenUtils::CreateJsRRect(napi_env env, const RRect& rrect)
69 {
70     napi_value objValue = nullptr;
71     napi_create_object(env, &objValue);
72     if (objValue == nullptr) {
73         WLOGFE("Failed to create object!");
74         return NapiGetUndefined(env);
75     }
76 
77     napi_set_named_property(env, objValue, "left", CreateJsValue(env, rrect.rect_.left_));
78     napi_set_named_property(env, objValue, "top", CreateJsValue(env, rrect.rect_.top_));
79     napi_set_named_property(env, objValue, "width", CreateJsValue(env, rrect.rect_.width_));
80     napi_set_named_property(env, objValue, "height", CreateJsValue(env, rrect.rect_.height_));
81     napi_set_named_property(env, objValue, "radius", CreateJsValue(env, rrect.radius_[0].x_));
82     return objValue;
83 }
84 
CreateJsScreenConnectChangeType(napi_env env)85 napi_value JsScreenUtils::CreateJsScreenConnectChangeType(napi_env env)
86 {
87     napi_value objValue = nullptr;
88     napi_create_object(env, &objValue);
89     if (objValue == nullptr) {
90         WLOGFE("Failed to create object!");
91         return NapiGetUndefined(env);
92     }
93 
94     napi_set_named_property(env, objValue, "CONNECT", CreateJsValue(env, 0));
95     napi_set_named_property(env, objValue, "DISCONNECT", CreateJsValue(env, 1));
96     return objValue;
97 }
98 
CreateJsScreenPropertyChangeReason(napi_env env)99 napi_value JsScreenUtils::CreateJsScreenPropertyChangeReason(napi_env env)
100 {
101     napi_value objValue = nullptr;
102     napi_create_object(env, &objValue);
103     if (objValue == nullptr) {
104         WLOGFE("Failed to create object!");
105         return NapiGetUndefined(env);
106     }
107 
108     napi_set_named_property(env, objValue, "UNDEFINED", CreateJsValue(env,
109         static_cast<int32_t>(ScreenPropertyChangeReason::UNDEFINED)));
110     napi_set_named_property(env, objValue, "ROTATION", CreateJsValue(env,
111         static_cast<int32_t>(ScreenPropertyChangeReason::ROTATION)));
112     napi_set_named_property(env, objValue, "CHANGE_MODE", CreateJsValue(env,
113         static_cast<int32_t>(ScreenPropertyChangeReason::CHANGE_MODE)));
114     napi_set_named_property(env, objValue, "FOLD_SCREEN_EXPAND", CreateJsValue(env,
115         static_cast<int32_t>(ScreenPropertyChangeReason::FOLD_SCREEN_EXPAND)));
116     napi_set_named_property(env, objValue, "SCREEN_CONNECT", CreateJsValue(env,
117         static_cast<int32_t>(ScreenPropertyChangeReason::SCREEN_CONNECT)));
118     napi_set_named_property(env, objValue, "SCREEN_DISCONNECT", CreateJsValue(env,
119         static_cast<int32_t>(ScreenPropertyChangeReason::SCREEN_DISCONNECT)));
120     napi_set_named_property(env, objValue, "FOLD_SCREEN_FOLDING", CreateJsValue(env,
121         static_cast<int32_t>(ScreenPropertyChangeReason::FOLD_SCREEN_FOLDING)));
122     napi_set_named_property(env, objValue, "VIRTUAL_SCREEN_RESIZE", CreateJsValue(env,
123         static_cast<int32_t>(ScreenPropertyChangeReason::VIRTUAL_SCREEN_RESIZE)));
124     return objValue;
125 }
126 
CreateJsFoldStatus(napi_env env)127 napi_value JsScreenUtils::CreateJsFoldStatus(napi_env env)
128 {
129     napi_value objValue = nullptr;
130     napi_create_object(env, &objValue);
131     if (objValue == nullptr) {
132         WLOGFE("Failed to create object!");
133         return NapiGetUndefined(env);
134     }
135 
136     napi_set_named_property(env, objValue, "FOLD_STATUS_UNKNOWN", CreateJsValue(env,
137         static_cast<int32_t>(FoldStatus::UNKNOWN)));
138     napi_set_named_property(env, objValue, "FOLD_STATUS_EXPANDED", CreateJsValue(env,
139         static_cast<int32_t>(FoldStatus::EXPAND)));
140     napi_set_named_property(env, objValue, "FOLD_STATUS_FOLDED", CreateJsValue(env,
141         static_cast<int32_t>(FoldStatus::FOLDED)));
142     napi_set_named_property(env, objValue, "FOLD_STATUS_HALF_FOLDED", CreateJsValue(env,
143         static_cast<int32_t>(FoldStatus::HALF_FOLD)));
144     return objValue;
145 }
146 
CreateJsScreenPropertyChangeType(napi_env env)147 napi_value JsScreenUtils::CreateJsScreenPropertyChangeType(napi_env env)
148 {
149     napi_value objValue = nullptr;
150     napi_create_object(env, &objValue);
151     if (objValue == nullptr) {
152         WLOGFE("Failed to create object!");
153         return NapiGetUndefined(env);
154     }
155 
156     napi_set_named_property(env, objValue, "UNSPECIFIED", CreateJsValue(env,
157         static_cast<int32_t>(ScreenPropertyChangeType::UNSPECIFIED)));
158     napi_set_named_property(env, objValue, "ROTATION_BEGIN", CreateJsValue(env,
159         static_cast<int32_t>(ScreenPropertyChangeType::ROTATION_BEGIN)));
160     napi_set_named_property(env, objValue, "ROTATION_END", CreateJsValue(env,
161         static_cast<int32_t>(ScreenPropertyChangeType::ROTATION_END)));
162     napi_set_named_property(env, objValue, "ROTATION_UPDATE_PROPERTY_ONLY", CreateJsValue(env,
163         static_cast<int32_t>(ScreenPropertyChangeType::ROTATION_UPDATE_PROPERTY_ONLY)));
164     return objValue;
165 }
166 
ConvertRRectFromJs(napi_env env,napi_value jsObject,RRect & bound)167 bool ConvertRRectFromJs(napi_env env, napi_value jsObject, RRect& bound)
168 {
169     napi_value jsLeft = nullptr, jsTop = nullptr, jsWidth = nullptr, jsHeight = nullptr, jsRadius = nullptr;
170     napi_get_named_property(env, jsObject, "left", &jsLeft);
171     napi_get_named_property(env, jsObject, "top", &jsTop);
172     napi_get_named_property(env, jsObject, "width", &jsWidth);
173     napi_get_named_property(env, jsObject, "height", &jsHeight);
174     napi_get_named_property(env, jsObject, "radius", &jsRadius);
175 
176     if (GetType(env, jsLeft) != napi_undefined) {
177         int32_t left;
178         if (!ConvertFromJsValue(env, jsLeft, left)) {
179             WLOGFE("[NAPI]Failed to convert parameter to left");
180             return false;
181         }
182         bound.rect_.left_ = left;
183     }
184     if (GetType(env, jsTop) != napi_undefined) {
185         int32_t top;
186         if (!ConvertFromJsValue(env, jsTop, top)) {
187             WLOGFE("[NAPI]Failed to convert parameter to top");
188             return false;
189         }
190         bound.rect_.top_ = top;
191     }
192     if (GetType(env, jsWidth) != napi_undefined) {
193         int32_t width;
194         if (!ConvertFromJsValue(env, jsWidth, width)) {
195             WLOGFE("[NAPI]Failed to convert parameter to width");
196             return false;
197         }
198         bound.rect_.width_ = width;
199     }
200     if (GetType(env, jsHeight) != napi_undefined) {
201         int32_t height;
202         if (!ConvertFromJsValue(env, jsHeight, height)) {
203             WLOGFE("[NAPI]Failed to convert parameter to height");
204             return false;
205         }
206         bound.rect_.height_ = height;
207     }
208     if (GetType(env, jsRadius) != napi_undefined) {
209         int radius;
210         if (!ConvertFromJsValue(env, jsRadius, radius)) {
211             WLOGFE("[NAPI]Failed to convert parameter to radius");
212             return false;
213         }
214         bound.radius_[0].x_ = static_cast<float>(radius);
215     }
216     return true;
217 }
218 
ConvertScreenDirectionInfoFromJs(napi_env env,napi_value jsObject,ScreenDirectionInfo & directionInfo)219 bool ConvertScreenDirectionInfoFromJs(napi_env env, napi_value jsObject, ScreenDirectionInfo& directionInfo)
220 {
221     napi_value jsNotifyRotation = nullptr, jsScreenRotation = nullptr, jsRotation = nullptr, jsPhyRotation = nullptr;
222     napi_get_named_property(env, jsObject, "notifyRotation", &jsNotifyRotation);
223     napi_get_named_property(env, jsObject, "screenRotation", &jsScreenRotation);
224     napi_get_named_property(env, jsObject, "rotation", &jsRotation);
225     napi_get_named_property(env, jsObject, "phyRotation", &jsPhyRotation);
226     if (GetType(env, jsNotifyRotation) != napi_undefined) {
227         int32_t notifyRotation;
228         if (!ConvertFromJsValue(env, jsNotifyRotation, notifyRotation)) {
229             WLOGFE("[NAPI]Failed to convert parameter to notifyRotation");
230             return false;
231         }
232         directionInfo.notifyRotation_ = notifyRotation;
233     }
234     if (GetType(env, jsScreenRotation) != napi_undefined) {
235         int32_t screenRotation;
236         if (!ConvertFromJsValue(env, jsScreenRotation, screenRotation)) {
237             WLOGFE("[NAPI]Failed to convert parameter to screenRotation");
238             return false;
239         }
240         directionInfo.screenRotation_ = screenRotation;
241     }
242     if (GetType(env, jsRotation) != napi_undefined) {
243         int32_t rotation;
244         if (!ConvertFromJsValue(env, jsRotation, rotation)) {
245             WLOGFE("[NAPI]Failed to convert parameter to rotation");
246             return false;
247         }
248         directionInfo.rotation_ = rotation;
249     }
250     if (GetType(env, jsPhyRotation) != napi_undefined) {
251         int32_t phyRotation;
252         if (!ConvertFromJsValue(env, jsPhyRotation, phyRotation)) {
253             WLOGFE("[NAPI]Failed to convert parameter to phyRotation");
254             return false;
255         }
256         directionInfo.phyRotation_ = phyRotation;
257     }
258     return true;
259 }
260 
ConvertDMRectFromJs(napi_env env,napi_value jsObject,DMRect & rect)261 bool ConvertDMRectFromJs(napi_env env, napi_value jsObject, DMRect& rect)
262 {
263     napi_value jsPosX = nullptr, jsPosY = nullptr, jsWidth = nullptr, jsHeight = nullptr;
264     napi_get_named_property(env, jsObject, "posX", &jsPosX);
265     napi_get_named_property(env, jsObject, "posY", &jsPosY);
266     napi_get_named_property(env, jsObject, "width", &jsWidth);
267     napi_get_named_property(env, jsObject, "height", &jsHeight);
268 
269     if (GetType(env, jsPosX) != napi_undefined) {
270         int32_t posX;
271         if (!ConvertFromJsValue(env, jsPosX, posX)) {
272             WLOGFE("[NAPI]Failed to convert parameter to posX");
273             return false;
274         }
275         rect.posX_ = posX;
276     }
277     if (GetType(env, jsPosY) != napi_undefined) {
278         int32_t top;
279         if (!ConvertFromJsValue(env, jsPosY, top)) {
280             WLOGFE("[NAPI]Failed to convert parameter to posY");
281             return false;
282         }
283         rect.posY_ = top;
284     }
285     if (GetType(env, jsWidth) != napi_undefined) {
286         int32_t width;
287         if (!ConvertFromJsValue(env, jsWidth, width)) {
288             WLOGFE("[NAPI]Failed to convert parameter to width");
289             return false;
290         }
291         rect.width_ = static_cast<uint32_t>(width);
292     }
293     if (GetType(env, jsHeight) != napi_undefined) {
294         int32_t height;
295         if (!ConvertFromJsValue(env, jsHeight, height)) {
296             WLOGFE("[NAPI]Failed to convert parameter to height");
297             return false;
298         }
299         rect.height_ = static_cast<uint32_t>(height);
300     }
301     return true;
302 }
303 
304 
305 struct AsyncInfo {
306     napi_env env;
307     napi_async_work work;
308     std::function<void()> func;
309 };
310 
NapiAsyncWork(napi_env env,std::function<void ()> task)311 void NapiAsyncWork(napi_env env, std::function<void()> task)
312 {
313     napi_value resource = nullptr;
314     AsyncInfo* info = new (std::nothrow) AsyncInfo();
315     if (info == nullptr) {
316         TLOGE(WmsLogTag::DMS, "malloc asyncinfo failed");
317         return;
318     }
319     info->env = env;
320     info->func = task;
321     napi_create_string_utf8(env, "DMSAsyncWork", NAPI_AUTO_LENGTH, &resource);
322     napi_create_async_work(env, nullptr, resource, [](napi_env env, void* data) {
323     },
324     [](napi_env env, napi_status status, void* data) {
325         AsyncInfo* info = (AsyncInfo*)data;
326         if (info == nullptr) {
327             TLOGE(WmsLogTag::DMS, "async info is nullptr");
328             return;
329         }
330         info->func();
331         napi_delete_async_work(env, info->work);
332         delete info;
333     }, (void*)info, &info->work);
334     napi_queue_async_work(env, info->work);
335 }
336 
MainThreadScheduler(napi_env env)337 MainThreadScheduler::MainThreadScheduler(napi_env env)
338     : env_(env)
339 {
340     GetMainEventHandler();
341 }
342 
GetMainEventHandler()343 inline void MainThreadScheduler::GetMainEventHandler()
344 {
345     if (handler_ != nullptr) {
346         return;
347     }
348     auto runner = OHOS::AppExecFwk::EventRunner::GetMainEventRunner();
349     if (runner == nullptr) {
350         return;
351     }
352     handler_ = std::make_shared<OHOS::AppExecFwk::EventHandler>(runner);
353 }
354 
PostMainThreadTask(Task && localTask,std::string traceInfo,int64_t delayTime)355 void MainThreadScheduler::PostMainThreadTask(Task&& localTask, std::string traceInfo, int64_t delayTime)
356 {
357     GetMainEventHandler();
358     auto task = [env = env_, localTask, traceInfo] () {
359         HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "DMS:%s", traceInfo.c_str());
360         napi_handle_scope scope = nullptr;
361         napi_open_handle_scope(env, &scope);
362         localTask();
363         napi_close_handle_scope(env, scope);
364     };
365     if (handler_ && handler_->GetEventRunner()->IsCurrentRunnerThread()) {
366         return task();
367     } else if (handler_ && !handler_->GetEventRunner()->IsCurrentRunnerThread()) {
368         handler_->PostTask(std::move(task), "dms:" + traceInfo, delayTime,
369             OHOS::AppExecFwk::EventQueue::Priority::IMMEDIATE);
370     } else {
371         NapiAsyncWork(env_, task);
372     }
373 }
374 } // namespace OHOS::Rosen
375