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