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 22class OnCallJS : public ThreadSafeCallback { 23 NapiApi::StrongRef jsThis_; 24 NapiApi::StrongRef ref_; 25 26public: 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 } 32 ~OnCallJS() 33 { 34 jsThis_.Reset(); 35 ref_.Reset(); 36 Release(); 37 } 38 void Finalize(napi_env env) 39 { 40 jsThis_.Reset(); 41 ref_.Reset(); 42 } 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 50void 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 74AnimationJS::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} 89void* AnimationJS::GetInstanceImpl(uint32_t id) 90{ 91 if (id == AnimationJS::ID) { 92 return this; 93 } 94 return SceneResourceImpl::GetInstanceImpl(id); 95} 96 97void AnimationJS::Finalize(napi_env env) 98{ 99 DisposeNative(); 100 BaseObject<AnimationJS>::Finalize(env); 101} 102AnimationJS::~AnimationJS() 103{ 104 LOG_F("AnimationJS -- "); 105 DisposeNative(); 106} 107 108void 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 153napi_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} 167void 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} 177napi_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 192napi_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} 206napi_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 221napi_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 253napi_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 285napi_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} 295napi_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} 305napi_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} 316napi_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 327napi_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} 337napi_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}