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