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