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