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 #include <cstring>
16 #include <memory>
17
18 #include "interfaces/napi/kits/utils/napi_utils.h"
19 #include "napi/native_api.h"
20 #include "napi/native_node_api.h"
21 #include "interfaces/napi/kits/display_sync/js_display_sync.h"
22
23 #include "base/log/log.h"
24 #include "base/memory/ace_type.h"
25 #include "base/memory/referenced.h"
26 #include "bridge/common/utils/utils.h"
27
28 namespace OHOS::Ace::Napi {
29 constexpr size_t STR_MAX_BUFFER_SIZE = 1024;
30 constexpr size_t CALLBACK_OJECT_NUM = 1;
31 constexpr size_t ARGC_NUM_SIZE = 2;
32
NapiGetUndefined(napi_env env)33 napi_value NapiGetUndefined(napi_env env)
34 {
35 napi_value result = nullptr;
36 napi_get_undefined(env, &result);
37 return result;
38 }
39
NapiPrintErrorInfo(napi_env env)40 void NapiPrintErrorInfo(napi_env env)
41 {
42 const napi_extended_error_info *error_info;
43 napi_get_last_error_info(env, &error_info);
44 LOGE("JsDisplaySync ErrorInfo: %{public}s", error_info->error_message);
45 return;
46 }
47
ParseJsValue(napi_env env,napi_value jsObject,const std::string & name,int32_t & data)48 bool ParseJsValue(napi_env env, napi_value jsObject, const std::string& name, int32_t& data)
49 {
50 napi_value value = nullptr;
51 napi_get_named_property(env, jsObject, name.c_str(), &value);
52 napi_valuetype type = napi_undefined;
53 napi_typeof(env, value, &type);
54 if (type == napi_number) {
55 napi_get_value_int32(env, value, &data);
56 return true;
57 } else {
58 return false;
59 }
60 return true;
61 }
62
ParseArgs(napi_env & env,napi_callback_info & info,napi_value & thisVar,napi_value & cb,CallbackType & callbackType)63 static size_t ParseArgs(
64 napi_env& env, napi_callback_info& info, napi_value& thisVar, napi_value& cb, CallbackType& callbackType)
65 {
66 size_t argc = ARGC_NUM_SIZE;
67 napi_value argv[ARGC_NUM_SIZE] = { 0 };
68 napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
69 NAPI_ASSERT_BASE(env, argc > 0, "too few parameter", 0);
70
71 napi_valuetype napiType;
72 NAPI_CALL_BASE(env, napi_typeof(env, argv[0], &napiType), 0);
73 NAPI_ASSERT_BASE(env, napiType == napi_string, "parameter 1 should be string", 0);
74 char type[STR_MAX_BUFFER_SIZE] = { 0 };
75 size_t len = 0;
76 napi_get_value_string_utf8(env, argv[0], type, STR_MAX_BUFFER_SIZE, &len);
77 NAPI_ASSERT_BASE(env, len < STR_MAX_BUFFER_SIZE, "condition string too long", 0);
78 NAPI_ASSERT_BASE(
79 env, (strcmp("frame", type) == 0), "type mismatch('frame')", 0);
80 if (strcmp("frame", type) == 0) {
81 callbackType = CallbackType::ONFRAME;
82 } else {
83 callbackType = CallbackType::UNKNOW;
84 }
85
86 if (argc <= 1) {
87 return argc;
88 }
89
90 NAPI_CALL_BASE(env, napi_typeof(env, argv[1], &napiType), 0);
91 NAPI_ASSERT_BASE(env, napiType == napi_function, "type mismatch for parameter 2", 0);
92 cb = argv[1];
93 return argc;
94 }
95
GetDisplaySync(napi_env env,napi_callback_info info)96 DisplaySync* GetDisplaySync(napi_env env, napi_callback_info info)
97 {
98 DisplaySync* displaySync = nullptr;
99 napi_value thisVar = nullptr;
100 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
101 napi_unwrap(env, thisVar, reinterpret_cast<void**>(&displaySync));
102 return displaySync;
103 }
104
CreateTimeInfoJsObject(const napi_env env,RefPtr<DisplaySyncData> displaySyncData,napi_value & intervalInfo)105 void CreateTimeInfoJsObject(const napi_env env, RefPtr<DisplaySyncData> displaySyncData,
106 napi_value& intervalInfo)
107 {
108 napi_status status = napi_create_object(env, &intervalInfo);
109 if (status != napi_ok) {
110 NapiPrintErrorInfo(env);
111 return;
112 }
113
114 napi_value timestamp;
115 napi_value targetTimestamp;
116 napi_create_int64(env, displaySyncData->timestamp_, ×tamp);
117 napi_create_int64(env, displaySyncData->targetTimestamp_, &targetTimestamp);
118 auto resultTimestamp = napi_set_named_property(env, intervalInfo, "timestamp", timestamp);
119 auto resultTargetTimestamp = napi_set_named_property(env, intervalInfo, "targetTimestamp", targetTimestamp);
120 if (resultTimestamp != napi_ok || resultTargetTimestamp != napi_ok) {
121 NapiPrintErrorInfo(env);
122 return;
123 }
124 }
125
ParseExpectedFrameRateRange(napi_env env,napi_callback_info info,FrameRateRange & frameRateRange)126 napi_value ParseExpectedFrameRateRange(napi_env env, napi_callback_info info, FrameRateRange& frameRateRange)
127 {
128 size_t argc = 1;
129 napi_value argv[1];
130 napi_value thisVar = nullptr;
131 napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
132 if (argc != 1) {
133 NapiThrow(env, "The number of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
134 return NapiGetUndefined(env);
135 }
136
137 napi_value nativeObj = argv[0];
138 if (nativeObj == nullptr) {
139 NapiThrow(env, "The nativeObj is nullptr.", ERROR_CODE_PARAM_INVALID);
140 return NapiGetUndefined(env);
141 }
142
143 int32_t minFPS = 0;
144 int32_t maxFPS = 0;
145 int32_t expectedFPS = 0;
146 ParseJsValue(env, nativeObj, "min", minFPS);
147 ParseJsValue(env, nativeObj, "max", maxFPS);
148 ParseJsValue(env, nativeObj, "expected", expectedFPS);
149
150 frameRateRange.Set(minFPS, maxFPS, expectedFPS);
151 if (!frameRateRange.IsValid()) {
152 NapiThrow(env, "ExpectedFrameRateRange Error", ERROR_CODE_PARAM_INVALID);
153 return NapiGetUndefined(env);
154 }
155 return NapiGetUndefined(env);
156 }
157
JSSetExpectedFrameRateRange(napi_env env,napi_callback_info info)158 napi_value JSSetExpectedFrameRateRange(napi_env env, napi_callback_info info)
159 {
160 FrameRateRange frameRateRange;
161 ParseExpectedFrameRateRange(env, info, frameRateRange);
162
163 auto displaySync = GetDisplaySync(env, info);
164 if (!displaySync) {
165 TAG_LOGW(AceLogTag::ACE_DISPLAY_SYNC, "JSSetExpectedFrameRateRange: cannot find displaySync.");
166 return NapiGetUndefined(env);
167 }
168 RefPtr<UIDisplaySync> uiDisplaySync = displaySync->GetUIDisplaySync();
169 if (!uiDisplaySync) {
170 TAG_LOGW(AceLogTag::ACE_DISPLAY_SYNC, "JSSetExpectedFrameRateRange: cannot get uiDisplaySync.");
171 return NapiGetUndefined(env);
172 }
173
174 uiDisplaySync->SetExpectedFrameRateRange(frameRateRange);
175 TAG_LOGI(AceLogTag::ACE_DISPLAY_SYNC, "Id: %{public}" PRIu64 " SetExpectedFrameRateRange"
176 "{%{public}d, %{public}d, %{public}d}", uiDisplaySync->GetId(), frameRateRange.min_, frameRateRange.max_,
177 frameRateRange.preferred_);
178 return NapiGetUndefined(env);
179 }
180
JSStart(napi_env env,napi_callback_info info)181 napi_value JSStart(napi_env env, napi_callback_info info)
182 {
183 auto displaySync = GetDisplaySync(env, info);
184 if (!displaySync) {
185 TAG_LOGW(AceLogTag::ACE_DISPLAY_SYNC, "JSStart: cannot find displaySync.");
186 return NapiGetUndefined(env);
187 }
188 RefPtr<UIDisplaySync> uiDisplaySync = displaySync->GetUIDisplaySync();
189 if (!uiDisplaySync) {
190 TAG_LOGW(AceLogTag::ACE_DISPLAY_SYNC, "JSStart: cannot get uiDisplaySync when starting.");
191 return NapiGetUndefined(env);
192 }
193
194 uiDisplaySync->AddToPipelineOnContainer();
195 TAG_LOGI(AceLogTag::ACE_DISPLAY_SYNC, "Id: %{public}" PRIu64 " Start", uiDisplaySync->GetId());
196 return NapiGetUndefined(env);
197 }
198
JSStop(napi_env env,napi_callback_info info)199 napi_value JSStop(napi_env env, napi_callback_info info)
200 {
201 auto displaySync = GetDisplaySync(env, info);
202 if (!displaySync) {
203 TAG_LOGW(AceLogTag::ACE_DISPLAY_SYNC, "JSStop: cannot find displaySync.");
204 return NapiGetUndefined(env);
205 }
206 RefPtr<UIDisplaySync> uiDisplaySync = displaySync->GetUIDisplaySync();
207 if (!uiDisplaySync) {
208 TAG_LOGW(AceLogTag::ACE_DISPLAY_SYNC, "JSStop: cannot get uiDisplaySync when stopping.");
209 return NapiGetUndefined(env);
210 }
211
212 uiDisplaySync->DelFromPipelineOnContainer();
213 TAG_LOGI(AceLogTag::ACE_DISPLAY_SYNC, "Id: %{public}" PRIu64 " Stop", uiDisplaySync->GetId());
214 return NapiGetUndefined(env);
215 }
216
Initialize(napi_env env,napi_value thisVar)217 void DisplaySync::Initialize(napi_env env, napi_value thisVar)
218 {
219 napi_handle_scope scope = nullptr;
220 napi_open_handle_scope(env, &scope);
221 if (!scope) {
222 return;
223 }
224 napi_create_reference(env, thisVar, 1, &thisVarRef_);
225 napi_close_handle_scope(env, scope);
226 }
227
NapiSerializer(napi_env & env,napi_value & jsDisplaySync)228 void DisplaySync::NapiSerializer(napi_env& env, napi_value& jsDisplaySync)
229 {
230 napi_status status = napi_create_object(env, &jsDisplaySync);
231 if (status != napi_ok) {
232 NapiPrintErrorInfo(env);
233 return;
234 }
235
236 napi_wrap(
237 env, jsDisplaySync, this,
238 [](napi_env env, void* data, void* hint) {
239 DisplaySync* displaySync = static_cast<DisplaySync*>(data);
240 if (displaySync) {
241 displaySync->Destroy(env);
242 delete displaySync;
243 }
244 },
245 nullptr, nullptr);
246 }
247
RegisterOnFrameCallback(napi_value cb,napi_ref & onFrameRef,CallbackType callbackType,napi_env env)248 void DisplaySync::RegisterOnFrameCallback(napi_value cb, napi_ref& onFrameRef,
249 CallbackType callbackType, napi_env env)
250 {
251 if (onFrameRef) {
252 return;
253 }
254 napi_create_reference(env, cb, 1, &onFrameRef);
255
256 GetUIDisplaySync()->RegisterOnFrameWithData([env, onFrameRef] (RefPtr<DisplaySyncData> displaySyncData) {
257 napi_handle_scope innerScope = nullptr;
258 napi_status status = napi_open_handle_scope(env, &innerScope);
259 if (status != napi_ok) {
260 NapiPrintErrorInfo(env);
261 return;
262 }
263
264 napi_value ret = nullptr;
265 napi_value onframe = nullptr;
266 auto result = napi_get_reference_value(env, onFrameRef, &onframe);
267 if (result != napi_ok || onframe == nullptr) {
268 NapiPrintErrorInfo(env);
269 napi_close_handle_scope(env, innerScope);
270 return;
271 }
272
273 napi_value intervalInfo = nullptr;
274 CreateTimeInfoJsObject(env, displaySyncData, intervalInfo);
275 napi_value args[CALLBACK_OJECT_NUM] = { intervalInfo };
276 napi_call_function(env, nullptr, onframe, CALLBACK_OJECT_NUM, args, &ret);
277 napi_close_handle_scope(env, innerScope);
278 });
279 }
280
UnregisterOnFrameCallback(napi_env env,size_t argc,napi_ref & onFrameRef)281 void DisplaySync::UnregisterOnFrameCallback(napi_env env, size_t argc, napi_ref& onFrameRef)
282 {
283 if (argc >= 1) {
284 napi_delete_reference(env, onFrameRef);
285 onFrameRef = nullptr;
286 GetUIDisplaySync()->UnregisterOnFrame();
287 }
288 return;
289 }
290
Destroy(napi_env env)291 void DisplaySync::Destroy(napi_env env)
292 {
293 if (onFrameRef_ != nullptr) {
294 napi_delete_reference(env, onFrameRef_);
295 }
296
297 if (thisVarRef_ != nullptr) {
298 napi_delete_reference(env, thisVarRef_);
299 }
300 }
301
JSOnFrame_On(napi_env env,napi_callback_info info)302 napi_value JSOnFrame_On(napi_env env, napi_callback_info info)
303 {
304 napi_value thisVar = nullptr;
305 napi_value cb = nullptr;
306 CallbackType callbackType = CallbackType::UNKNOW;
307 size_t argc = ParseArgs(env, info, thisVar, cb, callbackType);
308 NAPI_ASSERT(env, (argc == ARGC_NUM_SIZE && thisVar != nullptr && cb != nullptr), "Invalid arguments");
309
310 DisplaySync* displaySync = GetDisplaySync(env, info);
311 if (!displaySync) {
312 return NapiGetUndefined(env);
313 }
314
315 if (callbackType == CallbackType::ONFRAME) {
316 displaySync->RegisterOnFrameCallback(cb, displaySync->onFrameRef_, callbackType, env);
317 }
318 return NapiGetUndefined(env);
319 }
320
JSOnFrame_Off(napi_env env,napi_callback_info info)321 napi_value JSOnFrame_Off(napi_env env, napi_callback_info info)
322 {
323 napi_value thisVar = nullptr;
324 napi_value cb = nullptr;
325 CallbackType callbackType = CallbackType::UNKNOW;
326 size_t argc = ParseArgs(env, info, thisVar, cb, callbackType);
327 DisplaySync* displaySync = GetDisplaySync(env, info);
328 if (!displaySync) {
329 return NapiGetUndefined(env);
330 }
331 if (callbackType == CallbackType::ONFRAME) {
332 displaySync->UnregisterOnFrameCallback(env, argc, displaySync->onFrameRef_);
333 }
334 return NapiGetUndefined(env);
335 }
336
JSCreate(napi_env env,napi_callback_info info)337 static napi_value JSCreate(napi_env env, napi_callback_info info)
338 {
339 auto uiDisplaySync = AceType::MakeRefPtr<UIDisplaySync>();
340 DisplaySync* displaySync = new DisplaySync(uiDisplaySync);
341
342 napi_value jsDisplaySync = nullptr;
343 displaySync->NapiSerializer(env, jsDisplaySync);
344 if (!jsDisplaySync) {
345 delete displaySync;
346 return nullptr;
347 }
348
349 napi_property_descriptor resultFuncs[] = {
350 DECLARE_NAPI_FUNCTION("setExpectedFrameRateRange", JSSetExpectedFrameRateRange),
351 DECLARE_NAPI_FUNCTION("on", JSOnFrame_On),
352 DECLARE_NAPI_FUNCTION("off", JSOnFrame_Off),
353 DECLARE_NAPI_FUNCTION("start", JSStart),
354 DECLARE_NAPI_FUNCTION("stop", JSStop),
355 };
356
357 TAG_LOGD(AceLogTag::ACE_DISPLAY_SYNC, "Create UIDisplaySync Id: %{public}" PRIu64 "",
358 uiDisplaySync->GetId());
359 NAPI_CALL(env, napi_define_properties(
360 env, jsDisplaySync, sizeof(resultFuncs) / sizeof(resultFuncs[0]), resultFuncs));
361 return jsDisplaySync;
362 }
363
DisplaySyncExport(napi_env env,napi_value exports)364 static napi_value DisplaySyncExport(napi_env env, napi_value exports)
365 {
366 napi_property_descriptor displaySyncDesc[] = {
367 DECLARE_NAPI_FUNCTION("create", JSCreate),
368 };
369 NAPI_CALL(env, napi_define_properties(
370 env, exports, sizeof(displaySyncDesc) / sizeof(displaySyncDesc[0]), displaySyncDesc));
371 return exports;
372 }
373
374 static napi_module displaySyncModule = {
375 .nm_version = 1,
376 .nm_flags = 0,
377 .nm_filename = nullptr,
378 .nm_register_func = DisplaySyncExport,
379 .nm_modname = "graphics.displaySync",
380 .nm_priv = ((void*)0),
381 .reserved = { 0 },
382 };
383
DisplaySyncRegister()384 extern "C" __attribute__((constructor)) void DisplaySyncRegister()
385 {
386 napi_module_register(&displaySyncModule);
387 }
388 } // namespace OHOS::Ace::Napi
389