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