1 /*
2 * Copyright (C) 2024 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 "AnimationJS.h"
16
17 #include <meta/api/make_callback.h>
18 #include <meta/interface/animation/intf_animation.h>
19 #include <scene_plugin/interface/intf_scene.h>
20 #include "SceneJS.h"
21
22 class OnCallJS : public ThreadSafeCallback {
23 NapiApi::StrongRef jsThis_;
24 NapiApi::StrongRef ref_;
25
26 public:
OnCallJS(const char * name,napi_value jsThis,NapiApi::Function toCall)27 OnCallJS(const char* name, napi_value jsThis, NapiApi::Function toCall) : ThreadSafeCallback(toCall.GetEnv(), name)
28 {
29 jsThis_ = { toCall.GetEnv(), jsThis };
30 ref_ = { toCall.GetEnv(), toCall };
31 }
~OnCallJS()32 ~OnCallJS()
33 {
34 jsThis_.Reset();
35 ref_.Reset();
36 Release();
37 }
Finalize(napi_env env)38 void Finalize(napi_env env)
39 {
40 jsThis_.Reset();
41 ref_.Reset();
42 }
Invoked(napi_env env)43 void Invoked(napi_env env)
44 {
45 napi_value res;
46 napi_call_function(env, jsThis_.GetValue(), ref_.GetValue(), 0, nullptr, &res);
47 }
48 };
49
Init(napi_env env,napi_value exports)50 void AnimationJS::Init(napi_env env, napi_value exports)
51 {
52 BASE_NS::vector<napi_property_descriptor> node_props;
53 SceneResourceImpl::GetPropertyDescs(node_props);
54 // Try out the helper macros.
55 // Declare NAPI_API_JS_NAME to simplify the registering.
56 #define NAPI_API_JS_NAME Animation
57
58 DeclareGetSet(bool, "enabled", GetEnabled, SetEnabled);
59 DeclareGet(float, "duration", GetDuration);
60 DeclareGet(bool, "running", GetRunning);
61 DeclareGet(float, "progress", GetProgress);
62 DeclareMethod("pause", Pause);
63 DeclareMethod("restart", Restart);
64 DeclareMethod("seek", Seek, float);
65 DeclareMethod("start", Start);
66 DeclareMethod("stop", Stop);
67 DeclareMethod("finish", Finish);
68 DeclareMethod("onFinished", OnFinished, NapiApi::Function);
69 DeclareMethod("onStarted", OnStarted, NapiApi::Function);
70 DeclareClass();
71 #undef NAPI_API_JS_NAME
72 }
73
AnimationJS(napi_env e,napi_callback_info i)74 AnimationJS::AnimationJS(napi_env e, napi_callback_info i)
75 : BaseObject<AnimationJS>(e, i), SceneResourceImpl(SceneResourceImpl::ANIMATION)
76 {
77 NapiApi::FunctionContext<NapiApi::Object> fromJs(e, i);
78 NapiApi::Object meJs(e, fromJs.This());
79 NapiApi::Object scene = fromJs.Arg<0>(); // access to owning scene...
80 scene_ = { scene };
81 if (!GetNativeMeta<SCENE_NS::IScene>(scene_.GetObject())) {
82 CORE_LOG_F("INVALID SCENE!");
83 }
84
85 auto* tro = scene.Native<TrueRootObject>();
86 auto* sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
87 sceneJS->DisposeHook((uintptr_t)&scene_, meJs);
88 }
GetInstanceImpl(uint32_t id)89 void* AnimationJS::GetInstanceImpl(uint32_t id)
90 {
91 if (id == AnimationJS::ID) {
92 return this;
93 }
94 return SceneResourceImpl::GetInstanceImpl(id);
95 }
96
Finalize(napi_env env)97 void AnimationJS::Finalize(napi_env env)
98 {
99 DisposeNative();
100 BaseObject<AnimationJS>::Finalize(env);
101 }
~AnimationJS()102 AnimationJS::~AnimationJS()
103 {
104 LOG_F("AnimationJS -- ");
105 DisposeNative();
106 }
107
DisposeNative()108 void AnimationJS::DisposeNative()
109 {
110 // do nothing for now..
111 if (!disposed_) {
112 disposed_ = true;
113
114 LOG_F("AnimationJS::DisposeNative");
115 NapiApi::Object obj = scene_.GetObject();
116 auto* tro = obj.Native<TrueRootObject>();
117 SceneJS* sceneJS;
118 if (tro) {
119 sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
120 sceneJS->ReleaseDispose((uintptr_t)&scene_);
121 }
122 scene_.Reset();
123
124 // make sure we release postProc settings
125 if (auto animation = interface_pointer_cast<META_NS::IAnimation>(GetNativeObject())) {
126 // reset the native object refs
127 SetNativeObject(nullptr, false);
128 SetNativeObject(nullptr, true);
129 ExecSyncTask([this, anim = BASE_NS::move(animation)]() -> META_NS::IAny::Ptr {
130 // remove listeners.
131 if (OnStartedToken_) {
132 anim->OnStarted()->RemoveHandler(OnStartedToken_);
133 }
134 if (OnFinishedToken_) {
135 anim->OnFinished()->RemoveHandler(OnFinishedToken_);
136 }
137 return {};
138 });
139 if (OnStartedCB_) {
140 // does a delayed delete
141 OnStartedCB_->Release();
142 OnStartedCB_ = nullptr;
143 }
144 if (OnFinishedCB_) {
145 // does a delayed delete
146 OnFinishedCB_->Release();
147 OnFinishedCB_ = nullptr;
148 }
149 }
150 }
151 }
152
GetEnabled(NapiApi::FunctionContext<> & ctx)153 napi_value AnimationJS::GetEnabled(NapiApi::FunctionContext<>& ctx)
154 {
155 bool enabled { false };
156 if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
157 ExecSyncTask([a, &enabled]() {
158 if (a) {
159 enabled = a->Enabled()->GetValue();
160 }
161 return META_NS::IAny::Ptr {};
162 });
163 }
164
165 return ctx.GetBoolean(enabled);
166 }
SetEnabled(NapiApi::FunctionContext<bool> & ctx)167 void AnimationJS::SetEnabled(NapiApi::FunctionContext<bool>& ctx)
168 {
169 bool enabled = ctx.Arg<0>();
170 if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
171 ExecSyncTask([a, enabled]() {
172 a->Enabled()->SetValue(enabled);
173 return META_NS::IAny::Ptr {};
174 });
175 }
176 }
GetDuration(NapiApi::FunctionContext<> & ctx)177 napi_value AnimationJS::GetDuration(NapiApi::FunctionContext<>& ctx)
178 {
179 float duration = 0.0;
180 if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
181 ExecSyncTask([a, &duration]() {
182 if (a) {
183 duration = a->TotalDuration()->GetValue().ToSecondsFloat();
184 }
185 return META_NS::IAny::Ptr {};
186 });
187 }
188
189 return NapiApi::Value<float>(ctx, duration);
190 }
191
GetRunning(NapiApi::FunctionContext<> & ctx)192 napi_value AnimationJS::GetRunning(NapiApi::FunctionContext<>& ctx)
193 {
194 bool running { false };
195 if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
196 ExecSyncTask([a, &running]() {
197 if (a) {
198 running = a->Running()->GetValue();
199 }
200 return META_NS::IAny::Ptr {};
201 });
202 }
203
204 return ctx.GetBoolean(running);
205 }
GetProgress(NapiApi::FunctionContext<> & ctx)206 napi_value AnimationJS::GetProgress(NapiApi::FunctionContext<>& ctx)
207 {
208 float progress = 0.0;
209 if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
210 ExecSyncTask([a, &progress]() {
211 if (a) {
212 progress = a->Progress()->GetValue();
213 }
214 return META_NS::IAny::Ptr {};
215 });
216 }
217
218 return NapiApi::Value<float>(ctx, progress);
219 }
220
OnFinished(NapiApi::FunctionContext<NapiApi::Function> & ctx)221 napi_value AnimationJS::OnFinished(NapiApi::FunctionContext<NapiApi::Function>& ctx)
222 {
223 auto func = ctx.Arg<0>();
224 // do we have existing callback?
225 if (OnFinishedCB_) {
226 // stop listening ...
227 if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
228 ExecSyncTask([this, a]() -> META_NS::IAny::Ptr {
229 a->OnFinished()->RemoveHandler(OnFinishedToken_);
230 OnFinishedToken_ = 0;
231 return {};
232 });
233 }
234 // ... and release it
235 OnFinishedCB_->Release();
236 }
237 // do we have a new callback?
238 if (func) {
239 // create handler...
240 OnFinishedCB_ = new OnCallJS("OnFinished", ctx.This(), func);
241 // ... and start listening
242 if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
243 ExecSyncTask([this, a]() -> META_NS::IAny::Ptr {
244 OnFinishedToken_ = a->OnFinished()->AddHandler(
245 META_NS::MakeCallback<META_NS::IOnChanged>(OnFinishedCB_, &OnCallJS::Trigger));
246 return {};
247 });
248 }
249 }
250 return ctx.GetUndefined();
251 }
252
OnStarted(NapiApi::FunctionContext<NapiApi::Function> & ctx)253 napi_value AnimationJS::OnStarted(NapiApi::FunctionContext<NapiApi::Function>& ctx)
254 {
255 auto func = ctx.Arg<0>();
256 // do we have existing callback?
257 if (OnStartedCB_) {
258 // stop listening ...
259 if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
260 ExecSyncTask([this, a]() -> META_NS::IAny::Ptr {
261 a->OnStarted()->RemoveHandler(OnStartedToken_);
262 OnStartedToken_ = 0;
263 return {};
264 });
265 }
266 // ... and release it
267 OnStartedCB_->Release();
268 }
269 // do we have a new callback?
270 if (func) {
271 // create handler...
272 OnStartedCB_ = new OnCallJS("OnStart", ctx.This(), func);
273 // ... and start listening
274 if (auto a = interface_cast<META_NS::IAnimation>(GetNativeObject())) {
275 ExecSyncTask([this, a]() -> META_NS::IAny::Ptr {
276 OnStartedToken_ = a->OnStarted()->AddHandler(
277 META_NS::MakeCallback<META_NS::IOnChanged>(OnStartedCB_, &OnCallJS::Trigger));
278 return {};
279 });
280 }
281 }
282 return ctx.GetUndefined();
283 }
284
Pause(NapiApi::FunctionContext<> & ctx)285 napi_value AnimationJS::Pause(NapiApi::FunctionContext<>& ctx)
286 {
287 if (auto a = interface_cast<META_NS::IStartableAnimation>(GetNativeObject())) {
288 ExecSyncTask([a]() {
289 a->Pause();
290 return META_NS::IAny::Ptr {};
291 });
292 }
293 return ctx.GetUndefined();
294 }
Restart(NapiApi::FunctionContext<> & ctx)295 napi_value AnimationJS::Restart(NapiApi::FunctionContext<>& ctx)
296 {
297 if (auto a = interface_cast<META_NS::IStartableAnimation>(GetNativeObject())) {
298 ExecSyncTask([a]() {
299 a->Restart();
300 return META_NS::IAny::Ptr {};
301 });
302 }
303 return ctx.GetUndefined();
304 }
Seek(NapiApi::FunctionContext<float> & ctx)305 napi_value AnimationJS::Seek(NapiApi::FunctionContext<float>& ctx)
306 {
307 float pos = ctx.Arg<0>();
308 if (auto a = interface_cast<META_NS::IStartableAnimation>(GetNativeObject())) {
309 ExecSyncTask([a, pos]() {
310 a->Seek(pos);
311 return META_NS::IAny::Ptr {};
312 });
313 }
314 return ctx.GetUndefined();
315 }
Start(NapiApi::FunctionContext<> & ctx)316 napi_value AnimationJS::Start(NapiApi::FunctionContext<>& ctx)
317 {
318 if (auto a = interface_cast<META_NS::IStartableAnimation>(GetNativeObject())) {
319 ExecSyncTask([a]() {
320 a->Start();
321 return META_NS::IAny::Ptr {};
322 });
323 }
324 return ctx.GetUndefined();
325 }
326
Stop(NapiApi::FunctionContext<> & ctx)327 napi_value AnimationJS::Stop(NapiApi::FunctionContext<>& ctx)
328 {
329 if (auto a = interface_cast<META_NS::IStartableAnimation>(GetNativeObject())) {
330 ExecSyncTask([a]() {
331 a->Stop();
332 return META_NS::IAny::Ptr {};
333 });
334 }
335 return ctx.GetUndefined();
336 }
Finish(NapiApi::FunctionContext<> & ctx)337 napi_value AnimationJS::Finish(NapiApi::FunctionContext<>& ctx)
338 {
339 if (auto a = interface_cast<META_NS::IStartableAnimation>(GetNativeObject())) {
340 ExecSyncTask([a]() {
341 a->Finish();
342 return META_NS::IAny::Ptr {};
343 });
344 }
345 return ctx.GetUndefined();
346 }