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 }