1 /*
2 * Copyright (c) 2025 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 <ani.h>
17 #include "core/components_ng/manager/display_sync/ui_display_sync.h"
18
19 namespace OHOS::Ace::Ani {
20
21 class DisplaySync final {
22 public:
23 DisplaySync() = delete;
DisplaySync(RefPtr<UIDisplaySync> & uiDisplaySync)24 explicit DisplaySync(RefPtr<UIDisplaySync>& uiDisplaySync)
25 : uiDisplaySync_(uiDisplaySync) {}
26
~DisplaySync()27 ~DisplaySync()
28 {
29 if (uiDisplaySync_) {
30 uiDisplaySync_->DelFromPipelineOnContainer();
31 }
32 }
33
GetUIDisplaySync() const34 RefPtr<UIDisplaySync> GetUIDisplaySync() const
35 {
36 return uiDisplaySync_;
37 }
38
GetOnframeRef() const39 ani_ref GetOnframeRef() const
40 {
41 return onFrameRef_;
42 }
43
SetOnframeRef(const ani_ref & onframe)44 void SetOnframeRef(const ani_ref& onframe)
45 {
46 onFrameRef_ = onframe;
47 }
48
49 ani_ref onFrameRef_ = nullptr;
50
51 private:
52 RefPtr<UIDisplaySync> uiDisplaySync_;
53 };
54
ANIUtils_ANIStringToStdString(ani_env * env,ani_string ani_str)55 std::string ANIUtils_ANIStringToStdString(ani_env *env, ani_string ani_str)
56 {
57 ani_size strSize;
58 env->String_GetUTF8Size(ani_str, &strSize);
59
60 std::vector<char> buffer(strSize + 1);
61 char *utf8Buffer = buffer.data();
62
63 ani_size bytesWritten = 0;
64 env->String_GetUTF8(ani_str, utf8Buffer, strSize + 1, &bytesWritten);
65 utf8Buffer[bytesWritten] = '\0';
66 std::string content = std::string(utf8Buffer);
67 return content;
68 }
69
ParseExpectedFrameRateRange(ani_env * env,ani_object objOption,FrameRateRange & frameRateRange)70 void ParseExpectedFrameRateRange(ani_env *env, ani_object objOption,
71 FrameRateRange& frameRateRange)
72 {
73 static const char *className = "arkui.component.common.ExpectedFrameRateRange";
74 ani_class cls;
75 if (ANI_OK != env->FindClass(className, &cls)) {
76 return;
77 }
78
79 ani_boolean isInstance;
80 if (ANI_OK != env->Object_InstanceOf(objOption, cls, &isInstance)) {
81 return;
82 }
83
84 int32_t minFPS = 0;
85 int32_t maxFPS = 0;
86 int32_t expectedFPS = 0;
87
88 ani_double minAni;
89 ani_double maxAni;
90 ani_double expectedAni;
91 env->Object_GetPropertyByName_Double(objOption, "min", &minAni);
92 env->Object_GetPropertyByName_Double(objOption, "max", &maxAni);
93 env->Object_GetPropertyByName_Double(objOption, "expected", &expectedAni);
94
95 minFPS = static_cast<int32_t>(minAni);
96 maxFPS = static_cast<int32_t>(maxAni);
97 expectedFPS = static_cast<int32_t>(expectedAni);
98 frameRateRange.Set(minFPS, maxFPS, expectedFPS);
99 }
100
GetDisplaySync(ani_env * env,ani_object obj)101 static DisplaySync *GetDisplaySync(ani_env *env, ani_object obj)
102 {
103 ani_long displaySync;
104 if (ANI_OK != env->Object_GetFieldByName_Long(obj, "displaySync", &displaySync)) {
105 return nullptr;
106 }
107 return reinterpret_cast<DisplaySync *>(displaySync);
108 }
109
GetUIDisplaySync(ani_env * env,ani_object obj)110 static RefPtr<UIDisplaySync> GetUIDisplaySync(ani_env *env, ani_object obj)
111 {
112 auto displaySync = GetDisplaySync(env, obj);
113 if (displaySync == nullptr) {
114 return nullptr;
115 }
116 auto uiDisplaySync = displaySync->GetUIDisplaySync();
117 if (uiDisplaySync == nullptr) {
118 return nullptr;
119 }
120 return uiDisplaySync;
121 }
122
createIntervalInfo(ani_env * env,int64_t timestamp,int64_t targetTimestamp)123 ani_object createIntervalInfo(ani_env *env, int64_t timestamp, int64_t targetTimestamp)
124 {
125 static const char *className = "@ohos.graphics.displaySync.displaySync.IntervalInfo";
126 ani_class intervalInfo_cls;
127
128 if (ANI_OK != env->FindClass(className, &intervalInfo_cls))
129 {
130 return nullptr;
131 }
132 ani_method intervalInfoCtor;
133 env->Class_FindMethod(intervalInfo_cls, "<ctor>", "ll:", &intervalInfoCtor);
134 ani_object intervalInfoObj;
135 env->Object_New(
136 intervalInfo_cls, intervalInfoCtor, &intervalInfoObj, ani_long(timestamp), ani_long(targetTimestamp));
137 return intervalInfoObj;
138 }
139
JSOnFrame_On(ani_env * env,ani_object obj,ani_string callbackType,ani_object callbackObj)140 static void JSOnFrame_On(ani_env *env, ani_object obj, ani_string callbackType, ani_object callbackObj)
141 {
142 if (ANIUtils_ANIStringToStdString(env, callbackType) != "frame") {
143 return;
144 }
145 auto displaySync = GetDisplaySync(env, obj);
146 if (displaySync == nullptr) {
147 return;
148 }
149 if (displaySync->GetOnframeRef()) {
150 return;
151 }
152 auto uiDisplaySync = displaySync->GetUIDisplaySync();
153 if (uiDisplaySync == nullptr) {
154 return;
155 }
156
157 ani_ref onFrameRef = reinterpret_cast<ani_ref>(callbackObj);
158 ani_ref onFrameGlobalRef;
159 env->GlobalReference_Create(onFrameRef, &onFrameGlobalRef);
160 displaySync->SetOnframeRef(onFrameGlobalRef);
161 uiDisplaySync->RegisterOnFrameWithData([env, onFrameGlobalRef] (RefPtr<DisplaySyncData> displaySyncData) {
162 auto fnObj = reinterpret_cast<ani_fn_object>(onFrameGlobalRef);
163 ani_ref result;
164 auto intervalInfo = createIntervalInfo(env, displaySyncData->timestamp_, displaySyncData->targetTimestamp_);
165 std::vector<ani_ref> tmp = { reinterpret_cast<ani_ref>(intervalInfo) };
166 env->FunctionalObject_Call(fnObj, tmp.size(), tmp.data(), &result);
167 });
168 }
169
JSOnFrame_Off(ani_env * env,ani_object obj,ani_string callbackType,ani_object callbackObj)170 static void JSOnFrame_Off(ani_env *env, ani_object obj, ani_string callbackType, ani_object callbackObj)
171 {
172 if (ANIUtils_ANIStringToStdString(env, callbackType) != "frame") {
173 return;
174 }
175 auto displaySync = GetDisplaySync(env, obj);
176 if (displaySync == nullptr) {
177 return;
178 }
179 auto uiDisplaySync = displaySync->GetUIDisplaySync();
180 if (uiDisplaySync == nullptr) {
181 return;
182 }
183 displaySync->SetOnframeRef(nullptr);
184 uiDisplaySync->UnregisterOnFrame();
185 }
186
JSStart(ani_env * env,ani_object obj)187 static void JSStart(ani_env *env, ani_object obj) {
188 if (auto uiDisplaySync = GetUIDisplaySync(env, obj); uiDisplaySync != nullptr) {
189 uiDisplaySync->AddToPipelineOnContainer();
190 }
191 }
192
JSStop(ani_env * env,ani_object obj)193 static void JSStop(ani_env *env, ani_object obj) {
194 if (auto uiDisplaySync = GetUIDisplaySync(env, obj); uiDisplaySync != nullptr) {
195 uiDisplaySync->DelFromPipelineOnContainer();
196 }
197 }
198
JSSetExpectedFrameRateRange(ani_env * env,ani_object obj,ani_object objOption)199 static void JSSetExpectedFrameRateRange(ani_env *env, ani_object obj, ani_object objOption)
200 {
201 if (auto uiDisplaySync = GetUIDisplaySync(env, obj); uiDisplaySync != nullptr) {
202 FrameRateRange frameRateRange;
203 ParseExpectedFrameRateRange(env, objOption, frameRateRange);
204 uiDisplaySync->SetExpectedFrameRateRange(frameRateRange);
205 }
206 }
207
ANICreate(ani_env * env,ani_object object,ani_object aniOption)208 ani_object ANICreate(ani_env *env, [[maybe_unused]] ani_object object, [[maybe_unused]] ani_object aniOption)
209 {
210 ani_object displaySync_obj = {};
211 static const char *className = "@ohos.graphics.displaySync.displaySync.DisplaySync";
212 ani_class cls;
213 if (ANI_OK != env->FindClass(className, &cls)) {
214 TAG_LOGE(AceLogTag::ACE_DISPLAY_SYNC, "[ANI] find class fail");
215 return displaySync_obj;
216 }
217
218 ani_method ctor;
219 if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", nullptr, &ctor)) {
220 TAG_LOGE(AceLogTag::ACE_DISPLAY_SYNC, "[ANI] find method fail");
221 return displaySync_obj;
222 }
223
224 auto uiDisplaySync = AceType::MakeRefPtr<UIDisplaySync>();
225 DisplaySync* displaySync = new DisplaySync(uiDisplaySync);
226 if (ANI_OK != env->Object_New(cls, ctor, &displaySync_obj, reinterpret_cast<ani_long>(displaySync))) {
227 TAG_LOGE(AceLogTag::ACE_DISPLAY_SYNC, "[ANI] create displaySync fail");
228 delete displaySync;
229 return displaySync_obj;
230 }
231 return displaySync_obj;
232 }
233
clean(ani_env * env,ani_object object)234 static void clean([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object) {
235 ani_long ptr;
236 if (ANI_OK != env->Object_GetFieldByName_Long(object, "ptr", &ptr)) {
237 return;
238 }
239 delete reinterpret_cast<DisplaySync *>(ptr);
240 }
241
242
BindDisplaySync(ani_env * env)243 ani_status BindDisplaySync(ani_env *env)
244 {
245 static const char *className = "@ohos.graphics.displaySync.displaySync.DisplaySync";
246 ani_class cls;
247 if (ANI_OK != env->FindClass(className, &cls)) {
248 TAG_LOGE(AceLogTag::ACE_DISPLAY_SYNC, "[ANI] bind DisplaySync result fail");
249 return ANI_ERROR;
250 }
251
252 std::array methods = {
253 ani_native_function{"on", nullptr, reinterpret_cast<void *>(JSOnFrame_On)},
254 ani_native_function{"off", nullptr, reinterpret_cast<void *>(JSOnFrame_Off)},
255 ani_native_function{"start", ":", reinterpret_cast<void *>(JSStart)},
256 ani_native_function{"stop", ":", reinterpret_cast<void *>(JSStop)},
257 ani_native_function{"setExpectedFrameRateRange", nullptr,
258 reinterpret_cast<void *>(JSSetExpectedFrameRateRange)},
259 };
260 if (ANI_OK != env->Class_BindNativeMethods(cls, methods.data(), methods.size())) {
261 TAG_LOGE(AceLogTag::ACE_DISPLAY_SYNC, "[ANI] bind native method fail");
262 return ANI_ERROR;
263 };
264
265 static const char *cleanerName = "@ohos.graphics.displaySync.displaySync.Cleaner";
266 ani_class cleanerCls;
267 if (ANI_OK != env->FindClass(cleanerName, &cleanerCls)) {
268 return (ani_status)ANI_ERROR;
269 }
270
271 std::array cleanerMethods = {
272 ani_native_function {"clean", nullptr, reinterpret_cast<void *>(clean) },
273 };
274
275 if (ANI_OK != env->Class_BindNativeMethods(cleanerCls, cleanerMethods.data(), cleanerMethods.size())) {
276 return (ani_status)ANI_ERROR;
277 };
278 return ANI_OK;
279 }
280 } // namespace OHOS::Ace::Ani
281
ANI_Constructor(ani_vm * vm,uint32_t * result)282 ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
283 {
284 ani_env *env;
285 if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
286 return ANI_ERROR;
287 }
288 ani_namespace displaySyncNamespace;
289 if (ANI_OK != env->FindNamespace("@ohos.graphics.displaySync.displaySync", &displaySyncNamespace)) {
290 return ANI_ERROR;
291 }
292 std::array staticMethods = {
293 ani_native_function {"create", nullptr,
294 reinterpret_cast<void *>(OHOS::Ace::Ani::ANICreate)},
295 };
296 if (ANI_OK != env->Namespace_BindNativeFunctions(
297 displaySyncNamespace, staticMethods.data(), staticMethods.size())) {
298 return ANI_ERROR;
299 };
300
301 ani_status retBindResult = OHOS::Ace::Ani::BindDisplaySync(env);
302 if (retBindResult != ANI_OK) {
303 TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DISPLAY_SYNC, "[ANI] BindDisplaySyncResult fail");
304 return retBindResult;
305 }
306 *result = ANI_VERSION_1;
307 return ANI_OK;
308 }
309