1 /*
2  * Copyright (c) 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_screen.h"
17 
18 #include <cinttypes>
19 #include "screen.h"
20 #include "screen_info.h"
21 #include "window_manager_hilog.h"
22 #ifdef XPOWER_EVENT_ENABLE
23 #include "xpower_event_js.h"
24 #endif // XPOWER_EVENT_ENABLE
25 
26 namespace OHOS {
27 namespace Rosen {
28 using namespace AbilityRuntime;
29 constexpr size_t ARGC_ONE = 1;
30 constexpr size_t ARGC_TWO = 2;
31 namespace {
32 constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_DISPLAY, "JsScreen"};
33 }
34 
35 static thread_local std::map<ScreenId, std::shared_ptr<NativeReference>> g_JsScreenMap;
36 std::recursive_mutex g_mutex;
37 
JsScreen(const sptr<Screen> & screen)38 JsScreen::JsScreen(const sptr<Screen>& screen) : screen_(screen)
39 {
40 }
41 
~JsScreen()42 JsScreen::~JsScreen()
43 {
44     WLOGI("JsScreen::~JsScreen is called");
45 }
46 
Finalizer(napi_env env,void * data,void * hint)47 void JsScreen::Finalizer(napi_env env, void* data, void* hint)
48 {
49     WLOGI("JsScreen::Finalizer is called");
50     auto jsScreen = std::unique_ptr<JsScreen>(static_cast<JsScreen*>(data));
51     if (jsScreen == nullptr) {
52         WLOGFE("jsScreen::Finalizer jsScreen is null");
53         return;
54     }
55     sptr<Screen> screen = jsScreen->screen_;
56     if (screen == nullptr) {
57         WLOGFE("JsScreen::Finalizer screen is null");
58         return;
59     }
60     ScreenId screenId = screen->GetId();
61     WLOGI("JsScreen::Finalizer screenId : %{public}" PRIu64"", screenId);
62     std::lock_guard<std::recursive_mutex> lock(g_mutex);
63     if (g_JsScreenMap.find(screenId) != g_JsScreenMap.end()) {
64         WLOGI("JsScreen::Finalizer screen is destroyed: %{public}" PRIu64"", screenId);
65         g_JsScreenMap.erase(screenId);
66     }
67 }
68 
SetOrientation(napi_env env,napi_callback_info info)69 napi_value JsScreen::SetOrientation(napi_env env, napi_callback_info info)
70 {
71     JsScreen* me = CheckParamsAndGetThis<JsScreen>(env, info);
72     return (me != nullptr) ? me->OnSetOrientation(env, info) : nullptr;
73 }
74 
NapiGetUndefined(napi_env env)75 napi_value NapiGetUndefined(napi_env env)
76 {
77     napi_value result = nullptr;
78     napi_get_undefined(env, &result);
79     return result;
80 }
81 
GetType(napi_env env,napi_value value)82 napi_valuetype GetType(napi_env env, napi_value value)
83 {
84     napi_valuetype res = napi_undefined;
85     napi_typeof(env, value, &res);
86     return res;
87 }
88 
OnSetOrientation(napi_env env,napi_callback_info info)89 napi_value JsScreen::OnSetOrientation(napi_env env, napi_callback_info info)
90 {
91     WLOGI("OnSetOrientation is called");
92     bool paramValidFlag = true;
93     Orientation orientation = Orientation::UNSPECIFIED;
94     size_t argc = 4;
95     napi_value argv[4] = {nullptr};
96     std::string errMsg = "";
97     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
98     if (argc < ARGC_ONE) {
99         WLOGFE("OnSetOrientation Params not match, info argc: %{public}zu", argc);
100         errMsg = "Invalid args count, need one arg at least!";
101         paramValidFlag = false;
102     } else if (!ConvertFromJsValue(env, argv[0], orientation)) {
103         paramValidFlag = false;
104         WLOGFE("Failed to convert parameter to orientation");
105         errMsg = "Failed to convert parameter to orientation";
106     }
107     if (!paramValidFlag) {
108         WLOGE("OnSetOrientation paramValidFlag error");
109         napi_throw(env, CreateJsError(env, static_cast<int32_t>(DmErrorCode::DM_ERROR_INVALID_PARAM), errMsg));
110         return NapiGetUndefined(env);
111     }
112     if (orientation < Orientation::BEGIN || orientation > Orientation::END) {
113         WLOGE("Orientation param error! orientation value must from enum Orientation");
114         errMsg = "orientation value must from enum Orientation";
115         napi_throw(env, CreateJsError(env, static_cast<int32_t>(DmErrorCode::DM_ERROR_INVALID_PARAM), errMsg));
116         return NapiGetUndefined(env);
117     }
118 
119     NapiAsyncTask::CompleteCallback complete =
120         [=](napi_env env, NapiAsyncTask& task, int32_t status) {
121             DmErrorCode ret = DM_JS_TO_ERROR_CODE_MAP.at(screen_->SetOrientation(orientation));
122             if (ret == DmErrorCode::DM_OK) {
123                 task.Resolve(env, NapiGetUndefined(env));
124                 WLOGI("OnSetOrientation success");
125             } else {
126                 task.Reject(env, CreateJsError(env, static_cast<int32_t>(ret),
127                                                   "JsScreen::OnSetOrientation failed."));
128                 WLOGFE("OnSetOrientation failed");
129             }
130         };
131     napi_value lastParam = nullptr;
132     if (argc >= ARGC_TWO && argv[ARGC_TWO - 1] != nullptr &&
133         GetType(env, argv[ARGC_TWO - 1]) == napi_function) {
134         lastParam = argv[ARGC_TWO - 1];
135     }
136     napi_value result = nullptr;
137     NapiAsyncTask::Schedule("JsScreen::OnSetOrientation",
138         env, CreateAsyncTaskWithLastParam(env, lastParam, nullptr, std::move(complete), &result));
139     return result;
140 }
141 
142 
SetScreenActiveMode(napi_env env,napi_callback_info info)143 napi_value JsScreen::SetScreenActiveMode(napi_env env, napi_callback_info info)
144 {
145     WLOGI("SetScreenActiveMode is called");
146     JsScreen* me = CheckParamsAndGetThis<JsScreen>(env, info);
147 #ifdef XPOWER_EVENT_ENABLE
148     if (me != nullptr) {
149         HiviewDFX::ReportXPowerJsStackSysEvent(env, "EPS_LCD_FREQ");
150     }
151 #endif // XPOWER_EVENT_ENABLE
152     return (me != nullptr) ? me->OnSetScreenActiveMode(env, info) : nullptr;
153 }
154 
OnSetScreenActiveMode(napi_env env,napi_callback_info info)155 napi_value JsScreen::OnSetScreenActiveMode(napi_env env, napi_callback_info info)
156 {
157     WLOGI("OnSetScreenActiveMode is called");
158     bool paramValidFlag = true;
159     uint32_t modeId = 0;
160     size_t argc = 4;
161     napi_value argv[4] = {nullptr};
162     std::string errMsg = "";
163     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
164     if (argc < ARGC_ONE) {
165         WLOGFE("OnSetScreenActiveMode Params not match %{public}zu", argc);
166         errMsg = "Invalid args count, need one arg at least!";
167         paramValidFlag = false;
168     } else {
169         if (!ConvertFromJsValue(env, argv[0], modeId)) {
170             WLOGFE("Failed to convert parameter to modeId");
171             errMsg = "Failed to convert parameter to modeId";
172             paramValidFlag = false;
173         }
174     }
175     if (!paramValidFlag) {
176         WLOGFE("OnSetScreenActiveMode paramValidFlag error");
177         napi_throw(env, CreateJsError(env, static_cast<int32_t>(DmErrorCode::DM_ERROR_INVALID_PARAM), errMsg));
178         return NapiGetUndefined(env);
179     }
180 
181     NapiAsyncTask::CompleteCallback complete =
182         [=](napi_env env, NapiAsyncTask& task, int32_t status) {
183             DmErrorCode ret = DM_JS_TO_ERROR_CODE_MAP.at(screen_->SetScreenActiveMode(modeId));
184             if (ret == DmErrorCode::DM_OK) {
185                 task.Resolve(env, NapiGetUndefined(env));
186                 WLOGI("OnSetScreenActiveMode success");
187             } else {
188                 task.Reject(env, CreateJsError(env, static_cast<int32_t>(ret),
189                                                 "JsScreen::OnSetScreenActiveMode failed."));
190                 WLOGFE("OnSetScreenActiveMode failed");
191             }
192         };
193 
194     napi_value lastParam = nullptr;
195     if (argc >= ARGC_TWO && argv[ARGC_TWO - 1] != nullptr &&
196         GetType(env, argv[ARGC_TWO - 1]) == napi_function) {
197         lastParam = argv[ARGC_TWO - 1];
198     }
199 
200     napi_value result = nullptr;
201     NapiAsyncTask::Schedule("JsScreen::OnSetScreenActiveMode",
202         env, CreateAsyncTaskWithLastParam(env, lastParam, nullptr, std::move(complete), &result));
203     return result;
204 }
205 
SetDensityDpi(napi_env env,napi_callback_info info)206 napi_value JsScreen::SetDensityDpi(napi_env env, napi_callback_info info)
207 {
208     WLOGI("SetDensityDpi is called");
209     JsScreen* me = CheckParamsAndGetThis<JsScreen>(env, info);
210     return (me != nullptr) ? me->OnSetDensityDpi(env, info) : nullptr;
211 }
212 
OnSetDensityDpi(napi_env env,napi_callback_info info)213 napi_value JsScreen::OnSetDensityDpi(napi_env env, napi_callback_info info)
214 {
215     WLOGI("OnSetDensityDpi is called");
216     bool paramValidFlag = true;
217     uint32_t densityDpi = 0;
218     size_t argc = 4;
219     std::string errMsg = "";
220     napi_value argv[4] = {nullptr};
221     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
222     if (argc < ARGC_ONE) {
223         WLOGFE("OnSetDensityDpi Params not match %{public}zu", argc);
224         errMsg = "Invalid args count, need one arg at least!";
225         paramValidFlag = false;
226     } else {
227         if (!ConvertFromJsValue(env, argv[0], densityDpi)) {
228             WLOGFE("Failed to convert parameter to densityDpi");
229             errMsg = "Failed to convert parameter to densityDpi";
230             paramValidFlag = false;
231         }
232     }
233     if (!paramValidFlag) {
234         WLOGFE("OnSetDensityDpi paramValidFlag error");
235         napi_throw(env, CreateJsError(env, static_cast<int32_t>(DmErrorCode::DM_ERROR_INVALID_PARAM), errMsg));
236         return NapiGetUndefined(env);
237     }
238 
239     NapiAsyncTask::CompleteCallback complete =
240         [=](napi_env env, NapiAsyncTask& task, int32_t status) {
241             DmErrorCode ret = DM_JS_TO_ERROR_CODE_MAP.at(screen_->SetDensityDpi(densityDpi));
242             if (ret == DmErrorCode::DM_OK) {
243                 task.Resolve(env, NapiGetUndefined(env));
244                 WLOGI("OnSetDensityDpi success");
245             } else {
246                 task.Reject(env, CreateJsError(env, static_cast<int32_t>(ret),
247                                                 "JsScreen::OnSetDensityDpi failed."));
248                 WLOGFE("OnSetDensityDpi failed");
249             }
250         };
251     napi_value lastParam = nullptr;
252     if (argc >= ARGC_TWO && argv[ARGC_TWO - 1] != nullptr &&
253         GetType(env, argv[ARGC_TWO - 1]) == napi_function) {
254         lastParam = argv[ARGC_TWO - 1];
255     }
256     napi_value result = nullptr;
257     NapiAsyncTask::Schedule("JsScreen::OnSetDensityDpi",
258         env, CreateAsyncTaskWithLastParam(env, lastParam, nullptr, std::move(complete), &result));
259     return result;
260 }
261 
FindJsDisplayObject(ScreenId screenId)262 std::shared_ptr<NativeReference> FindJsDisplayObject(ScreenId screenId)
263 {
264     WLOGD("[NAPI]Try to find screen %{public}" PRIu64" in g_JsScreenMap", screenId);
265     std::lock_guard<std::recursive_mutex> lock(g_mutex);
266     if (g_JsScreenMap.find(screenId) == g_JsScreenMap.end()) {
267         WLOGI("[NAPI]Can not find screen %{public}" PRIu64" in g_JsScreenMap", screenId);
268         return nullptr;
269     }
270     return g_JsScreenMap[screenId];
271 }
272 
CreateJsScreenObject(napi_env env,sptr<Screen> & screen)273 napi_value CreateJsScreenObject(napi_env env, sptr<Screen>& screen)
274 {
275     WLOGD("JsScreen::CreateJsScreen is called");
276     napi_value objValue = nullptr;
277     std::shared_ptr<NativeReference> jsScreenObj = FindJsDisplayObject(screen->GetId());
278     if (jsScreenObj != nullptr && jsScreenObj->GetNapiValue() != nullptr) {
279         WLOGI("[NAPI]FindJsScreenObject %{public}" PRIu64"", screen->GetId());
280         objValue = jsScreenObj->GetNapiValue();
281     }
282     if (objValue == nullptr) {
283         napi_create_object(env, &objValue);
284     }
285     if (objValue == nullptr) {
286         WLOGFE("Failed to convert prop to jsObject");
287         return NapiGetUndefined(env);
288     }
289     std::unique_ptr<JsScreen> jsScreen = std::make_unique<JsScreen>(screen);
290     napi_wrap(env, objValue, jsScreen.release(), JsScreen::Finalizer, nullptr, nullptr);
291     auto info = screen->GetScreenInfo();
292     if (info == nullptr) {
293         WLOGFE("Failed to GetScreenInfo");
294         return NapiGetUndefined(env);
295     }
296     ScreenId screenId = info->GetScreenId();
297     napi_set_named_property(env, objValue, "id",
298         CreateJsValue(env, screenId == SCREEN_ID_INVALID ? -1 : static_cast<int64_t>(screenId)));
299     ScreenId parentId = info->GetParentId();
300     napi_set_named_property(env, objValue, "parent",
301         CreateJsValue(env, parentId == SCREEN_ID_INVALID ? -1 : static_cast<int64_t>(parentId)));
302     napi_set_named_property(env, objValue, "orientation", CreateJsValue(env, info->GetOrientation()));
303     napi_set_named_property(env, objValue, "sourceMode", CreateJsValue(env, info->GetSourceMode()));
304     napi_set_named_property(env, objValue, "activeModeIndex", CreateJsValue(env, info->GetModeId()));
305     napi_set_named_property(env, objValue, "supportedModeInfo", CreateJsScreenModeArrayObject(env, info->GetModes()));
306     napi_set_named_property(env, objValue, "densityDpi", CreateJsValue(env,
307         static_cast<uint32_t>(info->GetVirtualPixelRatio() * DOT_PER_INCH))); // Dpi = Density(VPR) * 160.
308     if (jsScreenObj == nullptr || jsScreenObj->GetNapiValue() == nullptr) {
309         std::shared_ptr<NativeReference> JsScreenRef;
310         napi_ref result = nullptr;
311         napi_create_reference(env, objValue, 1, &result);
312         JsScreenRef.reset(reinterpret_cast<NativeReference*>(result));
313         std::lock_guard<std::recursive_mutex> lock(g_mutex);
314         g_JsScreenMap[screenId] = JsScreenRef;
315         const char *moduleName = "JsScreen";
316         BindNativeFunction(env, objValue, "setScreenActiveMode", moduleName, JsScreen::SetScreenActiveMode);
317         BindNativeFunction(env, objValue, "setOrientation", moduleName, JsScreen::SetOrientation);
318         BindNativeFunction(env, objValue, "setDensityDpi", moduleName, JsScreen::SetDensityDpi);
319     }
320     return objValue;
321 }
322 
CreateJsScreenModeArrayObject(napi_env env,std::vector<sptr<SupportedScreenModes>> screenModes)323 napi_value CreateJsScreenModeArrayObject(napi_env env, std::vector<sptr<SupportedScreenModes>> screenModes)
324 {
325     napi_value arrayValue = nullptr;
326     napi_create_array_with_length(env, screenModes.size(), &arrayValue);
327     size_t i = 0;
328     for (const auto& mode : screenModes) {
329         napi_set_element(env, arrayValue, i++, CreateJsScreenModeObject(env, mode));
330     }
331     return arrayValue;
332 }
333 
CreateJsScreenModeObject(napi_env env,const sptr<SupportedScreenModes> & mode)334 napi_value CreateJsScreenModeObject(napi_env env, const sptr<SupportedScreenModes>& mode)
335 {
336     WLOGD("JsScreen::CreateJsScreenMode is called");
337     napi_value objValue = nullptr;
338     napi_create_object(env, &objValue);
339     if (objValue == nullptr) {
340         WLOGFE("Failed to convert prop to jsObject");
341         return NapiGetUndefined(env);
342     }
343     uint32_t id = mode->id_;
344     uint32_t width = mode->width_;
345     uint32_t height = mode->height_;
346     uint32_t refreshRate = mode->refreshRate_;
347     napi_set_named_property(env, objValue, "id", CreateJsValue(env, id));
348     napi_set_named_property(env, objValue, "width", CreateJsValue(env, width));
349     napi_set_named_property(env, objValue, "height", CreateJsValue(env, height));
350     napi_set_named_property(env, objValue, "refreshRate", CreateJsValue(env, refreshRate));
351     return objValue;
352 }
353 }  // namespace Rosen
354 }  // namespace OHOS
355