| /* |
| * Copyright 2025 Rive |
| */ |
| |
| #include "rive/command_server.hpp" |
| |
| #include "rive/animation/state_machine_instance.hpp" |
| #include "rive/assets/audio_asset.hpp" |
| #include "rive/assets/blob_asset.hpp" |
| #include "rive/assets/font_asset.hpp" |
| #include "rive/assets/image_asset.hpp" |
| #include "rive/assets/manifest_asset.hpp" |
| #include "rive/assets/script_module_asset.hpp" |
| #include "rive/assets/text_asset.hpp" |
| #include "rive/file.hpp" |
| #include "rive/semantic/semantic_manager.hpp" |
| #include "rive/viewmodel/runtime/viewmodel_runtime.hpp" |
| #ifdef WITH_RIVE_SCRIPTING_LUAU |
| #include "rive/lua/rive_lua_libs.hpp" |
| #endif |
| |
| namespace rive |
| { |
| |
| /** |
| * Stores registered resource handles and matching assets in loaded files for |
| * one file asset type. |
| * |
| * Entries are keyed by unique asset name. Each entry contains the one resource |
| * handle registered for that name and a map of the loaded files containing a |
| * matching out-of-band file asset. Registering a resource and applying it to |
| * those file assets are intentionally separate operations. |
| */ |
| template <typename TResourceHandle, typename TFileAsset> |
| class TypedGlobalAssetRegistry |
| { |
| public: |
| /** Adds a matching file asset after its file imports successfully. */ |
| void addFileAsset(FileHandle fileHandle, TFileAsset* asset) |
| { |
| m_entries[asset->uniqueName()].assetsByFileHandle[fileHandle] = |
| ref_rcp(asset); |
| } |
| |
| /** Removes a file asset when its containing file is deleted. */ |
| void removeFileAsset(FileHandle fileHandle, const std::string& name) |
| { |
| auto itr = m_entries.find(name); |
| if (itr == m_entries.end()) |
| { |
| return; |
| } |
| itr->second.assetsByFileHandle.erase(fileHandle); |
| eraseIfUnused(itr); |
| } |
| |
| /** Returns the resource registered for a name, or the null handle. */ |
| TResourceHandle getResourceHandle(const std::string& name) const |
| { |
| auto itr = m_entries.find(name); |
| return itr == m_entries.end() ? RIVE_NULL_HANDLE |
| : itr->second.resourceHandle; |
| } |
| |
| /** Sets the resource for a name without applying it to file assets. */ |
| void setResource(const std::string& name, TResourceHandle handle) |
| { |
| m_entries[name].resourceHandle = handle; |
| } |
| |
| /** Unregisters the resource for a name without clearing file assets. */ |
| void unregisterResourceByName(const std::string& name) |
| { |
| auto itr = m_entries.find(name); |
| if (itr == m_entries.end()) |
| { |
| return; |
| } |
| itr->second.resourceHandle = RIVE_NULL_HANDLE; |
| eraseIfUnused(itr); |
| } |
| |
| /** |
| * Unregisters every name backed by a resource handle. |
| * |
| * Returns the names that still have loaded file assets and therefore need |
| * to have the null resource applied separately. |
| */ |
| std::vector<std::string> unregisterResourceByHandle(TResourceHandle handle) |
| { |
| std::vector<std::string> names; |
| for (auto itr = m_entries.begin(); itr != m_entries.end();) |
| { |
| if (itr->second.resourceHandle != handle) |
| { |
| ++itr; |
| continue; |
| } |
| |
| itr->second.resourceHandle = RIVE_NULL_HANDLE; |
| if (itr->second.empty()) |
| { |
| itr = m_entries.erase(itr); |
| } |
| else |
| { |
| names.push_back(itr->first); |
| ++itr; |
| } |
| } |
| return names; |
| } |
| |
| /** Applies an operation to every loaded file asset matching a name. */ |
| template <typename Apply> |
| void applyToFileAssets(const std::string& name, Apply&& apply) const |
| { |
| auto itr = m_entries.find(name); |
| if (itr == m_entries.end()) |
| { |
| return; |
| } |
| |
| for (const auto& fileAsset : itr->second.assetsByFileHandle) |
| { |
| apply(*fileAsset.second); |
| } |
| } |
| |
| private: |
| struct Entry |
| { |
| TResourceHandle resourceHandle = RIVE_NULL_HANDLE; |
| std::unordered_map<FileHandle, rcp<TFileAsset>> assetsByFileHandle; |
| |
| bool empty() const |
| { |
| return resourceHandle == RIVE_NULL_HANDLE && |
| assetsByFileHandle.empty(); |
| } |
| }; |
| |
| using Entries = std::unordered_map<std::string, Entry>; |
| |
| void eraseIfUnused(typename Entries::iterator itr) |
| { |
| if (itr->second.empty()) |
| { |
| m_entries.erase(itr); |
| } |
| } |
| |
| Entries m_entries; |
| }; |
| |
| /** |
| * Maintains global resource registrations and the loaded out-of-band file |
| * assets that consume them. |
| * |
| * One typed registry is maintained for images, audio sources, and fonts. This |
| * class adds and removes file assets, registers and unregisters resource |
| * handles, resolves them from read-only references to CommandServer's resource |
| * maps, and applies the resources to matching loaded file assets. |
| * |
| * It does not own decoded resources. CommandServer owns those resources. |
| * |
| * Resource registration and application remain separate so callers control |
| * when each operation occurs. |
| */ |
| class GlobalAssetRegistry : public RefCnt<GlobalAssetRegistry> |
| { |
| public: |
| GlobalAssetRegistry( |
| const std::unordered_map<RenderImageHandle, rcp<RenderImage>>& images, |
| const std::unordered_map<AudioSourceHandle, rcp<AudioSource>>& |
| audioSources, |
| const std::unordered_map<FontHandle, rcp<Font>>& fonts) : |
| m_images(images), m_audioSources(audioSources), m_fonts(fonts) |
| {} |
| |
| /** Adds successfully imported out-of-band file assets to the registry. */ |
| void addFileAssets(FileHandle fileHandle, Span<const rcp<FileAsset>> assets) |
| { |
| for (const auto& asset : assets) |
| { |
| if (asset->is<ImageAsset>()) |
| { |
| m_imageRegistry.addFileAsset(fileHandle, |
| asset->as<ImageAsset>()); |
| } |
| else if (asset->is<AudioAsset>()) |
| { |
| m_audioRegistry.addFileAsset(fileHandle, |
| asset->as<AudioAsset>()); |
| } |
| else if (asset->is<FontAsset>()) |
| { |
| m_fontRegistry.addFileAsset(fileHandle, asset->as<FontAsset>()); |
| } |
| } |
| } |
| |
| /** Applies registered resources to newly imported file assets. */ |
| void applyResourcesToFileAssets(Span<const rcp<FileAsset>> fileAssets) const |
| { |
| for (const auto& asset : fileAssets) |
| { |
| if (asset->is<ImageAsset>()) |
| { |
| auto handle = renderImageHandle(asset->uniqueName()); |
| asset->as<ImageAsset>()->renderImage( |
| getResource(m_images, handle)); |
| } |
| else if (asset->is<AudioAsset>()) |
| { |
| auto handle = audioSourceHandle(asset->uniqueName()); |
| asset->as<AudioAsset>()->audioSource( |
| getResource(m_audioSources, handle)); |
| } |
| else if (asset->is<FontAsset>()) |
| { |
| auto handle = fontHandle(asset->uniqueName()); |
| asset->as<FontAsset>()->font(getResource(m_fonts, handle)); |
| } |
| } |
| } |
| |
| RenderImageHandle renderImageHandle(const std::string& name) const |
| { |
| return m_imageRegistry.getResourceHandle(name); |
| } |
| |
| AudioSourceHandle audioSourceHandle(const std::string& name) const |
| { |
| return m_audioRegistry.getResourceHandle(name); |
| } |
| |
| FontHandle fontHandle(const std::string& name) const |
| { |
| return m_fontRegistry.getResourceHandle(name); |
| } |
| |
| /** Sets the resource for a name. The caller applies it separately. */ |
| void setRenderImage(const std::string& name, RenderImageHandle handle) |
| { |
| m_imageRegistry.setResource(name, handle); |
| } |
| |
| void setAudioSource(const std::string& name, AudioSourceHandle handle) |
| { |
| m_audioRegistry.setResource(name, handle); |
| } |
| |
| void setFont(const std::string& name, FontHandle handle) |
| { |
| m_fontRegistry.setResource(name, handle); |
| } |
| |
| /** |
| * Unregisters the resource for a name. The caller applies the null resource |
| * separately. |
| */ |
| void removeRenderImage(const std::string& name) |
| { |
| m_imageRegistry.unregisterResourceByName(name); |
| } |
| |
| void removeAudioSource(const std::string& name) |
| { |
| m_audioRegistry.unregisterResourceByName(name); |
| } |
| |
| void removeFont(const std::string& name) |
| { |
| m_fontRegistry.unregisterResourceByName(name); |
| } |
| |
| void applyRenderImage(const std::string& name, |
| const rcp<RenderImage>& image) const |
| { |
| m_imageRegistry.applyToFileAssets(name, [&image](ImageAsset& asset) { |
| asset.renderImage(image); |
| }); |
| } |
| |
| void applyAudioSource(const std::string& name, |
| const rcp<AudioSource>& audioSource) const |
| { |
| m_audioRegistry.applyToFileAssets(name, |
| [&audioSource](AudioAsset& asset) { |
| asset.audioSource(audioSource); |
| }); |
| } |
| |
| void applyFont(const std::string& name, const rcp<Font>& font) const |
| { |
| m_fontRegistry.applyToFileAssets(name, [&font](FontAsset& asset) { |
| asset.font(font); |
| }); |
| } |
| |
| /** |
| * Unregisters every name backed by a deleted resource handle. The caller |
| * applies each returned name separately. |
| */ |
| std::vector<std::string> removeRenderImage(RenderImageHandle handle) |
| { |
| return m_imageRegistry.unregisterResourceByHandle(handle); |
| } |
| |
| std::vector<std::string> removeAudioSource(AudioSourceHandle handle) |
| { |
| return m_audioRegistry.unregisterResourceByHandle(handle); |
| } |
| |
| std::vector<std::string> removeFont(FontHandle handle) |
| { |
| return m_fontRegistry.unregisterResourceByHandle(handle); |
| } |
| |
| /** |
| * Removes a deleted file from each registry entry named by an asset in that |
| * loaded Rive file. No file-to-assets reverse registry is needed. |
| */ |
| void removeFileAssets(FileHandle fileHandle, |
| Span<const rcp<FileAsset>> assets) |
| { |
| for (const auto& asset : assets) |
| { |
| if (asset->is<ImageAsset>()) |
| { |
| m_imageRegistry.removeFileAsset(fileHandle, |
| asset->uniqueName()); |
| } |
| else if (asset->is<AudioAsset>()) |
| { |
| m_audioRegistry.removeFileAsset(fileHandle, |
| asset->uniqueName()); |
| } |
| else if (asset->is<FontAsset>()) |
| { |
| m_fontRegistry.removeFileAsset(fileHandle, asset->uniqueName()); |
| } |
| } |
| } |
| |
| #ifdef TESTING |
| RenderImageHandle testing_imageNamed(const std::string& name) |
| { |
| return renderImageHandle(name); |
| } |
| |
| AudioSourceHandle testing_audioNamed(const std::string& name) |
| { |
| return audioSourceHandle(name); |
| } |
| |
| FontHandle testing_fontNamed(const std::string& name) |
| { |
| return fontHandle(name); |
| } |
| #endif |
| |
| private: |
| template <typename TResourceHandle, typename TResource> |
| static rcp<TResource> getResource( |
| const std::unordered_map<TResourceHandle, rcp<TResource>>& resources, |
| TResourceHandle handle) |
| { |
| auto itr = resources.find(handle); |
| return itr == resources.end() ? nullptr : itr->second; |
| } |
| |
| const std::unordered_map<RenderImageHandle, rcp<RenderImage>>& m_images; |
| const std::unordered_map<AudioSourceHandle, rcp<AudioSource>>& |
| m_audioSources; |
| const std::unordered_map<FontHandle, rcp<Font>>& m_fonts; |
| TypedGlobalAssetRegistry<RenderImageHandle, ImageAsset> m_imageRegistry; |
| TypedGlobalAssetRegistry<AudioSourceHandle, AudioAsset> m_audioRegistry; |
| TypedGlobalAssetRegistry<FontHandle, FontAsset> m_fontRegistry; |
| }; |
| |
| /** |
| * The file asset loader for one command-queue import. |
| * |
| * It first delegates to the optional FileAssetLoader supplied to CommandServer. |
| * When that loader returns true, it is loading or has loaded the asset outside |
| * the command queue, so the asset is not registered for a global resource. |
| * Otherwise, eligible out-of-band assets are retained locally so they can be |
| * registered after a successful import. |
| */ |
| class CommandServer::CommandFileAssetLoader : public FileAssetLoader |
| { |
| public: |
| CommandFileAssetLoader(rcp<FileAssetLoader> internalLoader) : |
| m_internalLoader(std::move(internalLoader)) |
| {} |
| |
| bool loadContents(FileAsset& asset, |
| Span<const uint8_t> inBandBytes, |
| Factory* factory) override |
| { |
| if (m_internalLoader && |
| m_internalLoader->loadContents(asset, inBandBytes, factory)) |
| { |
| return true; |
| } |
| if (!inBandBytes.empty()) |
| { |
| return false; |
| } |
| if (asset.is<ImageAsset>() || asset.is<AudioAsset>() || |
| asset.is<FontAsset>()) |
| { |
| m_fileAssets.push_back(ref_rcp(&asset)); |
| return false; |
| } |
| if (asset.is<TextAsset>() || asset.is<ScriptModuleAsset>() || |
| asset.is<BlobAsset>() || asset.is<ManifestAsset>()) |
| { |
| // These assets cannot be registered externally with the command |
| // server. Returning false lets the importer decode their in-band |
| // contents. TextAsset includes ScriptAsset and ShaderAsset. |
| return false; |
| } |
| fprintf(stderr, |
| "ERROR: CommandFileAssetLoader::loadContents - Unsupported" |
| " asset type for asset: '%s'\n", |
| asset.uniqueFilename().c_str()); |
| return false; |
| } |
| |
| Span<const rcp<FileAsset>> fileAssets() const |
| { |
| return {m_fileAssets.data(), m_fileAssets.size()}; |
| } |
| |
| private: |
| rcp<FileAssetLoader> m_internalLoader; |
| std::vector<rcp<FileAsset>> m_fileAssets; |
| }; |
| |
| std::ostream& operator<<(std::ostream& os, DataType t) |
| { |
| switch (t) |
| { |
| case DataType::none: |
| os << "None"; |
| break; |
| case DataType::string: |
| os << "String"; |
| break; |
| case DataType::number: |
| os << "Number"; |
| break; |
| case DataType::boolean: |
| os << "Boolean"; |
| break; |
| case DataType::color: |
| os << "Color"; |
| break; |
| case DataType::list: |
| os << "List"; |
| break; |
| case DataType::enumType: |
| os << "Enum"; |
| break; |
| case DataType::trigger: |
| os << "Trigger"; |
| break; |
| case DataType::viewModel: |
| os << "View Model"; |
| break; |
| case DataType::integer: |
| os << "Integer"; |
| break; |
| case DataType::symbolListIndex: |
| os << "Symbol List Index"; |
| break; |
| case DataType::assetImage: |
| os << "Asset Image"; |
| break; |
| case DataType::assetBlob: |
| os << "Asset Blob"; |
| break; |
| default: |
| os << "Unknown DataType"; |
| break; |
| } |
| return os; |
| } |
| |
| #ifdef TESTING |
| |
| RenderImageHandle CommandServer::testing_globalImageNamed(std::string name) |
| { |
| return m_globalAssetRegistry->testing_imageNamed(name); |
| } |
| |
| AudioSourceHandle CommandServer::testing_globalAudioNamed(std::string name) |
| { |
| return m_globalAssetRegistry->testing_audioNamed(name); |
| } |
| |
| FontHandle CommandServer::testing_globalFontNamed(std::string name) |
| { |
| return m_globalAssetRegistry->testing_fontNamed(name); |
| } |
| |
| bool CommandServer::testing_globalImageContains(std::string name) |
| { |
| return m_globalAssetRegistry->testing_imageNamed(name) != nullptr; |
| } |
| |
| bool CommandServer::testing_globalAudioContains(std::string name) |
| { |
| return m_globalAssetRegistry->testing_audioNamed(name) != nullptr; |
| } |
| |
| bool CommandServer::testing_globalFontContains(std::string name) |
| { |
| return m_globalAssetRegistry->testing_fontNamed(name) != nullptr; |
| } |
| |
| #endif |
| |
| CommandServer::CommandServer( |
| rcp<CommandQueue> commandBuffer, |
| Factory* factory, |
| rcp<rive::FileAssetLoader> internalFileAssetLoader) : |
| m_commandQueue(std::move(commandBuffer)), |
| m_factory(factory), |
| #ifndef NDEBUG |
| m_threadID(std::this_thread::get_id()), |
| #endif |
| m_globalAssetRegistry( |
| make_rcp<GlobalAssetRegistry>(m_images, m_audioSources, m_fonts)), |
| m_internalFileAssetLoader(std::move(internalFileAssetLoader)) |
| {} |
| |
| CommandServer::~CommandServer() {} |
| |
| rive::HitResult CommandServer::pointerDownSynchronized( |
| StateMachineHandle handle, |
| const CommandQueue::PointerEvent& pointerEvent) |
| { |
| std::unique_lock<std::mutex> accesLock(m_stateMachineAccessMutex); |
| auto it = m_stateMachines.find(handle); |
| if (it != m_stateMachines.end()) |
| { |
| auto stateMachine = it->second; |
| accesLock.unlock(); |
| auto pos = cursorPosForPointerEvent(stateMachine->instance.get(), |
| pointerEvent); |
| { |
| std::unique_lock<std::mutex> lock(stateMachine->m_mutex); |
| auto hitResult = |
| stateMachine->instance->pointerDown(pos, |
| pointerEvent.pointerId); |
| // Process down-driven state changes before a following pointer up. |
| if (hitResult != HitResult::none) |
| { |
| stateMachine->instance->advanceAndApply(0.0f); |
| } |
| return hitResult; |
| } |
| } |
| |
| return rive::HitResult::none; |
| } |
| |
| rive::HitResult CommandServer::pointerMoveSynchronized( |
| StateMachineHandle handle, |
| const CommandQueue::PointerEvent& pointerEvent) |
| { |
| std::unique_lock<std::mutex> accesLock(m_stateMachineAccessMutex); |
| auto it = m_stateMachines.find(handle); |
| if (it != m_stateMachines.end()) |
| { |
| auto stateMachine = it->second; |
| accesLock.unlock(); |
| auto pos = cursorPosForPointerEvent(stateMachine->instance.get(), |
| pointerEvent); |
| { |
| std::unique_lock<std::mutex> lock(stateMachine->m_mutex); |
| return stateMachine->instance->pointerMove(pos, |
| 0, |
| pointerEvent.pointerId); |
| } |
| } |
| |
| return rive::HitResult::none; |
| } |
| |
| rive::HitResult CommandServer::pointerUpSynchronized( |
| StateMachineHandle handle, |
| const CommandQueue::PointerEvent& pointerEvent) |
| { |
| std::unique_lock<std::mutex> accesLock(m_stateMachineAccessMutex); |
| auto it = m_stateMachines.find(handle); |
| if (it != m_stateMachines.end()) |
| { |
| auto stateMachine = it->second; |
| accesLock.unlock(); |
| auto pos = cursorPosForPointerEvent(stateMachine->instance.get(), |
| pointerEvent); |
| { |
| std::unique_lock<std::mutex> lock(stateMachine->m_mutex); |
| auto hitResult = |
| stateMachine->instance->pointerUp(pos, pointerEvent.pointerId); |
| // Process up-driven state changes before any following pointer |
| // exit. |
| if (hitResult != HitResult::none) |
| { |
| stateMachine->instance->advanceAndApply(0.0f); |
| } |
| return hitResult; |
| } |
| } |
| |
| return rive::HitResult::none; |
| } |
| |
| File* CommandServer::getFile(FileHandle handle) const |
| { |
| assert(std::this_thread::get_id() == m_threadID); |
| auto it = m_files.find(handle); |
| return it != m_files.end() ? it->second.get() : nullptr; |
| } |
| |
| RenderImage* CommandServer::getImage(RenderImageHandle handle) const |
| { |
| assert(std::this_thread::get_id() == m_threadID); |
| auto it = m_images.find(handle); |
| return it != m_images.end() ? it->second.get() : nullptr; |
| } |
| |
| AudioSource* CommandServer::getAudioSource(AudioSourceHandle handle) const |
| { |
| assert(std::this_thread::get_id() == m_threadID); |
| auto it = m_audioSources.find(handle); |
| return it != m_audioSources.end() ? it->second.get() : nullptr; |
| } |
| |
| Font* CommandServer::getFont(FontHandle handle) const |
| { |
| assert(std::this_thread::get_id() == m_threadID); |
| auto it = m_fonts.find(handle); |
| return it != m_fonts.end() ? it->second.get() : nullptr; |
| } |
| |
| BlobAsset* CommandServer::getBlob(BlobAssetHandle handle) const |
| { |
| assert(std::this_thread::get_id() == m_threadID); |
| auto it = m_blobs.find(handle); |
| return it != m_blobs.end() ? it->second.get() : nullptr; |
| } |
| |
| ArtboardInstance* CommandServer::getArtboardInstance( |
| ArtboardHandle handle) const |
| { |
| assert(std::this_thread::get_id() == m_threadID); |
| auto it = m_artboards.find(handle); |
| return it != m_artboards.end() ? it->second.get()->artboard() : nullptr; |
| } |
| |
| rcp<BindableArtboard> CommandServer::getBindableArtboard( |
| ArtboardHandle handle) const |
| { |
| assert(std::this_thread::get_id() == m_threadID); |
| auto it = m_artboards.find(handle); |
| return it != m_artboards.end() ? it->second : nullptr; |
| } |
| |
| rcp<CommandServer::SynchronizedStateMachine> CommandServer:: |
| getStateMachineWrapper(StateMachineHandle handle) |
| { |
| assert(std::this_thread::get_id() == m_threadID); |
| auto it = m_stateMachines.find(handle); |
| return it != m_stateMachines.end() ? it->second : nullptr; |
| } |
| |
| StateMachineInstance* CommandServer::getStateMachineInstance( |
| StateMachineHandle handle) const |
| { |
| assert(std::this_thread::get_id() == m_threadID); |
| auto it = m_stateMachines.find(handle); |
| return it != m_stateMachines.end() ? it->second->instance.get() : nullptr; |
| } |
| |
| ViewModelInstanceRuntime* CommandServer::getViewModelInstance( |
| ViewModelInstanceHandle handle) const |
| { |
| assert(std::this_thread::get_id() == m_threadID); |
| auto it = m_viewModels.find(handle); |
| return it != m_viewModels.end() ? it->second.get() : nullptr; |
| } |
| |
| ViewModelInstanceHandle CommandServer::getHandleForInstance( |
| ViewModelInstanceRuntime* handle) const |
| { |
| assert(std::this_thread::get_id() == m_threadID); |
| for (auto& itr : m_viewModels) |
| { |
| if (itr.second.get() == handle) |
| { |
| return itr.first; |
| } |
| } |
| return RIVE_NULL_HANDLE; |
| } |
| |
| Vec2D CommandServer::cursorPosForPointerEvent( |
| StateMachineInstance* instance, |
| const CommandQueue::PointerEvent& pointerEvent) |
| { |
| AABB artboardBounds = instance->artboard()->bounds(); |
| AABB surfaceBounds = AABB(Vec2D{0.0f, 0.0f}, pointerEvent.screenBounds); |
| |
| if (surfaceBounds == artboardBounds || surfaceBounds.width() == 0.0f || |
| surfaceBounds.height() == 0.0f) |
| { |
| return pointerEvent.position; |
| } |
| |
| Mat2D forward = rive::computeAlignment(pointerEvent.fit, |
| pointerEvent.alignment, |
| surfaceBounds, |
| artboardBounds, |
| pointerEvent.scaleFactor); |
| |
| Mat2D inverse = forward.invertOrIdentity(); |
| |
| return inverse * pointerEvent.position; |
| } |
| |
| // Maps a semantics bounds payload from artboard space to view space. |
| // Templated so the same transform path can be reused for both |
| // SemanticsDiffNode and SemanticsBoundsUpdate via bounds()/setBounds(). |
| template <typename T> |
| static void mapSemanticsBounds(T& value, const Mat2D& transform) |
| { |
| value.setBounds(transform.mapBoundingBox(value.bounds())); |
| } |
| |
| // Applies artboard->view bounds mapping to all geometry-bearing collections |
| // in a semantic diff before it is delivered to listeners. |
| static void mapSemanticsDiffToViewSpace(SemanticsDiff& diff, |
| const Mat2D& transform) |
| { |
| for (auto& node : diff.added) |
| { |
| mapSemanticsBounds(node, transform); |
| } |
| for (auto& node : diff.moved) |
| { |
| mapSemanticsBounds(node, transform); |
| } |
| for (auto& node : diff.updatedSemantic) |
| { |
| mapSemanticsBounds(node, transform); |
| } |
| for (auto& bounds : diff.updatedGeometry) |
| { |
| mapSemanticsBounds(bounds, transform); |
| } |
| } |
| |
| void CommandServer::checkPropertySubscriptions() |
| { |
| for (auto& subscription : m_propertySubscriptions) |
| { |
| uint64_t requestId = subscription.requestId; |
| ViewModelInstanceHandle handle = subscription.rootViewModel; |
| CommandQueue::ViewModelInstanceData data; |
| data.metaData = subscription.data; |
| |
| if (auto viewModel = getViewModelInstance(handle)) |
| { |
| if (auto property = viewModel->property(data.metaData.name)) |
| { |
| if (property->hasChanged()) |
| { |
| property->clearChanges(); |
| switch (data.metaData.type) |
| { |
| // These don't have values but are still valid |
| // subscriptions. |
| case DataType::assetImage: |
| case DataType::assetBlob: |
| case DataType::trigger: |
| case DataType::list: |
| break; |
| case DataType::boolean: |
| if (auto value = static_cast< |
| ViewModelInstanceBooleanRuntime*>(property)) |
| { |
| data.boolValue = value->value(); |
| } |
| break; |
| |
| case DataType::color: |
| if (auto value = |
| static_cast<ViewModelInstanceColorRuntime*>( |
| property)) |
| { |
| data.colorValue = value->value(); |
| } |
| break; |
| |
| case DataType::number: |
| if (auto value = static_cast< |
| ViewModelInstanceNumberRuntime*>(property)) |
| { |
| data.numberValue = value->value(); |
| } |
| break; |
| |
| case DataType::enumType: |
| if (auto value = |
| static_cast<ViewModelInstanceEnumRuntime*>( |
| property)) |
| { |
| data.stringValue = value->value(); |
| } |
| break; |
| |
| case DataType::string: |
| if (auto value = static_cast< |
| ViewModelInstanceStringRuntime*>(property)) |
| { |
| data.stringValue = value->value(); |
| } |
| break; |
| default: |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "ERROR : Invalid data type {" |
| << data.metaData.type << "} when checking" |
| << "subscriptions"; |
| continue; |
| } |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| m_commandQueue->m_messageStream << CommandQueue::Message:: |
| viewModelPropertyValueReceived; |
| m_commandQueue->m_messageStream << handle; |
| m_commandQueue->m_messageStream << data.metaData.type; |
| m_commandQueue->m_messageNames << data.metaData.name; |
| m_commandQueue->m_messageStream << requestId; |
| switch (data.metaData.type) |
| { |
| case DataType::assetImage: |
| case DataType::assetBlob: |
| case DataType::trigger: |
| case DataType::list: |
| break; |
| case DataType::boolean: |
| m_commandQueue->m_messageStream << data.boolValue; |
| break; |
| case DataType::number: |
| m_commandQueue->m_messageStream << data.numberValue; |
| break; |
| case DataType::color: |
| m_commandQueue->m_messageStream << data.colorValue; |
| break; |
| case DataType::enumType: |
| case DataType::string: |
| m_commandQueue->m_messageNames << data.stringValue; |
| break; |
| default: |
| RIVE_UNREACHABLE(); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void CommandServer::serveUntilDisconnect() |
| { |
| while (waitCommands()) |
| { |
| } |
| } |
| |
| bool CommandServer::waitCommands() |
| { |
| PODStream& commandStream = m_commandQueue->m_commandStream; |
| if (commandStream.empty()) |
| { |
| std::unique_lock<std::mutex> lock(m_commandQueue->m_commandMutex); |
| while (commandStream.empty()) |
| { |
| assert(m_commandQueue->m_callbacks.empty()); |
| assert(m_commandQueue->m_byteVectors.empty()); |
| assert(m_commandQueue->m_names.empty()); |
| m_commandQueue->m_commandConditionVariable.wait(lock); |
| } |
| } |
| |
| return processCommands(); |
| } |
| |
| bool CommandServer::processCommands() |
| { |
| assert(m_wasDisconnectReceived == false); |
| assert(std::this_thread::get_id() == m_threadID); |
| |
| PODStream& commandStream = m_commandQueue->m_commandStream; |
| PODStream& messageStream = m_commandQueue->m_messageStream; |
| std::unique_lock<std::mutex> lock(m_commandQueue->m_commandMutex); |
| |
| // Early out if we don't have anything to process. |
| if (commandStream.empty()) |
| return !m_wasDisconnectReceived; |
| |
| // Ensure we stop processing messages and get to the draw loop. |
| // This avoids a race condition where we never stop processing messages and |
| // therefore never draw anything. |
| commandStream << CommandQueue::Command::commandLoopBreak; |
| |
| // The map should be empty at this point. |
| assert(m_uniqueDraws.empty()); |
| |
| bool shouldProcessCommands = true; |
| do |
| { |
| CommandQueue::Command command; |
| commandStream >> command; |
| |
| switch (command) |
| { |
| case CommandQueue::Command::loadFile: |
| { |
| FileHandle handle; |
| uint64_t requestId; |
| std::vector<uint8_t> rivBytes; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_byteVectors >> rivBytes; |
| #ifdef WITH_RIVE_SCRIPTING |
| ScriptingContextFactory scriptingContextFactory; |
| m_commandQueue->m_scriptingContextFactories >> |
| scriptingContextFactory; |
| #endif |
| lock.unlock(); |
| auto fileAssetLoader = |
| make_rcp<CommandFileAssetLoader>(m_internalFileAssetLoader); |
| |
| #if defined(WITH_RIVE_SCRIPTING) && defined(WITH_RIVE_SCRIPTING_LUAU) |
| std::cout << "Rive: Command Server Scripting Enabled.\n"; |
| // Use the host-provided scripting context when supplied (e.g. |
| // Unreal routes console/error output to UE_LOG); otherwise fall |
| // back to the default CPP runtime context. |
| std::unique_ptr<ScriptingContext> scriptingContext = |
| scriptingContextFactory ? scriptingContextFactory(m_factory) |
| : nullptr; |
| if (scriptingContext == nullptr) |
| { |
| scriptingContext = |
| std::make_unique<CPPRuntimeScriptingContext>(m_factory); |
| } |
| auto vm = make_rcp<ScriptingVM>(std::move(scriptingContext)); |
| rcp<rive::File> file = rive::File::import(rivBytes, |
| m_factory, |
| nullptr, |
| fileAssetLoader, |
| vm.get()); |
| |
| #else |
| #ifdef WITH_RIVE_SCRIPTING |
| // Wasm script modules need no host-created VM; the factory |
| // only serves the Luau backend. |
| (void)scriptingContextFactory; |
| #endif |
| rcp<rive::File> file = rive::File::import(rivBytes, |
| m_factory, |
| nullptr, |
| fileAssetLoader); |
| #endif |
| |
| if (file != nullptr) |
| { |
| auto fileAssets = fileAssetLoader->fileAssets(); |
| m_globalAssetRegistry->addFileAssets(handle, fileAssets); |
| m_globalAssetRegistry->applyResourcesToFileAssets( |
| fileAssets); |
| m_fileDependencies[handle] = {}; |
| m_files[handle] = file; |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::fileLoaded; |
| messageStream << handle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<FileHandle>(this, |
| handle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "failed to load Rive file."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::deleteFile: |
| { |
| FileHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| auto fileItr = m_files.find(handle); |
| if (fileItr != m_files.end()) |
| { |
| m_globalAssetRegistry->removeFileAssets( |
| handle, |
| fileItr->second->assets()); |
| m_files.erase(fileItr); |
| } |
| auto itr = m_fileDependencies.find(handle); |
| if (itr != m_fileDependencies.end()) |
| { |
| auto& artboardVector = itr->second; |
| for (auto artboardHandle : artboardVector) |
| { |
| cleanupArtboard(artboardHandle, requestId); |
| } |
| |
| m_fileDependencies.erase(itr); |
| } |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::fileDeleted; |
| messageStream << handle; |
| messageStream << requestId; |
| break; |
| } |
| |
| case CommandQueue::Command::decodeImage: |
| { |
| RenderImageHandle handle; |
| uint64_t requestId; |
| std::vector<uint8_t> bytes; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_byteVectors >> bytes; |
| lock.unlock(); |
| |
| auto image = factory()->decodeImage(bytes); |
| if (image) |
| { |
| m_images[handle] = std::move(image); |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::imageDecoded; |
| messageStream << handle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<RenderImageHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::imageError) |
| << "Command Server failed to decode image"; |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::externalImage: |
| { |
| RenderImageHandle handle; |
| uint64_t requestId; |
| rcp<RenderImage> image; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_externalImages >> image; |
| lock.unlock(); |
| |
| if (image) |
| { |
| m_images[handle] = std::move(image); |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::imageDecoded; |
| messageStream << handle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<RenderImageHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::imageError) |
| << "External image was empty"; |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::deleteImage: |
| { |
| RenderImageHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| m_images.erase(handle); |
| for (const auto& name : |
| m_globalAssetRegistry->removeRenderImage(handle)) |
| { |
| m_globalAssetRegistry->applyRenderImage(name, nullptr); |
| } |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::imageDeleted; |
| messageStream << handle; |
| messageStream << requestId; |
| break; |
| } |
| |
| case CommandQueue::Command::decodeBlob: |
| { |
| BlobAssetHandle handle; |
| uint64_t requestId; |
| std::vector<uint8_t> bytes; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_byteVectors >> bytes; |
| lock.unlock(); |
| |
| auto blob = make_rcp<BlobAsset>(); |
| SimpleArray<uint8_t> blobBytes(bytes.data(), bytes.size()); |
| if (blob->decode(blobBytes, m_factory)) |
| { |
| m_blobs[handle] = std::move(blob); |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::blobDecoded; |
| messageStream << handle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<BlobAssetHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::blobError) |
| << "Command Server failed to decode blob"; |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::externalBlob: |
| { |
| BlobAssetHandle handle; |
| uint64_t requestId; |
| rcp<BlobAsset> blob; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_externalBlobs >> blob; |
| lock.unlock(); |
| |
| if (blob) |
| { |
| m_blobs[handle] = std::move(blob); |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::blobDecoded; |
| messageStream << handle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<BlobAssetHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::blobError) |
| << "External blob was empty"; |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::deleteBlob: |
| { |
| BlobAssetHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| m_blobs.erase(handle); |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::blobDeleted; |
| messageStream << handle; |
| messageStream << requestId; |
| break; |
| } |
| |
| case CommandQueue::Command::decodeAudio: |
| { |
| AudioSourceHandle handle; |
| uint64_t requestId; |
| std::vector<uint8_t> bytes; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_byteVectors >> bytes; |
| lock.unlock(); |
| |
| auto audio = factory()->decodeAudio(bytes); |
| if (audio) |
| { |
| m_audioSources[handle] = std::move(audio); |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::audioDecoded; |
| messageStream << handle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<AudioSourceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::audioError) |
| << "Command Server failed to decode audio"; |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::externalAudio: |
| { |
| AudioSourceHandle handle; |
| uint64_t requestId; |
| rcp<AudioSource> audio; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_externalAudioSources >> audio; |
| lock.unlock(); |
| |
| if (audio) |
| { |
| m_audioSources[handle] = std::move(audio); |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::audioDecoded; |
| messageStream << handle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<AudioSourceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::audioError) |
| << "External audio source was invalid"; |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::deleteAudio: |
| { |
| AudioSourceHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| m_audioSources.erase(handle); |
| for (const auto& name : |
| m_globalAssetRegistry->removeAudioSource(handle)) |
| { |
| m_globalAssetRegistry->applyAudioSource(name, nullptr); |
| } |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::audioDeleted; |
| messageStream << handle; |
| messageStream << requestId; |
| break; |
| } |
| |
| case CommandQueue::Command::decodeFont: |
| { |
| FontHandle handle; |
| uint64_t requestId; |
| std::vector<uint8_t> bytes; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_byteVectors >> bytes; |
| lock.unlock(); |
| |
| auto font = factory()->decodeFont(bytes); |
| if (font) |
| { |
| m_fonts[handle] = std::move(font); |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::fontDecoded; |
| messageStream << handle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<FontHandle>(this, |
| handle, |
| requestId, |
| CommandQueue::Message::fontError) |
| << "Command Server failed to decode font"; |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::externalFont: |
| { |
| FontHandle handle; |
| uint64_t requestId; |
| rcp<Font> font; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_externalFonts >> font; |
| lock.unlock(); |
| |
| if (font) |
| { |
| m_fonts[handle] = std::move(font); |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::fontDecoded; |
| messageStream << handle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<FontHandle>(this, |
| handle, |
| requestId, |
| CommandQueue::Message::fontError) |
| << "Command Server failed to decode font"; |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::deleteFont: |
| { |
| FontHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| m_fonts.erase(handle); |
| for (const auto& name : |
| m_globalAssetRegistry->removeFont(handle)) |
| { |
| m_globalAssetRegistry->applyFont(name, nullptr); |
| } |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::fontDeleted; |
| messageStream << handle; |
| messageStream << requestId; |
| break; |
| } |
| |
| case CommandQueue::Command::instantiateArtboard: |
| { |
| ArtboardHandle handle; |
| FileHandle fileHandle; |
| uint64_t requestId; |
| std::string name; |
| commandStream >> handle; |
| commandStream >> fileHandle; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> name; |
| lock.unlock(); |
| if (rive::File* file = getFile(fileHandle)) |
| { |
| if (auto artboard = name.empty() |
| ? file->bindableArtboardDefault() |
| : file->bindableArtboardNamed(name)) |
| { |
| m_artboardDependencies[handle] = {}; |
| m_artboards[handle] = std::move(artboard); |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream |
| << CommandQueue::Message::artboardInstantiated; |
| messageStream << fileHandle; |
| messageStream << handle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<FileHandle>( |
| this, |
| fileHandle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "artboard \"" << name << "\" not found."; |
| } |
| } |
| else |
| { |
| ErrorReporter<FileHandle>(this, |
| fileHandle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "file " << fileHandle |
| << " not found when trying to create artboard"; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::setArtboardSize: |
| { |
| ArtboardHandle handle; |
| uint64_t requestId; |
| float width, height; |
| commandStream >> handle; |
| commandStream >> width; |
| commandStream >> height; |
| commandStream >> requestId; |
| lock.unlock(); |
| |
| if (auto artboardInstance = getArtboardInstance(handle)) |
| { |
| artboardInstance->width(width); |
| artboardInstance->height(height); |
| } |
| else |
| { |
| ErrorReporter<ArtboardHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::artboardError) |
| << "artboard " << handle |
| << " not found when trying to set artboard size"; |
| } |
| } |
| break; |
| |
| case CommandQueue::Command::resetArtboardSize: |
| { |
| ArtboardHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| |
| if (auto artboardInstance = getArtboardInstance(handle)) |
| { |
| artboardInstance->resetSize(); |
| } |
| else |
| { |
| ErrorReporter<ArtboardHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::artboardError) |
| << "artboard " << handle |
| << " not found when trying to reset artboard size"; |
| } |
| } |
| break; |
| |
| case CommandQueue::Command::setArtboardVolume: |
| { |
| ArtboardHandle handle; |
| float volume; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> volume; |
| commandStream >> requestId; |
| lock.unlock(); |
| |
| if (auto artboardInstance = getArtboardInstance(handle)) |
| { |
| artboardInstance->volume(volume); |
| } |
| else |
| { |
| ErrorReporter<ArtboardHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::artboardError) |
| << "artboard " << handle |
| << " not found when trying to set artboard volume"; |
| } |
| } |
| break; |
| |
| case CommandQueue::Command::getArtboardVolume: |
| { |
| ArtboardHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| auto artboard = getArtboardInstance(handle); |
| if (artboard) |
| { |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream |
| << CommandQueue::Message::artboardVolumeReceived; |
| messageStream << handle; |
| messageStream << requestId; |
| messageStream << artboard->volume(); |
| } |
| else |
| { |
| ErrorReporter<ArtboardHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::artboardError) |
| << "Invalid artboard handle " << handle |
| << " when getting artboard volume"; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::deleteArtboard: |
| { |
| ArtboardHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| cleanupArtboard(handle, requestId); |
| // We don't remove from the file dependencies here because |
| // calling erase on a non existent key is fine. |
| break; |
| } |
| |
| case CommandQueue::Command::instantiateViewModel: |
| case CommandQueue::Command::instantiateBlankViewModel: |
| case CommandQueue::Command::instantiateViewModelForArtboard: |
| case CommandQueue::Command::instantiateBlankViewModelForArtboard: |
| { |
| bool usesInstanceName = |
| command == CommandQueue::Command::instantiateViewModel || |
| command == |
| CommandQueue::Command::instantiateViewModelForArtboard; |
| bool usesArtboard = |
| command == CommandQueue::Command:: |
| instantiateBlankViewModelForArtboard || |
| command == |
| CommandQueue::Command::instantiateViewModelForArtboard; |
| |
| FileHandle fileHandle; |
| ArtboardHandle artboardHandle; |
| ViewModelInstanceHandle viewHandle; |
| uint64_t requestId; |
| std::string viewModelName; |
| std::string viewModelInstanceName; |
| commandStream >> fileHandle; |
| if (usesArtboard) |
| { |
| commandStream >> artboardHandle; |
| } |
| commandStream >> viewHandle; |
| commandStream >> requestId; |
| if (!usesArtboard) |
| { |
| m_commandQueue->m_names >> viewModelName; |
| } |
| if (usesInstanceName) |
| { |
| m_commandQueue->m_names >> viewModelInstanceName; |
| } |
| lock.unlock(); |
| if (auto file = getFile(fileHandle)) |
| { |
| ViewModelRuntime* viewModel = nullptr; |
| |
| if (usesArtboard) |
| { |
| if (auto artboardInstance = |
| getArtboardInstance(artboardHandle)) |
| { |
| viewModel = file->defaultArtboardViewModel( |
| artboardInstance); |
| if (viewModel == nullptr) |
| { |
| ErrorReporter<FileHandle>( |
| this, |
| fileHandle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "No view model found for artboard " |
| << artboardHandle; |
| } |
| } |
| else |
| { |
| if (usesInstanceName) |
| { |
| if (viewModelInstanceName.empty()) |
| { |
| ErrorReporter<FileHandle>( |
| this, |
| fileHandle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "ArtboardInstance " << artboardHandle |
| << " Not found when trying to create" |
| << " default view model with default " |
| "view " |
| << "model instance"; |
| } |
| else |
| { |
| ErrorReporter<FileHandle>( |
| this, |
| fileHandle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "ArtboardInstance " << artboardHandle |
| << " Not found when trying to create " |
| << "default view model with " |
| << "view model instance name " |
| << viewModelInstanceName; |
| } |
| } |
| else |
| { |
| ErrorReporter<FileHandle>( |
| this, |
| fileHandle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "ArtboardInstance " << artboardHandle |
| << " Not found when trying to create " |
| << "default view model with blank view " |
| << "model instance"; |
| } |
| } |
| } |
| else |
| { |
| viewModel = file->viewModelByName(viewModelName); |
| if (viewModel == nullptr) |
| { |
| ErrorReporter<FileHandle>( |
| this, |
| fileHandle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "View model " << viewModelName |
| << " not found"; |
| } |
| } |
| |
| if (viewModel) |
| { |
| rcp<ViewModelInstanceRuntime> instance; |
| |
| if (usesInstanceName) |
| { |
| if (viewModelInstanceName.empty()) |
| { |
| instance = viewModel->createDefaultInstance(); |
| if (instance == nullptr) |
| { |
| ErrorReporter<FileHandle>( |
| this, |
| fileHandle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "Could not create " |
| << "default view model instance " |
| << "from view model " << viewModelName; |
| } |
| } |
| else |
| { |
| instance = viewModel->createInstanceFromName( |
| viewModelInstanceName); |
| if (instance == nullptr) |
| { |
| ErrorReporter<FileHandle>( |
| this, |
| fileHandle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "Could not create " |
| "view model instance named " |
| << viewModelInstanceName |
| << "from view model " << viewModelName; |
| } |
| } |
| } |
| else |
| { |
| instance = viewModel->createInstance(); |
| if (instance == nullptr) |
| { |
| ErrorReporter<FileHandle>( |
| this, |
| fileHandle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "Could not create " |
| << "blank view model instance " |
| << "from view model " << viewModelName; |
| } |
| } |
| |
| if (instance) |
| { |
| m_viewModels[viewHandle] = std::move(instance); |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message:: |
| viewModelInstanceInstantiated; |
| messageStream << fileHandle; |
| messageStream << viewHandle; |
| messageStream << requestId; |
| } |
| } |
| } |
| else |
| { |
| ErrorReporter<FileHandle>(this, |
| fileHandle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "File " << fileHandle |
| << " not found when creating view model instance "; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::addViewModelListValue: |
| { |
| ViewModelInstanceHandle rootHandle; |
| ViewModelInstanceHandle viewHandle; |
| uint64_t requestId; |
| std::string path; |
| int index; |
| commandStream >> rootHandle; |
| commandStream >> viewHandle; |
| commandStream >> index; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> path; |
| lock.unlock(); |
| |
| if (auto root = getViewModelInstance(rootHandle)) |
| { |
| if (auto viewModel = getViewModelInstance(viewHandle)) |
| { |
| if (auto property = root->propertyList(path)) |
| { |
| if (index >= 0) |
| property->addInstanceAt(viewModel, index); |
| else |
| property->addInstance(viewModel); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| rootHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "failed to find list at path " << path |
| << " when trying to add to a list"; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| rootHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "failed to find value view model " << viewHandle |
| << "isntance for add list"; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| rootHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "failed to find root view model isntance " |
| << rootHandle << "for add list"; |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::removeViewModelListValue: |
| { |
| ViewModelInstanceHandle rootHandle; |
| ViewModelInstanceHandle viewHandle; |
| uint64_t requestId; |
| std::string path; |
| int index; |
| commandStream >> rootHandle; |
| commandStream >> viewHandle; |
| commandStream >> index; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> path; |
| lock.unlock(); |
| |
| if (auto root = getViewModelInstance(rootHandle)) |
| { |
| if (auto property = root->propertyList(path)) |
| { |
| if (index >= 0) |
| { |
| property->removeInstanceAt(index); |
| } |
| else if (auto viewModel = |
| getViewModelInstance(viewHandle)) |
| { |
| property->removeInstance(viewModel); |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| rootHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "failed to find list on view model " |
| "isntance for " |
| "remove at path " |
| << path; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| rootHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "failed to find view model instance " << rootHandle |
| << " for remove list"; |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::swapViewModelListValue: |
| { |
| ViewModelInstanceHandle rootHandle; |
| uint64_t requestId; |
| std::string path; |
| int indexa, indexb; |
| commandStream >> rootHandle; |
| commandStream >> indexa; |
| commandStream >> indexb; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> path; |
| lock.unlock(); |
| if (auto viewModel = getViewModelInstance(rootHandle)) |
| { |
| if (auto list = viewModel->propertyList(path)) |
| { |
| list->swap(indexa, indexb); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| rootHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "failed to find list on view model " |
| "isntance for " |
| "swap at path " |
| << path; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| rootHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "failed to find view model instance " << rootHandle |
| << " for swap"; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::subscribeViewModelProperty: |
| case CommandQueue::Command::unsubscribeViewModelProperty: |
| { |
| ViewModelInstanceHandle rootHandle; |
| PropertyData data; |
| uint64_t requestId; |
| commandStream >> rootHandle; |
| commandStream >> data.type; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> data.name; |
| lock.unlock(); |
| |
| if (command == |
| CommandQueue::Command::subscribeViewModelProperty) |
| { |
| // Validate that this is a valid thing to subscribe to. |
| |
| if (auto view = getViewModelInstance(rootHandle)) |
| { |
| if (data.type != DataType::viewModel && |
| data.type != DataType::integer && |
| data.type != DataType::none && |
| data.type != DataType::symbolListIndex) |
| { |
| if (view->property(data.name) != nullptr) |
| { |
| m_propertySubscriptions.push_back( |
| {requestId, data, rootHandle}); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| rootHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Property " << data.name |
| << " not found when subscribing"; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| rootHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Property type " << data.type |
| << " is not valid when subscribing"; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| rootHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Root view model " << rootHandle |
| << " not found when subscribing " |
| "to property " |
| << data.name; |
| } |
| } |
| else |
| { |
| auto itr = std::remove_if( |
| m_propertySubscriptions.begin(), |
| m_propertySubscriptions.end(), |
| [data, rootHandle](const Subscription& val) { |
| return val.data.name == data.name && |
| val.data.type == data.type && |
| val.rootViewModel == rootHandle; |
| }); |
| |
| m_propertySubscriptions.erase( |
| itr, |
| m_propertySubscriptions.end()); |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::refNestedViewModel: |
| { |
| ViewModelInstanceHandle rootViewHandle; |
| ViewModelInstanceHandle nestedViewHandle; |
| uint64_t requestId; |
| std::string path; |
| commandStream >> rootViewHandle; |
| commandStream >> nestedViewHandle; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> path; |
| lock.unlock(); |
| |
| if (auto rootViewInstance = |
| getViewModelInstance(rootViewHandle)) |
| { |
| if (auto nestedViewModel = |
| rootViewInstance->propertyViewModel(path)) |
| { |
| m_viewModels[nestedViewHandle] = nestedViewModel; |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| FileHandle fileHandle = RIVE_NULL_HANDLE; |
| messageStream << CommandQueue::Message:: |
| viewModelInstanceInstantiated; |
| messageStream << fileHandle; |
| messageStream << nestedViewHandle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| nestedViewHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Nested view not found at path" << path |
| << " when refing nested " |
| "view model"; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| nestedViewHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Root view model " << rootViewHandle |
| << " not found when refing nested " |
| "view model at path " |
| << path; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::refListViewModel: |
| { |
| ViewModelInstanceHandle rootViewHandle; |
| ViewModelInstanceHandle listViewHandle; |
| int index; |
| uint64_t requestId; |
| std::string path; |
| commandStream >> rootViewHandle; |
| commandStream >> index; |
| commandStream >> listViewHandle; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> path; |
| lock.unlock(); |
| |
| if (auto rootViewInstance = |
| getViewModelInstance(rootViewHandle)) |
| { |
| if (auto list = rootViewInstance->propertyList(path)) |
| { |
| auto viewModelInstance = list->instanceAt(index); |
| if (viewModelInstance) |
| { |
| m_viewModels[listViewHandle] = viewModelInstance; |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| FileHandle fileHandle = RIVE_NULL_HANDLE; |
| messageStream << CommandQueue::Message:: |
| viewModelInstanceInstantiated; |
| messageStream << fileHandle; |
| messageStream << listViewHandle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| rootViewHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "View model not found on list " << path |
| << " at index " << index; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| rootViewHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "List not found at path " << path |
| << " when refing view model at index " << index; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| rootViewHandle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Root view model" << rootViewHandle |
| << " not found when refing nested " |
| "view model at path " |
| << path; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::deleteViewModel: |
| { |
| ViewModelInstanceHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| m_viewModels.erase(handle); |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::viewModelDeleted; |
| messageStream << handle; |
| messageStream << requestId; |
| break; |
| } |
| case CommandQueue::Command::instantiateStateMachine: |
| { |
| StateMachineHandle handle; |
| ArtboardHandle artboardHandle; |
| uint64_t requestId; |
| std::string name; |
| commandStream >> handle; |
| commandStream >> artboardHandle; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> name; |
| lock.unlock(); |
| if (rive::ArtboardInstance* artboard = |
| getArtboardInstance(artboardHandle)) |
| { |
| if (auto stateMachine = |
| name.empty() ? artboard->defaultStateMachine() |
| : artboard->stateMachineNamed(name)) |
| { |
| std::unique_lock<std::mutex> accesLock( |
| m_stateMachineAccessMutex); |
| m_stateMachines[handle] = |
| make_rcp<SynchronizedStateMachine>( |
| std::move(stateMachine)); |
| accesLock.unlock(); |
| assert(m_artboardDependencies.find(artboardHandle) != |
| m_artboardDependencies.end()); |
| m_artboardDependencies[artboardHandle].push_back( |
| handle); |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream |
| << CommandQueue::Message::stateMachineInstantiated; |
| messageStream << artboardHandle; |
| messageStream << handle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<ArtboardHandle>( |
| this, |
| artboardHandle, |
| requestId, |
| CommandQueue::Message::artboardError) |
| << "Could not create state " |
| "machine with name \"" |
| << name << "\" because it was not found."; |
| } |
| } |
| else |
| { |
| ErrorReporter<ArtboardHandle>( |
| this, |
| artboardHandle, |
| requestId, |
| CommandQueue::Message::artboardError) |
| << "Could not create state " |
| "machine with name \"" |
| << name << "\" because the owning artboard " |
| << artboardHandle << " was not found."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::bindViewModelInstance: |
| { |
| StateMachineHandle handle; |
| ViewModelInstanceHandle viewModel; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> viewModel; |
| commandStream >> requestId; |
| lock.unlock(); |
| |
| if (auto stateMachineWrapper = getStateMachineWrapper(handle)) |
| { |
| if (auto viewModelInstance = |
| getViewModelInstance(viewModel)) |
| { |
| std::unique_lock<std::mutex> accesLock( |
| stateMachineWrapper->m_mutex); |
| stateMachineWrapper->instance->bindViewModelInstance( |
| viewModelInstance->instance()); |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "View model instance " << viewModel |
| << " not found for when trying to bind " |
| "to a state machine"; |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for binding view model."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::setViewModelInstance: |
| { |
| StateMachineHandle handle; |
| ViewModelInstanceHandle viewModel; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> viewModel; |
| commandStream >> requestId; |
| lock.unlock(); |
| |
| if (auto stateMachineWrapper = getStateMachineWrapper(handle)) |
| { |
| if (auto viewModelInstance = |
| getViewModelInstance(viewModel)) |
| { |
| std::unique_lock<std::mutex> accesLock( |
| stateMachineWrapper->m_mutex); |
| stateMachineWrapper->instance->setViewModelInstance( |
| viewModelInstance->instance()); |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "View model instance " << viewModel |
| << " not found when trying to set the main view " |
| "model instance on a state machine"; |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for setting view model instance."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::clearViewModelInstance: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| |
| if (auto stateMachineWrapper = getStateMachineWrapper(handle)) |
| { |
| std::unique_lock<std::mutex> accessLock( |
| stateMachineWrapper->m_mutex); |
| if (auto dataContext = |
| stateMachineWrapper->instance->dataContext()) |
| { |
| dataContext->setMainViewModelInstance(nullptr); |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for clearing view model instance."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::setGlobalViewModelInstance: |
| { |
| StateMachineHandle handle; |
| ViewModelInstanceHandle viewModel; |
| uint64_t requestId; |
| std::string name; |
| commandStream >> handle; |
| commandStream >> viewModel; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> name; |
| lock.unlock(); |
| |
| if (auto stateMachineWrapper = getStateMachineWrapper(handle)) |
| { |
| if (auto viewModelInstance = |
| getViewModelInstance(viewModel)) |
| { |
| std::unique_lock<std::mutex> accesLock( |
| stateMachineWrapper->m_mutex); |
| if (!stateMachineWrapper->instance |
| ->setGlobalViewModelInstance( |
| name, |
| viewModelInstance->instance())) |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "Could not set global view model instance " |
| << viewModel << " under name " << name |
| << " on a state machine"; |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "View model instance " << viewModel |
| << " not found when trying to set a global view " |
| "model instance on a state machine"; |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for setting global view model instance."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::clearGlobalViewModelInstance: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| std::string name; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> name; |
| lock.unlock(); |
| |
| if (auto stateMachineWrapper = getStateMachineWrapper(handle)) |
| { |
| std::unique_lock<std::mutex> accessLock( |
| stateMachineWrapper->m_mutex); |
| if (!stateMachineWrapper->instance |
| ->setGlobalViewModelInstance(name, nullptr)) |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "Could not clear global view model instance " |
| << "under name " << name << " on a state machine"; |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for clearing global view model " |
| "instance."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::getGlobalViewModelInstance: |
| { |
| StateMachineHandle handle; |
| ViewModelInstanceHandle viewHandle; |
| uint64_t requestId; |
| std::string name; |
| commandStream >> handle; |
| commandStream >> viewHandle; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> name; |
| lock.unlock(); |
| |
| if (auto stateMachineWrapper = getStateMachineWrapper(handle)) |
| { |
| rcp<ViewModelInstance> viewModelInstance; |
| { |
| std::unique_lock<std::mutex> accesLock( |
| stateMachineWrapper->m_mutex); |
| viewModelInstance = stateMachineWrapper->instance |
| ->globalViewModelInstance(name); |
| } |
| if (viewModelInstance != nullptr) |
| { |
| m_viewModels[viewHandle] = |
| make_rcp<ViewModelInstanceRuntime>( |
| viewModelInstance); |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message:: |
| stateMachineViewModelInstanceReceived; |
| messageStream << handle; |
| messageStream << viewHandle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "No global view model instance named " << name |
| << " bound to state machine " << handle; |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for getting global view model instance."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::getMainViewModelInstance: |
| { |
| StateMachineHandle handle; |
| ViewModelInstanceHandle viewHandle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> viewHandle; |
| commandStream >> requestId; |
| lock.unlock(); |
| |
| if (auto stateMachineWrapper = getStateMachineWrapper(handle)) |
| { |
| rcp<ViewModelInstance> viewModelInstance; |
| { |
| std::unique_lock<std::mutex> accessLock( |
| stateMachineWrapper->m_mutex); |
| if (auto dataContext = |
| stateMachineWrapper->instance->dataContext()) |
| { |
| viewModelInstance = |
| dataContext->mainViewModelInstance(); |
| } |
| } |
| if (viewModelInstance != nullptr) |
| { |
| m_viewModels[viewHandle] = |
| make_rcp<ViewModelInstanceRuntime>( |
| viewModelInstance); |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message:: |
| stateMachineViewModelInstanceReceived; |
| messageStream << handle; |
| messageStream << viewHandle; |
| messageStream << requestId; |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "No main view model instance bound to state " |
| "machine " |
| << handle; |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for getting main view model instance."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::bind: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| |
| if (auto stateMachineWrapper = getStateMachineWrapper(handle)) |
| { |
| std::unique_lock<std::mutex> accesLock( |
| stateMachineWrapper->m_mutex); |
| stateMachineWrapper->instance->bind(); |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for binding data context."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::advanceStateMachine: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| float timeToAdvance; |
| commandStream >> handle; |
| commandStream >> requestId; |
| commandStream >> timeToAdvance; |
| lock.unlock(); |
| |
| if (auto wrapper = getStateMachineWrapper(handle)) |
| { |
| std::unique_lock<std::mutex> advanceLock(wrapper->m_mutex); |
| if (!wrapper->instance->advanceAndApply(timeToAdvance)) |
| { |
| advanceLock.unlock(); |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream |
| << CommandQueue::Message::stateMachineSettled; |
| messageStream << handle; |
| messageStream << requestId; |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for " |
| "advance."; |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::deleteStateMachine: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| auto it = m_stateMachines.find(handle); |
| if (it != m_stateMachines.end()) |
| { |
| std::unique_lock<std::mutex> accesLock( |
| m_stateMachineAccessMutex); |
| m_stateMachines.erase(it); |
| accesLock.unlock(); |
| } |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::stateMachineDeleted; |
| messageStream << handle; |
| messageStream << requestId; |
| // We don't remove from the artboard dependencies here because |
| // calling erase on a non existent key is fine. |
| break; |
| } |
| |
| case CommandQueue::Command::enableSemantics: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| if (auto wrapper = getStateMachineWrapper(handle)) |
| { |
| std::unique_lock<std::mutex> stateMachineLock( |
| wrapper->m_mutex); |
| wrapper->instance->enableSemantics(); |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for enableSemantics."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::drainSemanticsDiff: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| Fit fit; |
| float alignmentX; |
| float alignmentY; |
| float scaleFactor; |
| Vec2D viewBounds; |
| commandStream >> handle; |
| commandStream >> requestId; |
| commandStream >> fit; |
| commandStream >> alignmentX; |
| commandStream >> alignmentY; |
| commandStream >> scaleFactor; |
| commandStream >> viewBounds; |
| lock.unlock(); |
| if (auto wrapper = getStateMachineWrapper(handle)) |
| { |
| std::unique_lock<std::mutex> stateMachineLock( |
| wrapper->m_mutex); |
| auto* manager = wrapper->instance->semanticManager(); |
| if (manager == nullptr) |
| { |
| stateMachineLock.unlock(); |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "Semantics not enabled on state machine " |
| << handle |
| << "; call enableSemantics before " |
| "drainSemanticsDiff."; |
| } |
| else |
| { |
| SemanticsDiff diff = manager->drainDiff(); |
| // Compute the transform while locked because it reads |
| // the artboard bounds. |
| Mat2D transform; |
| if (auto* artboard = wrapper->instance->artboard()) |
| { |
| AABB artboardBounds = artboard->bounds(); |
| AABB surfaceBounds = |
| AABB(Vec2D{0.0f, 0.0f}, viewBounds); |
| if (surfaceBounds.width() != 0.0f && |
| surfaceBounds.height() != 0.0f) |
| { |
| Alignment alignment(alignmentX, alignmentY); |
| transform = |
| rive::computeAlignment(fit, |
| alignment, |
| surfaceBounds, |
| artboardBounds, |
| scaleFactor); |
| } |
| } |
| |
| const bool transformChanged = |
| wrapper->m_hasLastSemanticsTransform && |
| wrapper->m_lastSemanticsTransform != transform; |
| wrapper->m_lastSemanticsTransform = transform; |
| wrapper->m_hasLastSemanticsTransform = true; |
| if (transformChanged) |
| { |
| // Viewport-only diffs bypass SemanticManager's |
| // normal metadata population. |
| diff.frameNumber = Artboard::frameId(); |
| diff.updatedGeometry = manager->boundsSnapshot(); |
| diff.treeVersion = manager->version(); |
| diff.rootId = manager->rootId(); |
| } |
| |
| if (!diff.empty()) |
| { |
| // Map the owned diff outside the lock. |
| stateMachineLock.unlock(); |
| if (transform != Mat2D()) |
| { |
| mapSemanticsDiffToViewSpace(diff, transform); |
| } |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream |
| << CommandQueue::Message::semanticsDiffReceived; |
| messageStream << handle; |
| messageStream << requestId; |
| m_commandQueue->m_messageSemanticsDiffs |
| << std::move(diff); |
| } |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for drainSemanticsDiff."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::fireSemanticAction: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| uint32_t semanticNodeId; |
| SemanticActionType actionType; |
| commandStream >> handle; |
| commandStream >> requestId; |
| commandStream >> semanticNodeId; |
| commandStream >> actionType; |
| lock.unlock(); |
| if (auto wrapper = getStateMachineWrapper(handle)) |
| { |
| std::unique_lock<std::mutex> stateMachineLock( |
| wrapper->m_mutex); |
| if (wrapper->instance->semanticManager() == nullptr) |
| { |
| stateMachineLock.unlock(); |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "Semantics not enabled on state machine " |
| << handle |
| << "; call enableSemantics before " |
| "fireSemanticAction."; |
| } |
| else |
| { |
| wrapper->instance->fireSemanticAction(semanticNodeId, |
| actionType); |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for fireSemanticAction."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::requestSemanticFocus: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| uint32_t semanticNodeId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| commandStream >> semanticNodeId; |
| lock.unlock(); |
| if (auto wrapper = getStateMachineWrapper(handle)) |
| { |
| std::unique_lock<std::mutex> stateMachineLock( |
| wrapper->m_mutex); |
| auto* manager = wrapper->instance->semanticManager(); |
| if (manager == nullptr) |
| { |
| stateMachineLock.unlock(); |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "Semantics not enabled on state machine " |
| << handle |
| << "; call enableSemantics before " |
| "requestSemanticFocus."; |
| } |
| else |
| { |
| // A missing/non-focusable node is a silent no-op. |
| manager->requestFocus(semanticNodeId); |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for requestSemanticFocus."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::clearSemanticFocus: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| if (auto wrapper = getStateMachineWrapper(handle)) |
| { |
| std::unique_lock<std::mutex> stateMachineLock( |
| wrapper->m_mutex); |
| if (auto* focusManager = wrapper->instance->focusManager()) |
| { |
| focusManager->clearFocus(); |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for clearSemanticFocus."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::runOnce: |
| { |
| CommandServerCallback callback; |
| m_commandQueue->m_callbacks >> callback; |
| lock.unlock(); |
| callback(this); |
| break; |
| } |
| |
| case CommandQueue::Command::draw: |
| { |
| DrawKey drawKey; |
| CommandServerDrawCallback drawCallback; |
| commandStream >> drawKey; |
| m_commandQueue->m_drawCallbacks >> drawCallback; |
| lock.unlock(); |
| m_uniqueDraws[drawKey] = std::move(drawCallback); |
| break; |
| } |
| |
| case CommandQueue::Command::cancelDraw: |
| { |
| DrawKey drawKey; |
| commandStream >> drawKey; |
| lock.unlock(); |
| m_uniqueDraws.erase(drawKey); |
| break; |
| } |
| |
| case CommandQueue::Command::commandLoopBreak: |
| { |
| lock.unlock(); |
| shouldProcessCommands = false; |
| break; |
| } |
| |
| case CommandQueue::Command::listArtboards: |
| { |
| FileHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| auto file = getFile(handle); |
| if (file) |
| { |
| auto artboards = file->artboards(); |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::artboardsListed; |
| messageStream << handle; |
| messageStream << requestId; |
| messageStream << artboards.size(); |
| for (auto artboard : artboards) |
| { |
| m_commandQueue->m_messageNames << artboard->name(); |
| } |
| } |
| else |
| { |
| ErrorReporter<FileHandle>(this, |
| handle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "Invalid file handle " << handle |
| << " when getting list of artboards"; |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::listFileAssets: |
| { |
| FileHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| auto file = getFile(handle); |
| if (file) |
| { |
| auto fileAssets = file->assets(); |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::fileAssetsListed; |
| messageStream << handle; |
| messageStream << requestId; |
| messageStream << fileAssets.size(); |
| for (const auto& asset : fileAssets) |
| { |
| messageStream << asset->assetId(); |
| messageStream << asset->coreType(); |
| m_commandQueue->m_messageNames << asset->name(); |
| m_commandQueue->m_messageNames << asset->uniqueName(); |
| m_commandQueue->m_messageNames << asset->cdnUuidStr(); |
| m_commandQueue->m_messageNames << asset->cdnBaseUrl(); |
| m_commandQueue->m_messageNames |
| << asset->fileExtension(); |
| } |
| } |
| else |
| { |
| ErrorReporter<FileHandle>(this, |
| handle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "Invalid file handle " << handle |
| << " when getting list of file assets"; |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::getViewModelInstanceViewModelName: |
| { |
| ViewModelInstanceHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| auto viewModelInstance = getViewModelInstance(handle); |
| if (viewModelInstance) |
| { |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message:: |
| viewModelInstanceViewModelNameReceived; |
| messageStream << handle; |
| messageStream << requestId; |
| m_commandQueue->m_messageNames |
| << viewModelInstance->viewModelName(); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Invalid view model instance handle " << handle; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::getViewModelInstanceName: |
| { |
| ViewModelInstanceHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| auto viewModelInstance = getViewModelInstance(handle); |
| if (viewModelInstance) |
| { |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream |
| << CommandQueue::Message::viewModelInstanceNameReceived; |
| messageStream << handle; |
| messageStream << requestId; |
| m_commandQueue->m_messageNames << viewModelInstance->name(); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Invalid view model instance handle " << handle; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::listViewModelEnums: |
| { |
| FileHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| auto file = getFile(handle); |
| if (file) |
| { |
| auto enums = file->enums(); |
| std::vector<ViewModelEnum> enumDatas; |
| |
| for (auto tenum : enums) |
| { |
| ViewModelEnum data; |
| data.name = tenum->enumName(); |
| auto values = tenum->values(); |
| for (auto value : values) |
| { |
| data.enumerants.push_back(value->key()); |
| } |
| |
| enumDatas.push_back(data); |
| } |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream |
| << CommandQueue::Message::viewModelEnumsListed; |
| messageStream << handle; |
| messageStream << requestId; |
| messageStream << enumDatas.size(); |
| for (auto& enumData : enumDatas) |
| { |
| m_commandQueue->m_messageNames << enumData.name; |
| messageStream << enumData.enumerants.size(); |
| for (auto& enumValueName : enumData.enumerants) |
| m_commandQueue->m_messageNames << enumValueName; |
| } |
| } |
| else |
| { |
| ErrorReporter<FileHandle>(this, |
| handle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "Invalid file handle " << handle |
| << " when getting list of enums"; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::listStateMachines: |
| { |
| ArtboardHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| auto artboard = getArtboardInstance(handle); |
| if (artboard) |
| { |
| auto numStateMachines = artboard->stateMachineCount(); |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::stateMachinesListed; |
| messageStream << handle; |
| messageStream << requestId; |
| messageStream << numStateMachines; |
| for (int i = 0; i < numStateMachines; ++i) |
| { |
| m_commandQueue->m_messageNames |
| << artboard->stateMachineNameAt(i); |
| } |
| } |
| else |
| { |
| ErrorReporter<ArtboardHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::artboardError) |
| << "Invalid artboard handle " << handle |
| << " when getting list of state machines"; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::getArtboardSize: |
| { |
| ArtboardHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| auto artboard = getArtboardInstance(handle); |
| if (artboard) |
| { |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream |
| << CommandQueue::Message::artboardSizeReceived; |
| messageStream << handle; |
| messageStream << requestId; |
| messageStream << artboard->width(); |
| messageStream << artboard->height(); |
| } |
| else |
| { |
| ErrorReporter<ArtboardHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::artboardError) |
| << "Invalid artboard handle " << handle |
| << " when getting artboard size"; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::getDefaultViewModel: |
| { |
| FileHandle fileHandle; |
| ArtboardHandle artboardHandle; |
| uint64_t requestId; |
| commandStream >> fileHandle; |
| commandStream >> artboardHandle; |
| commandStream >> requestId; |
| lock.unlock(); |
| auto artboard = getArtboardInstance(artboardHandle); |
| if (artboard) |
| { |
| auto file = getFile(fileHandle); |
| if (file) |
| { |
| auto defaultViewModel = |
| file->defaultArtboardViewModel(artboard); |
| |
| if (defaultViewModel) |
| { |
| auto defaultInstance = |
| defaultViewModel->createDefaultInstance(); |
| if (defaultInstance) |
| { |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message:: |
| defaultViewModelReceived; |
| messageStream << artboardHandle; |
| messageStream << requestId; |
| m_commandQueue->m_messageNames |
| << defaultViewModel->name(); |
| m_commandQueue->m_messageNames |
| << defaultInstance->name(); |
| } |
| else |
| { |
| ErrorReporter<ArtboardHandle>( |
| this, |
| artboardHandle, |
| requestId, |
| CommandQueue::Message::artboardError) |
| << "Could not find default view model " |
| "instance for " |
| "artboard" |
| << " when getting default view model info"; |
| } |
| } |
| else |
| { |
| ErrorReporter<ArtboardHandle>( |
| this, |
| artboardHandle, |
| requestId, |
| CommandQueue::Message::artboardError) |
| << "Could not find default view model for " |
| "artboard" |
| << " when getting default view model info"; |
| } |
| } |
| else |
| { |
| ErrorReporter<ArtboardHandle>( |
| this, |
| artboardHandle, |
| requestId, |
| CommandQueue::Message::artboardError) |
| << "Invalid file handle " << fileHandle |
| << " when getting default view model info"; |
| } |
| } |
| else |
| { |
| ErrorReporter<ArtboardHandle>( |
| this, |
| artboardHandle, |
| requestId, |
| CommandQueue::Message::artboardError) |
| << "Invalid artboard handle " << artboardHandle |
| << " when getting default view model info"; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::listViewModels: |
| { |
| FileHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| auto file = getFile(handle); |
| if (file) |
| { |
| auto numViewModels = file->viewModelCount(); |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::viewModelsListend; |
| messageStream << handle; |
| messageStream << requestId; |
| messageStream << numViewModels; |
| for (int i = 0; i < numViewModels; ++i) |
| { |
| m_commandQueue->m_messageNames |
| << file->viewModelByIndex(i)->name(); |
| } |
| } |
| else |
| { |
| ErrorReporter<FileHandle>(this, |
| handle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "Invalid file handle " << handle |
| << " when getting list of view models"; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::listGlobalViewModelNames: |
| { |
| FileHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| auto file = getFile(handle); |
| if (file) |
| { |
| auto names = file->globalViewModelNames(); |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream |
| << CommandQueue::Message::globalViewModelNamesListed; |
| messageStream << handle; |
| messageStream << requestId; |
| messageStream << names.size(); |
| for (auto& name : names) |
| { |
| m_commandQueue->m_messageNames << name; |
| } |
| } |
| else |
| { |
| ErrorReporter<FileHandle>(this, |
| handle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "Invalid file handle " << handle |
| << " when getting list of global view models"; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::listViewModelInstanceNames: |
| { |
| FileHandle handle; |
| uint64_t requestId; |
| std::string viewModelName; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> viewModelName; |
| lock.unlock(); |
| auto file = getFile(handle); |
| if (file) |
| { |
| auto model = file->viewModelByName(viewModelName); |
| if (model) |
| { |
| auto names = model->instanceNames(); |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message:: |
| viewModelInstanceNamesListed; |
| messageStream << handle; |
| messageStream << requestId; |
| messageStream << names.size(); |
| m_commandQueue->m_messageNames << viewModelName; |
| for (auto& name : names) |
| { |
| m_commandQueue->m_messageNames << name; |
| } |
| } |
| else |
| { |
| ErrorReporter<FileHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Invalid view model name " << viewModelName |
| << " when getting list of view model instance " |
| "names"; |
| } |
| } |
| else |
| { |
| ErrorReporter<FileHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Invalid file handle " << handle |
| << " when getting list of view model instance names"; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::listViewModelProperties: |
| { |
| FileHandle handle; |
| uint64_t requestId; |
| std::string viewModelName; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> viewModelName; |
| lock.unlock(); |
| auto file = getFile(handle); |
| if (file) |
| { |
| auto model = file->viewModelByName(viewModelName); |
| if (model) |
| { |
| // Used to get meta data about enums. |
| auto modelInstance = model->createDefaultInstance(); |
| auto properties = model->properties(); |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream |
| << CommandQueue::Message::viewModelPropertiesListed; |
| messageStream << handle; |
| messageStream << requestId; |
| messageStream << properties.size(); |
| m_commandQueue->m_messageNames << viewModelName; |
| for (auto& property : properties) |
| { |
| messageStream << property.type; |
| m_commandQueue->m_messageNames << property.name; |
| if (property.type == DataType::enumType) |
| { |
| auto enumProperty = |
| modelInstance->propertyEnum(property.name); |
| m_commandQueue->m_messageNames |
| << enumProperty->enumType(); |
| } |
| if (property.type == DataType::viewModel) |
| { |
| // Get the type of the view model property |
| auto viewModelTmp = |
| modelInstance->propertyViewModel( |
| property.name); |
| if (viewModelTmp) |
| { |
| m_commandQueue->m_messageNames |
| << viewModelTmp->instance() |
| ->viewModel() |
| ->name(); |
| } |
| else |
| { |
| // If we can't determine the name we still |
| // need to send something because the |
| // command queue is expecting a name. |
| m_commandQueue->m_messageNames << "Unkown"; |
| } |
| } |
| } |
| } |
| else |
| { |
| ErrorReporter<FileHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "Invalid view model name " << viewModelName |
| << " when getting list of view model properties"; |
| } |
| } |
| else |
| { |
| ErrorReporter<FileHandle>(this, |
| handle, |
| requestId, |
| CommandQueue::Message::fileError) |
| << "Invalid file handle " << handle |
| << " when getting list of view model properties"; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::setViewModelInstanceValue: |
| { |
| ViewModelInstanceHandle handle = RIVE_NULL_HANDLE; |
| ViewModelInstanceHandle nestedHandle = RIVE_NULL_HANDLE; |
| RenderImageHandle imageHandle = RIVE_NULL_HANDLE; |
| BlobAssetHandle blobHandle = RIVE_NULL_HANDLE; |
| ArtboardHandle artboardHandle = RIVE_NULL_HANDLE; |
| uint64_t requestId; |
| CommandQueue::ViewModelInstanceData value; |
| |
| commandStream >> handle; |
| commandStream >> value.metaData.type; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> value.metaData.name; |
| |
| switch (value.metaData.type) |
| { |
| // Triggers are valid but have no data. |
| case DataType::trigger: |
| break; |
| case DataType::boolean: |
| commandStream >> value.boolValue; |
| break; |
| case DataType::number: |
| commandStream >> value.numberValue; |
| break; |
| case DataType::color: |
| commandStream >> value.colorValue; |
| break; |
| case DataType::string: |
| case DataType::enumType: |
| m_commandQueue->m_names >> value.stringValue; |
| break; |
| case DataType::viewModel: |
| commandStream >> nestedHandle; |
| break; |
| case DataType::assetImage: |
| commandStream >> imageHandle; |
| break; |
| case DataType::assetBlob: |
| commandStream >> blobHandle; |
| break; |
| case DataType::artboard: |
| commandStream >> artboardHandle; |
| break; |
| default: |
| RIVE_UNREACHABLE(); |
| } |
| lock.unlock(); |
| |
| if (auto viewModelInstance = getViewModelInstance(handle)) |
| { |
| switch (value.metaData.type) |
| { |
| case DataType::trigger: |
| { |
| if (auto property = |
| viewModelInstance->propertyTrigger( |
| value.metaData.name)) |
| { |
| property->trigger(); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find view model " |
| "property " |
| "instance when setting property type " |
| << value.metaData.type << " with path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| case DataType::boolean: |
| { |
| if (auto property = |
| viewModelInstance->propertyBoolean( |
| value.metaData.name)) |
| { |
| property->value(value.boolValue); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find view model " |
| "property " |
| "instance when setting property type " |
| << value.metaData.type << " with path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| case DataType::number: |
| { |
| if (auto property = |
| viewModelInstance->propertyNumber( |
| value.metaData.name)) |
| { |
| property->value(value.numberValue); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find view model " |
| "property " |
| "instance when setting property type " |
| << value.metaData.type << " with path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| case DataType::color: |
| { |
| if (auto property = |
| viewModelInstance->propertyColor( |
| value.metaData.name)) |
| { |
| property->value(value.colorValue); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find view model " |
| "property " |
| "instance when setting property type " |
| << value.metaData.type << " with path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| case DataType::string: |
| { |
| if (auto property = |
| viewModelInstance->propertyString( |
| value.metaData.name)) |
| { |
| property->value(value.stringValue); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find view model " |
| "property " |
| "instance when setting property type " |
| << value.metaData.type << " with path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| case DataType::enumType: |
| { |
| if (auto property = viewModelInstance->propertyEnum( |
| value.metaData.name)) |
| { |
| // As far as I can tell, there is no built in |
| // way to do this from the core runtime. So we |
| // just verify manually ourselves. |
| auto values = property->values(); |
| if (std::find(values.begin(), |
| values.end(), |
| value.stringValue) != |
| values.end()) |
| { |
| property->value(value.stringValue); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Invalid enum value for " |
| "property " |
| << value.metaData.name |
| << " when trying to set enum to " |
| << value.stringValue |
| << " possible values " << values; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find view model " |
| "property " |
| "instance when setting property type " |
| << value.metaData.type << " with path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| case DataType::viewModel: |
| { |
| if (auto nestedViewModel = |
| getViewModelInstance(nestedHandle)) |
| { |
| if (!viewModelInstance->replaceViewModel( |
| value.metaData.name, |
| nestedViewModel)) |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not replace " |
| "view model at path " |
| << value.metaData.name; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find nested view " |
| "model " |
| "with handle " |
| << nestedHandle |
| << " to set for view model instance when " |
| "setting property with path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| case DataType::assetImage: |
| { |
| if (auto imageProperty = |
| viewModelInstance->propertyImage( |
| value.metaData.name)) |
| { |
| if (imageHandle == RIVE_NULL_HANDLE) |
| { |
| imageProperty->value(nullptr); |
| } |
| else if (auto image = getImage(imageHandle)) |
| { |
| imageProperty->value(image); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find image " |
| << imageHandle |
| << " to set for view model instance " |
| "when setting property with path " |
| << value.metaData.name; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find " |
| "image property at path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| case DataType::assetBlob: |
| { |
| if (auto blobProperty = |
| viewModelInstance->propertyBlob( |
| value.metaData.name)) |
| { |
| if (blobHandle == RIVE_NULL_HANDLE) |
| { |
| blobProperty->value(nullptr); |
| } |
| else if (auto blob = getBlob(blobHandle)) |
| { |
| blobProperty->value(blob); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find blob " << blobHandle |
| << " to set for view model instance " |
| "when setting property with path " |
| << value.metaData.name; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find " |
| "blob property at path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| case DataType::artboard: |
| { |
| if (auto artboardProperty = |
| viewModelInstance->propertyArtboard( |
| value.metaData.name)) |
| { |
| if (artboardHandle == RIVE_NULL_HANDLE) |
| { |
| artboardProperty->value(nullptr); |
| } |
| else if (auto bindableArtboard = |
| getBindableArtboard( |
| artboardHandle)) |
| { |
| artboardProperty->value(bindableArtboard); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find artboard " |
| << artboardHandle |
| << " to set for view model instance " |
| "when setting property with path " |
| << value.metaData.name; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find " |
| "artboard property at path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| default: |
| RIVE_UNREACHABLE(); |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find view model instance when " |
| "setting property type " |
| << value.metaData.type << " with path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::listViewModelPropertyValue: |
| { |
| ViewModelInstanceHandle handle; |
| uint64_t requestId; |
| CommandQueue::ViewModelInstanceData value; |
| commandStream >> value.metaData.type; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> value.metaData.name; |
| lock.unlock(); |
| |
| if (auto viewModelInstance = getViewModelInstance(handle)) |
| { |
| switch (value.metaData.type) |
| { |
| case DataType::boolean: |
| { |
| if (auto property = |
| viewModelInstance->propertyBoolean( |
| value.metaData.name)) |
| { |
| value.boolValue = property->value(); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find view model " |
| "property " |
| "instance when getting property type " |
| << value.metaData.type << " with path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| case DataType::number: |
| { |
| if (auto property = |
| viewModelInstance->propertyNumber( |
| value.metaData.name)) |
| { |
| value.numberValue = property->value(); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find view model " |
| "property " |
| "instance when getting property type " |
| << value.metaData.type << " with path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| case DataType::color: |
| { |
| if (auto property = |
| viewModelInstance->propertyColor( |
| value.metaData.name)) |
| { |
| value.colorValue = property->value(); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find view model " |
| "property " |
| "instance when getting property type " |
| << value.metaData.type << " with path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| case DataType::string: |
| { |
| if (auto property = |
| viewModelInstance->propertyString( |
| value.metaData.name)) |
| { |
| value.stringValue = property->value(); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find view model " |
| "property " |
| "instance when getting property type " |
| << value.metaData.type << " with path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| case DataType::enumType: |
| { |
| if (auto property = viewModelInstance->propertyEnum( |
| value.metaData.name)) |
| { |
| value.stringValue = property->value(); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find view model " |
| "property " |
| "instance when getting property type " |
| << value.metaData.type << " with path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| default: |
| RIVE_UNREACHABLE(); |
| } |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message:: |
| viewModelPropertyValueReceived; |
| messageStream << handle; |
| messageStream << value.metaData.type; |
| m_commandQueue->m_messageNames << value.metaData.name; |
| messageStream << requestId; |
| switch (value.metaData.type) |
| { |
| case DataType::boolean: |
| messageStream << value.boolValue; |
| break; |
| case DataType::number: |
| messageStream << value.numberValue; |
| break; |
| case DataType::color: |
| messageStream << value.colorValue; |
| break; |
| case DataType::enumType: |
| case DataType::string: |
| m_commandQueue->m_messageNames << value.stringValue; |
| break; |
| default: |
| RIVE_UNREACHABLE(); |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "Could not find view model instance when " |
| "getting property type " |
| << value.metaData.type << " with path " |
| << value.metaData.name; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::getViewModelListSize: |
| { |
| ViewModelInstanceHandle handle; |
| uint64_t requestId; |
| std::string path; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> path; |
| lock.unlock(); |
| |
| if (auto viewModel = getViewModelInstance(handle)) |
| { |
| if (auto property = viewModel->propertyList(path)) |
| { |
| auto size = property->size(); |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream |
| << CommandQueue::Message::viewModelListSizeReceived; |
| messageStream << handle; |
| messageStream << size; |
| messageStream << requestId; |
| m_commandQueue->m_messageNames << path; |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "failed to get list at path " << path |
| << " when getting list size"; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "failed to get view model " << handle |
| << " when getting list size"; |
| } |
| |
| break; |
| } |
| |
| case CommandQueue::Command::clearViewModelList: |
| { |
| ViewModelInstanceHandle handle; |
| uint64_t requestId; |
| std::string path; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> path; |
| lock.unlock(); |
| |
| if (auto viewModel = getViewModelInstance(handle)) |
| { |
| if (auto property = viewModel->propertyList(path)) |
| { |
| property->removeAllInstances(); |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream |
| << CommandQueue::Message::viewModelListCleared; |
| messageStream << handle; |
| messageStream << requestId; |
| m_commandQueue->m_messageNames << path; |
| messageLock.unlock(); |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "failed to get list at path " << path |
| << " when clearing list"; |
| } |
| } |
| else |
| { |
| ErrorReporter<ViewModelInstanceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::viewModelError) |
| << "failed to get view model " << handle |
| << " when clearing list"; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::pointerMove: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| CommandQueue::PointerEvent pointerEvent; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_pointerEvents >> pointerEvent; |
| lock.unlock(); |
| if (auto stateMachineWrapper = getStateMachineWrapper(handle)) |
| { |
| Vec2D position = cursorPosForPointerEvent( |
| stateMachineWrapper->instance.get(), |
| pointerEvent); |
| std::unique_lock<std::mutex> stateMachineLock( |
| stateMachineWrapper->m_mutex); |
| stateMachineWrapper->instance->pointerMove( |
| position, |
| 0.0f, |
| pointerEvent.pointerId); |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine \"" << handle |
| << "\" not found for pointerMove."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::pointerDown: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| CommandQueue::PointerEvent pointerEvent; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_pointerEvents >> pointerEvent; |
| lock.unlock(); |
| if (auto stateMachineWrapper = getStateMachineWrapper(handle)) |
| { |
| Vec2D position = cursorPosForPointerEvent( |
| stateMachineWrapper->instance.get(), |
| pointerEvent); |
| std::unique_lock<std::mutex> stateMachineLock( |
| stateMachineWrapper->m_mutex); |
| auto hitResult = stateMachineWrapper->instance->pointerDown( |
| position, |
| pointerEvent.pointerId); |
| // Process down-driven state changes before a following |
| // pointer up. |
| if (hitResult != HitResult::none) |
| { |
| stateMachineWrapper->instance->advanceAndApply(0.0f); |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine \"" << handle |
| << "\" not found for pointerDown."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::pointerUp: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| CommandQueue::PointerEvent pointerEvent; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_pointerEvents >> pointerEvent; |
| lock.unlock(); |
| if (auto stateMachineWrapper = getStateMachineWrapper(handle)) |
| { |
| Vec2D position = cursorPosForPointerEvent( |
| stateMachineWrapper->instance.get(), |
| pointerEvent); |
| std::unique_lock<std::mutex> stateMachineLock( |
| stateMachineWrapper->m_mutex); |
| auto hitResult = stateMachineWrapper->instance->pointerUp( |
| position, |
| pointerEvent.pointerId); |
| // Process up-driven state changes before any following |
| // pointer exit. |
| if (hitResult != HitResult::none) |
| { |
| stateMachineWrapper->instance->advanceAndApply(0.0f); |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine \"" << handle |
| << "\" not found for pointerUp."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::pointerExit: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| CommandQueue::PointerEvent pointerEvent; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_pointerEvents >> pointerEvent; |
| lock.unlock(); |
| if (auto stateMachineWrapper = getStateMachineWrapper(handle)) |
| { |
| Vec2D position = cursorPosForPointerEvent( |
| stateMachineWrapper->instance.get(), |
| pointerEvent); |
| std::unique_lock<std::mutex> stateMachineLock( |
| stateMachineWrapper->m_mutex); |
| stateMachineWrapper->instance->pointerExit( |
| position, |
| pointerEvent.pointerId); |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine \"" << handle |
| << "\" not found for pointerExit."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::addImageFileAsset: |
| { |
| RenderImageHandle handle; |
| std::string name; |
| uint64_t requestId; |
| CommandQueue::PointerEvent pointerEvent; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> name; |
| lock.unlock(); |
| auto image = ref_rcp(getImage(handle)); |
| if (image != nullptr) |
| { |
| m_globalAssetRegistry->setRenderImage(name, handle); |
| m_globalAssetRegistry->applyRenderImage(name, image); |
| } |
| else |
| { |
| ErrorReporter<RenderImageHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::imageError) |
| << "Invalid image handle when adding global image " |
| "asset \"" |
| << name << "\""; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::removeImageFileAsset: |
| { |
| std::string name; |
| uint64_t requestId; |
| CommandQueue::PointerEvent pointerEvent; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> name; |
| lock.unlock(); |
| m_globalAssetRegistry->removeRenderImage(name); |
| m_globalAssetRegistry->applyRenderImage(name, nullptr); |
| break; |
| } |
| |
| case CommandQueue::Command::addAudioFileAsset: |
| { |
| AudioSourceHandle handle; |
| std::string name; |
| uint64_t requestId; |
| CommandQueue::PointerEvent pointerEvent; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> name; |
| lock.unlock(); |
| auto audioSource = ref_rcp(getAudioSource(handle)); |
| if (audioSource != nullptr) |
| { |
| m_globalAssetRegistry->setAudioSource(name, handle); |
| m_globalAssetRegistry->applyAudioSource(name, audioSource); |
| } |
| else |
| { |
| ErrorReporter<AudioSourceHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::audioError) |
| << "Invalid audio handle when adding global audio " |
| "asset \"" |
| << name << "\""; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::removeAudioFileAsset: |
| { |
| std::string name; |
| uint64_t requestId; |
| CommandQueue::PointerEvent pointerEvent; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> name; |
| lock.unlock(); |
| m_globalAssetRegistry->removeAudioSource(name); |
| m_globalAssetRegistry->applyAudioSource(name, nullptr); |
| break; |
| } |
| |
| case CommandQueue::Command::addFontFileAsset: |
| { |
| FontHandle handle; |
| std::string name; |
| uint64_t requestId; |
| CommandQueue::PointerEvent pointerEvent; |
| commandStream >> handle; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> name; |
| lock.unlock(); |
| auto font = ref_rcp(getFont(handle)); |
| if (font != nullptr) |
| { |
| m_globalAssetRegistry->setFont(name, handle); |
| m_globalAssetRegistry->applyFont(name, font); |
| } |
| else |
| { |
| ErrorReporter<FontHandle>(this, |
| handle, |
| requestId, |
| CommandQueue::Message::fontError) |
| << "Invalid font handle when adding global font asset " |
| "\"" |
| << name << "\""; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::removeFontFileAsset: |
| { |
| std::string name; |
| uint64_t requestId; |
| CommandQueue::PointerEvent pointerEvent; |
| commandStream >> requestId; |
| m_commandQueue->m_names >> name; |
| lock.unlock(); |
| m_globalAssetRegistry->removeFont(name); |
| m_globalAssetRegistry->applyFont(name, nullptr); |
| break; |
| } |
| |
| case CommandQueue::Command::disconnect: |
| { |
| lock.unlock(); |
| m_wasDisconnectReceived = true; |
| return false; |
| } |
| |
| case CommandQueue::Command::focusNext: |
| case CommandQueue::Command::focusPrevious: |
| { |
| const bool moveNext = |
| command == CommandQueue::Command::focusNext; |
| StateMachineHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| if (auto wrapper = getStateMachineWrapper(handle)) |
| { |
| std::unique_lock<std::mutex> stateMachineLock( |
| wrapper->m_mutex); |
| if (moveNext) |
| { |
| wrapper->instance->focusNext(); |
| } |
| else |
| { |
| wrapper->instance->focusPrevious(); |
| } |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle << " not found for " |
| << (moveNext ? "focusNext" : "focusPrevious") << "."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::requestHasFocusNodes: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| if (auto wrapper = getStateMachineWrapper(handle)) |
| { |
| std::unique_lock<std::mutex> stateMachineLock( |
| wrapper->m_mutex); |
| const bool hasFocusNodes = |
| wrapper->instance->hasFocusNodes(); |
| stateMachineLock.unlock(); |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream |
| << CommandQueue::Message::hasFocusNodesReceived; |
| messageStream << handle; |
| messageStream << requestId; |
| messageStream << hasFocusNodes; |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for requestHasFocusNodes."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::clearFocus: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| if (auto wrapper = getStateMachineWrapper(handle)) |
| { |
| std::unique_lock<std::mutex> stateMachineLock( |
| wrapper->m_mutex); |
| wrapper->instance->clearFocus(); |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for clearFocus."; |
| } |
| break; |
| } |
| |
| case CommandQueue::Command::requestFocusState: |
| { |
| StateMachineHandle handle; |
| uint64_t requestId; |
| commandStream >> handle; |
| commandStream >> requestId; |
| lock.unlock(); |
| if (auto wrapper = getStateMachineWrapper(handle)) |
| { |
| std::unique_lock<std::mutex> stateMachineLock( |
| wrapper->m_mutex); |
| const auto focusState = wrapper->instance->focusState(); |
| stateMachineLock.unlock(); |
| |
| std::unique_lock<std::mutex> messageLock( |
| m_commandQueue->m_messageMutex); |
| messageStream << CommandQueue::Message::focusStateReceived; |
| messageStream << handle; |
| messageStream << requestId; |
| messageStream << focusState.hasFocus; |
| messageStream << focusState.expectsKeyboardInput; |
| } |
| else |
| { |
| ErrorReporter<StateMachineHandle>( |
| this, |
| handle, |
| requestId, |
| CommandQueue::Message::stateMachineError) |
| << "State machine " << handle |
| << " not found for requestFocusState."; |
| } |
| break; |
| } |
| } |
| |
| // Should have unlocked by now. |
| assert(!lock.owns_lock()); |
| lock.lock(); |
| } while (!commandStream.empty() && shouldProcessCommands); |
| |
| // Since we always retake the lock at the end of the do while we need to |
| // unlock here. |
| lock.unlock(); |
| |
| for (const auto& drawPair : m_uniqueDraws) |
| { |
| drawPair.second(drawPair.first, this); |
| } |
| |
| m_uniqueDraws.clear(); |
| |
| checkPropertySubscriptions(); |
| |
| return !m_wasDisconnectReceived; |
| } |
| CommandServer::SynchronizedStateMachine::~SynchronizedStateMachine() |
| { |
| std::unique_lock<std::mutex> lock(m_mutex); |
| if (instance != nullptr) |
| { |
| instance->dispose(); |
| } |
| } |
| |
| CommandServer::SynchronizedStateMachine& CommandServer:: |
| SynchronizedStateMachine::operator=(SynchronizedStateMachine&& other) |
| { |
| instance = std::move(other.instance); |
| m_lastSemanticsTransform = other.m_lastSemanticsTransform; |
| m_hasLastSemanticsTransform = other.m_hasLastSemanticsTransform; |
| return *this; |
| } |
| |
| CommandServer::SynchronizedStateMachine::SynchronizedStateMachine( |
| std::unique_ptr<StateMachineInstance> instance) : |
| instance(std::move(instance)) |
| {} |
| |
| bool CommandServer::focusNextSynchronized(StateMachineHandle handle) |
| { |
| std::unique_lock<std::mutex> accessLock(m_stateMachineAccessMutex); |
| auto it = m_stateMachines.find(handle); |
| if (it == m_stateMachines.end()) |
| { |
| return false; |
| } |
| |
| auto stateMachine = it->second; |
| accessLock.unlock(); |
| std::unique_lock<std::mutex> lock(stateMachine->m_mutex); |
| return stateMachine->instance->focusNext(); |
| } |
| |
| bool CommandServer::focusPreviousSynchronized(StateMachineHandle handle) |
| { |
| std::unique_lock<std::mutex> accessLock(m_stateMachineAccessMutex); |
| auto it = m_stateMachines.find(handle); |
| if (it == m_stateMachines.end()) |
| { |
| return false; |
| } |
| |
| auto stateMachine = it->second; |
| accessLock.unlock(); |
| std::unique_lock<std::mutex> lock(stateMachine->m_mutex); |
| return stateMachine->instance->focusPrevious(); |
| } |
| }; // namespace rive |