diff --git a/src/playlist/PlaylistCWrapper.cpp b/src/playlist/PlaylistCWrapper.cpp index 5a368c31a..d7e562695 100644 --- a/src/playlist/PlaylistCWrapper.cpp +++ b/src/playlist/PlaylistCWrapper.cpp @@ -103,6 +103,13 @@ void PlaylistCWrapper::SetPresetSwitchFailedCallback(projectm_playlist_preset_sw } +void PlaylistCWrapper::SetPresetLoadCallback(projectm_playlist_preset_load_event callback, void* userData) +{ + m_presetLoadEventCallback = callback; + m_presetLoadEventUserData = userData; +} + + void PlaylistCWrapper::PlayPresetIndex(uint32_t index, bool hardCut, bool resetFailureCount) { m_hardCutRequested = hardCut; @@ -117,8 +124,22 @@ void PlaylistCWrapper::PlayPresetIndex(uint32_t index, bool hardCut, bool resetF return; } - projectm_load_preset_file(m_projectMInstance, - playlistItems.at(index).Filename().c_str(), !hardCut); + const auto& filename = playlistItems.at(index).Filename(); + + // If a preset load callback is set, give the application a chance to handle loading + if (m_presetLoadEventCallback != nullptr) + { + if (m_presetLoadEventCallback(index, filename.c_str(), hardCut, m_presetLoadEventUserData)) + { + // Application handled the load - return without further action. + // The app is responsible for calling projectm_load_preset_file/data, + // handling errors, and firing the switched event when ready. + return; + } + } + + // Default behavior: load from filesystem + projectm_load_preset_file(m_projectMInstance, filename.c_str(), !hardCut); if (!m_lastPresetSwitchFailed) { @@ -249,6 +270,15 @@ void projectm_playlist_set_preset_switch_failed_event_callback(projectm_playlist } +void projectm_playlist_set_preset_load_event_callback(projectm_playlist_handle instance, + projectm_playlist_preset_load_event callback, + void* user_data) +{ + auto* playlist = playlist_handle_to_instance(instance); + playlist->SetPresetLoadCallback(callback, user_data); +} + + void projectm_playlist_connect(projectm_playlist_handle instance, projectm_handle projectm_instance) { auto* playlist = playlist_handle_to_instance(instance); diff --git a/src/playlist/PlaylistCWrapper.hpp b/src/playlist/PlaylistCWrapper.hpp index 125e22b83..434acead3 100644 --- a/src/playlist/PlaylistCWrapper.hpp +++ b/src/playlist/PlaylistCWrapper.hpp @@ -82,6 +82,14 @@ class PlaylistCWrapper : public Playlist virtual void SetPresetSwitchFailedCallback(projectm_playlist_preset_switch_failed_event callback, void* userData); + /** + * @brief Sets the preset load callback. + * @param callback The callback pointer. + * @param userData The callback context data. + */ + virtual void SetPresetLoadCallback(projectm_playlist_preset_load_event callback, + void* userData); + /** * @brief Sets the last navigation direction used to switch a preset. * This is used when retrying on a failed preset load, keeping the same direction/logic as in the original switch. @@ -111,6 +119,9 @@ class PlaylistCWrapper : public Playlist projectm_playlist_preset_switch_failed_event m_presetSwitchFailedEventCallback{nullptr}; //!< Preset switch failed callback pointer set by the application. void* m_presetSwitchFailedEventUserData{nullptr}; //!< Context data pointer set by the application. + projectm_playlist_preset_load_event m_presetLoadEventCallback{nullptr}; //!< Preset load callback pointer set by the application. + void* m_presetLoadEventUserData{nullptr}; //!< Context data pointer set by the application. + NavigationDirection m_lastNavigationDirection{NavigationDirection::Next}; //!< Last direction used to switch a preset. }; diff --git a/src/playlist/api/projectM-4/playlist_callbacks.h b/src/playlist/api/projectM-4/playlist_callbacks.h index 7735660b0..1c20c040d 100644 --- a/src/playlist/api/projectM-4/playlist_callbacks.h +++ b/src/playlist/api/projectM-4/playlist_callbacks.h @@ -69,6 +69,42 @@ typedef void (*projectm_playlist_preset_switched_event)(bool is_hard_cut, unsign typedef void (*projectm_playlist_preset_switch_failed_event)(const char* preset_filename, const char* message, void* user_data); +/** + * @brief Callback function that is executed when the playlist wants to load a preset. + * + * This callback allows applications to handle preset loading themselves instead of + * letting the playlist library load presets from the filesystem. This is useful for: + * - Loading presets from archives (e.g., ZIP files) + * - Loading presets from network sources (e.g., HTTP) + * - Custom preset storage solutions + * + * When this callback is set and returns true, the playlist library will NOT attempt + * to load the preset file itself and will return immediately without firing any events. + * The application takes full responsibility for: + * - Calling projectm_load_preset_file() or projectm_load_preset_data() with the preset content + * - Handling any loading errors + * - Firing the preset_switched_event callback when the preset is ready (if desired) + * + * Note: If implementing asynchronous loading, the application must complete the load and + * fire the preset_switched_event callback itself after the async operation completes. + * + * If the callback returns false or is not set, the playlist library will use the + * default behavior of loading the preset from the filesystem. + * + * @note The filename pointer is only valid inside the callback. Make a copy if it needs + * to be retained for later use. + * @note Do not call any playlist preset-switching functions from within this callback. + * @param index The playlist index of the preset to be loaded. + * @param filename The preset filename/URL at this index. Can be used as a key or path. + * @param hard_cut True if this should be a hard cut, false for a smooth transition. + * @param user_data A user-defined data pointer that was provided when registering the callback, + * e.g. context information. + * @return True if the application handles preset loading, false to use default behavior. + * @since 4.2.0 + */ +typedef bool (*projectm_playlist_preset_load_event)(unsigned int index, const char* filename, + bool hard_cut, void* user_data); + /** * @brief Sets a callback function that will be called when a preset changes. @@ -105,6 +141,25 @@ PROJECTM_PLAYLIST_EXPORT void projectm_playlist_set_preset_switch_failed_event_c projectm_playlist_preset_switch_failed_event callback, void* user_data); +/** + * @brief Sets a callback function that will be called when the playlist wants to load a preset. + * + * This allows applications to handle preset loading themselves, e.g., from archives, + * network sources, or using custom storage. When set, this callback is called before + * the playlist library attempts to load a preset file. + * + * Only one callback can be registered per playlist instance. To remove the callback, use NULL. + * + * @param instance The playlist manager instance. + * @param callback A pointer to the callback function. + * @param user_data A pointer to any data that will be sent back in the callback, e.g. context + * information. + * @since 4.2.0 + */ +PROJECTM_PLAYLIST_EXPORT void projectm_playlist_set_preset_load_event_callback(projectm_playlist_handle instance, + projectm_playlist_preset_load_event callback, + void* user_data); + #ifdef __cplusplus } // extern "C" #endif