diff --git a/include/node/block.h b/include/node/block.h index fa85a4de7..d5f084260 100644 --- a/include/node/block.h +++ b/include/node/block.h @@ -25,6 +25,8 @@ #include #endif +#include + #include "node/error.h" #ifdef __cplusplus @@ -32,25 +34,40 @@ extern "C" { #endif /** - * @brief Opaque handle to a timeline block (olive::Block). + * @brief Reference-counted handle to a timeline block (olive::Block). * * Covers the whole Block family: ClipBlock, GapBlock and the concrete - * TransitionBlock subclasses. The handle IS the C++ object pointer; no - * wrapper is allocated. Concrete instances are created through the - * oaknode_block_*_create() factories below; callers never touch C++ - * subclasses directly. + * TransitionBlock subclasses. The object never leaves the library that + * created it; every external reference is one of these handles. + * Semantics are shared_ptr-like: the oaknode_block_*_create() factories + * below return a handle with count 1, addref(ctx) takes another + * reference, release(ctx) drops one and the library destroys the object + * when the count reaches zero. Callers never touch C++ subclasses + * directly. + * + * Placing a block on a track (the oaknode_track_*_block() primitives) + * transfers ownership to the track; handles obtained from accessors + * (neighbours, lookups) are borrowed and never destroy the underlying + * object. */ -typedef struct OakNodeBlock OakNodeBlock; +typedef struct OakNodeBlock { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeBlock; /** - * @brief Opaque handle to a track (olive::Track), see node/track.h. + * @brief Reference-counted handle to a track (olive::Track), see + * node/track.h. * * Re-declared here so block.h is self-contained; the typedef is identical. */ typedef struct OakNodeTrack OakNodeTrack; /** - * @brief Opaque handle to a node (olive::Node), see node/node.h. + * @brief Reference-counted handle to a node (olive::Node), see + * node/node.h. * * Re-declared here so block.h is self-contained; the typedef is identical. */ @@ -79,27 +96,33 @@ enum OakNodeTransitionKind { * a project; a block that was never placed must be released with * oaknode_block_free(). * - * @return Block handle, or NULL on allocation failure. + * @return Block handle with reference count 1; ctx is NULL on allocation + * failure. */ -OakNodeBlock *oaknode_block_clip_create(void); +OakNodeBlock oaknode_block_clip_create(void); /** * @brief Create a GapBlock. Ownership as oaknode_block_clip_create(). * - * @return Block handle, or NULL on allocation failure. + * @return Block handle with reference count 1; ctx is NULL on allocation + * failure. */ -OakNodeBlock *oaknode_block_gap_create(void); +OakNodeBlock oaknode_block_gap_create(void); /** * @brief Create a concrete TransitionBlock. * * @param kind One of the OakNodeTransitionKind values. - * @return Block handle, or NULL on invalid kind / allocation failure. + * @return Block handle with reference count 1; ctx is NULL on invalid + * kind / allocation failure. */ -OakNodeBlock *oaknode_block_transition_create(int kind); +OakNodeBlock oaknode_block_transition_create(int kind); /** - * @brief Destroy a block. No-op on NULL. + * @brief Release one reference to a block handle. + * + * Destroys the block when the reference count reaches zero. NULL handle + * or NULL ctx is a no-op; clears `block->ctx` after releasing. * * The block must not be placed on a track or linked to other nodes; the * caller is responsible for detaching it first. @@ -116,32 +139,33 @@ enum OakNodeBlockKind { /** * @brief Concrete kind of a block (dynamic_cast query). */ -int oaknode_block_get_kind(OakNodeBlock *block, int *out_kind); +int oaknode_block_get_kind(OakNodeBlock block, int *out_kind); /** * @brief Borrowed cast from a block handle to its node handle. * - * Every Block is a Node; the result must not be freed. NULL for NULL. + * Every Block is a Node; releasing the result never destroys the block. + * Empty handle for an empty handle. */ -OakNodeNode *oaknode_block_as_node(OakNodeBlock *block); +OakNodeNode oaknode_block_as_node(OakNodeBlock block); /** * @brief Borrowed cast from a node handle to a block handle. * - * Returns NULL if the node is not a Block (or for NULL). + * Returns an empty handle if the node is not a Block (or is empty). */ -OakNodeBlock *oaknode_block_from_node(OakNodeNode *node); +OakNodeBlock oaknode_block_from_node(OakNodeNode node); /** * @brief Rational getters/setters use numerator/denominator out pairs. * * @return OAKNODE_OK or OAKNODE_E_INVALID. */ -int oaknode_block_get_in(OakNodeBlock *block, int *numerator, int *denominator); -int oaknode_block_set_in(OakNodeBlock *block, int numerator, int denominator); -int oaknode_block_get_out(OakNodeBlock *block, int *numerator, int *denominator); -int oaknode_block_set_out(OakNodeBlock *block, int numerator, int denominator); -int oaknode_block_get_length(OakNodeBlock *block, int *numerator, +int oaknode_block_get_in(OakNodeBlock block, int *numerator, int *denominator); +int oaknode_block_set_in(OakNodeBlock block, int numerator, int denominator); +int oaknode_block_get_out(OakNodeBlock block, int *numerator, int *denominator); +int oaknode_block_set_out(OakNodeBlock block, int numerator, int denominator); +int oaknode_block_get_length(OakNodeBlock block, int *numerator, int *denominator); /** @@ -150,9 +174,9 @@ int oaknode_block_get_length(OakNodeBlock *block, int *numerator, * * @return OAKNODE_OK or OAKNODE_E_INVALID. */ -int oaknode_block_set_length_and_media_out(OakNodeBlock *block, int numerator, +int oaknode_block_set_length_and_media_out(OakNodeBlock block, int numerator, int denominator); -int oaknode_block_set_length_and_media_in(OakNodeBlock *block, int numerator, +int oaknode_block_set_length_and_media_in(OakNodeBlock block, int numerator, int denominator); /** @@ -160,18 +184,18 @@ int oaknode_block_set_length_and_media_in(OakNodeBlock *block, int numerator, * * @return OAKNODE_OK or OAKNODE_E_INVALID. */ -int oaknode_block_get_enabled(OakNodeBlock *block, int *enabled); -int oaknode_block_set_enabled(OakNodeBlock *block, int enabled); +int oaknode_block_get_enabled(OakNodeBlock block, int *enabled); +int oaknode_block_set_enabled(OakNodeBlock block, int enabled); /** - * @brief Adjacency accessors. `out` receives a borrowed handle (NULL when + * @brief Adjacency accessors. `out` receives a borrowed handle (empty when * there is no neighbour / the block is not on a track). * * @return OAKNODE_OK or OAKNODE_E_INVALID. */ -int oaknode_block_get_previous(OakNodeBlock *block, OakNodeBlock **out); -int oaknode_block_get_next(OakNodeBlock *block, OakNodeBlock **out); -int oaknode_block_get_track(OakNodeBlock *block, OakNodeTrack **out); +int oaknode_block_get_previous(OakNodeBlock block, OakNodeBlock *out); +int oaknode_block_get_next(OakNodeBlock block, OakNodeBlock *out); +int oaknode_block_get_track(OakNodeBlock block, OakNodeTrack *out); /** * @brief Link two blocks (olive::Node::link/unlink/are_linked). @@ -181,24 +205,24 @@ int oaknode_block_get_track(OakNodeBlock *block, OakNodeTrack **out); * @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_FAILED (already * linked / not linked). */ -int oaknode_block_link(OakNodeBlock *a, OakNodeBlock *b); -int oaknode_block_unlink(OakNodeBlock *a, OakNodeBlock *b); -int oaknode_block_are_linked(OakNodeBlock *a, OakNodeBlock *b, int *linked); +int oaknode_block_link(OakNodeBlock a, OakNodeBlock b); +int oaknode_block_unlink(OakNodeBlock a, OakNodeBlock b); +int oaknode_block_are_linked(OakNodeBlock a, OakNodeBlock b, int *linked); /** * @brief Number of blocks linked to `block` (olive::Node::links()). * * @return OAKNODE_OK or OAKNODE_E_INVALID. */ -int oaknode_block_get_link_count(OakNodeBlock *block, int *count); +int oaknode_block_get_link_count(OakNodeBlock block, int *count); /** * @brief Borrowed handle to the linked block at `index`. * * @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND. */ -int oaknode_block_get_link_at(OakNodeBlock *block, int index, - OakNodeBlock **out); +int oaknode_block_get_link_at(OakNodeBlock block, int index, + OakNodeBlock *out); /* ---------------------------------------------------------------- Clip */ @@ -206,41 +230,41 @@ int oaknode_block_get_link_at(OakNodeBlock *block, int index, * @brief Media in/out accessors (olive::ClipBlock). Non-clip blocks return * OAKNODE_E_INVALID. */ -int oaknode_clip_get_media_in(OakNodeBlock *clip, int *numerator, +int oaknode_clip_get_media_in(OakNodeBlock clip, int *numerator, int *denominator); -int oaknode_clip_set_media_in(OakNodeBlock *clip, int numerator, +int oaknode_clip_set_media_in(OakNodeBlock clip, int numerator, int denominator); /** * @brief Playback speed factor, 1.0 = normal (olive::ClipBlock speed input). */ -int oaknode_clip_get_speed(OakNodeBlock *clip, double *speed); -int oaknode_clip_set_speed(OakNodeBlock *clip, double speed); +int oaknode_clip_get_speed(OakNodeBlock clip, double *speed); +int oaknode_clip_set_speed(OakNodeBlock clip, double speed); /** * @brief Reverse playback flag. */ -int oaknode_clip_get_reverse(OakNodeBlock *clip, int *reverse); -int oaknode_clip_set_reverse(OakNodeBlock *clip, int reverse); +int oaknode_clip_get_reverse(OakNodeBlock clip, int *reverse); +int oaknode_clip_set_reverse(OakNodeBlock clip, int reverse); /** * @brief Maintain-audio-pitch flag. */ -int oaknode_clip_get_maintain_audio_pitch(OakNodeBlock *clip, int *maintain); -int oaknode_clip_set_maintain_audio_pitch(OakNodeBlock *clip, int maintain); +int oaknode_clip_get_maintain_audio_pitch(OakNodeBlock clip, int *maintain); +int oaknode_clip_set_maintain_audio_pitch(OakNodeBlock clip, int maintain); /** * @brief Loop mode, one of the OakLoopMode values * (olive::ClipBlock::loop_mode/set_loop_mode). */ -int oaknode_clip_get_loop_mode(OakNodeBlock *clip, int *loop_mode); -int oaknode_clip_set_loop_mode(OakNodeBlock *clip, int loop_mode); +int oaknode_clip_get_loop_mode(OakNodeBlock clip, int *loop_mode); +int oaknode_clip_set_loop_mode(OakNodeBlock clip, int loop_mode); /** * @brief Type of the track the clip sits on (OakNodeTrackType values, * OAKNODE_TRACK_TYPE_NONE when trackless). */ -int oaknode_clip_get_track_type(OakNodeBlock *clip, int *type); +int oaknode_clip_get_track_type(OakNodeBlock clip, int *type); /* ----------------------------------------------------------- Transition */ @@ -248,39 +272,39 @@ int oaknode_clip_get_track_type(OakNodeBlock *clip, int *type); * @brief Transition offsets (olive::TransitionBlock). Non-transition blocks * return OAKNODE_E_INVALID. */ -int oaknode_transition_get_in_offset(OakNodeBlock *transition, int *numerator, +int oaknode_transition_get_in_offset(OakNodeBlock transition, int *numerator, int *denominator); -int oaknode_transition_get_out_offset(OakNodeBlock *transition, int *numerator, +int oaknode_transition_get_out_offset(OakNodeBlock transition, int *numerator, int *denominator); -int oaknode_transition_get_offset_center(OakNodeBlock *transition, +int oaknode_transition_get_offset_center(OakNodeBlock transition, int *numerator, int *denominator); -int oaknode_transition_set_offset_center(OakNodeBlock *transition, +int oaknode_transition_set_offset_center(OakNodeBlock transition, int numerator, int denominator); -int oaknode_transition_set_offsets_and_length(OakNodeBlock *transition, +int oaknode_transition_set_offsets_and_length(OakNodeBlock transition, int in_num, int in_den, int out_num, int out_den); /** * @brief Whether both sides of the transition are connected to clips. */ -int oaknode_transition_is_dual(OakNodeBlock *transition, int *dual); +int oaknode_transition_is_dual(OakNodeBlock transition, int *dual); /** - * @brief Borrowed handles to the connected out/in side blocks (NULL when + * @brief Borrowed handles to the connected out/in side blocks (empty when * unconnected). */ -int oaknode_transition_get_connected_out_block(OakNodeBlock *transition, - OakNodeBlock **out); -int oaknode_transition_get_connected_in_block(OakNodeBlock *transition, - OakNodeBlock **out); +int oaknode_transition_get_connected_out_block(OakNodeBlock transition, + OakNodeBlock *out); +int oaknode_transition_get_connected_in_block(OakNodeBlock transition, + OakNodeBlock *out); /** * @brief Forward cache passthroughs from another clip * (ClipBlock::add_cache_passthrough_from()). Used after splitting a * clip so the new part shares the render caches. */ -int oaknode_clip_add_cache_passthrough_from(OakNodeBlock *clip, - OakNodeBlock *other); +int oaknode_clip_add_cache_passthrough_from(OakNodeBlock clip, + OakNodeBlock other); #ifdef __cplusplus } diff --git a/include/node/colormanager.h b/include/node/colormanager.h index 0ea0867bc..b2571548a 100644 --- a/include/node/colormanager.h +++ b/include/node/colormanager.h @@ -25,28 +25,31 @@ #include #endif +#include + #include "common/colortransform.h" #include "node/error.h" +#include "node/project.h" #ifdef __cplusplus extern "C" { #endif /** - * @brief Opaque handle to a color manager (olive::ColorManager). + * @brief Reference-counted handle to a color manager + * (olive::ColorManager). * - * Unlike node handles this one IS a wrapper allocation (ColorManager is - * not a Node); release with oaknode_colormanager_free(). + * Semantics are shared_ptr-like: oaknode_colormanager_init() returns a + * handle whose object has reference count 1, addref(ctx) takes another + * reference, and release(ctx) (or oaknode_colormanager_free()) drops + * one; the library destroys the object when the count reaches zero. */ -typedef struct OakNodeColorManager OakNodeColorManager; - -/** - * @brief Opaque borrowed handle to a project (olive::Project). - * - * Owned by the project family; re-declared here so this header is - * self-contained. - */ -typedef struct OakNodeProject OakNodeProject; +typedef struct OakNodeColorManager { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeColorManager; /** * @brief Create a color manager bound to `project` (borrowed). @@ -56,12 +59,16 @@ typedef struct OakNodeProject OakNodeProject; * oaknode_colormanager_update_config_from_filename()) before using the * config-dependent queries. * - * @return Manager handle, or NULL on NULL project / allocation failure. + * @return Manager handle with reference count 1 (release with + * oaknode_colormanager_free()); ctx is NULL on an empty project + * handle or allocation failure. */ -OakNodeColorManager *oaknode_colormanager_init(OakNodeProject *project); +OakNodeColorManager oaknode_colormanager_init(OakNodeProject project); /** - * @brief Destroy a color manager. No-op on NULL. + * @brief Release the caller's reference to the color manager and null + * out the handle. No-op on NULL or an empty handle; the object is + * destroyed when its reference count reaches zero. */ void oaknode_colormanager_free(OakNodeColorManager *manager); @@ -72,7 +79,7 @@ void oaknode_colormanager_free(OakNodeColorManager *manager); * @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_FAILED (the OCIO * config could not be created). */ -int oaknode_colormanager_initialize(OakNodeColorManager *manager); +int oaknode_colormanager_initialize(OakNodeColorManager manager); /** * @brief (Re)build the process-wide default OCIO config @@ -87,9 +94,9 @@ int oaknode_colormanager_set_up_default_config(void); * returns the required buffer size in bytes including NUL; pass * buf == NULL or a too-small buffer to query the size. */ -int oaknode_colormanager_get_config_filename(OakNodeColorManager *manager, +int oaknode_colormanager_get_config_filename(OakNodeColorManager manager, char *buf, int buf_size); -int oaknode_colormanager_set_config_filename(OakNodeColorManager *manager, +int oaknode_colormanager_set_config_filename(OakNodeColorManager manager, const char *filename); /** @@ -98,21 +105,21 @@ int oaknode_colormanager_set_config_filename(OakNodeColorManager *manager, * olive::ColorManager::update_config_from_filename(). */ int oaknode_colormanager_update_config_from_filename( - OakNodeColorManager *manager); + OakNodeColorManager manager); /** * @brief Default input colorspace. Two-stage string accessor. */ int oaknode_colormanager_get_default_input_color_space( - OakNodeColorManager *manager, char *buf, int buf_size); + OakNodeColorManager manager, char *buf, int buf_size); int oaknode_colormanager_set_default_input_color_space( - OakNodeColorManager *manager, const char *colorspace); + OakNodeColorManager manager, const char *colorspace); /** * @brief Reference (working) colorspace. Two-stage string getter. */ int oaknode_colormanager_get_reference_color_space( - OakNodeColorManager *manager, char *buf, int buf_size); + OakNodeColorManager manager, char *buf, int buf_size); /** * @brief Return `colorspace` when the active config lists it, otherwise the @@ -120,7 +127,7 @@ int oaknode_colormanager_get_reference_color_space( * (OAKNODE_E_STATE when none is loaded). */ int oaknode_colormanager_get_compliant_color_space( - OakNodeColorManager *manager, const char *colorspace, char *buf, + OakNodeColorManager manager, const char *colorspace, char *buf, int buf_size); /** @@ -130,7 +137,7 @@ int oaknode_colormanager_get_compliant_color_space( * (OAKNODE_E_STATE when none is loaded). */ int oaknode_colormanager_get_colorspace_for_ffmpeg_tags( - OakNodeColorManager *manager, int primaries, int trc, char *buf, + OakNodeColorManager manager, int primaries, int trc, char *buf, int buf_size); /** @@ -138,27 +145,27 @@ int oaknode_colormanager_get_colorspace_for_ffmpeg_tags( * All require a loaded config (OAKNODE_E_STATE otherwise); index out of * range yields OAKNODE_E_NOT_FOUND. */ -int oaknode_colormanager_get_display_count(OakNodeColorManager *manager, +int oaknode_colormanager_get_display_count(OakNodeColorManager manager, int *count); -int oaknode_colormanager_get_display_at(OakNodeColorManager *manager, +int oaknode_colormanager_get_display_at(OakNodeColorManager manager, int index, char *buf, int buf_size); -int oaknode_colormanager_get_default_display(OakNodeColorManager *manager, +int oaknode_colormanager_get_default_display(OakNodeColorManager manager, char *buf, int buf_size); -int oaknode_colormanager_get_view_count(OakNodeColorManager *manager, +int oaknode_colormanager_get_view_count(OakNodeColorManager manager, const char *display, int *count); -int oaknode_colormanager_get_view_at(OakNodeColorManager *manager, +int oaknode_colormanager_get_view_at(OakNodeColorManager manager, const char *display, int index, char *buf, int buf_size); -int oaknode_colormanager_get_default_view(OakNodeColorManager *manager, +int oaknode_colormanager_get_default_view(OakNodeColorManager manager, const char *display, char *buf, int buf_size); -int oaknode_colormanager_get_look_count(OakNodeColorManager *manager, +int oaknode_colormanager_get_look_count(OakNodeColorManager manager, int *count); -int oaknode_colormanager_get_look_at(OakNodeColorManager *manager, int index, +int oaknode_colormanager_get_look_at(OakNodeColorManager manager, int index, char *buf, int buf_size); -int oaknode_colormanager_get_colorspace_count(OakNodeColorManager *manager, +int oaknode_colormanager_get_colorspace_count(OakNodeColorManager manager, int *count); -int oaknode_colormanager_get_colorspace_at(OakNodeColorManager *manager, +int oaknode_colormanager_get_colorspace_at(OakNodeColorManager manager, int index, char *buf, int buf_size); @@ -166,7 +173,7 @@ int oaknode_colormanager_get_colorspace_at(OakNodeColorManager *manager, * @brief Default luma coefficients of the active config into rgb[3]. * Requires a loaded config (OAKNODE_E_STATE otherwise). */ -int oaknode_colormanager_get_default_luma_coefs(OakNodeColorManager *manager, +int oaknode_colormanager_get_default_luma_coefs(OakNodeColorManager manager, double rgb[3]); /** @@ -179,7 +186,7 @@ int oaknode_colormanager_get_default_luma_coefs(OakNodeColorManager *manager, * loaded config (OAKNODE_E_STATE otherwise). */ int oaknode_colormanager_get_compliant_color_transform( - OakNodeColorManager *manager, OakColorTransform transform, + OakNodeColorManager manager, OakColorTransform transform, int force_display, OakColorTransform *out); #ifdef __cplusplus diff --git a/include/node/error.h b/include/node/error.h index c9a0b9857..bb8033f24 100644 --- a/include/node/error.h +++ b/include/node/error.h @@ -29,6 +29,16 @@ * failure. String getters return the required buffer size in bytes * (including the terminating NUL) as a non-negative value instead. */ +/** + * @brief Current ABI version stamped into every oaknode handle. + * + * Bump whenever a handle layout or the semantics of any exported + * function change incompatibly. Consumers should compare a handle's + * abi_version field against the value they were compiled with before + * dereferencing ctx. + */ +#define OAKNODE_ABI_VERSION 1 + #define OAKNODE_OK 0 /**< Success. */ #define OAKNODE_E_INVALID (-1) /**< NULL handle or invalid argument. */ #define OAKNODE_E_STATE (-2) /**< Call not valid in the current state. */ diff --git a/include/node/factory.h b/include/node/factory.h index d27aef920..5b6c6c35b 100644 --- a/include/node/factory.h +++ b/include/node/factory.h @@ -34,9 +34,11 @@ extern "C" { * internal node-type library. * * The library must be populated with oaknode_factory_initialize() before - * any other call; oaknode_factory_destroy() releases it. Prototype nodes - * from oaknode_factory_node_at() are owned by the library: read-only - * metadata queries only, never free them or add them to a graph. + * any other call; oaknode_factory_destroy() releases it. The factory is + * a process-wide singleton (static olive::NodeFactory), so there is no + * OakNodeFactory handle type. Prototype nodes from + * oaknode_factory_node_at() are owned by the library: read-only metadata + * queries only, never add them to a graph. */ /** @@ -77,17 +79,19 @@ int oaknode_factory_name_from_id(const char *type_id, char *buf, /** * @brief Create a node of `type_id` WITHOUT adding it to any graph * (NodeFactory::create_from_id()). The caller owns the returned node - * and must release it with oaknode_node_free() while it is still - * orphaned. Returns NULL when the id is unknown or not initialized. + * (reference count 1) and must release it with oaknode_node_free() while + * it is still orphaned. ctx is NULL when the id is unknown or not + * initialized. */ -OakNodeNode *oaknode_factory_create_from_id(const char *type_id); +OakNodeNode oaknode_factory_create_from_id(const char *type_id); /** - * @brief Borrow the prototype node at `index` in the library. + * @brief Borrow the prototype node at `index` in the library (non-owning + * handle written to `out_node`; release it with oaknode_node_free()). * OAKNODE_E_NOT_FOUND for an out-of-range index, OAKNODE_E_STATE when * not initialized. */ -int oaknode_factory_node_at(int index, OakNodeNode **out_node); +int oaknode_factory_node_at(int index, OakNodeNode *out_node); #ifdef __cplusplus } diff --git a/include/node/folder.h b/include/node/folder.h index f75e48cfd..a2387c30a 100644 --- a/include/node/folder.h +++ b/include/node/folder.h @@ -21,6 +21,8 @@ #ifndef OAK_EDITOR_NODE_FOLDER_H #define OAK_EDITOR_NODE_FOLDER_H +#include + #include "node/error.h" #include "undo/undocommand.h" #include "node/project.h" @@ -42,50 +44,76 @@ extern "C" { * facade layer's job, not this layer's. */ +/** + * @brief Reference-counted handle to a folder node (olive::Folder). + * + * Semantics are shared_ptr-like (see OakNodeProject): addref(ctx) takes a + * reference, release(ctx) drops one. Folder handles handed out by this API + * are borrowed views into the owning project's graph: releasing them only + * releases the handle itself, never the folder. + */ +typedef struct OakNodeFolder { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeFolder; + /** * @brief Create a folder node owned by `project`. * * The folder is added to the project's graph (Project::add_node()) but is * NOT attached under any parent folder; use oaknode_folder_add_child() to - * place it. The handle is borrowed: the project owns the folder. + * place it. The returned handle is borrowed: the project owns the folder, + * so releasing the handle only releases the handle itself. * - * @return Folder handle, or NULL on failure. + * @return Folder handle; ctx is NULL on failure. */ -OakNodeFolder *oaknode_folder_create(OakNodeProject *project); +OakNodeFolder oaknode_folder_create(OakNodeProject project); /** * @brief Number of direct item children (Folder::item_child_count()). - * Negative OAKNODE_E_* code on NULL. + * Negative OAKNODE_E_* code on an empty handle. */ -int oaknode_folder_child_count(const OakNodeFolder *folder); +int oaknode_folder_child_count(OakNodeFolder folder); /** * @brief Borrowed node handle of the item child at `index` - * (Folder::item_child()). NULL when out of range. + * (Folder::item_child()). + * + * The returned handle only releases the handle itself. Empty handle + * (ctx == NULL) when out of range. */ -OakNodeNode *oaknode_folder_child_at(const OakNodeFolder *folder, int index); +OakNodeNode oaknode_folder_child_at(OakNodeFolder folder, int index); /** * @brief Add `child` as a direct item child of `folder` (live, non-undoable; * executes FolderAddChild::redo()). * + * After a successful call the graph owns `child`: releasing the child + * handle only releases the handle itself. + * * @return OAKNODE_OK, OAKNODE_E_STATE if `child` already belongs to a * folder, or another negative OAKNODE_E_* error code. */ -int oaknode_folder_add_child(OakNodeFolder *folder, OakNodeNode *child); +int oaknode_folder_add_child(OakNodeFolder folder, OakNodeNode child); /** * @brief Borrowed cast from a folder handle to its node handle. - * NULL for NULL. + * + * The returned handle only releases the handle itself. Empty handle for an + * empty handle. */ -OakNodeNode *oaknode_folder_as_node(OakNodeFolder *folder); +OakNodeNode oaknode_folder_as_node(OakNodeFolder folder); /** - * @brief Create an undoable FolderAddChild command. Owned; free with - * oakundo_command_free(). + * @brief Create an undoable FolderAddChild command. + * + * @return Command handle with reference count 1 (release with + * oakundo_command_free()); ctx is NULL on failure. */ OakUndoCommand oaknode_command_create_folder_add_child( - OakNodeFolder *folder, OakNodeNode *child); + OakNodeFolder folder, OakNodeNode child); /** * @brief Remove `child` from `folder` without deleting it (live, @@ -94,41 +122,46 @@ OakUndoCommand oaknode_command_create_folder_add_child( * @return OAKNODE_OK, OAKNODE_E_NOT_FOUND if `child` is not a direct child, * or another negative OAKNODE_E_* error code. */ -int oaknode_folder_remove_child(OakNodeFolder *folder, OakNodeNode *child); +int oaknode_folder_remove_child(OakNodeFolder folder, OakNodeNode child); /** * @brief Move several nodes into `dest_folder` (live, non-undoable). * * Each node is removed from its current folder (if any) and appended to - * `dest_folder`. Nodes already directly inside `dest_folder` are skipped. + * `dest_folder`; the graph assumes the lifetime of every moved node. Nodes + * already directly inside `dest_folder` are skipped. * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_folder_move_children(OakNodeNode *const *nodes, int count, - OakNodeFolder *dest_folder); +int oaknode_folder_move_children(const OakNodeNode *nodes, int count, + OakNodeFolder dest_folder); /** * @brief 1 if `folder` recursively contains `child`, 0 otherwise - * (Folder::has_child_recursive()). Negative OAKNODE_E_* code on NULL args. + * (Folder::has_child_recursive()). Negative OAKNODE_E_* code on empty + * handles. */ -int oaknode_folder_has_child_recursive(const OakNodeFolder *folder, - const OakNodeNode *child); +int oaknode_folder_has_child_recursive(OakNodeFolder folder, + OakNodeNode child); /** * @brief Index of `child` in `folder`'s direct children * (Folder::index_of_child()). * * @return The index, OAKNODE_E_NOT_FOUND if not a direct child, or - * OAKNODE_E_INVALID on NULL args. + * OAKNODE_E_INVALID on empty handles. */ -int oaknode_folder_index_of_child(const OakNodeFolder *folder, - const OakNodeNode *child); +int oaknode_folder_index_of_child(OakNodeFolder folder, + OakNodeNode child); /** * @brief Borrowed handle of the folder a node currently belongs to - * (Node::folder()), or NULL if the node is not in any folder. + * (Node::folder()). + * + * The returned handle only releases the handle itself. Empty handle + * (ctx == NULL) if the node is not in any folder. */ -OakNodeFolder *oaknode_folder_parent_of(const OakNodeNode *node); +OakNodeFolder oaknode_folder_parent_of(OakNodeNode node); #ifdef __cplusplus } diff --git a/include/node/footage.h b/include/node/footage.h index b570a89f6..939728110 100644 --- a/include/node/footage.h +++ b/include/node/footage.h @@ -50,26 +50,41 @@ extern "C" { */ /** - * @brief Opaque footage handle. Borrowed from the owning project. + * @brief Reference-counted handle to a footage node (olive::Footage). + * + * Semantics are shared_ptr-like (see OakNodeProject): addref(ctx) takes a + * reference, release(ctx) drops one. Footage handles handed out by this + * API are borrowed views into the owning project's graph: releasing them + * only releases the handle itself, never the footage. */ -typedef struct OakNodeFootage OakNodeFootage; +typedef struct OakNodeFootage { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeFootage; /** * @brief Create a footage node owned by `project` (added to the project's * graph, not attached to any folder). * + * The returned handle is borrowed: the project owns the footage, so + * releasing the handle only releases the handle itself. + * * @param filename Initial media path, may be NULL/empty. * - * @return Footage handle, or NULL on failure. + * @return Footage handle; ctx is NULL on failure. */ -OakNodeFootage *oaknode_footage_create(OakNodeProject *project, - const char *filename); +OakNodeFootage oaknode_footage_create(OakNodeProject project, + const char *filename); /** * @brief Borrowed cast from a footage handle to its node handle. - * NULL for NULL. + * + * The returned handle only releases the handle itself. Empty handle for an + * empty handle. */ -OakNodeNode *oaknode_footage_as_node(OakNodeFootage *footage); +OakNodeNode oaknode_footage_as_node(OakNodeFootage footage); /** * @brief Current media path (Footage::filename()). Two-stage string getter. @@ -77,7 +92,7 @@ OakNodeNode *oaknode_footage_as_node(OakNodeFootage *footage); * @return Required buffer size in bytes including the NUL, or a negative * OAKNODE_E_* error code. */ -int oaknode_footage_filename(const OakNodeFootage *footage, char *buf, +int oaknode_footage_filename(OakNodeFootage footage, char *buf, int buf_size); /** @@ -86,13 +101,14 @@ int oaknode_footage_filename(const OakNodeFootage *footage, char *buf, * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_footage_set_filename(OakNodeFootage *footage, const char *filename); +int oaknode_footage_set_filename(OakNodeFootage footage, const char *filename); /** * @brief 1 if the footage was successfully probed and is ready for use - * (Footage::is_valid()), 0 otherwise. Negative OAKNODE_E_* code on NULL. + * (Footage::is_valid()), 0 otherwise. Negative OAKNODE_E_* code on an + * empty handle. */ -int oaknode_footage_is_valid(const OakNodeFootage *footage); +int oaknode_footage_is_valid(OakNodeFootage footage); /** * @brief Last-modified timestamp of the media file in milliseconds since the @@ -102,7 +118,7 @@ int oaknode_footage_is_valid(const OakNodeFootage *footage); * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_footage_timestamp(const OakNodeFootage *footage, +int oaknode_footage_timestamp(OakNodeFootage footage, int64_t *out_timestamp); /** @@ -110,38 +126,38 @@ int oaknode_footage_timestamp(const OakNodeFootage *footage, * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_footage_set_timestamp(OakNodeFootage *footage, int64_t timestamp); +int oaknode_footage_set_timestamp(OakNodeFootage footage, int64_t timestamp); /** * @brief Decoder ID recorded when the footage was probed * (Footage::decoder()). Two-stage string getter. */ -int oaknode_footage_decoder(const OakNodeFootage *footage, char *buf, +int oaknode_footage_decoder(OakNodeFootage footage, char *buf, int buf_size); /** * @brief Total number of streams (Footage::get_total_stream_count()). - * Negative OAKNODE_E_* code on NULL. + * Negative OAKNODE_E_* code on an empty handle. */ -int oaknode_footage_total_stream_count(const OakNodeFootage *footage); +int oaknode_footage_total_stream_count(OakNodeFootage footage); /** * @brief Number of video streams (ViewerOutput::get_video_stream_count()). - * Negative OAKNODE_E_* code on NULL. + * Negative OAKNODE_E_* code on an empty handle. */ -int oaknode_footage_video_stream_count(const OakNodeFootage *footage); +int oaknode_footage_video_stream_count(OakNodeFootage footage); /** * @brief Number of audio streams (ViewerOutput::get_audio_stream_count()). - * Negative OAKNODE_E_* code on NULL. + * Negative OAKNODE_E_* code on an empty handle. */ -int oaknode_footage_audio_stream_count(const OakNodeFootage *footage); +int oaknode_footage_audio_stream_count(OakNodeFootage footage); /** * @brief Number of subtitle streams (ViewerOutput::get_subtitle_stream_count()). - * Negative OAKNODE_E_* code on NULL. + * Negative OAKNODE_E_* code on an empty handle. */ -int oaknode_footage_subtitle_stream_count(const OakNodeFootage *footage); +int oaknode_footage_subtitle_stream_count(OakNodeFootage footage); /** * @brief Footage duration as a rational number of seconds @@ -152,34 +168,34 @@ int oaknode_footage_subtitle_stream_count(const OakNodeFootage *footage); * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_footage_duration(const OakNodeFootage *footage, - int *out_numerator, int *out_denominator); +int oaknode_footage_duration(OakNodeFootage footage, int *out_numerator, + int *out_denominator); /** * @brief 1 if proxy playback is enabled (Footage::proxy_enabled()). - * Negative OAKNODE_E_* code on NULL. + * Negative OAKNODE_E_* code on an empty handle. */ -int oaknode_footage_proxy_enabled(const OakNodeFootage *footage); +int oaknode_footage_proxy_enabled(OakNodeFootage footage); /** * @brief Enable/disable proxy playback (Footage::set_proxy_enabled()). * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_footage_set_proxy_enabled(OakNodeFootage *footage, int enabled); +int oaknode_footage_set_proxy_enabled(OakNodeFootage footage, int enabled); /** * @brief Proxy file path, or "" when none (Footage::proxy_path()). * Two-stage string getter. */ -int oaknode_footage_proxy_path(const OakNodeFootage *footage, char *buf, +int oaknode_footage_proxy_path(OakNodeFootage footage, char *buf, int buf_size); /** * @brief Proxy state enum value (Footage::proxy_state(): - * ProxyManager::ProxyState). Negative OAKNODE_E_* code on NULL. + * ProxyManager::ProxyState). Negative OAKNODE_E_* code on an empty handle. */ -int oaknode_footage_proxy_state(const OakNodeFootage *footage); +int oaknode_footage_proxy_state(OakNodeFootage footage); /** * @brief Set all proxy fields at once (Footage::set_proxy()). @@ -192,7 +208,7 @@ int oaknode_footage_proxy_state(const OakNodeFootage *footage); * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_footage_set_proxy(OakNodeFootage *footage, const char *path, +int oaknode_footage_set_proxy(OakNodeFootage footage, const char *path, int state, int video_stream_index, int preset_version, int enabled); @@ -201,7 +217,7 @@ int oaknode_footage_set_proxy(OakNodeFootage *footage, const char *path, * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_footage_clear_proxy(OakNodeFootage *footage); +int oaknode_footage_clear_proxy(OakNodeFootage footage); /** * @brief Video stream parameters as an oakcommon video-params handle @@ -209,20 +225,20 @@ int oaknode_footage_clear_proxy(OakNodeFootage *footage); * reference count 1 (release with oakcommon_videoparams_free()). * OAKNODE_E_NOT_FOUND for an out-of-range index. */ -int oaknode_footage_get_video_params(OakNodeFootage *footage, int index, +int oaknode_footage_get_video_params(OakNodeFootage footage, int index, OakVideoParams *out); /** * @brief Set a video stream's parameters from an oakcommon handle * (ViewerOutput::set_video_params()). */ -int oaknode_footage_set_video_params(OakNodeFootage *footage, int index, +int oaknode_footage_set_video_params(OakNodeFootage footage, int index, const OakVideoParams *params); /** * @brief Video length as a rational pair (ViewerOutput::get_video_length()). */ -int oaknode_footage_get_video_length(OakNodeFootage *footage, +int oaknode_footage_get_video_length(OakNodeFootage footage, int64_t *out_num, int64_t *out_den); /** @@ -230,7 +246,7 @@ int oaknode_footage_get_video_length(OakNodeFootage *footage, * (Footage::set_cancel_pointer()). `atom` may be an empty OakCancelAtom * (ctx == NULL) to clear. */ -int oaknode_footage_set_cancel_atom(OakNodeFootage *footage, +int oaknode_footage_set_cancel_atom(OakNodeFootage footage, OakCancelAtom atom); #ifdef __cplusplus diff --git a/include/node/group.h b/include/node/group.h index 7a3cf5641..eb9bcc527 100644 --- a/include/node/group.h +++ b/include/node/group.h @@ -21,6 +21,8 @@ #ifndef OAK_EDITOR_NODE_GROUP_H #define OAK_EDITOR_NODE_GROUP_H +#include + #include "node/error.h" #include "node/node.h" #include "undo/undocommand.h" @@ -34,33 +36,51 @@ extern "C" { * @brief C ABI for olive::NodeGroup (src/node/src/group/group.h): * input passthrough management and input resolution. * - * An OakNodeGroup is a reinterpreted olive::NodeGroup (a Node subclass); - * group handles borrow the same lifetime rules as OakNodeNode. + * An OakNodeGroup wraps an olive::NodeGroup (a Node subclass); group + * handles share the reference-counted lifetime rules of OakNodeNode. */ /** - * @brief Opaque group handle (olive::NodeGroup). + * @brief Reference-counted handle to a node group (olive::NodeGroup). + * + * The object never leaves the library that created it; every external + * reference is one of these handles. Semantics are shared_ptr-like: + * oaknode_group_create() returns a handle with count 1, addref(ctx) + * takes another reference, release(ctx) drops one and the library + * destroys the object when the count reaches zero. Handles returned by + * oaknode_group_cast() are borrowed views of a node: releasing them + * never destroys the underlying group. */ -typedef struct OakNodeGroup OakNodeGroup; +typedef struct OakNodeGroup { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeGroup; /** * @brief Create a standalone NodeGroup (owned; release with - * oaknode_node_free() on the OakNodeNode view or oaknode_group_free() - * while still orphaned). + * oaknode_group_free() while still orphaned). * - * @return Group handle, or NULL on allocation failure. + * @return Group handle with count 1; ctx is NULL on allocation failure. */ -OakNodeGroup *oaknode_group_create(void); +OakNodeGroup oaknode_group_create(void); /** - * @brief Borrow a group view of a node, or NULL when the node is not a - * NodeGroup (dynamic_cast). + * @brief Borrow a group view of a node (dynamic_cast). The returned + * handle is non-owning; release it with oaknode_group_free(). + * + * @return Borrowed group handle; ctx is NULL when the node is not a + * NodeGroup. */ -OakNodeGroup *oaknode_group_cast(OakNodeNode *node); +OakNodeGroup oaknode_group_cast(OakNodeNode node); /** - * @brief Destroy an OWNED group (same rules as oaknode_node_free()). - * NULL is a no-op. + * @brief Release one reference to a group handle. + * + * Convenience wrapper around handle.release(handle.ctx): destroys the + * group when the count reaches zero and the handle owns it. NULL handle + * or NULL ctx is a no-op; clears `group->ctx` after releasing. */ void oaknode_group_free(OakNodeGroup *group); @@ -72,8 +92,8 @@ void oaknode_group_free(OakNodeGroup *group); * @return Required buffer size in bytes including the terminating NUL * (non-negative), or a negative OAKNODE_E_* error code. */ -int oaknode_group_add_input_passthrough(OakNodeGroup *group, - OakNodeNode *node, +int oaknode_group_add_input_passthrough(OakNodeGroup group, + OakNodeNode node, const char *input_id, int element, char *buf, int buf_size); @@ -84,8 +104,8 @@ int oaknode_group_add_input_passthrough(OakNodeGroup *group, * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_group_add_input_passthrough_undoable(OakNodeGroup *group, - OakNodeNode *node, +int oaknode_group_add_input_passthrough_undoable(OakNodeGroup group, + OakNodeNode node, const char *input_id, int element, OakUndoCommand *out_command); @@ -94,66 +114,69 @@ int oaknode_group_add_input_passthrough_undoable(OakNodeGroup *group, * @brief Remove the passthrough for (`node`, `input_id`, `element`) * (live). OAKNODE_E_NOT_FOUND when no such passthrough exists. */ -int oaknode_group_remove_input_passthrough(OakNodeGroup *group, - OakNodeNode *node, +int oaknode_group_remove_input_passthrough(OakNodeGroup group, + OakNodeNode node, const char *input_id, int element); /** * @brief Number of registered input passthroughs. */ -int oaknode_group_passthrough_count(const OakNodeGroup *group, int *out_count); +int oaknode_group_passthrough_count(OakNodeGroup group, int *out_count); /** * @brief The passthrough id at `index`. Two-stage getter; * OAKNODE_E_NOT_FOUND for an out-of-range index. */ -int oaknode_group_passthrough_id_at(const OakNodeGroup *group, int index, +int oaknode_group_passthrough_id_at(OakNodeGroup group, int index, char *buf, int buf_size); /** * @brief The inner input behind passthrough `index`: node (borrowed - * handle), input id (two-stage string) and element. + * handle written to `out_node` when non-NULL; release it with + * oaknode_node_free()), input id (two-stage string) and element. * OAKNODE_E_NOT_FOUND for an out-of-range index. */ -int oaknode_group_passthrough_input_at(const OakNodeGroup *group, int index, - OakNodeNode **out_node, char *buf, +int oaknode_group_passthrough_input_at(OakNodeGroup group, int index, + OakNodeNode *out_node, char *buf, int buf_size, int *out_element); /** - * @brief The output passthrough node (borrowed handle), or NULL when + * @brief The output passthrough node (borrowed handle written to + * `out_node`; release it with oaknode_node_free()), an empty handle when * unset. OAKNODE_OK is returned either way. */ -int oaknode_group_get_output_passthrough(const OakNodeGroup *group, - OakNodeNode **out_node); +int oaknode_group_get_output_passthrough(OakNodeGroup group, + OakNodeNode *out_node); /** - * @brief Set the output passthrough node directly (live). + * @brief Set the output passthrough node directly (live). `node` may be + * an empty handle to clear the passthrough. */ -int oaknode_group_set_output_passthrough(OakNodeGroup *group, - OakNodeNode *node); +int oaknode_group_set_output_passthrough(OakNodeGroup group, + OakNodeNode node); /** * @brief Create a set-output-passthrough command * (olive::NodeGroupSetOutputPassthrough). */ int oaknode_group_set_output_passthrough_undoable( - OakNodeGroup *group, OakNodeNode *node, OakUndoCommand *out_command); + OakNodeGroup group, OakNodeNode node, OakUndoCommand *out_command); /** * @brief Resolve an input through group passthroughs * (NodeGroup::resolve_input()): follows a group's passthrough id to the * inner node input. Non-group inputs resolve to themselves. * - * `out_node` (may be NULL) receives a borrowed handle; the resolved input - * id uses the two-stage string convention; `out_element` (may be NULL) - * receives the element. OAKNODE_E_NOT_FOUND when the input does not - * resolve to a valid target. + * `out_node` (may be NULL) receives a borrowed handle (release it with + * oaknode_node_free()); the resolved input id uses the two-stage string + * convention; `out_element` (may be NULL) receives the element. + * OAKNODE_E_NOT_FOUND when the input does not resolve to a valid target. * * @return Required buffer size in bytes including the terminating NUL * (non-negative), or a negative OAKNODE_E_* error code. */ -int oaknode_group_resolve_input(OakNodeNode *node, const char *input_id, - int element, OakNodeNode **out_node, +int oaknode_group_resolve_input(OakNodeNode node, const char *input_id, + int element, OakNodeNode *out_node, char *buf, int buf_size, int *out_element); #ifdef __cplusplus diff --git a/include/node/keyframe.h b/include/node/keyframe.h index 9362d53fe..09898b663 100644 --- a/include/node/keyframe.h +++ b/include/node/keyframe.h @@ -35,10 +35,10 @@ extern "C" { * @file keyframe.h * @brief C ABI for olive::NodeKeyframe (src/node/src/keyframe.h). * - * An OakNodeKeyframe is a reinterpreted olive::NodeKeyframe. Handles - * created by oaknode_keyframe_create() are owned and must be released - * with oaknode_keyframe_free(); keyframes attached to a node input's - * track are owned by the node. + * An OakNodeKeyframe wraps an olive::NodeKeyframe. Handles created by + * oaknode_keyframe_create() are owned and must be released with + * oaknode_keyframe_free(); keyframes attached to a node input's track + * are owned by the node. * * Every setter comes in a live variant and an undoable variant (suffix * _undoable) returning an owned, un-executed OakUndoCommand. @@ -63,9 +63,20 @@ typedef enum oaknode_keyframe_bezier { } oaknode_keyframe_bezier; /** - * @brief Opaque keyframe handle (olive::NodeKeyframe). + * @brief Reference-counted handle to a keyframe (olive::NodeKeyframe). + * + * The object never leaves the library that created it; every external + * reference is one of these handles. Semantics are shared_ptr-like: + * oaknode_keyframe_create() returns a handle with count 1, addref(ctx) + * takes another reference, release(ctx) drops one and the library + * destroys the object when the count reaches zero. */ -typedef struct OakNodeKeyframe OakNodeKeyframe; +typedef struct OakNodeKeyframe { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeKeyframe; /** * @brief Create a standalone keyframe (owned; release with @@ -73,20 +84,24 @@ typedef struct OakNodeKeyframe OakNodeKeyframe; * * `value` may be NULL (null variant); OAKNODE_VALUE_STRING is rejected * (use oaknode_keyframe_set_value_string() after creation). `type` is an - * oaknode_keyframe_type. `parent_or_null` may be NULL. + * oaknode_keyframe_type. `parent_or_null` may be an empty handle. * - * @return Keyframe handle, or NULL on invalid argument or allocation - * failure. + * @return Keyframe handle with count 1; ctx is NULL on invalid argument + * or allocation failure. */ -OakNodeKeyframe *oaknode_keyframe_create(int64_t time_num, int64_t time_den, - const oaknode_value *value, int type, - int track, int element, - const char *input_id, - OakNodeNode *parent_or_null); +OakNodeKeyframe oaknode_keyframe_create(int64_t time_num, int64_t time_den, + const oaknode_value *value, int type, + int track, int element, + const char *input_id, + OakNodeNode parent_or_null); /** - * @brief Destroy an OWNED keyframe. NULL is a no-op. Never free a - * keyframe that is attached to a node's track. + * @brief Release one reference to a keyframe handle. + * + * Convenience wrapper around handle.release(handle.ctx): destroys the + * keyframe when the count reaches zero and the handle owns it. NULL + * handle or NULL ctx is a no-op; clears `keyframe->ctx` after releasing. + * Never free a keyframe that is attached to a node's track. */ void oaknode_keyframe_free(OakNodeKeyframe *keyframe); @@ -95,19 +110,19 @@ void oaknode_keyframe_free(OakNodeKeyframe *keyframe); * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_keyframe_get_time(const OakNodeKeyframe *keyframe, +int oaknode_keyframe_get_time(OakNodeKeyframe keyframe, int64_t *out_num, int64_t *out_den); /** * @brief Set the keyframe's time directly (live). */ -int oaknode_keyframe_set_time(OakNodeKeyframe *keyframe, int64_t time_num, +int oaknode_keyframe_set_time(OakNodeKeyframe keyframe, int64_t time_num, int64_t time_den); /** * @brief Create a set-time command (olive::NodeParamSetKeyframeTimeCommand). */ -int oaknode_keyframe_set_time_undoable(OakNodeKeyframe *keyframe, +int oaknode_keyframe_set_time_undoable(OakNodeKeyframe keyframe, int64_t time_num, int64_t time_den, OakUndoCommand *out_command); @@ -115,7 +130,7 @@ int oaknode_keyframe_set_time_undoable(OakNodeKeyframe *keyframe, * @brief Read the keyframe's value mapped into `out`. Values without a * POD representation fail with OAKNODE_E_FAILED. */ -int oaknode_keyframe_get_value(const OakNodeKeyframe *keyframe, +int oaknode_keyframe_get_value(OakNodeKeyframe keyframe, oaknode_value *out); /** @@ -123,14 +138,14 @@ int oaknode_keyframe_get_value(const OakNodeKeyframe *keyframe, * OAKNODE_VALUE_STRING is rejected (use * oaknode_keyframe_set_value_string()). */ -int oaknode_keyframe_set_value(OakNodeKeyframe *keyframe, +int oaknode_keyframe_set_value(OakNodeKeyframe keyframe, const oaknode_value *v); /** * @brief Create a set-value command * (olive::NodeParamSetKeyframeValueCommand). */ -int oaknode_keyframe_set_value_undoable(OakNodeKeyframe *keyframe, +int oaknode_keyframe_set_value_undoable(OakNodeKeyframe keyframe, const oaknode_value *v, OakUndoCommand *out_command); @@ -140,84 +155,85 @@ int oaknode_keyframe_set_value_undoable(OakNodeKeyframe *keyframe, * @return Required buffer size in bytes including the terminating NUL * (non-negative), or a negative OAKNODE_E_* error code. */ -int oaknode_keyframe_get_value_string(const OakNodeKeyframe *keyframe, +int oaknode_keyframe_get_value_string(OakNodeKeyframe keyframe, char *buf, int buf_size); /** * @brief Set a string value directly (live). */ -int oaknode_keyframe_set_value_string(OakNodeKeyframe *keyframe, +int oaknode_keyframe_set_value_string(OakNodeKeyframe keyframe, const char *value); /** * @brief Create a set-string-value command. */ -int oaknode_keyframe_set_value_string_undoable(OakNodeKeyframe *keyframe, +int oaknode_keyframe_set_value_string_undoable(OakNodeKeyframe keyframe, const char *value, OakUndoCommand *out_command); /** * @brief The keyframe's interpolation type (oaknode_keyframe_type). */ -int oaknode_keyframe_get_type(const OakNodeKeyframe *keyframe, int *out_type); +int oaknode_keyframe_get_type(OakNodeKeyframe keyframe, int *out_type); /** * @brief Set the interpolation type directly (live, * NodeKeyframe::set_type(), which adjusts neighbouring bezier handles). */ -int oaknode_keyframe_set_type(OakNodeKeyframe *keyframe, int type); +int oaknode_keyframe_set_type(OakNodeKeyframe keyframe, int type); /** * @brief Create a set-type command (same semantics as the live variant). */ -int oaknode_keyframe_set_type_undoable(OakNodeKeyframe *keyframe, int type, +int oaknode_keyframe_set_type_undoable(OakNodeKeyframe keyframe, int type, OakUndoCommand *out_command); /** * @brief A bezier control point (`handle` is an * oaknode_keyframe_bezier). */ -int oaknode_keyframe_get_bezier_control(const OakNodeKeyframe *keyframe, +int oaknode_keyframe_get_bezier_control(OakNodeKeyframe keyframe, int handle, double *out_x, double *out_y); /** * @brief Set a bezier control point directly (live). */ -int oaknode_keyframe_set_bezier_control(OakNodeKeyframe *keyframe, int handle, +int oaknode_keyframe_set_bezier_control(OakNodeKeyframe keyframe, int handle, double x, double y); /** * @brief Create a set-bezier-control command. */ -int oaknode_keyframe_set_bezier_control_undoable(OakNodeKeyframe *keyframe, +int oaknode_keyframe_set_bezier_control_undoable(OakNodeKeyframe keyframe, int handle, double x, double y, OakUndoCommand *out_command); /** * @brief The keyframe's track index. */ -int oaknode_keyframe_get_track(const OakNodeKeyframe *keyframe, +int oaknode_keyframe_get_track(OakNodeKeyframe keyframe, int *out_track); /** * @brief The keyframe's element index. */ -int oaknode_keyframe_get_element(const OakNodeKeyframe *keyframe, +int oaknode_keyframe_get_element(OakNodeKeyframe keyframe, int *out_element); /** * @brief The id of the input this keyframe belongs to. Two-stage getter. */ -int oaknode_keyframe_get_input(const OakNodeKeyframe *keyframe, char *buf, +int oaknode_keyframe_get_input(OakNodeKeyframe keyframe, char *buf, int buf_size); /** - * @brief The node this keyframe belongs to (borrowed handle), or NULL - * when orphaned. OAKNODE_OK either way. + * @brief The node this keyframe belongs to (borrowed handle written to + * `out_node`; release it with oaknode_node_free()), an empty handle when + * orphaned. OAKNODE_OK either way. */ -int oaknode_keyframe_get_parent(const OakNodeKeyframe *keyframe, - OakNodeNode **out_node); +int oaknode_keyframe_get_parent(OakNodeKeyframe keyframe, + OakNodeNode *out_node); #ifdef __cplusplus } diff --git a/include/node/node.h b/include/node/node.h index 017ed5fe7..a132b5c86 100644 --- a/include/node/node.h +++ b/include/node/node.h @@ -35,13 +35,15 @@ extern "C" { * @file node.h * @brief C ABI for olive::Node (src/node/src/node.h). * - * The handle IS the C++ object pointer (M3 handle convention 3): an - * OakNodeNode is a reinterpreted olive::Node, no wrapper allocation. - * Handles borrowed from a graph become invalid when the owning project or - * node is destroyed. Owned handles (from oaknode_factory_create_from_id() - * or oaknode_node_create_copy()) must be released with - * oaknode_node_free() while still orphaned; once a node lives in a project - * graph its lifetime belongs to the graph. + * Handles are by-value reference-counted structs (see + * include/common/handle.h): every OakNodeNode carries ctx/addref/release/ + * abi_version and behaves like a shared_ptr at the ABI level. Factory + * functions return a handle with reference count 1; release it with + * oaknode_node_free(). Handles borrowed from a graph only release the + * handle itself when freed; once a node lives in a project graph its + * lifetime belongs to the graph (the implementation flips ownership + * internally), and borrowed handles become invalid when the owning project + * or node is destroyed. * * Parameter values cross the boundary as the POD oaknode_value; the * meaningful fields depend on its type (oaknode_value_type). String-typed @@ -96,9 +98,21 @@ typedef struct oaknode_value { } oaknode_value; /** - * @brief Opaque node handle (olive::Node). + * @brief Reference-counted handle to a node (olive::Node). + * + * The object never leaves the library that created it; every external + * reference is one of these handles. Semantics are shared_ptr-like: + * init/factory functions return a handle with reference count 1, + * addref(ctx) takes another reference, release(ctx) drops one; release a + * handle with oaknode_node_free(). Borrowed handles into graph-owned + * objects only release the handle itself. */ -typedef struct OakNodeNode OakNodeNode; +typedef struct OakNodeNode { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeNode; /* Re-declared here so node.h is self-contained; see node/project.h. */ typedef struct OakNodeProject OakNodeProject; @@ -141,26 +155,26 @@ int oaknode_debug_alive_count(void); * @return Required buffer size in bytes including the terminating NUL * (non-negative), or a negative OAKNODE_E_* error code. */ -int oaknode_node_get_id(const OakNodeNode *node, char *buf, int buf_size); +int oaknode_node_get_id(OakNodeNode node, char *buf, int buf_size); /** * @brief The node's display name (Node::name()). Two-stage getter, * same return convention as oaknode_node_get_id(). */ -int oaknode_node_get_name(const OakNodeNode *node, char *buf, int buf_size); +int oaknode_node_get_name(OakNodeNode node, char *buf, int buf_size); /** * @brief The node's user label (Node::get_label()). Two-stage getter, * same return convention as oaknode_node_get_id(). */ -int oaknode_node_get_label(const OakNodeNode *node, char *buf, int buf_size); +int oaknode_node_get_label(OakNodeNode node, char *buf, int buf_size); /** * @brief Set the node's user label directly (Node::set_label(), live). * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_node_set_label(OakNodeNode *node, const char *label); +int oaknode_node_set_label(OakNodeNode node, const char *label); /** * @brief Create a label-change command (olive::NodeRenameCommand). @@ -170,7 +184,7 @@ int oaknode_node_set_label(OakNodeNode *node, const char *label); * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_node_set_label_undoable(OakNodeNode *node, const char *label, +int oaknode_node_set_label_undoable(OakNodeNode node, const char *label, OakUndoCommand *out_command); /** @@ -181,21 +195,21 @@ int oaknode_node_set_label_undoable(OakNodeNode *node, const char *label, * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_node_get_override_color(const OakNodeNode *node, int *out_value); +int oaknode_node_get_override_color(OakNodeNode node, int *out_value); /** * @brief Set the override color index directly (-1 = none; live). * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_node_set_override_color(OakNodeNode *node, int index); +int oaknode_node_set_override_color(OakNodeNode node, int index); /** * @brief Create an override-color command (olive::NodeOverrideColorCommand). * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_node_set_override_color_undoable(OakNodeNode *node, int index, +int oaknode_node_set_override_color_undoable(OakNodeNode node, int index, OakUndoCommand *out_command); /** @@ -204,14 +218,14 @@ int oaknode_node_set_override_color_undoable(OakNodeNode *node, int index, * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_node_is_enabled(const OakNodeNode *node, int *out_value); +int oaknode_node_is_enabled(OakNodeNode node, int *out_value); /** * @brief Set the node's enabled state directly (live). * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_node_set_enabled(OakNodeNode *node, int enabled); +int oaknode_node_set_enabled(OakNodeNode node, int enabled); /** * @brief Create an enabled-state command @@ -219,7 +233,7 @@ int oaknode_node_set_enabled(OakNodeNode *node, int enabled); * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_node_set_enabled_undoable(OakNodeNode *node, int enabled, +int oaknode_node_set_enabled_undoable(OakNodeNode node, int enabled, OakUndoCommand *out_command); /* ---- Input introspection ------------------------------------------------ */ @@ -230,13 +244,13 @@ int oaknode_node_set_enabled_undoable(OakNodeNode *node, int enabled, * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_node_input_count(const OakNodeNode *node, int *out_count); +int oaknode_node_input_count(OakNodeNode node, int *out_count); /** * @brief The input id at `index` (Node::inputs()). Two-stage getter; * returns OAKNODE_E_NOT_FOUND for an out-of-range index. */ -int oaknode_node_input_id(const OakNodeNode *node, int index, char *buf, +int oaknode_node_input_id(OakNodeNode node, int index, char *buf, int buf_size); /** @@ -244,38 +258,39 @@ int oaknode_node_input_id(const OakNodeNode *node, int index, char *buf, * pinned mapping on oaknode_value_type). OAKNODE_E_NOT_FOUND for an * unknown input id. */ -int oaknode_node_input_get_type(const OakNodeNode *node, const char *input_id, +int oaknode_node_input_get_type(OakNodeNode node, const char *input_id, int *out_type); /** * @brief 1 if the input currently has a connected edge * (Node::is_input_connected()). OAKNODE_E_NOT_FOUND for an unknown id. */ -int oaknode_node_input_is_connected(const OakNodeNode *node, - const char *input_id, int *out_value); +int oaknode_node_input_is_connected(OakNodeNode node, const char *input_id, + int *out_value); /** * @brief 1 if the input accepts connections (Node::is_input_connectable()). * OAKNODE_E_NOT_FOUND for an unknown id. */ -int oaknode_node_input_is_connectable(const OakNodeNode *node, - const char *input_id, int *out_value); +int oaknode_node_input_is_connectable(OakNodeNode node, const char *input_id, + int *out_value); /** * @brief The human-readable name of the input (Node::get_input_name()). * Two-stage getter; OAKNODE_E_NOT_FOUND for an unknown id. */ -int oaknode_node_get_input_name(const OakNodeNode *node, const char *input_id, +int oaknode_node_get_input_name(OakNodeNode node, const char *input_id, char *buf, int buf_size); /** - * @brief The node feeding this input, or NULL when not connected - * (Node::get_connected_output(), element -1). `out_node` receives a - * borrowed handle. OAKNODE_E_NOT_FOUND for an unknown input id. + * @brief The node feeding this input (Node::get_connected_output(), + * element -1). `out_node` receives a borrowed handle (empty, ctx == NULL, + * when not connected; releasing it only releases the handle). + * OAKNODE_E_NOT_FOUND for an unknown input id. */ -int oaknode_node_input_get_connected_node(const OakNodeNode *node, +int oaknode_node_input_get_connected_node(OakNodeNode node, const char *input_id, - OakNodeNode **out_node); + OakNodeNode *out_node); /* ---- Parameter access ----------------------------------------------------- */ @@ -288,7 +303,7 @@ int oaknode_node_input_get_connected_node(const OakNodeNode *node, * fail with OAKNODE_E_FAILED; an unknown input id fails with * OAKNODE_E_NOT_FOUND. */ -int oaknode_node_get_input(const OakNodeNode *node, const char *input_id, +int oaknode_node_get_input(OakNodeNode node, const char *input_id, oaknode_value *out); /** @@ -298,7 +313,7 @@ int oaknode_node_get_input(const OakNodeNode *node, const char *input_id, * `v->type` must match the input's declared type; OAKNODE_VALUE_STRING is * rejected (use oaknode_node_set_input_string()). */ -int oaknode_node_set_input(OakNodeNode *node, const char *input_id, +int oaknode_node_set_input(OakNodeNode node, const char *input_id, const oaknode_value *v); /** @@ -308,27 +323,26 @@ int oaknode_node_set_input(OakNodeNode *node, const char *input_id, * * Same type rules as oaknode_node_set_input(). */ -int oaknode_node_set_input_undoable(OakNodeNode *node, const char *input_id, +int oaknode_node_set_input_undoable(OakNodeNode node, const char *input_id, const oaknode_value *v, OakUndoCommand *out_command); /** * @brief Read a string-family input's standard value. Two-stage getter. */ -int oaknode_node_get_input_string(const OakNodeNode *node, - const char *input_id, char *buf, - int buf_size); +int oaknode_node_get_input_string(OakNodeNode node, const char *input_id, + char *buf, int buf_size); /** * @brief Write a string-family input's standard value directly (live). */ -int oaknode_node_set_input_string(OakNodeNode *node, const char *input_id, +int oaknode_node_set_input_string(OakNodeNode node, const char *input_id, const char *value); /** * @brief Create a set-standard-value command for a string-family input. */ -int oaknode_node_set_input_string_undoable(OakNodeNode *node, +int oaknode_node_set_input_string_undoable(OakNodeNode node, const char *input_id, const char *value, OakUndoCommand *out_command); @@ -344,7 +358,7 @@ int oaknode_node_set_input_string_undoable(OakNodeNode *node, * OAKNODE_E_STATE when the input is already connected or the nodes belong * to different graphs. */ -int oaknode_node_connect(OakNodeNode *output_node, OakNodeNode *input_node, +int oaknode_node_connect(OakNodeNode output_node, OakNodeNode input_node, const char *input_id); /** @@ -353,8 +367,8 @@ int oaknode_node_connect(OakNodeNode *output_node, OakNodeNode *input_node, * different-graph check (the command may legitimately be redone after * graph changes). */ -int oaknode_node_connect_undoable(OakNodeNode *output_node, - OakNodeNode *input_node, +int oaknode_node_connect_undoable(OakNodeNode output_node, + OakNodeNode input_node, const char *input_id, OakUndoCommand *out_command); @@ -363,44 +377,43 @@ int oaknode_node_connect_undoable(OakNodeNode *output_node, * (live, Node::disconnect_edge(), element -1). OAKNODE_E_NOT_FOUND when * the input is unknown or not connected. */ -int oaknode_node_disconnect(OakNodeNode *input_node, const char *input_id); +int oaknode_node_disconnect(OakNodeNode input_node, const char *input_id); /** * @brief Create an edge-remove command (olive::NodeEdgeRemoveCommand, * element -1). OAKNODE_E_NOT_FOUND when not connected. */ -int oaknode_node_disconnect_undoable(OakNodeNode *input_node, +int oaknode_node_disconnect_undoable(OakNodeNode input_node, const char *input_id, OakUndoCommand *out_command); /** * @brief Number of outgoing edges (Node::output_connections()). */ -int oaknode_node_output_connection_count(const OakNodeNode *node, - int *out_count); +int oaknode_node_output_connection_count(OakNodeNode node, int *out_count); /** - * @brief The node at the input end of outgoing edge `index` - * (borrowed handle). OAKNODE_E_NOT_FOUND for an out-of-range index. + * @brief The node at the input end of outgoing edge `index` (borrowed + * handle; releasing it only releases the handle). OAKNODE_E_NOT_FOUND for + * an out-of-range index. */ -int oaknode_node_output_connection_node_at(const OakNodeNode *node, int index, - OakNodeNode **out_node); +int oaknode_node_output_connection_node_at(OakNodeNode node, int index, + OakNodeNode *out_node); /** * @brief The input id at the input end of outgoing edge `index`. * Two-stage getter; OAKNODE_E_NOT_FOUND for an out-of-range index. */ -int oaknode_node_output_connection_input_id_at(const OakNodeNode *node, - int index, char *buf, - int buf_size); +int oaknode_node_output_connection_input_id_at(OakNodeNode node, int index, + char *buf, int buf_size); /** * @brief The input element at the input end of outgoing edge `index` * (-1 for non-array inputs). OAKNODE_E_NOT_FOUND for an out-of-range * index. */ -int oaknode_node_output_connection_element_at(const OakNodeNode *node, - int index, int *out_element); +int oaknode_node_output_connection_element_at(OakNodeNode node, int index, + int *out_element); /* ---- Links --------------------------------------------------------------- */ @@ -409,73 +422,72 @@ int oaknode_node_output_connection_element_at(const OakNodeNode *node, * receives 1 on success, 0 when the link was rejected (e.g. either node * rejects links). `out_linked` may be NULL. */ -int oaknode_node_link(OakNodeNode *a, OakNodeNode *b, int *out_linked); +int oaknode_node_link(OakNodeNode a, OakNodeNode b, int *out_linked); /** * @brief Unlink two nodes directly (live, Node::unlink()). * `out_unlinked` receives 1 on success, 0 otherwise; may be NULL. */ -int oaknode_node_unlink(OakNodeNode *a, OakNodeNode *b, int *out_unlinked); +int oaknode_node_unlink(OakNodeNode a, OakNodeNode b, int *out_unlinked); /** * @brief Create a link/unlink command (olive::NodeLinkCommand; * `link` != 0 links, 0 unlinks). */ -int oaknode_node_link_undoable(OakNodeNode *a, OakNodeNode *b, int link, +int oaknode_node_link_undoable(OakNodeNode a, OakNodeNode b, int link, OakUndoCommand *out_command); /** * @brief 1 if the two nodes are linked (Node::are_linked()). */ -int oaknode_node_are_linked(const OakNodeNode *a, const OakNodeNode *b, - int *out_value); +int oaknode_node_are_linked(OakNodeNode a, OakNodeNode b, int *out_value); /** * @brief Number of linked nodes (Node::links()). */ -int oaknode_node_link_count(const OakNodeNode *node, int *out_count); +int oaknode_node_link_count(OakNodeNode node, int *out_count); /** - * @brief The linked node at `index` (borrowed handle). - * OAKNODE_E_NOT_FOUND for an out-of-range index. + * @brief The linked node at `index` (borrowed handle; releasing it only + * releases the handle). OAKNODE_E_NOT_FOUND for an out-of-range index. */ -int oaknode_node_link_at(const OakNodeNode *node, int index, - OakNodeNode **out_node); +int oaknode_node_link_at(OakNodeNode node, int index, + OakNodeNode *out_node); /* ---- Context positions ---------------------------------------------------- */ /** * @brief Number of context entries (Node::get_context_positions()). */ -int oaknode_node_context_count(const OakNodeNode *node, int *out_count); +int oaknode_node_context_count(OakNodeNode node, int *out_count); /** - * @brief The context node at `index` (borrowed handle). - * OAKNODE_E_NOT_FOUND for an out-of-range index. + * @brief The context node at `index` (borrowed handle; releasing it only + * releases the handle). OAKNODE_E_NOT_FOUND for an out-of-range index. */ -int oaknode_node_context_node_at(const OakNodeNode *node, int index, - OakNodeNode **out_node); +int oaknode_node_context_node_at(OakNodeNode node, int index, + OakNodeNode *out_node); /** * @brief The node's position in `context` (any out pointer may be NULL). * OAKNODE_E_NOT_FOUND when the context does not contain this node. */ -int oaknode_node_get_context_position(const OakNodeNode *node, - OakNodeNode *context, double *out_x, - double *out_y, int *out_expanded); +int oaknode_node_get_context_position(OakNodeNode node, OakNodeNode context, + double *out_x, double *out_y, + int *out_expanded); /** * @brief Set the node's position in `context` directly (live, * Node::set_node_position_in_context() + set_node_expanded_in_context()). */ -int oaknode_node_set_context_position(OakNodeNode *node, OakNodeNode *context, +int oaknode_node_set_context_position(OakNodeNode node, OakNodeNode context, double x, double y, int expanded); /** * @brief Create a set-position command (olive::NodeSetPositionCommand). */ -int oaknode_node_set_context_position_undoable(OakNodeNode *node, - OakNodeNode *context, double x, +int oaknode_node_set_context_position_undoable(OakNodeNode node, + OakNodeNode context, double x, double y, int expanded, OakUndoCommand *out_command); @@ -483,16 +495,17 @@ int oaknode_node_set_context_position_undoable(OakNodeNode *node, * @brief Remove the node from `context` directly (live). * OAKNODE_E_NOT_FOUND when not contained. */ -int oaknode_node_remove_from_context(OakNodeNode *node, OakNodeNode *context); +int oaknode_node_remove_from_context(OakNodeNode node, OakNodeNode context); /* ---- Lifetime --------------------------------------------------------------- */ /** * @brief Create a standalone copy of the node (Node::copy()). The copy is - * NOT added to any graph; the caller owns it and must release it with - * oaknode_node_free() while it is still orphaned. Returns NULL for NULL. + * NOT added to any graph; the returned handle has reference count 1 and + * must be released with oaknode_node_free() while it is still orphaned. + * Returns an empty handle (ctx == NULL) for an empty handle or on failure. */ -OakNodeNode *oaknode_node_create_copy(const OakNodeNode *node); +OakNodeNode oaknode_node_create_copy(OakNodeNode node); /** * @brief Copy a node inside its graph (Node::copy_node_in_graph()), @@ -501,44 +514,44 @@ OakNodeNode *oaknode_node_create_copy(const OakNodeNode *node); * `*out_command` receives an owned undo command handle (free with * oakundo_command_free()). The copy is inserted into the graph only when * the returned command is redone; treat it as owned (oaknode_node_free()) - * until then. Returns NULL on failure. + * until then. Returns an empty handle (ctx == NULL) on failure. */ -OakNodeNode *oaknode_node_copy_in_graph(OakNodeNode *node, - OakUndoCommand *out_command); +OakNodeNode oaknode_node_copy_in_graph(OakNodeNode node, + OakUndoCommand *out_command); /** - * @brief Get the project this node belongs to (borrowed). *out may be - * NULL if the node is orphaned. + * @brief Get the project this node belongs to. `out` receives a borrowed + * handle (empty, ctx == NULL, if the node is orphaned; releasing it only + * releases the handle). */ -int oaknode_node_get_project(const OakNodeNode *node, - OakNodeProject **out); +int oaknode_node_get_project(OakNodeNode node, OakNodeProject *out); /** * @brief Insert/remove an element in an input array (live, * Node::input_array_insert/remove()). OAKNODE_E_NOT_FOUND for an * unknown input id. */ -int oaknode_node_input_array_insert(OakNodeNode *node, - const char *input_id, int index); -int oaknode_node_input_array_remove(OakNodeNode *node, - const char *input_id, int index); +int oaknode_node_input_array_insert(OakNodeNode node, const char *input_id, + int index); +int oaknode_node_input_array_remove(OakNodeNode node, const char *input_id, + int index); /** * @brief Element-aware variants of oaknode_node_connect()/disconnect() * (NodeInput element != -1, e.g. Sequence's track_in_N array inputs). */ -int oaknode_node_connect_element(OakNodeNode *output_node, - OakNodeNode *input_node, +int oaknode_node_connect_element(OakNodeNode output_node, + OakNodeNode input_node, const char *input_id, int element); -int oaknode_node_disconnect_element(OakNodeNode *input_node, +int oaknode_node_disconnect_element(OakNodeNode input_node, const char *input_id, int element); /** * @brief Create a command that adds a node to a project's graph * (olive::NodeAddCommand). Owned; free with oakundo_command_free(). */ -OakUndoCommand oaknode_command_create_add_node(OakNodeProject *graph, - OakNodeNode *node); +OakUndoCommand oaknode_command_create_add_node(OakNodeProject graph, + OakNodeNode node); /** * @brief Create a command that sets a node's position in a context and @@ -546,22 +559,20 @@ OakUndoCommand oaknode_command_create_add_node(OakNodeProject *graph, * (olive::NodeSetPositionAndDependenciesRecursivelyCommand). Owned. */ OakUndoCommand oaknode_command_create_set_position_recursive( - OakNodeNode *node, OakNodeNode *context, double x, double y); + OakNodeNode node, OakNodeNode context, double x, double y); /** * @brief Borrowed marker list / work area of a viewer node. *out is NULL - * when the node is not a viewer (or for NULL input). + * when the node is not a viewer (or for an empty handle). */ -int oaknode_node_get_markers(const OakNodeNode *node, - OakNodeMarkerList **out); -int oaknode_node_get_work_area(const OakNodeNode *node, - OakNodeWorkArea **out); +int oaknode_node_get_markers(OakNodeNode node, OakNodeMarkerList **out); +int oaknode_node_get_work_area(OakNodeNode node, OakNodeWorkArea **out); /** * @brief Borrowed video frame cache of a node (NULL when the node has - * none or for NULL input). + * none or for an empty handle). */ -int oaknode_node_get_video_frame_cache(const OakNodeNode *node, +int oaknode_node_get_video_frame_cache(OakNodeNode node, OakNodeFrameCache **out); /** @@ -569,7 +580,7 @@ int oaknode_node_get_video_frame_cache(const OakNodeNode *node, * (Node::copy_inputs()). include_connections != 0 also copies * input connections. */ -int oaknode_node_copy_inputs(OakNodeNode *dst, const OakNodeNode *src, +int oaknode_node_copy_inputs(OakNodeNode dst, OakNodeNode src, int include_connections); /** @@ -577,8 +588,7 @@ int oaknode_node_copy_inputs(OakNodeNode *dst, const OakNodeNode *src, * (Node::set_value_hint_for_input() with a single texture type * and a Track::Reference string). */ -int oaknode_node_set_value_hint_track(OakNodeNode *node, - const char *input_id, +int oaknode_node_set_value_hint_track(OakNodeNode node, const char *input_id, int track_type, int track_index); /** @@ -586,37 +596,37 @@ int oaknode_node_set_value_hint_track(OakNodeNode *node, * set_video_params/set_audio_params, stream index 0). `params` is an * oakcommon handle (video) or borrowed oakcore handle (audio). */ -int oaknode_viewer_set_video_params(OakNodeNode *viewer, +int oaknode_viewer_set_video_params(OakNodeNode viewer, const OakVideoParams *params); -int oaknode_viewer_set_audio_params(OakNodeNode *viewer, +int oaknode_viewer_set_audio_params(OakNodeNode viewer, const OakAudioParams *params); /** * @brief Find a footage node upstream of this node's inputs - * (Node::find_input_nodes(), first match). *out is a - * borrowed handle or NULL when none. + * (Node::find_input_nodes(), first match). `out` receives + * a borrowed handle (empty, ctx == NULL, when none; releasing it + * only releases the handle). */ -int oaknode_node_find_input_footage(const OakNodeNode *node, - OakNodeFootage **out); +int oaknode_node_find_input_footage(OakNodeNode node, OakNodeFootage *out); /** * @brief Create a command that removes a node from its graph together * with its exclusive dependencies and disconnects its edges * (NodeRemoveWithExclusiveDependenciesAndDisconnect). * - * Owned command handle; free with oakundo_command_free(). Returns NULL - * on failure. + * Owned command handle; free with oakundo_command_free(). Returns an + * empty handle (ctx == NULL) on failure. */ -OakUndoCommand oaknode_command_create_remove_node(OakNodeNode *node); +OakUndoCommand oaknode_command_create_remove_node(OakNodeNode node); /** - * @brief Destroy an OWNED node immediately (C++ delete). NULL is a no-op. + * @brief Release one reference to a node handle. * - * ONLY valid for owned handles that were never added to a graph: the - * products of oaknode_factory_create_from_id(), - * oaknode_node_create_copy() and oaknode_group_create() while still - * orphaned. Freeing a graph-owned node, or freeing twice, is a - * use-after-free. + * Convenience wrapper around handle.release(handle.ctx): the underlying + * node is destroyed only when the last reference of an OWNED handle is + * released; releasing a borrowed handle into a graph-owned object only + * destroys the handle itself. NULL handle or NULL ctx is a no-op; clears + * `node->ctx` after releasing. */ void oaknode_node_free(OakNodeNode *node); diff --git a/include/node/project.h b/include/node/project.h index 73c11143c..42795a59b 100644 --- a/include/node/project.h +++ b/include/node/project.h @@ -21,6 +21,8 @@ #ifndef OAK_EDITOR_NODE_PROJECT_H #define OAK_EDITOR_NODE_PROJECT_H +#include + #include "node/error.h" #ifdef __cplusplus @@ -33,9 +35,9 @@ extern "C" { * * An OakNodeProject owns its whole node graph: nodes added with * oaknode_project_add_node() (directly, or indirectly through the folder and - * footage families) are deleted by oaknode_project_free(). Handles to nodes, - * folders and footage obtained from a project are borrowed views and must not - * be freed. + * footage families) are deleted when the project's last reference is + * released. Handles to nodes, folders and footage obtained from a project + * are borrowed views: releasing them only releases the handle itself. * * Conventions (shared by all oaknode C API families): * - Return codes: 0 (OAKNODE_OK) on success, a negative OAKNODE_E_* code on @@ -44,27 +46,37 @@ extern "C" { * query the required size; the return value is the required buffer size in * bytes INCLUDING the terminating NUL. The output is NUL-terminated * whenever buf_size > 0. - * - NULL handles yield OAKNODE_E_INVALID (or a no-op for free()). + * - Empty handles (ctx == NULL) yield OAKNODE_E_INVALID (or a no-op for + * free()). * - Disk save/load of project files is NOT part of this layer; it belongs to * oakstorage (milestone M10). */ /** - * @brief Opaque project handle. Owned by the caller; release with - * oaknode_project_free(). + * @brief Reference-counted handle to a project (olive::Project). + * + * Semantics are shared_ptr-like: oaknode_project_init() returns a handle + * whose underlying object has reference count 1, addref(ctx) takes another + * reference, and release(ctx) (or oaknode_project_free()) drops one; the + * project and every node it owns are destroyed when the count reaches zero. */ -typedef struct OakNodeProject OakNodeProject; +typedef struct OakNodeProject { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeProject; /** - * @brief Opaque node handle (defined by the node family; forward-declared + * @brief Node handle (defined by the node family; forward-declared * here so the headers can be included in any order). */ typedef struct OakNodeNode OakNodeNode; /** - * @brief Opaque folder handle (defined in node/folder.h; forward-declared - * here so the headers can be included in any order). Borrowed from the - * owning project. + * @brief Folder handle (defined in node/folder.h; forward-declared + * here so the headers can be included in any order). Handles obtained from + * a project are borrowed from it. */ typedef struct OakNodeFolder OakNodeFolder; @@ -74,12 +86,16 @@ typedef struct OakNodeFolder OakNodeFolder; * The project has no root folder until oaknode_project_initialize() is * called (mirrors Project::initialize()). * - * @return Project handle, or NULL on allocation failure. + * @return Project handle with reference count 1 (release with + * oaknode_project_free()); ctx is NULL on allocation failure. */ -OakNodeProject *oaknode_project_init(void); +OakNodeProject oaknode_project_init(void); /** - * @brief Destroy a project and every node it owns. NULL is a no-op. + * @brief Release one reference to a project handle. + * + * Destroys the project and every node it owns when the count reaches zero. + * NULL handle or NULL ctx is a no-op; clears `project->ctx` after releasing. */ void oaknode_project_free(OakNodeProject *project); @@ -88,7 +104,7 @@ void oaknode_project_free(OakNodeProject *project); * * @return OAKNODE_OK, or OAKNODE_E_STATE if already initialized. */ -int oaknode_project_initialize(OakNodeProject *project); +int oaknode_project_initialize(OakNodeProject project); /** * @brief Destructively destroy all nodes in the graph (Project::clear()). @@ -98,14 +114,16 @@ int oaknode_project_initialize(OakNodeProject *project); * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_project_clear(OakNodeProject *project); +int oaknode_project_clear(OakNodeProject project); /** * @brief Borrowed handle of the project's root folder (Project::root()). * - * NULL if the project has not been initialized. + * The returned handle only releases the handle itself; the project owns the + * folder. Empty handle (ctx == NULL) if the project has not been + * initialized. */ -OakNodeFolder *oaknode_project_root(OakNodeProject *project); +OakNodeFolder oaknode_project_root(OakNodeProject project); /** * @brief Project display name (Project::name(): the filename's base name, or @@ -114,20 +132,20 @@ OakNodeFolder *oaknode_project_root(OakNodeProject *project); * @return Required buffer size in bytes including the NUL, or a negative * OAKNODE_E_* error code. */ -int oaknode_project_name(const OakNodeProject *project, char *buf, int buf_size); +int oaknode_project_name(OakNodeProject project, char *buf, int buf_size); /** * @brief Full path the project was saved as, or "" if untitled * (Project::filename()). Two-stage string getter. */ -int oaknode_project_filename(const OakNodeProject *project, char *buf, +int oaknode_project_filename(OakNodeProject project, char *buf, int buf_size); /** * @brief Display name safe for window titles (Project::pretty_filename()). * Two-stage string getter. */ -int oaknode_project_pretty_filename(const OakNodeProject *project, char *buf, +int oaknode_project_pretty_filename(OakNodeProject project, char *buf, int buf_size); /** @@ -135,46 +153,47 @@ int oaknode_project_pretty_filename(const OakNodeProject *project, char *buf, * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_project_set_filename(OakNodeProject *project, const char *filename); +int oaknode_project_set_filename(OakNodeProject project, const char *filename); /** * @brief 1 if the project has unsaved changes, 0 otherwise - * (Project::is_modified()). Negative OAKNODE_E_* code on NULL. + * (Project::is_modified()). Negative OAKNODE_E_* code on an empty handle. */ -int oaknode_project_is_modified(const OakNodeProject *project); +int oaknode_project_is_modified(OakNodeProject project); /** * @brief Set the modified flag (Project::set_modified()). * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_project_set_modified(OakNodeProject *project, int modified); +int oaknode_project_set_modified(OakNodeProject project, int modified); /** * @brief 1 if the project is new (untitled and unmodified, Project::is_new()). - * Negative OAKNODE_E_* code on NULL. + * Negative OAKNODE_E_* code on an empty handle. */ -int oaknode_project_is_new(const OakNodeProject *project); +int oaknode_project_is_new(OakNodeProject project); /** * @brief Effective cache directory (Project::cache_path(), honoring the cache * location setting). Two-stage string getter. */ -int oaknode_project_cache_path(const OakNodeProject *project, char *buf, +int oaknode_project_cache_path(OakNodeProject project, char *buf, int buf_size); /** * @brief Copy all project settings (Project::copy_settings()). */ -int oaknode_project_copy_settings(OakNodeProject *dst, - const OakNodeProject *src); +int oaknode_project_copy_settings(OakNodeProject dst, + OakNodeProject src); /** * @brief Cache location setting enum value * (Project::get_cache_location_setting(): 0 = default location, - * 1 = alongside project, 2 = custom path). Negative OAKNODE_E_* code on NULL. + * 1 = alongside project, 2 = custom path). Negative OAKNODE_E_* code on an + * empty handle. */ -int oaknode_project_get_cache_location_setting(const OakNodeProject *project); +int oaknode_project_get_cache_location_setting(OakNodeProject project); /** * @brief Set the cache location setting (0/1/2, see @@ -182,14 +201,14 @@ int oaknode_project_get_cache_location_setting(const OakNodeProject *project); * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_project_set_cache_location_setting(OakNodeProject *project, +int oaknode_project_set_cache_location_setting(OakNodeProject project, int setting); /** * @brief Custom cache directory, or "" when none is set * (Project::get_custom_cache_path()). Two-stage string getter. */ -int oaknode_project_get_custom_cache_path(const OakNodeProject *project, +int oaknode_project_get_custom_cache_path(OakNodeProject project, char *buf, int buf_size); /** @@ -198,22 +217,25 @@ int oaknode_project_get_custom_cache_path(const OakNodeProject *project, * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_project_set_custom_cache_path(OakNodeProject *project, +int oaknode_project_set_custom_cache_path(OakNodeProject project, const char *path); /** * @brief Project UUID string (Project::get_uuid()). Two-stage string getter. */ -int oaknode_project_get_uuid(const OakNodeProject *project, char *buf, +int oaknode_project_get_uuid(OakNodeProject project, char *buf, int buf_size); /** - * @brief Add a node to the graph; the project takes ownership + * @brief Add a node to the graph; the graph assumes the node's lifetime * (Project::add_node()). * + * After a successful call the graph owns the node: releasing `node` only + * releases the handle itself. + * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_project_add_node(OakNodeProject *project, OakNodeNode *node); +int oaknode_project_add_node(OakNodeProject project, OakNodeNode node); /** * @brief Detach a node from the graph without deleting it @@ -222,19 +244,21 @@ int oaknode_project_add_node(OakNodeProject *project, OakNodeNode *node); * @return OAKNODE_OK, OAKNODE_E_NOT_FOUND if the node is not in the graph, or * another negative OAKNODE_E_* error code. */ -int oaknode_project_remove_node(OakNodeProject *project, OakNodeNode *node); +int oaknode_project_remove_node(OakNodeProject project, OakNodeNode node); /** * @brief Number of nodes belonging to the graph (Project::nodes().size()). - * Negative OAKNODE_E_* code on NULL. + * Negative OAKNODE_E_* code on an empty handle. */ -int oaknode_project_node_count(const OakNodeProject *project); +int oaknode_project_node_count(OakNodeProject project); /** - * @brief Borrowed handle of the graph node at `index`, or NULL when out of - * range. + * @brief Borrowed handle of the graph node at `index`. + * + * The returned handle only releases the handle itself. Empty handle + * (ctx == NULL) when out of range. */ -OakNodeNode *oaknode_project_node_at(const OakNodeProject *project, int index); +OakNodeNode oaknode_project_node_at(OakNodeProject project, int index); #ifdef __cplusplus } diff --git a/include/node/sequence.h b/include/node/sequence.h index 5e6a5d0cd..ee76ed628 100644 --- a/include/node/sequence.h +++ b/include/node/sequence.h @@ -25,6 +25,8 @@ #include #endif +#include + #include "common/videoparams.h" #include "node/error.h" #include "olive/core/oakcore/audioparams.h" @@ -46,109 +48,129 @@ extern "C" { typedef struct OakNodeNode OakNodeNode; /** - * @brief Opaque handle to a sequence (olive::Sequence). + * @brief Reference-counted handle to a sequence (olive::Sequence). * - * The handle IS the C++ object pointer; no wrapper is allocated. + * The object never leaves the library that created it; every external + * reference is one of these handles. Semantics are shared_ptr-like: + * oaknode_sequence_create() returns a handle with count 1, addref(ctx) + * takes another reference, release(ctx) drops one and the library + * destroys the object when the count reaches zero. + * + * Handles obtained from accessors (track lists, tracks) are borrowed: + * releasing them does not destroy the underlying object, which stays + * owned by the sequence graph. */ -typedef struct OakNodeSequence OakNodeSequence; +typedef struct OakNodeSequence { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeSequence; /** - * @brief Opaque handle to a track list (olive::TrackList), see node/track.h. + * @brief Reference-counted handle to a track list (olive::TrackList), + * see node/track.h. */ typedef struct OakNodeTrackList OakNodeTrackList; /** - * @brief Opaque handle to a track (olive::Track), see node/track.h. + * @brief Reference-counted handle to a track (olive::Track), see + * node/track.h. */ typedef struct OakNodeTrack OakNodeTrack; /** * @brief Create an empty sequence with zero tracks. * - * @return Sequence handle, or NULL on allocation failure. + * @return Sequence handle with reference count 1 (release with + * oaknode_sequence_free()); ctx is NULL on allocation failure. */ -OakNodeSequence *oaknode_sequence_create(void); +OakNodeSequence oaknode_sequence_create(void); /** - * @brief Destroy a sequence and its track lists. No-op on NULL. + * @brief Release one reference to a sequence handle. + * + * Destroys the sequence (and its owned track lists) when the reference + * count reaches zero. NULL handle or NULL ctx is a no-op; clears + * `sequence->ctx` after releasing. * * Tracks and blocks connected to the sequence are owned by the graph and * are not deleted here; the caller must have torn them down first. */ void oaknode_sequence_free(OakNodeSequence *sequence); +/** + * @brief Apply the default video/audio parameters + * (ViewerOutput::set_default_parameters()). + */ +int oaknode_sequence_set_default_parameters(OakNodeSequence sequence); + +/** + * @brief Borrowed cast from a sequence handle to its node handle. + * Empty handle for an empty handle. + */ +OakNodeNode oaknode_sequence_as_node(OakNodeSequence sequence); + /** * @brief Borrowed handle to the per-type track list. * * @param type One of OAKNODE_TRACK_TYPE_VIDEO / _AUDIO / _SUBTITLE. * @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND (bad type). */ -/** - * @brief Apply the default video/audio parameters - * (ViewerOutput::set_default_parameters()). - */ -int oaknode_sequence_set_default_parameters(OakNodeSequence *sequence); - -/** - * @brief Borrowed cast from a sequence handle to its node handle. - * NULL for NULL. - */ -OakNodeNode *oaknode_sequence_as_node(OakNodeSequence *sequence); - -int oaknode_sequence_get_track_list(OakNodeSequence *sequence, int type, - OakNodeTrackList **out); +int oaknode_sequence_get_track_list(OakNodeSequence sequence, int type, + OakNodeTrackList *out); /** * @brief Number of connected tracks of the given type. */ -int oaknode_sequence_get_track_count(OakNodeSequence *sequence, int type, +int oaknode_sequence_get_track_count(OakNodeSequence sequence, int type, int *count); /** * @brief Borrowed handle to the track of `type` at `index`. */ -int oaknode_sequence_get_track_at(OakNodeSequence *sequence, int type, - int index, OakNodeTrack **out); +int oaknode_sequence_get_track_at(OakNodeSequence sequence, int type, + int index, OakNodeTrack *out); /** * @brief Flat track cache across all types (olive::Sequence::get_tracks()). */ -int oaknode_sequence_get_all_track_count(OakNodeSequence *sequence, int *count); -int oaknode_sequence_get_all_track_at(OakNodeSequence *sequence, int index, - OakNodeTrack **out); +int oaknode_sequence_get_all_track_count(OakNodeSequence sequence, int *count); +int oaknode_sequence_get_all_track_at(OakNodeSequence sequence, int index, + OakNodeTrack *out); /** * @brief Playhead position in sequence time. */ -int oaknode_sequence_get_playhead(OakNodeSequence *sequence, int *numerator, +int oaknode_sequence_get_playhead(OakNodeSequence sequence, int *numerator, int *denominator); -int oaknode_sequence_set_playhead(OakNodeSequence *sequence, int numerator, +int oaknode_sequence_set_playhead(OakNodeSequence sequence, int numerator, int denominator); /** * @brief Cached overall/video/audio lengths (olive::ViewerOutput). */ -int oaknode_sequence_get_length(OakNodeSequence *sequence, int *numerator, +int oaknode_sequence_get_length(OakNodeSequence sequence, int *numerator, int *denominator); -int oaknode_sequence_get_video_length(OakNodeSequence *sequence, +int oaknode_sequence_get_video_length(OakNodeSequence sequence, int *numerator, int *denominator); -int oaknode_sequence_get_audio_length(OakNodeSequence *sequence, +int oaknode_sequence_get_audio_length(OakNodeSequence sequence, int *numerator, int *denominator); /** * @brief Recompute the cached lengths from the track lists * (olive::ViewerOutput::verify_length()). */ -int oaknode_sequence_verify_length(OakNodeSequence *sequence); +int oaknode_sequence_verify_length(OakNodeSequence sequence); /* --------------------------------------------------- Video/audio params */ /** * @brief Number of video/audio parameter slots. */ -int oaknode_sequence_get_video_stream_count(OakNodeSequence *sequence, +int oaknode_sequence_get_video_stream_count(OakNodeSequence sequence, int *count); -int oaknode_sequence_get_audio_stream_count(OakNodeSequence *sequence, +int oaknode_sequence_get_audio_stream_count(OakNodeSequence sequence, int *count); /** @@ -159,29 +181,29 @@ int oaknode_sequence_get_audio_stream_count(OakNodeSequence *sequence, * @return OAKNODE_OK, OAKNODE_E_INVALID, OAKNODE_E_NOT_FOUND or * OAKNODE_E_NOMEM. */ -int oaknode_sequence_get_video_params(OakNodeSequence *sequence, int index, +int oaknode_sequence_get_video_params(OakNodeSequence sequence, int index, OakVideoParams *out); /** * @brief Replace the video parameters at `index` with a copy of `params`. * - * @return OAKNODE_E_INVALID if sequence is NULL, params.ctx is NULL, or - * index is negative. + * @return OAKNODE_E_INVALID if the sequence handle is empty, params.ctx is + * NULL, or index is negative. */ -int oaknode_sequence_set_video_params(OakNodeSequence *sequence, int index, +int oaknode_sequence_set_video_params(OakNodeSequence sequence, int index, OakVideoParams params); /** * @brief Audio parameters at `index` as a NEW handle owned by the caller * (release with oakcore_audioparams_free()). */ -int oaknode_sequence_get_audio_params(OakNodeSequence *sequence, int index, +int oaknode_sequence_get_audio_params(OakNodeSequence sequence, int index, OakAudioParams **out); /** * @brief Replace the audio parameters at `index` with a copy of `params`. */ -int oaknode_sequence_set_audio_params(OakNodeSequence *sequence, int index, +int oaknode_sequence_set_audio_params(OakNodeSequence sequence, int index, const OakAudioParams *params); #ifdef __cplusplus diff --git a/include/node/serializer.h b/include/node/serializer.h index 09e1e911d..4b5782094 100644 --- a/include/node/serializer.h +++ b/include/node/serializer.h @@ -21,7 +21,10 @@ #ifndef OAK_EDITOR_NODE_SERIALIZER_H #define OAK_EDITOR_NODE_SERIALIZER_H +#include + #include "node/error.h" +#include "node/node.h" #include "node/project.h" #ifdef __cplusplus @@ -73,17 +76,33 @@ extern "C" { #define OAKNODE_SERIALIZER_NO_DATA 7 /** - * @brief Opaque save descriptor (wraps ProjectSerializer::SaveData). - * Owned by the caller; release with oaknode_serializer_savedata_free(). + * @brief Reference-counted save descriptor (wraps + * olive::ProjectSerializer::SaveData). + * + * oaknode_serializer_savedata_create() returns a handle whose object has + * reference count 1; release it with oaknode_serializer_savedata_free(). */ -typedef struct OakNodeSerializerSaveData OakNodeSerializerSaveData; +typedef struct OakNodeSerializerSaveData { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeSerializerSaveData; /** - * @brief Opaque load result (wraps ProjectSerializer::LoadData). - * Owned by the caller; release with oaknode_serializer_loaddata_free(). + * @brief Reference-counted load result (wraps + * olive::ProjectSerializer::LoadData). + * + * The handle returned through oaknode_serializer_load_from_xml() has + * reference count 1; release it with oaknode_serializer_loaddata_free(). * Node handles obtained from it are borrowed from the target project. */ -typedef struct OakNodeSerializerLoadData OakNodeSerializerLoadData; +typedef struct OakNodeSerializerLoadData { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeSerializerLoadData; /** * @brief Register the versioned serializers and initialize the node factory. @@ -104,16 +123,19 @@ void oaknode_serializer_shutdown(void); * * @param load_type One of OAKNODE_SERIALIZER_LOAD_*; use * OAKNODE_SERIALIZER_LOAD_ONLY_NODES for clipboard-style node copies. - * @param project Context project (borrowed), may be NULL for load types - * that do not require it. + * @param project Context project (borrowed), may be an empty handle for + * load types that do not require it. * - * @return Save-data handle, or NULL on failure. + * @return Save-data handle with reference count 1 (release with + * oaknode_serializer_savedata_free()); ctx is NULL on failure. */ -OakNodeSerializerSaveData *oaknode_serializer_savedata_create( - int load_type, OakNodeProject *project); +OakNodeSerializerSaveData oaknode_serializer_savedata_create( + int load_type, OakNodeProject project); /** - * @brief Destroy a save descriptor. NULL is a no-op. + * @brief Release the caller's reference to the save descriptor and null + * out the handle. NULL and empty handles are a no-op; the object is + * destroyed when its reference count reaches zero. */ void oaknode_serializer_savedata_free(OakNodeSerializerSaveData *save_data); @@ -125,7 +147,7 @@ void oaknode_serializer_savedata_free(OakNodeSerializerSaveData *save_data); * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ int oaknode_serializer_savedata_set_nodes( - OakNodeSerializerSaveData *save_data, OakNodeNode *const *nodes, int count); + OakNodeSerializerSaveData save_data, const OakNodeNode *nodes, int count); /** * @brief Attach a free-form (key, value) property to a node in the @@ -135,7 +157,7 @@ int oaknode_serializer_savedata_set_nodes( * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ int oaknode_serializer_savedata_set_property( - OakNodeSerializerSaveData *save_data, OakNodeNode *node, const char *key, + OakNodeSerializerSaveData save_data, OakNodeNode node, const char *key, const char *value); /** @@ -146,21 +168,22 @@ int oaknode_serializer_savedata_set_property( * OAKNODE_E_* error code (OAKNODE_E_STATE if the serializers have * not been initialized). */ -int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData *save_data, +int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData save_data, char *buf, int buf_size); /** * @brief Parse an in-memory XML document into `project` ("paste"). * - * @param project Target project (borrowed), may be NULL for load types that - * do not attach nodes to a project. + * @param project Target project (borrowed), may be an empty handle for + * load types that do not attach nodes to a project. * @param xml Complete XML document text. Must not be NULL. * @param load_type One of OAKNODE_SERIALIZER_LOAD_*. * @param out_result Receives one of the OAKNODE_SERIALIZER_* result codes. * Must not be NULL. * @param out_load_data Receives the load result on OAKNODE_SERIALIZER_OK - * (caller-owned, may be NULL if the caller does not need it; - * receives NULL on failure). + * (reference count 1, release with oaknode_serializer_loaddata_free(); + * may be NULL if the caller does not need it; receives an empty + * handle on failure). * @param details_buf Optional human-readable error detail buffer * (two-stage convention is NOT used; truncation is silent). May be * NULL. @@ -169,13 +192,14 @@ int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData *save_data, * @return OAKNODE_OK if the call itself succeeded (inspect *out_result for * the serializer outcome), or a negative OAKNODE_E_* error code. */ -int oaknode_serializer_load_from_xml(OakNodeProject *project, const char *xml, +int oaknode_serializer_load_from_xml(OakNodeProject project, const char *xml, int load_type, int *out_result, - OakNodeSerializerLoadData **out_load_data, + OakNodeSerializerLoadData *out_load_data, char *details_buf, int details_buf_size); /** - * @brief Destroy a load result. NULL is a no-op. + * @brief Release the caller's reference to the load result and null out + * the handle. NULL and empty handles are a no-op. * * Does not delete the loaded nodes: they are newly created objects owned by * the CALLER until adopted into a project with oaknode_project_add_node() @@ -185,17 +209,17 @@ void oaknode_serializer_loaddata_free(OakNodeSerializerLoadData *load_data); /** * @brief Number of nodes created by the load. Negative OAKNODE_E_* code on - * NULL. + * an empty handle. */ int oaknode_serializer_loaddata_node_count( - const OakNodeSerializerLoadData *load_data); + OakNodeSerializerLoadData load_data); /** - * @brief Borrowed handle of the loaded node at `index`, or NULL when out of - * range. + * @brief Borrowed handle of the loaded node at `index`, or an empty handle + * when out of range. */ -OakNodeNode *oaknode_serializer_loaddata_node_at( - const OakNodeSerializerLoadData *load_data, int index); +OakNodeNode oaknode_serializer_loaddata_node_at( + OakNodeSerializerLoadData load_data, int index); /** * @brief Look up a serialized property attached to a loaded node. @@ -206,15 +230,15 @@ OakNodeNode *oaknode_serializer_loaddata_node_at( * negative OAKNODE_E_* error code. */ int oaknode_serializer_loaddata_get_property( - const OakNodeSerializerLoadData *load_data, OakNodeNode *node, - const char *key, char *buf, int buf_size); + OakNodeSerializerLoadData load_data, OakNodeNode node, const char *key, + char *buf, int buf_size); /** * @brief Number of promised (deferred) connections in the load result. - * Negative OAKNODE_E_* code on NULL. + * Negative OAKNODE_E_* code on an empty handle. */ int oaknode_serializer_loaddata_connection_count( - const OakNodeSerializerLoadData *load_data); + OakNodeSerializerLoadData load_data); /** * @brief Read the promised connection at `index`. @@ -223,8 +247,8 @@ int oaknode_serializer_loaddata_connection_count( * `input_id_buf` follows the two-stage string convention inside a * fixed call: pass NULL/0 to skip copying the id. * - * @param out_output_node Receives the output (source) node. - * @param out_input_node Receives the input (destination) node. + * @param out_output_node Receives the output (source) node (borrowed). + * @param out_input_node Receives the input (destination) node (borrowed). * @param input_id_buf Receives the input id string, may be NULL. * @param input_id_buf_size Size of input_id_buf. * @param out_element Receives the input element index. @@ -233,8 +257,8 @@ int oaknode_serializer_loaddata_connection_count( * negative OAKNODE_E_* error code. */ int oaknode_serializer_loaddata_connection_at( - const OakNodeSerializerLoadData *load_data, int index, - OakNodeNode **out_output_node, OakNodeNode **out_input_node, + OakNodeSerializerLoadData load_data, int index, + OakNodeNode *out_output_node, OakNodeNode *out_input_node, char *input_id_buf, int input_id_buf_size, int *out_element); #ifdef __cplusplus @@ -268,9 +292,10 @@ enum OakNodeSerializerResultCode { * string (e.g. the fallback filename on overwrite errors). * @return OAKNODE_OK when the result code is * OAKNODE_SERIALIZER_RESULT_SUCCESS, OAKNODE_E_FAILED otherwise - * (details in out_code/details), OAKNODE_E_INVALID for NULL args. + * (details in out_code/details), OAKNODE_E_INVALID for empty + * handles/NULL args. */ -int oaknode_serializer_save_to_file(OakNodeProject *project, +int oaknode_serializer_save_to_file(OakNodeProject project, const char *filename, int use_compression, int *out_code, char *details, int details_size); @@ -280,6 +305,6 @@ int oaknode_serializer_save_to_file(OakNodeProject *project, * * Same return/out-param convention as oaknode_serializer_save_to_file(). */ -int oaknode_serializer_load_from_file(OakNodeProject *project, +int oaknode_serializer_load_from_file(OakNodeProject project, const char *filename, int *out_code, char *details, int details_size); diff --git a/include/node/track.h b/include/node/track.h index f63611e16..7e0de834d 100644 --- a/include/node/track.h +++ b/include/node/track.h @@ -25,6 +25,8 @@ #include #endif +#include + #include "node/error.h" #ifdef __cplusplus @@ -32,27 +34,49 @@ extern "C" { #endif /** - * @brief Opaque handle to a track (olive::Track). + * @brief Reference-counted handle to a track (olive::Track). * - * The handle IS the C++ object pointer; no wrapper is allocated. + * The object never leaves the library that created it; every external + * reference is one of these handles. Semantics are shared_ptr-like: + * oaknode_track_create() returns a handle with count 1, addref(ctx) + * takes another reference, release(ctx) drops one and the library + * destroys the object when the count reaches zero. + * + * Adding a track to a track list (oaknode_tracklist_add_track()) + * transfers ownership to the graph; handles obtained from accessors + * (sequence/track-list lookups) are borrowed and never destroy the + * underlying object. */ -typedef struct OakNodeTrack OakNodeTrack; +typedef struct OakNodeTrack { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeTrack; /** - * @brief Opaque handle to a per-type track container (olive::TrackList). + * @brief Reference-counted handle to a per-type track container + * (olive::TrackList). * - * Borrowed from oaknode_sequence_get_track_list(); invalidated when the - * owning sequence is destroyed. + * Always borrowed from oaknode_sequence_get_track_list(); releasing the + * handle never destroys the list, which stays owned by its sequence. */ -typedef struct OakNodeTrackList OakNodeTrackList; +typedef struct OakNodeTrackList { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeTrackList; /** - * @brief Opaque handle to a block (olive::Block), see node/block.h. + * @brief Reference-counted handle to a block (olive::Block), see + * node/block.h. */ typedef struct OakNodeBlock OakNodeBlock; /** - * @brief Opaque handle to a sequence (olive::Sequence), see node/sequence.h. + * @brief Reference-counted handle to a sequence (olive::Sequence), see + * node/sequence.h. */ typedef struct OakNodeSequence OakNodeSequence; @@ -72,9 +96,9 @@ typedef struct OakNodeNode OakNodeNode; /** * @brief Borrowed cast from a track handle to its node handle. - * NULL for NULL. + * Empty handle for an empty handle. */ -OakNodeNode *oaknode_track_as_node(OakNodeTrack *track); +OakNodeNode oaknode_track_as_node(OakNodeTrack track); /* ---------------------------------------------------------------- Track */ @@ -84,12 +108,16 @@ OakNodeNode *oaknode_track_as_node(OakNodeTrack *track); * The caller owns the track until it is added to a track list; a track * that was never added must be released with oaknode_track_free(). * - * @return Track handle, or NULL on invalid type / allocation failure. + * @return Track handle with reference count 1; ctx is NULL on invalid + * type / allocation failure. */ -OakNodeTrack *oaknode_track_create(int type); +OakNodeTrack oaknode_track_create(int type); /** - * @brief Destroy a track. No-op on NULL. + * @brief Release one reference to a track handle. + * + * Destroys the track when the reference count reaches zero. NULL handle + * or NULL ctx is a no-op; clears `track->ctx` after releasing. * * The track must have been removed from its track list first. */ @@ -100,20 +128,20 @@ void oaknode_track_free(OakNodeTrack *track); * * @return OAKNODE_OK or OAKNODE_E_INVALID. */ -int oaknode_track_get_type(OakNodeTrack *track, int *type); -int oaknode_track_set_type(OakNodeTrack *track, int type); +int oaknode_track_get_type(OakNodeTrack track, int *type); +int oaknode_track_set_type(OakNodeTrack track, int type); /** * @brief Track height in internal units (olive::Track::get/set_track_height). */ -int oaknode_track_get_height(OakNodeTrack *track, double *height); -int oaknode_track_set_height(OakNodeTrack *track, double height); +int oaknode_track_get_height(OakNodeTrack track, double *height); +int oaknode_track_set_height(OakNodeTrack track, double height); /** * @brief Track height in pixels (converted through the default font height). */ -int oaknode_track_get_height_in_pixels(OakNodeTrack *track, int *height); -int oaknode_track_set_height_in_pixels(OakNodeTrack *track, int height); +int oaknode_track_get_height_in_pixels(OakNodeTrack track, int *height); +int oaknode_track_set_height_in_pixels(OakNodeTrack track, int height); /** * @brief Default / minimum track heights in pixels (static). @@ -124,47 +152,47 @@ int oaknode_track_get_minimum_height_in_pixels(void); /** * @brief Index of the track inside its track list. */ -int oaknode_track_get_index(OakNodeTrack *track, int *index); -int oaknode_track_set_index(OakNodeTrack *track, int index); +int oaknode_track_get_index(OakNodeTrack track, int *index); +int oaknode_track_set_index(OakNodeTrack track, int index); /** * @brief Mute / lock flags. */ -int oaknode_track_get_muted(OakNodeTrack *track, int *muted); -int oaknode_track_set_muted(OakNodeTrack *track, int muted); -int oaknode_track_get_locked(OakNodeTrack *track, int *locked); -int oaknode_track_set_locked(OakNodeTrack *track, int locked); +int oaknode_track_get_muted(OakNodeTrack track, int *muted); +int oaknode_track_set_muted(OakNodeTrack track, int muted); +int oaknode_track_get_locked(OakNodeTrack track, int *locked); +int oaknode_track_set_locked(OakNodeTrack track, int locked); /** * @brief Track reference as a (type, index) pair (olive::Track::Reference). */ -int oaknode_track_get_reference(OakNodeTrack *track, int *type, int *index); +int oaknode_track_get_reference(OakNodeTrack track, int *type, int *index); /** * @brief Total length of the track (end of the last block). */ -int oaknode_track_get_length(OakNodeTrack *track, int *numerator, +int oaknode_track_get_length(OakNodeTrack track, int *numerator, int *denominator); /** - * @brief Owning sequence as a borrowed handle (NULL when trackless). + * @brief Owning sequence as a borrowed handle (empty when trackless). */ -int oaknode_track_get_sequence(OakNodeTrack *track, OakNodeSequence **out); +int oaknode_track_get_sequence(OakNodeTrack track, OakNodeSequence *out); /* ------------------------------------------------------- Track blocks */ /** * @brief Number of blocks on the track. */ -int oaknode_track_get_block_count(OakNodeTrack *track, int *count); +int oaknode_track_get_block_count(OakNodeTrack track, int *count); /** * @brief Borrowed handle to the block at `index`. * * @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND. */ -int oaknode_track_get_block_at(OakNodeTrack *track, int index, - OakNodeBlock **out); +int oaknode_track_get_block_at(OakNodeTrack track, int index, + OakNodeBlock *out); /** * @brief Append/prepend/insert primitives (olive::Track::*_block). @@ -174,54 +202,54 @@ int oaknode_track_get_block_at(OakNodeTrack *track, int index, * * @return OAKNODE_OK or OAKNODE_E_INVALID. */ -int oaknode_track_append_block(OakNodeTrack *track, OakNodeBlock *block); -int oaknode_track_prepend_block(OakNodeTrack *track, OakNodeBlock *block); -int oaknode_track_insert_block_at_index(OakNodeTrack *track, - OakNodeBlock *block, int index); -int oaknode_track_insert_block_after(OakNodeTrack *track, OakNodeBlock *block, - OakNodeBlock *before); -int oaknode_track_insert_block_before(OakNodeTrack *track, OakNodeBlock *block, - OakNodeBlock *after); +int oaknode_track_append_block(OakNodeTrack track, OakNodeBlock block); +int oaknode_track_prepend_block(OakNodeTrack track, OakNodeBlock block); +int oaknode_track_insert_block_at_index(OakNodeTrack track, + OakNodeBlock block, int index); +int oaknode_track_insert_block_after(OakNodeTrack track, OakNodeBlock block, + OakNodeBlock before); +int oaknode_track_insert_block_before(OakNodeTrack track, OakNodeBlock block, + OakNodeBlock after); /** * @brief Remove `block` and shift all subsequent blocks earlier * (olive::Track::ripple_remove_block). The block is NOT deleted; ownership * returns to the caller. */ -int oaknode_track_ripple_remove_block(OakNodeTrack *track, OakNodeBlock *block); +int oaknode_track_ripple_remove_block(OakNodeTrack track, OakNodeBlock block); /** * @brief Replace `old_block` with `new_block`; both must have equal lengths. */ -int oaknode_track_replace_block(OakNodeTrack *track, OakNodeBlock *old_block, - OakNodeBlock *new_block); +int oaknode_track_replace_block(OakNodeTrack track, OakNodeBlock old_block, + OakNodeBlock new_block); /** * @brief Index of `block` in the track's block array, or OAKNODE_E_NOT_FOUND. */ -int oaknode_track_get_block_index(OakNodeTrack *track, OakNodeBlock *block, +int oaknode_track_get_block_index(OakNodeTrack track, OakNodeBlock block, int *index); /** * @brief Block strictly containing `time` (in < time < out), or * OAKNODE_E_NOT_FOUND. */ -int oaknode_track_get_block_containing_time(OakNodeTrack *track, int numerator, +int oaknode_track_get_block_containing_time(OakNodeTrack track, int numerator, int denominator, - OakNodeBlock **out); + OakNodeBlock *out); /** * @brief Block visible at `time` (in <= time < out), or OAKNODE_E_NOT_FOUND. */ -int oaknode_track_get_visible_block_at_time(OakNodeTrack *track, int numerator, +int oaknode_track_get_visible_block_at_time(OakNodeTrack track, int numerator, int denominator, - OakNodeBlock **out); + OakNodeBlock *out); /** * @brief Whether the [in, out) range holds no block or only a gap * (olive::Track::is_range_free). `is_free` receives 1/0. */ -int oaknode_track_is_range_free(OakNodeTrack *track, int in_num, int in_den, +int oaknode_track_is_range_free(OakNodeTrack track, int in_num, int in_den, int out_num, int out_den, int *is_free); /* ------------------------------------------------------------ TrackList */ @@ -231,66 +259,66 @@ int oaknode_track_is_range_free(OakNodeTrack *track, int in_num, int in_den, */ /** * @brief Nearest block lookups (Track::nearest_block_before_or_at / - * nearest_block_after_or_at). *out is a borrowed handle or NULL. + * nearest_block_after_or_at). *out is a borrowed handle (empty when none). */ -int oaknode_track_get_nearest_block_before_or_at(OakNodeTrack *track, - int numerator, int denominator, OakNodeBlock **out); -int oaknode_track_get_nearest_block_after_or_at(OakNodeTrack *track, - int numerator, int denominator, OakNodeBlock **out); +int oaknode_track_get_nearest_block_before_or_at(OakNodeTrack track, + int numerator, int denominator, OakNodeBlock *out); +int oaknode_track_get_nearest_block_after_or_at(OakNodeTrack track, + int numerator, int denominator, OakNodeBlock *out); /** * @brief Borrowed sequence owning this track list. */ -int oaknode_tracklist_get_sequence(OakNodeTrackList *list, - OakNodeSequence **out); +int oaknode_tracklist_get_sequence(OakNodeTrackList list, + OakNodeSequence *out); /** * @brief The list's track input id on the parent sequence * (e.g. "track_in_0"). Two-stage string getter. */ -int oaknode_tracklist_get_track_input_id(OakNodeTrackList *list, +int oaknode_tracklist_get_track_input_id(OakNodeTrackList list, char *buf, int buf_size); /** * @brief Live input-array append/remove on the parent sequence for this * list's track input (TrackList::array_append/array_remove_last()). */ -int oaknode_tracklist_array_append(OakNodeTrackList *list); -int oaknode_tracklist_array_remove_last(OakNodeTrackList *list); +int oaknode_tracklist_array_append(OakNodeTrackList list); +int oaknode_tracklist_array_remove_last(OakNodeTrackList list); /** * @brief Map a cached track index to the input-array element index * (TrackList::get_array_index_from_cache_index()). */ int oaknode_tracklist_get_array_index_from_cache_index( - OakNodeTrackList *list, int cache_index, int *out_index); + OakNodeTrackList list, int cache_index, int *out_index); -int oaknode_tracklist_get_type(OakNodeTrackList *list, int *type); +int oaknode_tracklist_get_type(OakNodeTrackList list, int *type); /** * @brief Number of connected tracks. */ -int oaknode_tracklist_get_track_count(OakNodeTrackList *list, int *count); +int oaknode_tracklist_get_track_count(OakNodeTrackList list, int *count); /** * @brief Borrowed handle to the track at `index`. * * @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND. */ -int oaknode_tracklist_get_track_at(OakNodeTrackList *list, int index, - OakNodeTrack **out); +int oaknode_tracklist_get_track_at(OakNodeTrackList list, int index, + OakNodeTrack *out); /** * @brief Combined length of the longest track in the list. */ -int oaknode_tracklist_get_total_length(OakNodeTrackList *list, int *numerator, +int oaknode_tracklist_get_total_length(OakNodeTrackList list, int *numerator, int *denominator); /** * @brief Size of the underlying input array (>= track count; may contain * disconnected slots). */ -int oaknode_tracklist_get_array_size(OakNodeTrackList *list, int *size); +int oaknode_tracklist_get_array_size(OakNodeTrackList list, int *size); /** * @brief Add `track` to the list (non-undoable primitive). @@ -301,9 +329,12 @@ int oaknode_tracklist_get_array_size(OakNodeTrackList *list, int *size); * the track is connected to it. The sequence's flat track cache and * lengths are refreshed before returning. * + * The list takes ownership of the track on success; the caller's handle + * becomes a non-owning reference. + * * @return OAKNODE_OK or OAKNODE_E_INVALID. */ -int oaknode_tracklist_add_track(OakNodeTrackList *list, OakNodeTrack *track); +int oaknode_tracklist_add_track(OakNodeTrackList list, OakNodeTrack track); /** * @brief Remove `track` from the list (non-undoable primitive). @@ -314,8 +345,8 @@ int oaknode_tracklist_add_track(OakNodeTrackList *list, OakNodeTrack *track); * * @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND. */ -int oaknode_tracklist_remove_track(OakNodeTrackList *list, - OakNodeTrack *track); +int oaknode_tracklist_remove_track(OakNodeTrackList list, + OakNodeTrack track); #ifdef __cplusplus } diff --git a/include/node/traverser.h b/include/node/traverser.h index 9f38d889c..53f9a7624 100644 --- a/include/node/traverser.h +++ b/include/node/traverser.h @@ -41,26 +41,46 @@ extern "C" { */ /** - * @brief Opaque traverser handle (olive::NodeTraverser). + * @brief Reference-counted handle to a traverser (olive::NodeTraverser). + * + * Semantics are shared_ptr-like: oaknode_traverser_init() returns a + * handle with count 1, addref(ctx) takes another reference, release(ctx) + * drops one and the library destroys the object when the count reaches + * zero. */ -typedef struct OakNodeTraverser OakNodeTraverser; +typedef struct OakNodeTraverser { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeTraverser; /** - * @brief Opaque value-database handle (an owned copy of an - * olive::NodeValueDatabase). Release with - * oaknode_traverser_database_free(). + * @brief Reference-counted handle to an owned copy of an + * olive::NodeValueDatabase. Same reference-counting rules as + * OakNodeTraverser; release with oaknode_traverser_database_free(). */ -typedef struct OakNodeValueDatabase OakNodeValueDatabase; +typedef struct OakNodeValueDatabase { + void *ctx; /**< Opaque pointer to the reference-counted object. */ + void (*addref)(void *ctx); /**< Atomically increments the count. */ + void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */ + uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */ +} OakNodeValueDatabase; /** * @brief Create a traverser. * - * @return Traverser handle, or NULL on allocation failure. + * @return Traverser handle with count 1; ctx is NULL on allocation + * failure. */ -OakNodeTraverser *oaknode_traverser_init(void); +OakNodeTraverser oaknode_traverser_init(void); /** - * @brief Destroy a traverser. NULL is a no-op. + * @brief Release one reference to a traverser handle. + * + * Convenience wrapper around handle.release(handle.ctx): destroys the + * traverser when the count reaches zero. NULL handle or NULL ctx is a + * no-op; clears `traverser->ctx` after releasing. */ void oaknode_traverser_free(OakNodeTraverser *traverser); @@ -69,39 +89,43 @@ void oaknode_traverser_free(OakNodeTraverser *traverser); * [`in_num`/`in_den`, `out_num`/`out_den`) seconds * (NodeTraverser::generate_database()). * - * `out_db` receives an owned database handle. + * `out_db` receives an owned database handle with count 1. * * @return OAKNODE_OK or a negative OAKNODE_E_* error code. */ -int oaknode_traverser_generate_database(OakNodeTraverser *traverser, - OakNodeNode *node, int64_t in_num, +int oaknode_traverser_generate_database(OakNodeTraverser traverser, + OakNodeNode node, int64_t in_num, int64_t in_den, int64_t out_num, int64_t out_den, - OakNodeValueDatabase **out_db); + OakNodeValueDatabase *out_db); /** - * @brief Destroy a database handle. NULL is a no-op. + * @brief Release one reference to a database handle. + * + * Convenience wrapper around handle.release(handle.ctx): destroys the + * database when the count reaches zero. NULL handle or NULL ctx is a + * no-op; clears `db->ctx` after releasing. */ void oaknode_traverser_database_free(OakNodeValueDatabase *db); /** * @brief Number of rows (input tables) in the database. */ -int oaknode_traverser_database_row_count(const OakNodeValueDatabase *db, +int oaknode_traverser_database_row_count(OakNodeValueDatabase db, int *out_count); /** * @brief The input id (key) of the row at `index`. Two-stage getter; * OAKNODE_E_NOT_FOUND for an out-of-range index. */ -int oaknode_traverser_database_row_key_at(const OakNodeValueDatabase *db, +int oaknode_traverser_database_row_key_at(OakNodeValueDatabase db, int index, char *buf, int buf_size); /** * @brief Number of values in the row named `key`. * OAKNODE_E_NOT_FOUND for an unknown key. */ -int oaknode_traverser_database_row_value_count(const OakNodeValueDatabase *db, +int oaknode_traverser_database_row_value_count(OakNodeValueDatabase db, const char *key, int *out_count); @@ -110,7 +134,7 @@ int oaknode_traverser_database_row_value_count(const OakNodeValueDatabase *db, * Values without a POD representation fail with OAKNODE_E_FAILED; * OAKNODE_E_NOT_FOUND for an unknown key or out-of-range index. */ -int oaknode_traverser_database_value_at(const OakNodeValueDatabase *db, +int oaknode_traverser_database_value_at(OakNodeValueDatabase db, const char *key, int index, oaknode_value *out); @@ -119,7 +143,7 @@ int oaknode_traverser_database_value_at(const OakNodeValueDatabase *db, * (NodeValue::value_to_string()). Two-stage getter; * OAKNODE_E_NOT_FOUND for an unknown key or out-of-range index. */ -int oaknode_traverser_database_value_string_at(const OakNodeValueDatabase *db, +int oaknode_traverser_database_value_string_at(OakNodeValueDatabase db, const char *key, int index, char *buf, int buf_size); diff --git a/include/render/copier.h b/include/render/copier.h index 947a13011..dbf226966 100644 --- a/include/render/copier.h +++ b/include/render/copier.h @@ -45,17 +45,19 @@ OakRenderProjectCopier *oakrender_project_copier_create(void); /** @brief Free the copier AND its copied project. NULL-safe. */ void oakrender_project_copier_free(OakRenderProjectCopier *copier); -/** @brief (Re)build the copy from `project`. */ +/** @brief (Re)build the copy from `project` (borrowed handle). */ int oakrender_project_copier_set_project(OakRenderProjectCopier *copier, - OakNodeProject *project); + OakNodeProject project); -/** @brief The copied counterpart of an original node (borrowed), NULL - * when the node is not in the copied project. */ -OakNodeNode *oakrender_project_copier_get_copy( - OakRenderProjectCopier *copier, OakNodeNode *original); +/** @brief The copied counterpart of an original node (borrowed handle; + * freeing it only releases the handle box), empty handle when the + * node is not in the copied project. */ +OakNodeNode oakrender_project_copier_get_copy( + OakRenderProjectCopier *copier, OakNodeNode original); -/** @brief The copied project (borrowed). */ -OakNodeProject *oakrender_project_copier_get_copied_project( +/** @brief The copied project (borrowed handle; freeing it only releases + * the handle box). */ +OakNodeProject oakrender_project_copier_get_copied_project( OakRenderProjectCopier *copier); #ifdef __cplusplus diff --git a/include/render/manager.h b/include/render/manager.h index 69e29a6f8..6fa440aca 100644 --- a/include/render/manager.h +++ b/include/render/manager.h @@ -24,6 +24,7 @@ #include // See cache.h for why these are same-dir relative includes. +#include "node/node.h" /* OakNodeNode (by-value handle) */ #include "cache.h" /* OakCodecFrame */ #include "color.h" /* OakColorProcessor */ #include "error.h" @@ -50,12 +51,6 @@ extern "C" { * are no event subscription interfaces. */ -/** - * @brief Borrowed node handle (olive::Node) from the oaknode ABI, - * re-declared here so this header is self-contained. - */ -typedef struct OakNodeNode OakNodeNode; - /** * @brief Create the RenderManager singleton (spawns render/audio * threads, loads the configured backend). @@ -91,11 +86,12 @@ typedef void (*oakrender_frame_ready_fn)(OakCodecFrame *frame, int64_t ts, * cancelled with oakrender_cancel_request(). * * @return A positive request id, or a negative OAKRENDER_E_* code - * (OAKRENDER_E_INVALID for NULL viewer/callback, - * OAKRENDER_E_STATE when the manager is not initialized, - * OAKRENDER_E_FAILED when no ticket could be created). + * (OAKRENDER_E_INVALID for an empty viewer handle or NULL + * callback, OAKRENDER_E_STATE when the manager is not + * initialized, OAKRENDER_E_FAILED when no ticket could be + * created). */ -int64_t oakrender_request_frame(OakNodeNode *viewer, int64_t ts, +int64_t oakrender_request_frame(OakNodeNode viewer, int64_t ts, oakrender_frame_ready_fn cb, void *userdata); /** @@ -109,11 +105,11 @@ int oakrender_cancel_request(int64_t request_id); /** * @brief Set the multicam node on the manager's auto-cacher * (PreviewAutoCacher::set_multicam_node()). `multicam_or_NULL` is a - * borrowed oaknode handle to a MultiCamNode (NULL to clear). + * borrowed oaknode handle to a MultiCamNode (empty handle to clear). * * @return OAKRENDER_OK or OAKRENDER_E_STATE. */ -int oakrender_set_cacher_multicam(OakNodeNode *multicam_or_NULL); +int oakrender_set_cacher_multicam(OakNodeNode multicam_or_NULL); /** * @brief Set the display color processor on the manager's auto-cacher diff --git a/include/render/ticket.h b/include/render/ticket.h index 24271b1e7..a8f1efa85 100644 --- a/include/render/ticket.h +++ b/include/render/ticket.h @@ -64,12 +64,12 @@ enum OakRenderTicketType { * (RenderManager::RenderVideoParams). */ typedef struct oakrender_video_ticket_params { - OakNodeNode *output_node; /**< Connected texture output node. */ + OakNodeNode output_node; /**< Connected texture output node (borrowed). */ OakVideoParams video_params; /**< By value (oakcommon handle). */ OakAudioParams *audio_params; /**< Borrowed oakcore handle, may be NULL. */ int64_t time_num; /**< Frame timestamp as rational. */ int64_t time_den; - OakNodeColorManager *color_manager; /**< Borrowed, may be NULL. */ + OakNodeColorManager color_manager; /**< Borrowed, empty ctx = NULL. */ int mode; /**< olive::RenderMode::Mode as int. */ int force_width; /**< 0/0 = off. */ int force_height; @@ -100,7 +100,7 @@ OakRenderTicket *oakrender_ticket_render_frame( * @param params Audio params (borrowed oakcore handle). */ OakRenderTicket *oakrender_ticket_render_audio( - OakNodeNode *output_node, int64_t in_num, int64_t in_den, + OakNodeNode output_node, int64_t in_num, int64_t in_den, int64_t out_num, int64_t out_den, const OakAudioParams *params, int mode, oakrender_ticket_finished_fn cb, void *userdata); diff --git a/include/task/project.h b/include/task/project.h index 3be9c7675..a58ccd9b1 100644 --- a/include/task/project.h +++ b/include/task/project.h @@ -41,18 +41,21 @@ extern "C" { /** @brief olive::ProjectLoadTask. */ OakTaskTask *oaktask_create_project_load(const char *filename); -/** @brief Take the loaded project (ownership transfer). */ -OakNodeProject *oaktask_load_take_project(OakTaskTask *t); +/** @brief Take the loaded project (ownership transfer). Empty handle + * (ctx == NULL) when the task has not succeeded or the project was + * already taken. */ +OakNodeProject oaktask_load_take_project(OakTaskTask *t); /** @brief olive::ProjectSaveTask. `filename_or_NULL` overrides the - * project's own filename. */ -OakTaskTask *oaktask_create_project_save(OakNodeProject *project, + * project's own filename. `project` is borrowed by the task. */ +OakTaskTask *oaktask_create_project_save(OakNodeProject project, const char *filename_or_NULL, int use_compression); -/** @brief olive::ProjectImportTask. */ -OakTaskTask *oaktask_create_project_import(OakNodeFolder *folder, - OakNodeProject *project, +/** @brief olive::ProjectImportTask. `folder`/`project` are borrowed by + * the task. */ +OakTaskTask *oaktask_create_project_import(OakNodeFolder folder, + OakNodeProject project, const char *const *urls, int url_count); @@ -61,8 +64,10 @@ OakUndoCommand oaktask_import_take_command(OakTaskTask *t); int oaktask_import_footage_count(OakTaskTask *t); -/** @brief Borrowed footage handle at index, NULL when out of range. */ -OakNodeFootage *oaktask_import_footage_at(OakTaskTask *t, int index); +/** @brief Footage handle at index (addref'd; release with + * handle.release(handle.ctx) - box only, the project owns the + * footage). Empty handle when out of range. */ +OakNodeFootage oaktask_import_footage_at(OakTaskTask *t, int index); int oaktask_import_invalid_count(OakTaskTask *t); @@ -73,11 +78,13 @@ int oaktask_import_invalid_at(OakTaskTask *t, int index, char *buf, /** @brief olive::LoadOTIOTask. */ OakTaskTask *oaktask_create_project_load_otio(const char *filename); -/** @brief Take the loaded project (ownership transfer). */ -OakNodeProject *oaktask_load_otio_take_project(OakTaskTask *t); +/** @brief Take the loaded project (ownership transfer). Empty handle + * (ctx == NULL) when the task has not succeeded or the project was + * already taken. */ +OakNodeProject oaktask_load_otio_take_project(OakTaskTask *t); -/** @brief olive::SaveOTIOTask. */ -OakTaskTask *oaktask_create_project_save_otio(OakNodeProject *project, +/** @brief olive::SaveOTIOTask. `project` is borrowed by the task. */ +OakTaskTask *oaktask_create_project_save_otio(OakNodeProject project, const char *filename); /** @@ -89,13 +96,15 @@ typedef int (*oaktask_otio_import_confirm_fn)( void oaktask_load_otio_set_confirm_cb(oaktask_otio_import_confirm_fn fn, void *userdata); -/** @brief olive::PreCacheTask. */ -OakTaskTask *oaktask_create_precache(OakNodeFootage *footage, int index, - OakNodeSequence *sequence); +/** @brief olive::PreCacheTask. `footage`/`sequence` are borrowed by the + * task. */ +OakTaskTask *oaktask_create_precache(OakNodeFootage footage, int index, + OakNodeSequence sequence); -/** @brief olive::ExportTask (params POD from codec/encoder.h). */ -OakTaskTask *oaktask_create_export(OakNodeNode *viewer, - OakNodeColorManager *color_manager, +/** @brief olive::ExportTask (params POD from codec/encoder.h). + * `viewer`/`color_manager` are borrowed by the task. */ +OakTaskTask *oaktask_create_export(OakNodeNode viewer, + OakNodeColorManager color_manager, const oakcodec_encoding_params *params); /** diff --git a/include/timeline/edit.h b/include/timeline/edit.h index aadd739b3..33cea9a62 100644 --- a/include/timeline/edit.h +++ b/include/timeline/edit.h @@ -38,63 +38,67 @@ extern "C" { * consumers create commands through these factories, receiving base * OakUndoCommand handles (owned; free with oakundo_command_free()). * Redo a command directly or push it on an undo stack. + * + * OakNode* handles are passed by value per the oaknode handle + * convention; an empty handle (ctx == NULL) yields an empty + * OakUndoCommand result. */ /** @brief olive::TimelineAddTrackCommand. */ -OakUndoCommand oaktimeline_add_track_command(OakNodeTrackList *list); +OakUndoCommand oaktimeline_add_track_command(OakNodeTrackList list); /** @brief olive::TimelineRemoveTrackCommand. */ -OakUndoCommand oaktimeline_remove_track_command(OakNodeTrack *track); +OakUndoCommand oaktimeline_remove_track_command(OakNodeTrack track); /** @brief olive::TrackPlaceBlockCommand. */ -OakUndoCommand oaktimeline_place_block_command(OakNodeTrackList *list, +OakUndoCommand oaktimeline_place_block_command(OakNodeTrackList list, int track_index, - OakNodeBlock *block, + OakNodeBlock block, int64_t in_num, int64_t in_den); /** @brief olive::TrackReplaceBlockWithGapCommand. */ OakUndoCommand oaktimeline_replace_block_with_gap_command( - OakNodeTrack *track, OakNodeBlock *block); + OakNodeTrack track, OakNodeBlock block); /** * @brief olive::BlockTrimCommand. `mode` is an OakTimelineMovementMode * value (k_trim_in / k_trim_out). */ -OakUndoCommand oaktimeline_trim_command(OakNodeTrack *track, - OakNodeBlock *block, +OakUndoCommand oaktimeline_trim_command(OakNodeTrack track, + OakNodeBlock block, int64_t new_length_num, int64_t new_length_den, int mode); /** @brief olive::BlockSplitCommand on a set of blocks at one point. */ -OakUndoCommand oaktimeline_split_command(OakNodeBlock *const *blocks, +OakUndoCommand oaktimeline_split_command(const OakNodeBlock *blocks, int count, int64_t point_num, int64_t point_den); /** @brief olive::BlockSplitPreservingLinksCommand. */ OakUndoCommand oaktimeline_split_preserving_links_command( - OakNodeBlock *const *blocks, int count, const int64_t *point_nums, + const OakNodeBlock *blocks, int count, const int64_t *point_nums, const int64_t *point_dens, int time_count); /** @brief olive::TimelineRippleDeleteGapsAtRegionsCommand. */ OakUndoCommand oaktimeline_ripple_delete_gaps_command( - OakNodeSequence *sequence, const int64_t *in_nums, + OakNodeSequence sequence, const int64_t *in_nums, const int64_t *in_dens, const int64_t *out_nums, - const int64_t *out_dens, OakNodeTrack *const *tracks, int range_count); + const int64_t *out_dens, const OakNodeTrack *tracks, int range_count); /** @brief olive::TrackSlideCommand. */ OakUndoCommand oaktimeline_slide_command( - OakNodeTrack *track, OakNodeBlock *const *blocks, int block_count, - OakNodeBlock *in_adjacent, OakNodeBlock *out_adjacent, + OakNodeTrack track, const OakNodeBlock *blocks, int block_count, + OakNodeBlock in_adjacent, OakNodeBlock out_adjacent, int64_t movement_num, int64_t movement_den); /** @brief olive::TrackRippleRemoveAreaCommand. */ OakUndoCommand oaktimeline_ripple_remove_area_command( - OakNodeTrack *track, int64_t in_num, int64_t in_den, int64_t out_num, + OakNodeTrack track, int64_t in_num, int64_t in_den, int64_t out_num, int64_t out_den); /** @brief olive::TrackListInsertGaps. */ -OakUndoCommand oaktimeline_insert_gaps_command(OakNodeTrackList *list, +OakUndoCommand oaktimeline_insert_gaps_command(OakNodeTrackList list, int64_t point_num, int64_t point_den, int64_t length_num, diff --git a/include/timeline/marker.h b/include/timeline/marker.h index ce914214a..99c0e3e18 100644 --- a/include/timeline/marker.h +++ b/include/timeline/marker.h @@ -39,10 +39,10 @@ extern "C" { typedef struct OakTimelineMarkerList OakTimelineMarkerList; /** - * @brief Borrowed marker list of a viewer node (sequence). NULL for NULL - * or when the node is not a viewer. + * @brief Borrowed marker list of a viewer node (sequence). NULL for an + * empty handle or when the node is not a viewer. */ -OakTimelineMarkerList *oaktimeline_marker_list_of(OakNodeNode *owner); +OakTimelineMarkerList *oaktimeline_marker_list_of(OakNodeNode owner); /** * @brief Number of markers. Out-param convention; OAKTIMELINE_E_INVALID diff --git a/include/timeline/workarea.h b/include/timeline/workarea.h index 5bb90028a..0bb20ec60 100644 --- a/include/timeline/workarea.h +++ b/include/timeline/workarea.h @@ -37,10 +37,10 @@ extern "C" { typedef struct OakTimelineWorkArea OakTimelineWorkArea; /** - * @brief Borrowed work area of a viewer node (sequence). NULL for NULL - * or when the node is not a viewer. + * @brief Borrowed work area of a viewer node (sequence). NULL for an + * empty handle or when the node is not a viewer. */ -OakTimelineWorkArea *oaktimeline_workarea_of(OakNodeNode *owner); +OakTimelineWorkArea *oaktimeline_workarea_of(OakNodeNode owner); /** * @brief Read the work area state. Out params may individually be NULL. diff --git a/src/node/c_api/block.cpp b/src/node/c_api/block.cpp index 306b320df..88c2947a0 100644 --- a/src/node/c_api/block.cpp +++ b/src/node/c_api/block.cpp @@ -20,7 +20,8 @@ #include "node/block.h" -#include "alivecount.h" +#include "node/node.h" +#include "node/track.h" #include "block/block.h" #include "block/clip/clip.h" @@ -30,27 +31,26 @@ #include "block/transition/transition.h" #include "output/track/track.h" +#include "nodehandle.h" + +using oaknode_c_api::delete_as; +using oaknode_c_api::free_handle; +using oaknode_c_api::make_handle; +using oaknode_c_api::to_native; + namespace { -olive::Block *impl(OakNodeBlock *h) +olive::ClipBlock *clip_impl(OakNodeBlock h) { - return reinterpret_cast(h); + olive::Block *b = to_native(h); + return b ? dynamic_cast(b) : nullptr; } -olive::ClipBlock *clip_impl(OakNodeBlock *h) +olive::TransitionBlock *transition_impl(OakNodeBlock h) { - return h ? dynamic_cast(impl(h)) : nullptr; -} - -olive::TransitionBlock *transition_impl(OakNodeBlock *h) -{ - return h ? dynamic_cast(impl(h)) : nullptr; -} - -OakNodeBlock *wrap(olive::Block *b) -{ - return reinterpret_cast(b); + olive::Block *b = to_native(h); + return b ? dynamic_cast(b) : nullptr; } int get_rational(const olive::core::Rational &r, int *numerator, @@ -65,30 +65,29 @@ int get_rational(const olive::core::Rational &r, int *numerator, } template -OakNodeBlock *create_block(Args &&...args) +OakNodeBlock create_block(Args &&...args) { try { - T *b = new T(std::forward(args)...); - oaknode_c_api::alive_inc(); - return wrap(b); + return make_handle(new T(std::forward(args)...), + true, &delete_as); } catch (...) { - return nullptr; + return OakNodeBlock{}; } } } // namespace -OakNodeBlock *oaknode_block_clip_create(void) +OakNodeBlock oaknode_block_clip_create(void) { return create_block(); } -OakNodeBlock *oaknode_block_gap_create(void) +OakNodeBlock oaknode_block_gap_create(void) { return create_block(); } -OakNodeBlock *oaknode_block_transition_create(int kind) +OakNodeBlock oaknode_block_transition_create(int kind) { switch (kind) { case OAKNODE_TRANSITION_CROSS_DISSOLVE: @@ -96,70 +95,72 @@ OakNodeBlock *oaknode_block_transition_create(int kind) case OAKNODE_TRANSITION_DIP_TO_COLOR: return create_block(); default: - return nullptr; + return OakNodeBlock{}; } } void oaknode_block_free(OakNodeBlock *block) { - if (!block) { - return; - } - delete impl(block); - oaknode_c_api::alive_dec(); + free_handle(block); } -int oaknode_block_get_in(OakNodeBlock *block, int *numerator, int *denominator) +int oaknode_block_get_in(OakNodeBlock block, int *numerator, int *denominator) { - if (!block) { + olive::Block *b = to_native(block); + if (!b) { return OAKNODE_E_INVALID; } - return get_rational(impl(block)->in(), numerator, denominator); + return get_rational(b->in(), numerator, denominator); } -int oaknode_block_set_in(OakNodeBlock *block, int numerator, int denominator) +int oaknode_block_set_in(OakNodeBlock block, int numerator, int denominator) { - if (!block) { + olive::Block *b = to_native(block); + if (!b) { return OAKNODE_E_INVALID; } - impl(block)->set_in(olive::core::Rational(numerator, denominator)); + b->set_in(olive::core::Rational(numerator, denominator)); return OAKNODE_OK; } -int oaknode_block_get_out(OakNodeBlock *block, int *numerator, int *denominator) +int oaknode_block_get_out(OakNodeBlock block, int *numerator, int *denominator) { - if (!block) { + olive::Block *b = to_native(block); + if (!b) { return OAKNODE_E_INVALID; } - return get_rational(impl(block)->out(), numerator, denominator); + return get_rational(b->out(), numerator, denominator); } -int oaknode_block_set_out(OakNodeBlock *block, int numerator, int denominator) +int oaknode_block_set_out(OakNodeBlock block, int numerator, int denominator) { - if (!block) { + olive::Block *b = to_native(block); + if (!b) { return OAKNODE_E_INVALID; } - impl(block)->set_out(olive::core::Rational(numerator, denominator)); + b->set_out(olive::core::Rational(numerator, denominator)); return OAKNODE_OK; } -int oaknode_block_get_length(OakNodeBlock *block, int *numerator, +int oaknode_block_get_length(OakNodeBlock block, int *numerator, int *denominator) { - if (!block) { + olive::Block *b = to_native(block); + if (!b) { return OAKNODE_E_INVALID; } - return get_rational(impl(block)->length(), numerator, denominator); + return get_rational(b->length(), numerator, denominator); } -int oaknode_block_set_length_and_media_out(OakNodeBlock *block, int numerator, +int oaknode_block_set_length_and_media_out(OakNodeBlock block, int numerator, int denominator) { - if (!block) { + olive::Block *b = to_native(block); + if (!b) { return OAKNODE_E_INVALID; } try { - impl(block)->set_length_and_media_out( + b->set_length_and_media_out( olive::core::Rational(numerator, denominator)); } catch (...) { return OAKNODE_E_FAILED; @@ -167,14 +168,15 @@ int oaknode_block_set_length_and_media_out(OakNodeBlock *block, int numerator, return OAKNODE_OK; } -int oaknode_block_set_length_and_media_in(OakNodeBlock *block, int numerator, +int oaknode_block_set_length_and_media_in(OakNodeBlock block, int numerator, int denominator) { - if (!block) { + olive::Block *b = to_native(block); + if (!b) { return OAKNODE_E_INVALID; } try { - impl(block)->set_length_and_media_in( + b->set_length_and_media_in( olive::core::Rational(numerator, denominator)); } catch (...) { return OAKNODE_E_FAILED; @@ -182,102 +184,123 @@ int oaknode_block_set_length_and_media_in(OakNodeBlock *block, int numerator, return OAKNODE_OK; } -int oaknode_block_get_enabled(OakNodeBlock *block, int *enabled) +int oaknode_block_get_enabled(OakNodeBlock block, int *enabled) { - if (!block || !enabled) { + olive::Block *b = to_native(block); + if (!b || !enabled) { return OAKNODE_E_INVALID; } - *enabled = impl(block)->is_enabled() ? 1 : 0; + *enabled = b->is_enabled() ? 1 : 0; return OAKNODE_OK; } -int oaknode_block_set_enabled(OakNodeBlock *block, int enabled) +int oaknode_block_set_enabled(OakNodeBlock block, int enabled) { - if (!block) { + olive::Block *b = to_native(block); + if (!b) { return OAKNODE_E_INVALID; } - impl(block)->set_enabled(enabled != 0); + b->set_enabled(enabled != 0); return OAKNODE_OK; } -int oaknode_block_get_previous(OakNodeBlock *block, OakNodeBlock **out) +int oaknode_block_get_previous(OakNodeBlock block, OakNodeBlock *out) { - if (!block || !out) { + olive::Block *b = to_native(block); + if (!b || !out) { return OAKNODE_E_INVALID; } - *out = wrap(impl(block)->previous()); + // Borrowed (empty when there is no neighbour) + *out = make_handle(b->previous(), false, + &delete_as); return OAKNODE_OK; } -int oaknode_block_get_next(OakNodeBlock *block, OakNodeBlock **out) +int oaknode_block_get_next(OakNodeBlock block, OakNodeBlock *out) { - if (!block || !out) { + olive::Block *b = to_native(block); + if (!b || !out) { return OAKNODE_E_INVALID; } - *out = wrap(impl(block)->next()); + *out = make_handle(b->next(), false, + &delete_as); return OAKNODE_OK; } -int oaknode_block_get_track(OakNodeBlock *block, OakNodeTrack **out) +int oaknode_block_get_track(OakNodeBlock block, OakNodeTrack *out) { - if (!block || !out) { + olive::Block *b = to_native(block); + if (!b || !out) { return OAKNODE_E_INVALID; } - *out = reinterpret_cast(impl(block)->track()); + // Borrowed (empty when the block is not on a track) + *out = make_handle(b->track(), false, + &delete_as); return OAKNODE_OK; } -int oaknode_block_link(OakNodeBlock *a, OakNodeBlock *b) +int oaknode_block_link(OakNodeBlock a, OakNodeBlock b) { - if (!a || !b) { + olive::Block *na = to_native(a); + olive::Block *nb = to_native(b); + if (!na || !nb) { return OAKNODE_E_INVALID; } - return olive::Node::link(impl(a), impl(b)) ? OAKNODE_OK : OAKNODE_E_FAILED; + return olive::Node::link(na, nb) ? OAKNODE_OK : OAKNODE_E_FAILED; } -int oaknode_block_unlink(OakNodeBlock *a, OakNodeBlock *b) +int oaknode_block_unlink(OakNodeBlock a, OakNodeBlock b) { - if (!a || !b) { + olive::Block *na = to_native(a); + olive::Block *nb = to_native(b); + if (!na || !nb) { return OAKNODE_E_INVALID; } - return olive::Node::unlink(impl(a), impl(b)) ? OAKNODE_OK : OAKNODE_E_FAILED; + return olive::Node::unlink(na, nb) ? OAKNODE_OK : OAKNODE_E_FAILED; } -int oaknode_block_are_linked(OakNodeBlock *a, OakNodeBlock *b, int *linked) +int oaknode_block_are_linked(OakNodeBlock a, OakNodeBlock b, int *linked) { - if (!a || !b || !linked) { + olive::Block *na = to_native(a); + olive::Block *nb = to_native(b); + if (!na || !nb || !linked) { return OAKNODE_E_INVALID; } - *linked = olive::Node::are_linked(impl(a), impl(b)) ? 1 : 0; + *linked = olive::Node::are_linked(na, nb) ? 1 : 0; return OAKNODE_OK; } -int oaknode_block_get_link_count(OakNodeBlock *block, int *count) +int oaknode_block_get_link_count(OakNodeBlock block, int *count) { - if (!block || !count) { + olive::Block *b = to_native(block); + if (!b || !count) { return OAKNODE_E_INVALID; } - *count = int(impl(block)->links().size()); + *count = int(b->links().size()); return OAKNODE_OK; } -int oaknode_block_get_link_at(OakNodeBlock *block, int index, - OakNodeBlock **out) +int oaknode_block_get_link_at(OakNodeBlock block, int index, + OakNodeBlock *out) { - if (!block || !out || index < 0) { + olive::Block *b = to_native(block); + if (!b || !out || index < 0) { return OAKNODE_E_INVALID; } - const auto &links = impl(block)->links(); + const auto &links = b->links(); if (index >= int(links.size())) { return OAKNODE_E_NOT_FOUND; } - *out = wrap(static_cast(links.at(index))); - return OAKNODE_OK; + // Borrowed; the linked block stays owned by its graph + *out = make_handle( + static_cast(links.at(index)), false, + &delete_as); + return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM; } /* ---------------------------------------------------------------- Clip */ -int oaknode_clip_get_media_in(OakNodeBlock *clip, int *numerator, +int oaknode_clip_get_media_in(OakNodeBlock clip, int *numerator, int *denominator) { olive::ClipBlock *c = clip_impl(clip); @@ -287,7 +310,7 @@ int oaknode_clip_get_media_in(OakNodeBlock *clip, int *numerator, return get_rational(c->media_in(), numerator, denominator); } -int oaknode_clip_set_media_in(OakNodeBlock *clip, int numerator, +int oaknode_clip_set_media_in(OakNodeBlock clip, int numerator, int denominator) { olive::ClipBlock *c = clip_impl(clip); @@ -298,7 +321,7 @@ int oaknode_clip_set_media_in(OakNodeBlock *clip, int numerator, return OAKNODE_OK; } -int oaknode_clip_get_speed(OakNodeBlock *clip, double *speed) +int oaknode_clip_get_speed(OakNodeBlock clip, double *speed) { olive::ClipBlock *c = clip_impl(clip); if (!c || !speed) { @@ -308,7 +331,7 @@ int oaknode_clip_get_speed(OakNodeBlock *clip, double *speed) return OAKNODE_OK; } -int oaknode_clip_set_speed(OakNodeBlock *clip, double speed) +int oaknode_clip_set_speed(OakNodeBlock clip, double speed) { olive::ClipBlock *c = clip_impl(clip); if (!c) { @@ -318,7 +341,7 @@ int oaknode_clip_set_speed(OakNodeBlock *clip, double speed) return OAKNODE_OK; } -int oaknode_clip_get_reverse(OakNodeBlock *clip, int *reverse) +int oaknode_clip_get_reverse(OakNodeBlock clip, int *reverse) { olive::ClipBlock *c = clip_impl(clip); if (!c || !reverse) { @@ -328,7 +351,7 @@ int oaknode_clip_get_reverse(OakNodeBlock *clip, int *reverse) return OAKNODE_OK; } -int oaknode_clip_set_reverse(OakNodeBlock *clip, int reverse) +int oaknode_clip_set_reverse(OakNodeBlock clip, int reverse) { olive::ClipBlock *c = clip_impl(clip); if (!c) { @@ -338,7 +361,7 @@ int oaknode_clip_set_reverse(OakNodeBlock *clip, int reverse) return OAKNODE_OK; } -int oaknode_clip_get_maintain_audio_pitch(OakNodeBlock *clip, int *maintain) +int oaknode_clip_get_maintain_audio_pitch(OakNodeBlock clip, int *maintain) { olive::ClipBlock *c = clip_impl(clip); if (!c || !maintain) { @@ -348,7 +371,7 @@ int oaknode_clip_get_maintain_audio_pitch(OakNodeBlock *clip, int *maintain) return OAKNODE_OK; } -int oaknode_clip_set_maintain_audio_pitch(OakNodeBlock *clip, int maintain) +int oaknode_clip_set_maintain_audio_pitch(OakNodeBlock clip, int maintain) { olive::ClipBlock *c = clip_impl(clip); if (!c) { @@ -358,7 +381,7 @@ int oaknode_clip_set_maintain_audio_pitch(OakNodeBlock *clip, int maintain) return OAKNODE_OK; } -int oaknode_clip_get_loop_mode(OakNodeBlock *clip, int *loop_mode) +int oaknode_clip_get_loop_mode(OakNodeBlock clip, int *loop_mode) { olive::ClipBlock *c = clip_impl(clip); if (!c || !loop_mode) { @@ -368,7 +391,7 @@ int oaknode_clip_get_loop_mode(OakNodeBlock *clip, int *loop_mode) return OAKNODE_OK; } -int oaknode_clip_set_loop_mode(OakNodeBlock *clip, int loop_mode) +int oaknode_clip_set_loop_mode(OakNodeBlock clip, int loop_mode) { olive::ClipBlock *c = clip_impl(clip); if (!c) { @@ -378,7 +401,7 @@ int oaknode_clip_set_loop_mode(OakNodeBlock *clip, int loop_mode) return OAKNODE_OK; } -int oaknode_clip_get_track_type(OakNodeBlock *clip, int *type) +int oaknode_clip_get_track_type(OakNodeBlock clip, int *type) { olive::ClipBlock *c = clip_impl(clip); if (!c || !type) { @@ -390,7 +413,7 @@ int oaknode_clip_get_track_type(OakNodeBlock *clip, int *type) /* ----------------------------------------------------------- Transition */ -int oaknode_transition_get_in_offset(OakNodeBlock *transition, int *numerator, +int oaknode_transition_get_in_offset(OakNodeBlock transition, int *numerator, int *denominator) { olive::TransitionBlock *t = transition_impl(transition); @@ -400,7 +423,7 @@ int oaknode_transition_get_in_offset(OakNodeBlock *transition, int *numerator, return get_rational(t->in_offset(), numerator, denominator); } -int oaknode_transition_get_out_offset(OakNodeBlock *transition, int *numerator, +int oaknode_transition_get_out_offset(OakNodeBlock transition, int *numerator, int *denominator) { olive::TransitionBlock *t = transition_impl(transition); @@ -410,7 +433,7 @@ int oaknode_transition_get_out_offset(OakNodeBlock *transition, int *numerator, return get_rational(t->out_offset(), numerator, denominator); } -int oaknode_transition_get_offset_center(OakNodeBlock *transition, +int oaknode_transition_get_offset_center(OakNodeBlock transition, int *numerator, int *denominator) { olive::TransitionBlock *t = transition_impl(transition); @@ -420,7 +443,7 @@ int oaknode_transition_get_offset_center(OakNodeBlock *transition, return get_rational(t->offset_center(), numerator, denominator); } -int oaknode_transition_set_offset_center(OakNodeBlock *transition, +int oaknode_transition_set_offset_center(OakNodeBlock transition, int numerator, int denominator) { olive::TransitionBlock *t = transition_impl(transition); @@ -431,7 +454,7 @@ int oaknode_transition_set_offset_center(OakNodeBlock *transition, return OAKNODE_OK; } -int oaknode_transition_set_offsets_and_length(OakNodeBlock *transition, +int oaknode_transition_set_offsets_and_length(OakNodeBlock transition, int in_num, int in_den, int out_num, int out_den) { @@ -444,7 +467,7 @@ int oaknode_transition_set_offsets_and_length(OakNodeBlock *transition, return OAKNODE_OK; } -int oaknode_transition_is_dual(OakNodeBlock *transition, int *dual) +int oaknode_transition_is_dual(OakNodeBlock transition, int *dual) { olive::TransitionBlock *t = transition_impl(transition); if (!t || !dual) { @@ -454,35 +477,34 @@ int oaknode_transition_is_dual(OakNodeBlock *transition, int *dual) return OAKNODE_OK; } -int oaknode_transition_get_connected_out_block(OakNodeBlock *transition, - OakNodeBlock **out) +int oaknode_transition_get_connected_out_block(OakNodeBlock transition, + OakNodeBlock *out) { olive::TransitionBlock *t = transition_impl(transition); if (!t || !out) { return OAKNODE_E_INVALID; } - *out = wrap(t->connected_out_block()); + // Borrowed (empty when unconnected) + *out = make_handle(t->connected_out_block(), false, + &delete_as); return OAKNODE_OK; } -int oaknode_transition_get_connected_in_block(OakNodeBlock *transition, - OakNodeBlock **out) +int oaknode_transition_get_connected_in_block(OakNodeBlock transition, + OakNodeBlock *out) { olive::TransitionBlock *t = transition_impl(transition); if (!t || !out) { return OAKNODE_E_INVALID; } - *out = wrap(t->connected_in_block()); + *out = make_handle(t->connected_in_block(), false, + &delete_as); return OAKNODE_OK; } -int oaknode_clip_add_cache_passthrough_from(OakNodeBlock *clip, - OakNodeBlock *other) +int oaknode_clip_add_cache_passthrough_from(OakNodeBlock clip, + OakNodeBlock other) { - if (!clip || !other) { - return OAKNODE_E_INVALID; - } - olive::ClipBlock *c = clip_impl(clip); olive::ClipBlock *o = clip_impl(other); if (!c || !o) { @@ -497,27 +519,31 @@ int oaknode_clip_add_cache_passthrough_from(OakNodeBlock *clip, } } -OakNodeNode *oaknode_block_as_node(OakNodeBlock *block) +OakNodeNode oaknode_block_as_node(OakNodeBlock block) { - return reinterpret_cast(block); + // Borrowed; releasing the result never destroys the block + return make_handle(to_native(block), false, + &delete_as); } -OakNodeBlock *oaknode_block_from_node(OakNodeNode *node) +OakNodeBlock oaknode_block_from_node(OakNodeNode node) { - if (!node) { - return NULL; + olive::Node *n = to_native(node); + if (!n) { + return OakNodeBlock{}; } - olive::Node *n = reinterpret_cast(node); - return wrap(dynamic_cast(n)); + // Borrowed (empty when the node is not a Block) + return make_handle(dynamic_cast(n), false, + &delete_as); } -int oaknode_block_get_kind(OakNodeBlock *block, int *out_kind) +int oaknode_block_get_kind(OakNodeBlock block, int *out_kind) { - if (!block || !out_kind) { + olive::Block *b = to_native(block); + if (!b || !out_kind) { return OAKNODE_E_INVALID; } - olive::Block *b = impl(block); if (dynamic_cast(b)) { *out_kind = OAKNODE_BLOCK_TRANSITION; } else if (dynamic_cast(b)) { diff --git a/src/node/c_api/colormanager.cpp b/src/node/c_api/colormanager.cpp index 316f78417..efb88c2a8 100644 --- a/src/node/c_api/colormanager.cpp +++ b/src/node/c_api/colormanager.cpp @@ -22,15 +22,11 @@ #include -#include "alivecount.h" - #include "color/colormanager/colormanager.h" #include "colortransform.h" #include "project.h" -struct OakNodeColorManager { - olive::ColorManager impl; -}; +#include "nodehandle.h" namespace { @@ -44,9 +40,9 @@ int copy_string(const std::string &value, char *buf, int buf_size) return needed; } -bool has_config(olive::ColorManager *cm) +bool has_config(const olive::ColorManager *cm) { - return cm && cm->get_config() != nullptr; + return cm->get_config() != nullptr; } int list_at(const olive::StringList &list, int index, char *buf, int buf_size) @@ -59,37 +55,35 @@ int list_at(const olive::StringList &list, int index, char *buf, int buf_size) } // namespace -OakNodeColorManager *oaknode_colormanager_init(OakNodeProject *project) +OakNodeColorManager oaknode_colormanager_init(OakNodeProject project) { - if (!project) { - return nullptr; + olive::Project *native = oaknode_c_api::to_native(project); + if (!native) { + return OakNodeColorManager{}; } try { - auto *m = new OakNodeColorManager{ - olive::ColorManager(reinterpret_cast(project))}; - oaknode_c_api::alive_inc(); - return m; + return oaknode_c_api::make_handle( + new olive::ColorManager(native), true, + &oaknode_c_api::delete_as); } catch (...) { - return nullptr; + return OakNodeColorManager{}; } } void oaknode_colormanager_free(OakNodeColorManager *manager) { - if (!manager) { - return; - } - delete manager; - oaknode_c_api::alive_dec(); + oaknode_c_api::free_handle(manager); } -int oaknode_colormanager_initialize(OakNodeColorManager *manager) +int oaknode_colormanager_initialize(OakNodeColorManager manager) { - if (!manager) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm) { return OAKNODE_E_INVALID; } try { - manager->impl.init(); + cm->init(); } catch (...) { return OAKNODE_E_FAILED; } @@ -106,33 +100,39 @@ int oaknode_colormanager_set_up_default_config(void) return OAKNODE_OK; } -int oaknode_colormanager_get_config_filename(OakNodeColorManager *manager, +int oaknode_colormanager_get_config_filename(OakNodeColorManager manager, char *buf, int buf_size) { - if (!manager) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm) { return OAKNODE_E_INVALID; } - return copy_string(manager->impl.get_config_filename(), buf, buf_size); + return copy_string(cm->get_config_filename(), buf, buf_size); } -int oaknode_colormanager_set_config_filename(OakNodeColorManager *manager, +int oaknode_colormanager_set_config_filename(OakNodeColorManager manager, const char *filename) { - if (!manager || !filename) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm || !filename) { return OAKNODE_E_INVALID; } - manager->impl.set_config_filename(filename); + cm->set_config_filename(filename); return OAKNODE_OK; } int oaknode_colormanager_update_config_from_filename( - OakNodeColorManager *manager) + OakNodeColorManager manager) { - if (!manager) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm) { return OAKNODE_E_INVALID; } try { - manager->impl.update_config_from_filename(); + cm->update_config_from_filename(); } catch (...) { return OAKNODE_E_FAILED; } @@ -140,212 +140,240 @@ int oaknode_colormanager_update_config_from_filename( } int oaknode_colormanager_get_default_input_color_space( - OakNodeColorManager *manager, char *buf, int buf_size) + OakNodeColorManager manager, char *buf, int buf_size) { - if (!manager) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm) { return OAKNODE_E_INVALID; } - return copy_string(manager->impl.get_default_input_color_space(), buf, - buf_size); + return copy_string(cm->get_default_input_color_space(), buf, buf_size); } int oaknode_colormanager_set_default_input_color_space( - OakNodeColorManager *manager, const char *colorspace) + OakNodeColorManager manager, const char *colorspace) { - if (!manager || !colorspace) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm || !colorspace) { return OAKNODE_E_INVALID; } - manager->impl.set_default_input_color_space(colorspace); + cm->set_default_input_color_space(colorspace); return OAKNODE_OK; } int oaknode_colormanager_get_reference_color_space( - OakNodeColorManager *manager, char *buf, int buf_size) + OakNodeColorManager manager, char *buf, int buf_size) { - if (!manager) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm) { return OAKNODE_E_INVALID; } - return copy_string(manager->impl.get_reference_color_space(), buf, - buf_size); + return copy_string(cm->get_reference_color_space(), buf, buf_size); } int oaknode_colormanager_get_compliant_color_space( - OakNodeColorManager *manager, const char *colorspace, char *buf, + OakNodeColorManager manager, const char *colorspace, char *buf, int buf_size) { - if (!manager || !colorspace) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm || !colorspace) { return OAKNODE_E_INVALID; } - if (!has_config(&manager->impl)) { + if (!has_config(cm)) { return OAKNODE_E_STATE; } - return copy_string(manager->impl.get_compliant_color_space(colorspace), buf, + return copy_string(cm->get_compliant_color_space(colorspace), buf, buf_size); } int oaknode_colormanager_get_colorspace_for_ffmpeg_tags( - OakNodeColorManager *manager, int primaries, int trc, char *buf, + OakNodeColorManager manager, int primaries, int trc, char *buf, int buf_size) { - if (!manager) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm) { return OAKNODE_E_INVALID; } - if (!has_config(&manager->impl)) { + if (!has_config(cm)) { return OAKNODE_E_STATE; } - return copy_string( - manager->impl.get_colorspace_for_ffmpeg_tags(primaries, trc), buf, - buf_size); + return copy_string(cm->get_colorspace_for_ffmpeg_tags(primaries, trc), buf, + buf_size); } -int oaknode_colormanager_get_display_count(OakNodeColorManager *manager, +int oaknode_colormanager_get_display_count(OakNodeColorManager manager, int *count) { - if (!manager || !count) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm || !count) { return OAKNODE_E_INVALID; } - if (!has_config(&manager->impl)) { + if (!has_config(cm)) { return OAKNODE_E_STATE; } - *count = int(manager->impl.list_available_displays().size()); + *count = int(cm->list_available_displays().size()); return OAKNODE_OK; } -int oaknode_colormanager_get_display_at(OakNodeColorManager *manager, +int oaknode_colormanager_get_display_at(OakNodeColorManager manager, int index, char *buf, int buf_size) { - if (!manager) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm) { return OAKNODE_E_INVALID; } - if (!has_config(&manager->impl)) { + if (!has_config(cm)) { return OAKNODE_E_STATE; } - return list_at(manager->impl.list_available_displays(), index, buf, - buf_size); + return list_at(cm->list_available_displays(), index, buf, buf_size); } -int oaknode_colormanager_get_default_display(OakNodeColorManager *manager, +int oaknode_colormanager_get_default_display(OakNodeColorManager manager, char *buf, int buf_size) { - if (!manager) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm) { return OAKNODE_E_INVALID; } - if (!has_config(&manager->impl)) { + if (!has_config(cm)) { return OAKNODE_E_STATE; } - return copy_string(manager->impl.get_default_display(), buf, buf_size); + return copy_string(cm->get_default_display(), buf, buf_size); } -int oaknode_colormanager_get_view_count(OakNodeColorManager *manager, +int oaknode_colormanager_get_view_count(OakNodeColorManager manager, const char *display, int *count) { - if (!manager || !display || !count) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm || !display || !count) { return OAKNODE_E_INVALID; } - if (!has_config(&manager->impl)) { + if (!has_config(cm)) { return OAKNODE_E_STATE; } - *count = int(manager->impl.list_available_views(display).size()); + *count = int(cm->list_available_views(display).size()); return OAKNODE_OK; } -int oaknode_colormanager_get_view_at(OakNodeColorManager *manager, +int oaknode_colormanager_get_view_at(OakNodeColorManager manager, const char *display, int index, char *buf, int buf_size) { - if (!manager || !display) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm || !display) { return OAKNODE_E_INVALID; } - if (!has_config(&manager->impl)) { + if (!has_config(cm)) { return OAKNODE_E_STATE; } - return list_at(manager->impl.list_available_views(display), index, buf, - buf_size); + return list_at(cm->list_available_views(display), index, buf, buf_size); } -int oaknode_colormanager_get_default_view(OakNodeColorManager *manager, +int oaknode_colormanager_get_default_view(OakNodeColorManager manager, const char *display, char *buf, int buf_size) { - if (!manager || !display) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm || !display) { return OAKNODE_E_INVALID; } - if (!has_config(&manager->impl)) { + if (!has_config(cm)) { return OAKNODE_E_STATE; } - return copy_string(manager->impl.get_default_view(display), buf, buf_size); + return copy_string(cm->get_default_view(display), buf, buf_size); } -int oaknode_colormanager_get_look_count(OakNodeColorManager *manager, +int oaknode_colormanager_get_look_count(OakNodeColorManager manager, int *count) { - if (!manager || !count) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm || !count) { return OAKNODE_E_INVALID; } - if (!has_config(&manager->impl)) { + if (!has_config(cm)) { return OAKNODE_E_STATE; } - *count = int(manager->impl.list_available_looks().size()); + *count = int(cm->list_available_looks().size()); return OAKNODE_OK; } -int oaknode_colormanager_get_look_at(OakNodeColorManager *manager, int index, +int oaknode_colormanager_get_look_at(OakNodeColorManager manager, int index, char *buf, int buf_size) { - if (!manager) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm) { return OAKNODE_E_INVALID; } - if (!has_config(&manager->impl)) { + if (!has_config(cm)) { return OAKNODE_E_STATE; } - return list_at(manager->impl.list_available_looks(), index, buf, buf_size); + return list_at(cm->list_available_looks(), index, buf, buf_size); } -int oaknode_colormanager_get_colorspace_count(OakNodeColorManager *manager, +int oaknode_colormanager_get_colorspace_count(OakNodeColorManager manager, int *count) { - if (!manager || !count) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm || !count) { return OAKNODE_E_INVALID; } - if (!has_config(&manager->impl)) { + if (!has_config(cm)) { return OAKNODE_E_STATE; } - *count = int(manager->impl.list_available_colorspaces().size()); + *count = int(cm->list_available_colorspaces().size()); return OAKNODE_OK; } -int oaknode_colormanager_get_colorspace_at(OakNodeColorManager *manager, +int oaknode_colormanager_get_colorspace_at(OakNodeColorManager manager, int index, char *buf, int buf_size) { - if (!manager) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm) { return OAKNODE_E_INVALID; } - if (!has_config(&manager->impl)) { + if (!has_config(cm)) { return OAKNODE_E_STATE; } - return list_at(manager->impl.list_available_colorspaces(), index, buf, - buf_size); + return list_at(cm->list_available_colorspaces(), index, buf, buf_size); } -int oaknode_colormanager_get_default_luma_coefs(OakNodeColorManager *manager, +int oaknode_colormanager_get_default_luma_coefs(OakNodeColorManager manager, double rgb[3]) { - if (!manager || !rgb) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm || !rgb) { return OAKNODE_E_INVALID; } - if (!has_config(&manager->impl)) { + if (!has_config(cm)) { return OAKNODE_E_STATE; } - manager->impl.get_default_luma_coefs(rgb); + cm->get_default_luma_coefs(rgb); return OAKNODE_OK; } int oaknode_colormanager_get_compliant_color_transform( - OakNodeColorManager *manager, OakColorTransform transform, + OakNodeColorManager manager, OakColorTransform transform, int force_display, OakColorTransform *out) { - if (!manager || !out) { + olive::ColorManager *cm = + oaknode_c_api::to_native(manager); + if (!cm || !out) { return OAKNODE_E_INVALID; } const olive::ColorTransform *native = @@ -353,13 +381,12 @@ int oaknode_colormanager_get_compliant_color_transform( if (!native) { return OAKNODE_E_INVALID; } - if (!has_config(&manager->impl)) { + if (!has_config(cm)) { return OAKNODE_E_STATE; } try { const olive::ColorTransform compliant = - manager->impl.get_compliant_color_space(*native, - force_display != 0); + cm->get_compliant_color_space(*native, force_display != 0); *out = oakcommon_colortransform_init_from_native(&compliant); } catch (...) { return OAKNODE_E_NOMEM; diff --git a/src/node/c_api/factory.cpp b/src/node/c_api/factory.cpp index 5c2c247fe..9973c5188 100644 --- a/src/node/c_api/factory.cpp +++ b/src/node/c_api/factory.cpp @@ -22,17 +22,10 @@ #include "factory.h" +#include "nodehandle.h" #include "valueconvert.h" -namespace -{ - -inline OakNodeNode *from_node(olive::Node *node) -{ - return reinterpret_cast(node); -} - -} +using oaknode_c_api::make_handle; int oaknode_factory_initialize(void) { @@ -104,24 +97,22 @@ int oaknode_factory_name_from_id(const char *type_id, char *buf, } } -OakNodeNode *oaknode_factory_create_from_id(const char *type_id) +OakNodeNode oaknode_factory_create_from_id(const char *type_id) { if (!type_id) { - return NULL; + return OakNodeNode{}; } try { - olive::Node *node = olive::NodeFactory::create_from_id(type_id); - if (node) { - oaknode_c_api::alive_inc(); - } - return from_node(node); + return make_handle( + olive::NodeFactory::create_from_id(type_id), true, + oaknode_c_api::delete_as); } catch (...) { - return NULL; + return OakNodeNode{}; } } -int oaknode_factory_node_at(int index, OakNodeNode **out_node) +int oaknode_factory_node_at(int index, OakNodeNode *out_node) { if (!out_node) { return OAKNODE_E_INVALID; @@ -136,7 +127,8 @@ int oaknode_factory_node_at(int index, OakNodeNode **out_node) if (index < 0 || index >= static_cast(library.size())) { return OAKNODE_E_NOT_FOUND; } - *out_node = from_node(library[size_t(index)]); + *out_node = make_handle(library[size_t(index)], false, + nullptr); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; diff --git a/src/node/c_api/folder.cpp b/src/node/c_api/folder.cpp index 14213c029..9049e6103 100644 --- a/src/node/c_api/folder.cpp +++ b/src/node/c_api/folder.cpp @@ -24,97 +24,75 @@ #include +#include "node/node.h" + #include "../src/project.h" #include "../src/project/folder/folder.h" -namespace -{ +#include "nodehandle.h" -olive::Folder *to_cpp(OakNodeFolder *folder) -{ - return reinterpret_cast(folder); -} +using oaknode_c_api::delete_as; +using oaknode_c_api::make_handle; +using oaknode_c_api::to_native; -const olive::Folder *to_cpp(const OakNodeFolder *folder) +OakNodeFolder oaknode_folder_create(OakNodeProject project) { - return reinterpret_cast(folder); -} - -olive::Node *to_cpp(OakNodeNode *node) -{ - return reinterpret_cast(node); -} - -const olive::Node *to_cpp(const OakNodeNode *node) -{ - return reinterpret_cast(node); -} - -OakNodeNode *to_c(olive::Node *node) -{ - return reinterpret_cast(node); -} - -olive::Project *to_cpp(OakNodeProject *project) -{ - return reinterpret_cast(project); -} - -} // namespace - -OakNodeFolder *oaknode_folder_create(OakNodeProject *project) -{ - if (!project) { - return NULL; + if (!project.ctx) { + return OakNodeFolder{}; } try { auto *folder = new (std::nothrow) olive::Folder(); if (!folder) { - return NULL; + return OakNodeFolder{}; } - to_cpp(project)->add_node(folder); - return reinterpret_cast(folder); + to_native(project)->add_node(folder); + // Borrowed handle: the project graph owns the folder. + return make_handle(folder, false, + &delete_as); } catch (...) { - return NULL; + return OakNodeFolder{}; } } -int oaknode_folder_child_count(const OakNodeFolder *folder) +int oaknode_folder_child_count(OakNodeFolder folder) { - if (!folder) { + if (!folder.ctx) { return OAKNODE_E_INVALID; } try { - return to_cpp(folder)->item_child_count(); + return to_native(folder)->item_child_count(); } catch (...) { return OAKNODE_E_FAILED; } } -OakNodeNode *oaknode_folder_child_at(const OakNodeFolder *folder, int index) +OakNodeNode oaknode_folder_child_at(OakNodeFolder folder, int index) { - if (!folder || index < 0 || index >= to_cpp(folder)->item_child_count()) { - return NULL; + if (!folder.ctx || index < 0 || + index >= to_native(folder)->item_child_count()) { + return OakNodeNode{}; } try { - return to_c(to_cpp(folder)->item_child(index)); + return make_handle( + to_native(folder)->item_child(index), false, + &delete_as); } catch (...) { - return NULL; + return OakNodeNode{}; } } -int oaknode_folder_add_child(OakNodeFolder *folder, OakNodeNode *child) +int oaknode_folder_add_child(OakNodeFolder folder, OakNodeNode child) { - if (!folder || !child) { + if (!folder.ctx || !child.ctx) { return OAKNODE_E_INVALID; } try { - olive::Folder *f = to_cpp(folder); - olive::Node *c = to_cpp(child); + olive::Folder *f = to_native(folder); + olive::Node *c = to_native(child); if (c->folder()) { return OAKNODE_E_STATE; @@ -122,21 +100,24 @@ int oaknode_folder_add_child(OakNodeFolder *folder, OakNodeNode *child) olive::FolderAddChild cmd(f, c); cmd.redo_now(); + // The graph now owns the child; releasing `child` must not + // delete it. + oaknode_c_api::mark_container_owned(child); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_folder_remove_child(OakNodeFolder *folder, OakNodeNode *child) +int oaknode_folder_remove_child(OakNodeFolder folder, OakNodeNode child) { - if (!folder || !child) { + if (!folder.ctx || !child.ctx) { return OAKNODE_E_INVALID; } try { - olive::Folder *f = to_cpp(folder); - olive::Node *c = to_cpp(child); + olive::Folder *f = to_native(folder); + olive::Node *c = to_native(child); if (f->index_of_child(c) == -1) { return OAKNODE_E_NOT_FOUND; @@ -150,22 +131,22 @@ int oaknode_folder_remove_child(OakNodeFolder *folder, OakNodeNode *child) } } -int oaknode_folder_move_children(OakNodeNode *const *nodes, int count, - OakNodeFolder *dest_folder) +int oaknode_folder_move_children(const OakNodeNode *nodes, int count, + OakNodeFolder dest_folder) { - if (!nodes || count < 0 || !dest_folder) { + if (!nodes || count < 0 || !dest_folder.ctx) { return OAKNODE_E_INVALID; } try { - olive::Folder *dest = to_cpp(dest_folder); + olive::Folder *dest = to_native(dest_folder); for (int i = 0; i < count; i++) { - if (!nodes[i]) { + if (!nodes[i].ctx) { return OAKNODE_E_INVALID; } - olive::Node *node = to_cpp(nodes[i]); + olive::Node *node = to_native(nodes[i]); olive::Folder *old_folder = node->folder(); if (old_folder == dest) { @@ -179,6 +160,9 @@ int oaknode_folder_move_children(OakNodeNode *const *nodes, int count, olive::FolderAddChild add_cmd(dest, node); add_cmd.redo_now(); + // The graph owns the moved node; releasing the caller's + // handle must not delete it. + oaknode_c_api::mark_container_owned(nodes[i]); } return OAKNODE_OK; @@ -187,16 +171,16 @@ int oaknode_folder_move_children(OakNodeNode *const *nodes, int count, } } -int oaknode_folder_has_child_recursive(const OakNodeFolder *folder, - const OakNodeNode *child) +int oaknode_folder_has_child_recursive(OakNodeFolder folder, + OakNodeNode child) { - if (!folder || !child) { + if (!folder.ctx || !child.ctx) { return OAKNODE_E_INVALID; } try { - return to_cpp(folder)->has_child_recursive( - const_cast(to_cpp(child))) + return to_native(folder)->has_child_recursive( + to_native(child)) ? 1 : 0; } catch (...) { @@ -204,52 +188,55 @@ int oaknode_folder_has_child_recursive(const OakNodeFolder *folder, } } -int oaknode_folder_index_of_child(const OakNodeFolder *folder, - const OakNodeNode *child) +int oaknode_folder_index_of_child(OakNodeFolder folder, + OakNodeNode child) { - if (!folder || !child) { + if (!folder.ctx || !child.ctx) { return OAKNODE_E_INVALID; } try { - int index = to_cpp(folder)->index_of_child( - const_cast(to_cpp(child))); + int index = to_native(folder)->index_of_child( + to_native(child)); return index == -1 ? OAKNODE_E_NOT_FOUND : index; } catch (...) { return OAKNODE_E_FAILED; } } -OakNodeFolder *oaknode_folder_parent_of(const OakNodeNode *node) +OakNodeFolder oaknode_folder_parent_of(OakNodeNode node) { - if (!node) { - return NULL; + if (!node.ctx) { + return OakNodeFolder{}; } try { - return reinterpret_cast(to_cpp(node)->folder()); + return make_handle( + to_native(node)->folder(), false, + &delete_as); } catch (...) { - return NULL; + return OakNodeFolder{}; } } -OakNodeNode *oaknode_folder_as_node(OakNodeFolder *folder) +OakNodeNode oaknode_folder_as_node(OakNodeFolder folder) { - return reinterpret_cast(folder); + // Borrowed cast: same object, the handle only releases itself. + return make_handle(to_native(folder), false, + &delete_as); } OakUndoCommand oaknode_command_create_folder_add_child( - OakNodeFolder *folder, OakNodeNode *child) + OakNodeFolder folder, OakNodeNode child) { - if (!folder || !child) { + if (!folder.ctx || !child.ctx) { return OakUndoCommand{}; } try { return oakundo_capi::make_command_handle( - new olive::FolderAddChild( - reinterpret_cast(folder), - reinterpret_cast(child)), + new olive::FolderAddChild(to_native(folder), + to_native(child)), true); } catch (...) { return OakUndoCommand{}; diff --git a/src/node/c_api/footage.cpp b/src/node/c_api/footage.cpp index 184eb4518..6c5460dd3 100644 --- a/src/node/c_api/footage.cpp +++ b/src/node/c_api/footage.cpp @@ -27,27 +27,16 @@ #include #include +#include "node/node.h" + #include "../src/project.h" #include "../src/project/footage/footage.h" +#include "nodehandle.h" + namespace { -olive::Footage *to_cpp(OakNodeFootage *footage) -{ - return reinterpret_cast(footage); -} - -const olive::Footage *to_cpp(const OakNodeFootage *footage) -{ - return reinterpret_cast(footage); -} - -olive::Project *to_cpp(OakNodeProject *project) -{ - return reinterpret_cast(project); -} - /** * @brief Shared two-stage string getter. * @@ -72,167 +61,176 @@ int copy_string(const std::string &value, char *buf, int buf_size) } // namespace -OakNodeFootage *oaknode_footage_create(OakNodeProject *project, - const char *filename) +using oaknode_c_api::delete_as; +using oaknode_c_api::make_handle; +using oaknode_c_api::to_native; + +OakNodeFootage oaknode_footage_create(OakNodeProject project, + const char *filename) { - if (!project) { - return NULL; + if (!project.ctx) { + return OakNodeFootage{}; } try { auto *footage = new (std::nothrow) olive::Footage(filename ? filename : ""); if (!footage) { - return NULL; + return OakNodeFootage{}; } - to_cpp(project)->add_node(footage); - return reinterpret_cast(footage); + to_native(project)->add_node(footage); + // Borrowed handle: the project graph owns the footage. + return make_handle(footage, false, + &delete_as); } catch (...) { - return NULL; + return OakNodeFootage{}; } } -int oaknode_footage_filename(const OakNodeFootage *footage, char *buf, +int oaknode_footage_filename(OakNodeFootage footage, char *buf, int buf_size) { - if (!footage) { + if (!footage.ctx) { return OAKNODE_E_INVALID; } try { - return copy_string(to_cpp(footage)->filename(), buf, buf_size); + return copy_string(to_native(footage)->filename(), buf, + buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_footage_set_filename(OakNodeFootage *footage, const char *filename) +int oaknode_footage_set_filename(OakNodeFootage footage, const char *filename) { - if (!footage || !filename) { + if (!footage.ctx || !filename) { return OAKNODE_E_INVALID; } try { - to_cpp(footage)->set_filename(filename); + to_native(footage)->set_filename(filename); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_footage_is_valid(const OakNodeFootage *footage) +int oaknode_footage_is_valid(OakNodeFootage footage) { - if (!footage) { + if (!footage.ctx) { return OAKNODE_E_INVALID; } - return to_cpp(footage)->is_valid() ? 1 : 0; + return to_native(footage)->is_valid() ? 1 : 0; } -int oaknode_footage_timestamp(const OakNodeFootage *footage, +int oaknode_footage_timestamp(OakNodeFootage footage, int64_t *out_timestamp) { - if (!footage || !out_timestamp) { + if (!footage.ctx || !out_timestamp) { return OAKNODE_E_INVALID; } try { - *out_timestamp = to_cpp(footage)->timestamp(); + *out_timestamp = to_native(footage)->timestamp(); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_footage_set_timestamp(OakNodeFootage *footage, int64_t timestamp) +int oaknode_footage_set_timestamp(OakNodeFootage footage, int64_t timestamp) { - if (!footage) { + if (!footage.ctx) { return OAKNODE_E_INVALID; } try { - to_cpp(footage)->set_timestamp(timestamp); + to_native(footage)->set_timestamp(timestamp); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_footage_decoder(const OakNodeFootage *footage, char *buf, +int oaknode_footage_decoder(OakNodeFootage footage, char *buf, int buf_size) { - if (!footage) { + if (!footage.ctx) { return OAKNODE_E_INVALID; } try { - return copy_string(to_cpp(footage)->decoder(), buf, buf_size); + return copy_string(to_native(footage)->decoder(), buf, + buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_footage_total_stream_count(const OakNodeFootage *footage) +int oaknode_footage_total_stream_count(OakNodeFootage footage) { - if (!footage) { + if (!footage.ctx) { return OAKNODE_E_INVALID; } try { - return to_cpp(footage)->get_total_stream_count(); + return to_native(footage)->get_total_stream_count(); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_footage_video_stream_count(const OakNodeFootage *footage) +int oaknode_footage_video_stream_count(OakNodeFootage footage) { - if (!footage) { + if (!footage.ctx) { return OAKNODE_E_INVALID; } try { - return to_cpp(footage)->get_video_stream_count(); + return to_native(footage)->get_video_stream_count(); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_footage_audio_stream_count(const OakNodeFootage *footage) +int oaknode_footage_audio_stream_count(OakNodeFootage footage) { - if (!footage) { + if (!footage.ctx) { return OAKNODE_E_INVALID; } try { - return to_cpp(footage)->get_audio_stream_count(); + return to_native(footage)->get_audio_stream_count(); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_footage_subtitle_stream_count(const OakNodeFootage *footage) +int oaknode_footage_subtitle_stream_count(OakNodeFootage footage) { - if (!footage) { + if (!footage.ctx) { return OAKNODE_E_INVALID; } try { - return to_cpp(footage)->get_subtitle_stream_count(); + return to_native(footage)->get_subtitle_stream_count(); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_footage_duration(const OakNodeFootage *footage, int *out_numerator, +int oaknode_footage_duration(OakNodeFootage footage, int *out_numerator, int *out_denominator) { - if (!footage || !out_numerator || !out_denominator) { + if (!footage.ctx || !out_numerator || !out_denominator) { return OAKNODE_E_INVALID; } try { - const olive::Rational &length = to_cpp(footage)->get_length(); + const olive::Rational &length = + to_native(footage)->get_length(); *out_numerator = length.numerator(); *out_denominator = length.denominator(); return OAKNODE_OK; @@ -241,66 +239,68 @@ int oaknode_footage_duration(const OakNodeFootage *footage, int *out_numerator, } } -int oaknode_footage_proxy_enabled(const OakNodeFootage *footage) +int oaknode_footage_proxy_enabled(OakNodeFootage footage) { - if (!footage) { + if (!footage.ctx) { return OAKNODE_E_INVALID; } - return to_cpp(footage)->proxy_enabled() ? 1 : 0; + return to_native(footage)->proxy_enabled() ? 1 : 0; } -int oaknode_footage_set_proxy_enabled(OakNodeFootage *footage, int enabled) +int oaknode_footage_set_proxy_enabled(OakNodeFootage footage, int enabled) { - if (!footage) { + if (!footage.ctx) { return OAKNODE_E_INVALID; } try { - to_cpp(footage)->set_proxy_enabled(enabled != 0); + to_native(footage)->set_proxy_enabled(enabled != 0); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_footage_proxy_path(const OakNodeFootage *footage, char *buf, +int oaknode_footage_proxy_path(OakNodeFootage footage, char *buf, int buf_size) { - if (!footage) { + if (!footage.ctx) { return OAKNODE_E_INVALID; } try { - return copy_string(to_cpp(footage)->proxy_path(), buf, buf_size); + return copy_string(to_native(footage)->proxy_path(), + buf, buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_footage_proxy_state(const OakNodeFootage *footage) +int oaknode_footage_proxy_state(OakNodeFootage footage) { - if (!footage) { + if (!footage.ctx) { return OAKNODE_E_INVALID; } try { - return static_cast(to_cpp(footage)->proxy_state()); + return static_cast( + to_native(footage)->proxy_state()); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_footage_set_proxy(OakNodeFootage *footage, const char *path, +int oaknode_footage_set_proxy(OakNodeFootage footage, const char *path, int state, int video_stream_index, int preset_version, int enabled) { - if (!footage) { + if (!footage.ctx) { return OAKNODE_E_INVALID; } try { - to_cpp(footage)->set_proxy( + to_native(footage)->set_proxy( path ? path : "", static_cast(state), video_stream_index, preset_version, enabled != 0); @@ -310,24 +310,24 @@ int oaknode_footage_set_proxy(OakNodeFootage *footage, const char *path, } } -int oaknode_footage_clear_proxy(OakNodeFootage *footage) +int oaknode_footage_clear_proxy(OakNodeFootage footage) { - if (!footage) { + if (!footage.ctx) { return OAKNODE_E_INVALID; } try { - to_cpp(footage)->clear_proxy(); + to_native(footage)->clear_proxy(); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_footage_get_video_params(OakNodeFootage *footage, int index, +int oaknode_footage_get_video_params(OakNodeFootage footage, int index, OakVideoParams *out) { - olive::Footage *f = to_cpp(footage); + olive::Footage *f = to_native(footage); if (!f || !out || index < 0) { return OAKNODE_E_INVALID; } @@ -345,10 +345,10 @@ int oaknode_footage_get_video_params(OakNodeFootage *footage, int index, } } -int oaknode_footage_set_video_params(OakNodeFootage *footage, int index, +int oaknode_footage_set_video_params(OakNodeFootage footage, int index, const OakVideoParams *params) { - olive::Footage *f = to_cpp(footage); + olive::Footage *f = to_native(footage); if (!f || !params || !params->ctx) { return OAKNODE_E_INVALID; } @@ -367,10 +367,10 @@ int oaknode_footage_set_video_params(OakNodeFootage *footage, int index, } } -int oaknode_footage_get_video_length(OakNodeFootage *footage, +int oaknode_footage_get_video_length(OakNodeFootage footage, int64_t *out_num, int64_t *out_den) { - olive::Footage *f = to_cpp(footage); + olive::Footage *f = to_native(footage); if (!f || !out_num || !out_den) { return OAKNODE_E_INVALID; } @@ -381,10 +381,10 @@ int oaknode_footage_get_video_length(OakNodeFootage *footage, return OAKNODE_OK; } -int oaknode_footage_set_cancel_atom(OakNodeFootage *footage, +int oaknode_footage_set_cancel_atom(OakNodeFootage footage, OakCancelAtom atom) { - olive::Footage *f = to_cpp(footage); + olive::Footage *f = to_native(footage); if (!f) { return OAKNODE_E_INVALID; } @@ -394,7 +394,9 @@ int oaknode_footage_set_cancel_atom(OakNodeFootage *footage, return OAKNODE_OK; } -OakNodeNode *oaknode_footage_as_node(OakNodeFootage *footage) +OakNodeNode oaknode_footage_as_node(OakNodeFootage footage) { - return reinterpret_cast(footage); + // Borrowed cast: same object, the handle only releases itself. + return make_handle(to_native(footage), false, + &delete_as); } diff --git a/src/node/c_api/group.cpp b/src/node/c_api/group.cpp index ab7b49475..9b0718ec8 100644 --- a/src/node/c_api/group.cpp +++ b/src/node/c_api/group.cpp @@ -22,106 +22,82 @@ #include "group/group.h" +#include "nodehandle.h" #include "valueconvert.h" -namespace -{ +using oaknode_c_api::make_handle; +using oaknode_c_api::to_native; -inline olive::NodeGroup *to_group(OakNodeGroup *group) -{ - return reinterpret_cast(group); -} - -inline const olive::NodeGroup *to_group(const OakNodeGroup *group) -{ - return reinterpret_cast(group); -} - -inline olive::Node *to_node(OakNodeNode *node) -{ - return reinterpret_cast(node); -} - -inline OakNodeNode *from_node(olive::Node *node) -{ - return reinterpret_cast(node); -} - -} - -OakNodeGroup *oaknode_group_create(void) +OakNodeGroup oaknode_group_create(void) { try { - olive::NodeGroup *group = new (std::nothrow) olive::NodeGroup(); - if (group) { - oaknode_c_api::alive_inc(); - } - return reinterpret_cast(group); + return make_handle(new (std::nothrow) olive::NodeGroup(), + true, + oaknode_c_api::delete_as); } catch (...) { - return NULL; + return OakNodeGroup{}; } } -OakNodeGroup *oaknode_group_cast(OakNodeNode *node) +OakNodeGroup oaknode_group_cast(OakNodeNode node) { - if (!node) { - return NULL; + olive::Node *native = to_native(node); + if (!native) { + return OakNodeGroup{}; } try { - return reinterpret_cast( - dynamic_cast(to_node(node))); + return make_handle( + dynamic_cast(native), false, nullptr); } catch (...) { - return NULL; + return OakNodeGroup{}; } } void oaknode_group_free(OakNodeGroup *group) { - if (!group) { - return; - } - try { - delete to_group(group); - oaknode_c_api::alive_dec(); + oaknode_c_api::free_handle(group); } catch (...) { } } -int oaknode_group_add_input_passthrough(OakNodeGroup *group, - OakNodeNode *node, +int oaknode_group_add_input_passthrough(OakNodeGroup group, + OakNodeNode node, const char *input_id, int element, char *buf, int buf_size) { - if (!group || !node || !input_id) { + olive::NodeGroup *g = to_native(group); + olive::Node *n = to_native(node); + if (!g || !n || !input_id) { return OAKNODE_E_INVALID; } try { - std::string id = to_group(group)->add_input_passthrough( - olive::NodeInput(to_node(node), input_id, element)); + std::string id = g->add_input_passthrough( + olive::NodeInput(n, input_id, element)); return oaknode_c_api::copy_string(id, buf, buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_group_add_input_passthrough_undoable(OakNodeGroup *group, - OakNodeNode *node, +int oaknode_group_add_input_passthrough_undoable(OakNodeGroup group, + OakNodeNode node, const char *input_id, int element, OakUndoCommand *out_command) { - if (!group || !node || !input_id || !out_command) { + olive::NodeGroup *g = to_native(group); + olive::Node *n = to_native(node); + if (!g || !n || !input_id || !out_command) { return OAKNODE_E_INVALID; } try { OakUndoCommand handle = oaknode_c_api::wrap_command( new olive::NodeGroupAddInputPassthrough( - to_group(group), - olive::NodeInput(to_node(node), input_id, element))); + g, olive::NodeInput(n, input_id, element))); if (!handle.ctx) { return OAKNODE_E_NOMEM; } @@ -132,51 +108,54 @@ int oaknode_group_add_input_passthrough_undoable(OakNodeGroup *group, } } -int oaknode_group_remove_input_passthrough(OakNodeGroup *group, - OakNodeNode *node, +int oaknode_group_remove_input_passthrough(OakNodeGroup group, + OakNodeNode node, const char *input_id, int element) { - if (!group || !node || !input_id) { + olive::NodeGroup *g = to_native(group); + olive::Node *n = to_native(node); + if (!g || !n || !input_id) { return OAKNODE_E_INVALID; } try { - olive::NodeInput input(to_node(node), input_id, element); - if (!to_group(group)->contains_input_passthrough(input)) { + olive::NodeInput input(n, input_id, element); + if (!g->contains_input_passthrough(input)) { return OAKNODE_E_NOT_FOUND; } - to_group(group)->remove_input_passthrough(input); + g->remove_input_passthrough(input); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_group_passthrough_count(const OakNodeGroup *group, int *out_count) +int oaknode_group_passthrough_count(OakNodeGroup group, int *out_count) { - if (!group || !out_count) { + olive::NodeGroup *g = to_native(group); + if (!g || !out_count) { return OAKNODE_E_INVALID; } try { - *out_count = - static_cast(to_group(group)->get_input_passthroughs().size()); + *out_count = static_cast(g->get_input_passthroughs().size()); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_group_passthrough_id_at(const OakNodeGroup *group, int index, +int oaknode_group_passthrough_id_at(OakNodeGroup group, int index, char *buf, int buf_size) { - if (!group) { + olive::NodeGroup *g = to_native(group); + if (!g) { return OAKNODE_E_INVALID; } try { const olive::NodeGroup::InputPassthroughs &passthroughs = - to_group(group)->get_input_passthroughs(); + g->get_input_passthroughs(); if (index < 0 || index >= static_cast(passthroughs.size())) { return OAKNODE_E_NOT_FOUND; } @@ -187,24 +166,25 @@ int oaknode_group_passthrough_id_at(const OakNodeGroup *group, int index, } } -int oaknode_group_passthrough_input_at(const OakNodeGroup *group, int index, - OakNodeNode **out_node, char *buf, +int oaknode_group_passthrough_input_at(OakNodeGroup group, int index, + OakNodeNode *out_node, char *buf, int buf_size, int *out_element) { - if (!group) { + olive::NodeGroup *g = to_native(group); + if (!g) { return OAKNODE_E_INVALID; } try { const olive::NodeGroup::InputPassthroughs &passthroughs = - to_group(group)->get_input_passthroughs(); + g->get_input_passthroughs(); if (index < 0 || index >= static_cast(passthroughs.size())) { return OAKNODE_E_NOT_FOUND; } const olive::NodeInput &input = passthroughs[size_t(index)].second; if (out_node) { - *out_node = from_node(input.node()); + *out_node = make_handle(input.node(), false, nullptr); } if (out_element) { *out_element = input.element(); @@ -215,30 +195,33 @@ int oaknode_group_passthrough_input_at(const OakNodeGroup *group, int index, } } -int oaknode_group_get_output_passthrough(const OakNodeGroup *group, - OakNodeNode **out_node) +int oaknode_group_get_output_passthrough(OakNodeGroup group, + OakNodeNode *out_node) { - if (!group || !out_node) { + olive::NodeGroup *g = to_native(group); + if (!g || !out_node) { return OAKNODE_E_INVALID; } try { - *out_node = from_node(to_group(group)->get_output_passthrough()); + *out_node = + make_handle(g->get_output_passthrough(), false, nullptr); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_group_set_output_passthrough(OakNodeGroup *group, - OakNodeNode *node) +int oaknode_group_set_output_passthrough(OakNodeGroup group, + OakNodeNode node) { - if (!group) { + olive::NodeGroup *g = to_native(group); + if (!g) { return OAKNODE_E_INVALID; } try { - to_group(group)->set_output_passthrough(to_node(node)); + g->set_output_passthrough(to_native(node)); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; @@ -246,16 +229,17 @@ int oaknode_group_set_output_passthrough(OakNodeGroup *group, } int oaknode_group_set_output_passthrough_undoable( - OakNodeGroup *group, OakNodeNode *node, OakUndoCommand *out_command) + OakNodeGroup group, OakNodeNode node, OakUndoCommand *out_command) { - if (!group || !out_command) { + olive::NodeGroup *g = to_native(group); + if (!g || !out_command) { return OAKNODE_E_INVALID; } try { OakUndoCommand handle = oaknode_c_api::wrap_command( - new olive::NodeGroupSetOutputPassthrough(to_group(group), - to_node(node))); + new olive::NodeGroupSetOutputPassthrough( + g, to_native(node))); if (!handle.ctx) { return OAKNODE_E_NOMEM; } @@ -266,23 +250,24 @@ int oaknode_group_set_output_passthrough_undoable( } } -int oaknode_group_resolve_input(OakNodeNode *node, const char *input_id, - int element, OakNodeNode **out_node, +int oaknode_group_resolve_input(OakNodeNode node, const char *input_id, + int element, OakNodeNode *out_node, char *buf, int buf_size, int *out_element) { - if (!node || !input_id) { + olive::Node *n = to_native(node); + if (!n || !input_id) { return OAKNODE_E_INVALID; } try { olive::NodeInput resolved = olive::NodeGroup::resolve_input( - olive::NodeInput(to_node(node), input_id, element)); + olive::NodeInput(n, input_id, element)); if (!resolved.is_valid()) { return OAKNODE_E_NOT_FOUND; } if (out_node) { - *out_node = from_node(resolved.node()); + *out_node = make_handle(resolved.node(), false, nullptr); } if (out_element) { *out_element = resolved.element(); diff --git a/src/node/c_api/keyframe.cpp b/src/node/c_api/keyframe.cpp index 36968c813..bd4e73888 100644 --- a/src/node/c_api/keyframe.cpp +++ b/src/node/c_api/keyframe.cpp @@ -24,31 +24,15 @@ #include "node.h" #include "nodeundo.h" +#include "nodehandle.h" #include "valueconvert.h" +using oaknode_c_api::make_handle; +using oaknode_c_api::to_native; + namespace { -inline olive::NodeKeyframe *to_key(OakNodeKeyframe *keyframe) -{ - return reinterpret_cast(keyframe); -} - -inline const olive::NodeKeyframe *to_key(const OakNodeKeyframe *keyframe) -{ - return reinterpret_cast(keyframe); -} - -inline olive::Node *to_node(OakNodeNode *node) -{ - return reinterpret_cast(node); -} - -inline OakNodeNode *from_node(olive::Node *node) -{ - return reinterpret_cast(node); -} - /** * @brief Convert an oaknode_keyframe_type to olive::NodeKeyframe::Type. * The oaknode enum mirrors the olive ordinals exactly (invalid = -1, @@ -132,61 +116,56 @@ private: } -OakNodeKeyframe *oaknode_keyframe_create(int64_t time_num, int64_t time_den, - const oaknode_value *value, int type, - int track, int element, - const char *input_id, - OakNodeNode *parent_or_null) +OakNodeKeyframe oaknode_keyframe_create(int64_t time_num, int64_t time_den, + const oaknode_value *value, int type, + int track, int element, + const char *input_id, + OakNodeNode parent_or_null) { olive::NodeKeyframe::Type keyframe_type; if (!keyframe_type_from_oak(type, &keyframe_type)) { - return NULL; + return OakNodeKeyframe{}; } try { olive::Variant variant; if (value) { if (!oaknode_c_api::variant_from_value(value, &variant)) { - return NULL; + return OakNodeKeyframe{}; } } olive::core::Rational time(static_cast(time_num), static_cast(time_den)); - olive::NodeKeyframe *key = new (std::nothrow) olive::NodeKeyframe( - time, variant, keyframe_type, track, element, - input_id ? input_id : "", to_node(parent_or_null)); - if (key) { - oaknode_c_api::alive_inc(); - } - return reinterpret_cast(key); + return make_handle( + new (std::nothrow) olive::NodeKeyframe( + time, variant, keyframe_type, track, element, + input_id ? input_id : "", + to_native(parent_or_null)), + true, oaknode_c_api::delete_as); } catch (...) { - return NULL; + return OakNodeKeyframe{}; } } void oaknode_keyframe_free(OakNodeKeyframe *keyframe) { - if (!keyframe) { - return; - } - try { - delete to_key(keyframe); - oaknode_c_api::alive_dec(); + oaknode_c_api::free_handle(keyframe); } catch (...) { } } -int oaknode_keyframe_get_time(const OakNodeKeyframe *keyframe, +int oaknode_keyframe_get_time(OakNodeKeyframe keyframe, int64_t *out_num, int64_t *out_den) { - if (!keyframe || !out_num || !out_den) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key || !out_num || !out_den) { return OAKNODE_E_INVALID; } try { - const olive::core::Rational &time = to_key(keyframe)->time(); + const olive::core::Rational &time = key->time(); *out_num = time.numerator(); *out_den = time.denominator(); return OAKNODE_OK; @@ -195,36 +174,37 @@ int oaknode_keyframe_get_time(const OakNodeKeyframe *keyframe, } } -int oaknode_keyframe_set_time(OakNodeKeyframe *keyframe, int64_t time_num, +int oaknode_keyframe_set_time(OakNodeKeyframe keyframe, int64_t time_num, int64_t time_den) { - if (!keyframe) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key) { return OAKNODE_E_INVALID; } try { - to_key(keyframe)->set_time(olive::core::Rational( - static_cast(time_num), static_cast(time_den))); + key->set_time(olive::core::Rational(static_cast(time_num), + static_cast(time_den))); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_keyframe_set_time_undoable(OakNodeKeyframe *keyframe, +int oaknode_keyframe_set_time_undoable(OakNodeKeyframe keyframe, int64_t time_num, int64_t time_den, OakUndoCommand *out_command) { - if (!keyframe || !out_command) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key || !out_command) { return OAKNODE_E_INVALID; } try { OakUndoCommand handle = oaknode_c_api::wrap_command( new olive::NodeParamSetKeyframeTimeCommand( - to_key(keyframe), - olive::core::Rational(static_cast(time_num), - static_cast(time_den)))); + key, olive::core::Rational(static_cast(time_num), + static_cast(time_den)))); if (!handle.ctx) { return OAKNODE_E_NOMEM; } @@ -235,15 +215,15 @@ int oaknode_keyframe_set_time_undoable(OakNodeKeyframe *keyframe, } } -int oaknode_keyframe_get_value(const OakNodeKeyframe *keyframe, +int oaknode_keyframe_get_value(OakNodeKeyframe keyframe, oaknode_value *out) { - if (!keyframe || !out) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key || !out) { return OAKNODE_E_INVALID; } try { - const olive::NodeKeyframe *key = to_key(keyframe); const olive::Variant &variant = key->value(); // Preferred path: the parent node's declared input type pins the @@ -295,10 +275,11 @@ int oaknode_keyframe_get_value(const OakNodeKeyframe *keyframe, } } -int oaknode_keyframe_set_value(OakNodeKeyframe *keyframe, +int oaknode_keyframe_set_value(OakNodeKeyframe keyframe, const oaknode_value *v) { - if (!keyframe || !v) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key || !v) { return OAKNODE_E_INVALID; } @@ -307,18 +288,19 @@ int oaknode_keyframe_set_value(OakNodeKeyframe *keyframe, if (!oaknode_c_api::variant_from_value(v, &variant)) { return OAKNODE_E_INVALID; } - to_key(keyframe)->set_value(variant); + key->set_value(variant); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_keyframe_set_value_undoable(OakNodeKeyframe *keyframe, +int oaknode_keyframe_set_value_undoable(OakNodeKeyframe keyframe, const oaknode_value *v, OakUndoCommand *out_command) { - if (!keyframe || !v || !out_command) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key || !v || !out_command) { return OAKNODE_E_INVALID; } @@ -329,8 +311,7 @@ int oaknode_keyframe_set_value_undoable(OakNodeKeyframe *keyframe, } OakUndoCommand handle = oaknode_c_api::wrap_command( - new olive::NodeParamSetKeyframeValueCommand(to_key(keyframe), - variant)); + new olive::NodeParamSetKeyframeValueCommand(key, variant)); if (!handle.ctx) { return OAKNODE_E_NOMEM; } @@ -341,48 +322,51 @@ int oaknode_keyframe_set_value_undoable(OakNodeKeyframe *keyframe, } } -int oaknode_keyframe_get_value_string(const OakNodeKeyframe *keyframe, +int oaknode_keyframe_get_value_string(OakNodeKeyframe keyframe, char *buf, int buf_size) { - if (!keyframe) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key) { return OAKNODE_E_INVALID; } try { - return oaknode_c_api::copy_string(to_key(keyframe)->value().to_string(), - buf, buf_size); + return oaknode_c_api::copy_string(key->value().to_string(), buf, + buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_keyframe_set_value_string(OakNodeKeyframe *keyframe, +int oaknode_keyframe_set_value_string(OakNodeKeyframe keyframe, const char *value) { - if (!keyframe || !value) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key || !value) { return OAKNODE_E_INVALID; } try { - to_key(keyframe)->set_value(olive::Variant(value)); + key->set_value(olive::Variant(value)); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_keyframe_set_value_string_undoable(OakNodeKeyframe *keyframe, +int oaknode_keyframe_set_value_string_undoable(OakNodeKeyframe keyframe, const char *value, OakUndoCommand *out_command) { - if (!keyframe || !value || !out_command) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key || !value || !out_command) { return OAKNODE_E_INVALID; } try { OakUndoCommand handle = oaknode_c_api::wrap_command( - new olive::NodeParamSetKeyframeValueCommand( - to_key(keyframe), olive::Variant(value))); + new olive::NodeParamSetKeyframeValueCommand(key, + olive::Variant(value))); if (!handle.ctx) { return OAKNODE_E_NOMEM; } @@ -393,23 +377,25 @@ int oaknode_keyframe_set_value_string_undoable(OakNodeKeyframe *keyframe, } } -int oaknode_keyframe_get_type(const OakNodeKeyframe *keyframe, int *out_type) +int oaknode_keyframe_get_type(OakNodeKeyframe keyframe, int *out_type) { - if (!keyframe || !out_type) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key || !out_type) { return OAKNODE_E_INVALID; } try { - *out_type = static_cast(to_key(keyframe)->type()); + *out_type = static_cast(key->type()); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_keyframe_set_type(OakNodeKeyframe *keyframe, int type) +int oaknode_keyframe_set_type(OakNodeKeyframe keyframe, int type) { - if (!keyframe) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key) { return OAKNODE_E_INVALID; } @@ -418,17 +404,18 @@ int oaknode_keyframe_set_type(OakNodeKeyframe *keyframe, int type) if (!keyframe_type_from_oak(type, &keyframe_type)) { return OAKNODE_E_INVALID; } - to_key(keyframe)->set_type(keyframe_type); + key->set_type(keyframe_type); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_keyframe_set_type_undoable(OakNodeKeyframe *keyframe, int type, +int oaknode_keyframe_set_type_undoable(OakNodeKeyframe keyframe, int type, OakUndoCommand *out_command) { - if (!keyframe || !out_command) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key || !out_command) { return OAKNODE_E_INVALID; } @@ -439,7 +426,7 @@ int oaknode_keyframe_set_type_undoable(OakNodeKeyframe *keyframe, int type, } OakUndoCommand handle = oaknode_c_api::wrap_command( - new KeyframeSetTypeCommand(to_key(keyframe), keyframe_type)); + new KeyframeSetTypeCommand(key, keyframe_type)); if (!handle.ctx) { return OAKNODE_E_NOMEM; } @@ -450,20 +437,21 @@ int oaknode_keyframe_set_type_undoable(OakNodeKeyframe *keyframe, int type, } } -int oaknode_keyframe_get_bezier_control(const OakNodeKeyframe *keyframe, +int oaknode_keyframe_get_bezier_control(OakNodeKeyframe keyframe, int handle, double *out_x, double *out_y) { - if (!keyframe || !out_x || !out_y) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key || !out_x || !out_y) { return OAKNODE_E_INVALID; } try { olive::PointF point; if (handle == OAKNODE_KEYFRAME_IN_HANDLE) { - point = to_key(keyframe)->bezier_control_in(); + point = key->bezier_control_in(); } else if (handle == OAKNODE_KEYFRAME_OUT_HANDLE) { - point = to_key(keyframe)->bezier_control_out(); + point = key->bezier_control_out(); } else { return OAKNODE_E_INVALID; } @@ -475,18 +463,19 @@ int oaknode_keyframe_get_bezier_control(const OakNodeKeyframe *keyframe, } } -int oaknode_keyframe_set_bezier_control(OakNodeKeyframe *keyframe, int handle, +int oaknode_keyframe_set_bezier_control(OakNodeKeyframe keyframe, int handle, double x, double y) { - if (!keyframe) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key) { return OAKNODE_E_INVALID; } try { if (handle == OAKNODE_KEYFRAME_IN_HANDLE) { - to_key(keyframe)->set_bezier_control_in(olive::PointF(x, y)); + key->set_bezier_control_in(olive::PointF(x, y)); } else if (handle == OAKNODE_KEYFRAME_OUT_HANDLE) { - to_key(keyframe)->set_bezier_control_out(olive::PointF(x, y)); + key->set_bezier_control_out(olive::PointF(x, y)); } else { return OAKNODE_E_INVALID; } @@ -496,11 +485,12 @@ int oaknode_keyframe_set_bezier_control(OakNodeKeyframe *keyframe, int handle, } } -int oaknode_keyframe_set_bezier_control_undoable(OakNodeKeyframe *keyframe, +int oaknode_keyframe_set_bezier_control_undoable(OakNodeKeyframe keyframe, int handle, double x, double y, OakUndoCommand *out_command) { - if (!keyframe || !out_command) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key || !out_command) { return OAKNODE_E_INVALID; } @@ -514,73 +504,76 @@ int oaknode_keyframe_set_bezier_control_undoable(OakNodeKeyframe *keyframe, return OAKNODE_E_INVALID; } - OakUndoCommand handle_ptr = oaknode_c_api::wrap_command( - new KeyframeSetBezierControlCommand(to_key(keyframe), bezier_handle, + OakUndoCommand command = oaknode_c_api::wrap_command( + new KeyframeSetBezierControlCommand(key, bezier_handle, olive::PointF(x, y))); - if (!handle_ptr.ctx) { + if (!command.ctx) { return OAKNODE_E_NOMEM; } - *out_command = handle_ptr; + *out_command = command; return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_keyframe_get_track(const OakNodeKeyframe *keyframe, +int oaknode_keyframe_get_track(OakNodeKeyframe keyframe, int *out_track) { - if (!keyframe || !out_track) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key || !out_track) { return OAKNODE_E_INVALID; } try { - *out_track = to_key(keyframe)->track(); + *out_track = key->track(); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_keyframe_get_element(const OakNodeKeyframe *keyframe, +int oaknode_keyframe_get_element(OakNodeKeyframe keyframe, int *out_element) { - if (!keyframe || !out_element) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key || !out_element) { return OAKNODE_E_INVALID; } try { - *out_element = to_key(keyframe)->element(); + *out_element = key->element(); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_keyframe_get_input(const OakNodeKeyframe *keyframe, char *buf, +int oaknode_keyframe_get_input(OakNodeKeyframe keyframe, char *buf, int buf_size) { - if (!keyframe) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key) { return OAKNODE_E_INVALID; } try { - return oaknode_c_api::copy_string(to_key(keyframe)->input(), buf, - buf_size); + return oaknode_c_api::copy_string(key->input(), buf, buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_keyframe_get_parent(const OakNodeKeyframe *keyframe, - OakNodeNode **out_node) +int oaknode_keyframe_get_parent(OakNodeKeyframe keyframe, + OakNodeNode *out_node) { - if (!keyframe || !out_node) { + olive::NodeKeyframe *key = to_native(keyframe); + if (!key || !out_node) { return OAKNODE_E_INVALID; } try { - *out_node = from_node(to_key(keyframe)->parent()); + *out_node = make_handle(key->parent(), false, nullptr); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; diff --git a/src/node/c_api/node.cpp b/src/node/c_api/node.cpp index b5545d351..cda4e4f2e 100644 --- a/src/node/c_api/node.cpp +++ b/src/node/c_api/node.cpp @@ -21,11 +21,14 @@ #include "node/node.h" #include "common/videoparams.h" +#include "node/footage.h" +#include "node/project.h" #include "olive/core/oakcore/audioparams.h" #include #include +#include "nodehandle.h" #include "nodeundo.h" #include "project.h" @@ -39,21 +42,6 @@ namespace { -inline olive::Node *to_node(OakNodeNode *node) -{ - return reinterpret_cast(node); -} - -inline const olive::Node *to_node(const OakNodeNode *node) -{ - return reinterpret_cast(node); -} - -inline OakNodeNode *from_node(olive::Node *node) -{ - return reinterpret_cast(node); -} - std::atomic g_alive_count(0); /** @@ -113,69 +101,74 @@ int oaknode_debug_alive_count(void) /* ---- Metadata --------------------------------------------------------- */ -int oaknode_node_get_id(const OakNodeNode *node, char *buf, int buf_size) +int oaknode_node_get_id(OakNodeNode node, char *buf, int buf_size) { - if (!node) { + if (!node.ctx) { return OAKNODE_E_INVALID; } try { - return oaknode_c_api::copy_string(to_node(node)->id(), buf, buf_size); + return oaknode_c_api::copy_string( + oaknode_c_api::to_native(node)->id(), buf, buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_get_name(const OakNodeNode *node, char *buf, int buf_size) +int oaknode_node_get_name(OakNodeNode node, char *buf, int buf_size) { - if (!node) { + if (!node.ctx) { return OAKNODE_E_INVALID; } try { - return oaknode_c_api::copy_string(to_node(node)->name(), buf, buf_size); + return oaknode_c_api::copy_string( + oaknode_c_api::to_native(node)->name(), buf, buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_get_label(const OakNodeNode *node, char *buf, int buf_size) +int oaknode_node_get_label(OakNodeNode node, char *buf, int buf_size) { - if (!node) { + if (!node.ctx) { return OAKNODE_E_INVALID; } try { - return oaknode_c_api::copy_string(to_node(node)->get_label(), buf, buf_size); + return oaknode_c_api::copy_string( + oaknode_c_api::to_native(node)->get_label(), buf, + buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_set_label(OakNodeNode *node, const char *label) +int oaknode_node_set_label(OakNodeNode node, const char *label) { - if (!node || !label) { + if (!node.ctx || !label) { return OAKNODE_E_INVALID; } try { - to_node(node)->set_label(label); + oaknode_c_api::to_native(node)->set_label(label); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_set_label_undoable(OakNodeNode *node, const char *label, +int oaknode_node_set_label_undoable(OakNodeNode node, const char *label, OakUndoCommand *out_command) { - if (!node || !label || !out_command) { + if (!node.ctx || !label || !out_command) { return OAKNODE_E_INVALID; } try { OakUndoCommand handle = oaknode_c_api::wrap_command( - new olive::NodeRenameCommand(to_node(node), label)); + new olive::NodeRenameCommand( + oaknode_c_api::to_native(node), label)); if (!handle.ctx) { return OAKNODE_E_NOMEM; } @@ -186,44 +179,46 @@ int oaknode_node_set_label_undoable(OakNodeNode *node, const char *label, } } -int oaknode_node_get_override_color(const OakNodeNode *node, int *out_value) +int oaknode_node_get_override_color(OakNodeNode node, int *out_value) { - if (!node || !out_value) { + if (!node.ctx || !out_value) { return OAKNODE_E_INVALID; } try { - *out_value = to_node(node)->get_override_color(); + *out_value = + oaknode_c_api::to_native(node)->get_override_color(); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_set_override_color(OakNodeNode *node, int index) +int oaknode_node_set_override_color(OakNodeNode node, int index) { - if (!node) { + if (!node.ctx) { return OAKNODE_E_INVALID; } try { - to_node(node)->set_override_color(index); + oaknode_c_api::to_native(node)->set_override_color(index); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_set_override_color_undoable(OakNodeNode *node, int index, +int oaknode_node_set_override_color_undoable(OakNodeNode node, int index, OakUndoCommand *out_command) { - if (!node || !out_command) { + if (!node.ctx || !out_command) { return OAKNODE_E_INVALID; } try { OakUndoCommand handle = oaknode_c_api::wrap_command( - new olive::NodeOverrideColorCommand(to_node(node), index)); + new olive::NodeOverrideColorCommand( + oaknode_c_api::to_native(node), index)); if (!handle.ctx) { return OAKNODE_E_NOMEM; } @@ -234,14 +229,14 @@ int oaknode_node_set_override_color_undoable(OakNodeNode *node, int index, } } -int oaknode_node_is_enabled(const OakNodeNode *node, int *out_value) +int oaknode_node_is_enabled(OakNodeNode node, int *out_value) { - if (!node || !out_value) { + if (!node.ctx || !out_value) { return OAKNODE_E_INVALID; } try { - *out_value = to_node(node) + *out_value = oaknode_c_api::to_native(node) ->get_standard_value(olive::Node::k_enabled_input) .to_bool() ? 1 @@ -252,30 +247,31 @@ int oaknode_node_is_enabled(const OakNodeNode *node, int *out_value) } } -int oaknode_node_set_enabled(OakNodeNode *node, int enabled) +int oaknode_node_set_enabled(OakNodeNode node, int enabled) { - if (!node) { + if (!node.ctx) { return OAKNODE_E_INVALID; } try { - to_node(node)->set_standard_value(olive::Node::k_enabled_input, - olive::Variant(enabled != 0)); + oaknode_c_api::to_native(node)->set_standard_value( + olive::Node::k_enabled_input, olive::Variant(enabled != 0)); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_set_enabled_undoable(OakNodeNode *node, int enabled, +int oaknode_node_set_enabled_undoable(OakNodeNode node, int enabled, OakUndoCommand *out_command) { - if (!node || !out_command) { + if (!node.ctx || !out_command) { return OAKNODE_E_INVALID; } try { - olive::NodeInput ref(to_node(node), olive::Node::k_enabled_input); + olive::NodeInput ref(oaknode_c_api::to_native(node), + olive::Node::k_enabled_input); olive::SplitValue split = olive::NodeValue::split_normal_value_into_track_values( olive::NodeValue::k_boolean, olive::Variant(enabled != 0)); @@ -293,29 +289,31 @@ int oaknode_node_set_enabled_undoable(OakNodeNode *node, int enabled, /* ---- Input introspection ------------------------------------------------ */ -int oaknode_node_input_count(const OakNodeNode *node, int *out_count) +int oaknode_node_input_count(OakNodeNode node, int *out_count) { - if (!node || !out_count) { + if (!node.ctx || !out_count) { return OAKNODE_E_INVALID; } try { - *out_count = static_cast(to_node(node)->inputs().size()); + *out_count = static_cast( + oaknode_c_api::to_native(node)->inputs().size()); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_input_id(const OakNodeNode *node, int index, char *buf, +int oaknode_node_input_id(OakNodeNode node, int index, char *buf, int buf_size) { - if (!node) { + if (!node.ctx) { return OAKNODE_E_INVALID; } try { - const std::vector &inputs = to_node(node)->inputs(); + const std::vector &inputs = + oaknode_c_api::to_native(node)->inputs(); if (index < 0 || index >= static_cast(inputs.size())) { return OAKNODE_E_NOT_FOUND; } @@ -325,92 +323,104 @@ int oaknode_node_input_id(const OakNodeNode *node, int index, char *buf, } } -int oaknode_node_input_get_type(const OakNodeNode *node, const char *input_id, +int oaknode_node_input_get_type(OakNodeNode node, const char *input_id, int *out_type) { - if (!node || !out_type) { + if (!node.ctx || !out_type) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(node), input_id)) { + if (!has_input(oaknode_c_api::to_native(node), input_id)) { return OAKNODE_E_NOT_FOUND; } try { *out_type = oaknode_c_api::value_type_to_oak( - to_node(node)->get_input_data_type(input_id)); + oaknode_c_api::to_native(node)->get_input_data_type( + input_id)); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_input_is_connected(const OakNodeNode *node, - const char *input_id, int *out_value) +int oaknode_node_input_is_connected(OakNodeNode node, const char *input_id, + int *out_value) { - if (!node || !out_value) { + if (!node.ctx || !out_value) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(node), input_id)) { + if (!has_input(oaknode_c_api::to_native(node), input_id)) { return OAKNODE_E_NOT_FOUND; } try { - *out_value = to_node(node)->is_input_connected(input_id) ? 1 : 0; + *out_value = oaknode_c_api::to_native(node) + ->is_input_connected(input_id) + ? 1 + : 0; return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_input_is_connectable(const OakNodeNode *node, - const char *input_id, int *out_value) +int oaknode_node_input_is_connectable(OakNodeNode node, const char *input_id, + int *out_value) { - if (!node || !out_value) { + if (!node.ctx || !out_value) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(node), input_id)) { + if (!has_input(oaknode_c_api::to_native(node), input_id)) { return OAKNODE_E_NOT_FOUND; } try { - *out_value = to_node(node)->is_input_connectable(input_id) ? 1 : 0; + *out_value = oaknode_c_api::to_native(node) + ->is_input_connectable(input_id) + ? 1 + : 0; return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_get_input_name(const OakNodeNode *node, const char *input_id, +int oaknode_node_get_input_name(OakNodeNode node, const char *input_id, char *buf, int buf_size) { - if (!node) { + if (!node.ctx) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(node), input_id)) { + if (!has_input(oaknode_c_api::to_native(node), input_id)) { return OAKNODE_E_NOT_FOUND; } try { - return oaknode_c_api::copy_string(to_node(node)->get_input_name(input_id), - buf, buf_size); + return oaknode_c_api::copy_string( + oaknode_c_api::to_native(node)->get_input_name( + input_id), + buf, buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_input_get_connected_node(const OakNodeNode *node, +int oaknode_node_input_get_connected_node(OakNodeNode node, const char *input_id, - OakNodeNode **out_node) + OakNodeNode *out_node) { - if (!node || !out_node) { + if (!node.ctx || !out_node) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(node), input_id)) { + if (!has_input(oaknode_c_api::to_native(node), input_id)) { return OAKNODE_E_NOT_FOUND; } try { - *out_node = from_node(to_node(node)->get_connected_output(input_id)); + *out_node = oaknode_c_api::make_handle( + oaknode_c_api::to_native(node)->get_connected_output( + input_id), + false, &oaknode_c_api::delete_as); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; @@ -419,71 +429,83 @@ int oaknode_node_input_get_connected_node(const OakNodeNode *node, /* ---- Parameter access ----------------------------------------------------- */ -int oaknode_node_get_input(const OakNodeNode *node, const char *input_id, +int oaknode_node_get_input(OakNodeNode node, const char *input_id, oaknode_value *out) { - if (!node || !out) { + if (!node.ctx || !out) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(node), input_id)) { + if (!has_input(oaknode_c_api::to_native(node), input_id)) { return OAKNODE_E_NOT_FOUND; } try { - olive::NodeValue::Type type = to_node(node)->get_input_data_type(input_id); + olive::NodeValue::Type type = + oaknode_c_api::to_native(node)->get_input_data_type( + input_id); return oaknode_c_api::value_from_variant( - type, to_node(node)->get_standard_value(input_id), out); + type, + oaknode_c_api::to_native(node)->get_standard_value( + input_id), + out); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_set_input(OakNodeNode *node, const char *input_id, +int oaknode_node_set_input(OakNodeNode node, const char *input_id, const oaknode_value *v) { - if (!node) { + if (!node.ctx) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(node), input_id)) { + if (!has_input(oaknode_c_api::to_native(node), input_id)) { return OAKNODE_E_NOT_FOUND; } try { olive::Variant variant; - int rc = variant_for_input(to_node(node), input_id, v, &variant); + int rc = variant_for_input(oaknode_c_api::to_native(node), + input_id, v, &variant); if (rc != OAKNODE_OK) { return rc; } - to_node(node)->set_standard_value(input_id, variant); + oaknode_c_api::to_native(node)->set_standard_value( + input_id, variant); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_set_input_undoable(OakNodeNode *node, const char *input_id, +int oaknode_node_set_input_undoable(OakNodeNode node, const char *input_id, const oaknode_value *v, OakUndoCommand *out_command) { - if (!node || !out_command) { + if (!node.ctx || !out_command) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(node), input_id)) { + if (!has_input(oaknode_c_api::to_native(node), input_id)) { return OAKNODE_E_NOT_FOUND; } try { olive::Variant variant; - int rc = variant_for_input(to_node(node), input_id, v, &variant); + int rc = variant_for_input(oaknode_c_api::to_native(node), + input_id, v, &variant); if (rc != OAKNODE_OK) { return rc; } - olive::NodeValue::Type type = to_node(node)->get_input_data_type(input_id); - olive::NodeInput ref(to_node(node), input_id); + olive::NodeValue::Type type = + oaknode_c_api::to_native(node)->get_input_data_type( + input_id); + olive::NodeInput ref(oaknode_c_api::to_native(node), + input_id); olive::SplitValue split = - olive::NodeValue::split_normal_value_into_track_values(type, variant); + olive::NodeValue::split_normal_value_into_track_values(type, + variant); OakUndoCommand handle = oaknode_c_api::wrap_command( new olive::NodeParamSetSplitStandardValueCommand(ref, split)); @@ -497,73 +519,81 @@ int oaknode_node_set_input_undoable(OakNodeNode *node, const char *input_id, } } -int oaknode_node_get_input_string(const OakNodeNode *node, - const char *input_id, char *buf, - int buf_size) +int oaknode_node_get_input_string(OakNodeNode node, const char *input_id, + char *buf, int buf_size) { - if (!node) { + if (!node.ctx) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(node), input_id)) { + if (!has_input(oaknode_c_api::to_native(node), input_id)) { return OAKNODE_E_NOT_FOUND; } try { if (!oaknode_c_api::value_type_is_string( - to_node(node)->get_input_data_type(input_id))) { + oaknode_c_api::to_native(node) + ->get_input_data_type(input_id))) { return OAKNODE_E_INVALID; } return oaknode_c_api::copy_string( - to_node(node)->get_standard_value(input_id).to_string(), buf, buf_size); + oaknode_c_api::to_native(node) + ->get_standard_value(input_id) + .to_string(), + buf, buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_set_input_string(OakNodeNode *node, const char *input_id, +int oaknode_node_set_input_string(OakNodeNode node, const char *input_id, const char *value) { - if (!node || !value) { + if (!node.ctx || !value) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(node), input_id)) { + if (!has_input(oaknode_c_api::to_native(node), input_id)) { return OAKNODE_E_NOT_FOUND; } try { if (!oaknode_c_api::value_type_is_string( - to_node(node)->get_input_data_type(input_id))) { + oaknode_c_api::to_native(node) + ->get_input_data_type(input_id))) { return OAKNODE_E_INVALID; } - to_node(node)->set_standard_value(input_id, olive::Variant(value)); + oaknode_c_api::to_native(node)->set_standard_value( + input_id, olive::Variant(value)); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_set_input_string_undoable(OakNodeNode *node, +int oaknode_node_set_input_string_undoable(OakNodeNode node, const char *input_id, const char *value, OakUndoCommand *out_command) { - if (!node || !value || !out_command) { + if (!node.ctx || !value || !out_command) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(node), input_id)) { + if (!has_input(oaknode_c_api::to_native(node), input_id)) { return OAKNODE_E_NOT_FOUND; } try { if (!oaknode_c_api::value_type_is_string( - to_node(node)->get_input_data_type(input_id))) { + oaknode_c_api::to_native(node) + ->get_input_data_type(input_id))) { return OAKNODE_E_INVALID; } - olive::NodeInput ref(to_node(node), input_id); + olive::NodeInput ref(oaknode_c_api::to_native(node), + input_id); olive::SplitValue split = olive::NodeValue::split_normal_value_into_track_values( - to_node(node)->get_input_data_type(input_id), + oaknode_c_api::to_native(node) + ->get_input_data_type(input_id), olive::Variant(value)); OakUndoCommand handle = oaknode_c_api::wrap_command( @@ -580,54 +610,64 @@ int oaknode_node_set_input_string_undoable(OakNodeNode *node, /* ---- Graph editing -------------------------------------------------------- */ -int oaknode_node_connect(OakNodeNode *output_node, OakNodeNode *input_node, +int oaknode_node_connect(OakNodeNode output_node, OakNodeNode input_node, const char *input_id) { - if (!output_node || !input_node) { + if (!output_node.ctx || !input_node.ctx) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(input_node), input_id)) { + if (!has_input(oaknode_c_api::to_native(input_node), + input_id)) { return OAKNODE_E_NOT_FOUND; } try { - if (!to_node(input_node)->is_input_connectable(input_id)) { + if (!oaknode_c_api::to_native(input_node) + ->is_input_connectable(input_id)) { return OAKNODE_E_INVALID; } - if (to_node(input_node)->is_input_connected(input_id) || - to_node(input_node)->parent() != to_node(output_node)->parent()) { + if (oaknode_c_api::to_native(input_node) + ->is_input_connected(input_id) || + oaknode_c_api::to_native(input_node)->parent() != + oaknode_c_api::to_native(output_node)->parent()) { return OAKNODE_E_STATE; } - olive::Node::connect_edge(to_node(output_node), - olive::NodeInput(to_node(input_node), input_id)); + olive::Node::connect_edge( + oaknode_c_api::to_native(output_node), + olive::NodeInput(oaknode_c_api::to_native(input_node), + input_id)); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_connect_undoable(OakNodeNode *output_node, - OakNodeNode *input_node, +int oaknode_node_connect_undoable(OakNodeNode output_node, + OakNodeNode input_node, const char *input_id, OakUndoCommand *out_command) { - if (!output_node || !input_node || !out_command) { + if (!output_node.ctx || !input_node.ctx || !out_command) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(input_node), input_id)) { + if (!has_input(oaknode_c_api::to_native(input_node), + input_id)) { return OAKNODE_E_NOT_FOUND; } try { - if (!to_node(input_node)->is_input_connectable(input_id)) { + if (!oaknode_c_api::to_native(input_node) + ->is_input_connectable(input_id)) { return OAKNODE_E_INVALID; } OakUndoCommand handle = oaknode_c_api::wrap_command( new olive::NodeEdgeAddCommand( - to_node(output_node), - olive::NodeInput(to_node(input_node), input_id))); + oaknode_c_api::to_native(output_node), + olive::NodeInput( + oaknode_c_api::to_native(input_node), + input_id))); if (!handle.ctx) { return OAKNODE_E_NOMEM; } @@ -638,49 +678,60 @@ int oaknode_node_connect_undoable(OakNodeNode *output_node, } } -int oaknode_node_disconnect(OakNodeNode *input_node, const char *input_id) +int oaknode_node_disconnect(OakNodeNode input_node, const char *input_id) { - if (!input_node) { + if (!input_node.ctx) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(input_node), input_id)) { + if (!has_input(oaknode_c_api::to_native(input_node), + input_id)) { return OAKNODE_E_NOT_FOUND; } try { - olive::Node *output = to_node(input_node)->get_connected_output(input_id); + olive::Node *output = + oaknode_c_api::to_native(input_node) + ->get_connected_output(input_id); if (!output) { return OAKNODE_E_NOT_FOUND; } olive::Node::disconnect_edge( - output, olive::NodeInput(to_node(input_node), input_id)); + output, + olive::NodeInput(oaknode_c_api::to_native(input_node), + input_id)); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_disconnect_undoable(OakNodeNode *input_node, +int oaknode_node_disconnect_undoable(OakNodeNode input_node, const char *input_id, OakUndoCommand *out_command) { - if (!input_node || !out_command) { + if (!input_node.ctx || !out_command) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(input_node), input_id)) { + if (!has_input(oaknode_c_api::to_native(input_node), + input_id)) { return OAKNODE_E_NOT_FOUND; } try { - olive::Node *output = to_node(input_node)->get_connected_output(input_id); + olive::Node *output = + oaknode_c_api::to_native(input_node) + ->get_connected_output(input_id); if (!output) { return OAKNODE_E_NOT_FOUND; } OakUndoCommand handle = oaknode_c_api::wrap_command( new olive::NodeEdgeRemoveCommand( - output, olive::NodeInput(to_node(input_node), input_id))); + output, + olive::NodeInput( + oaknode_c_api::to_native(input_node), + input_id))); if (!handle.ctx) { return OAKNODE_E_NOMEM; } @@ -691,52 +742,55 @@ int oaknode_node_disconnect_undoable(OakNodeNode *input_node, } } -int oaknode_node_output_connection_count(const OakNodeNode *node, - int *out_count) +int oaknode_node_output_connection_count(OakNodeNode node, int *out_count) { - if (!node || !out_count) { + if (!node.ctx || !out_count) { return OAKNODE_E_INVALID; } try { - *out_count = static_cast(to_node(node)->output_connections().size()); + *out_count = static_cast( + oaknode_c_api::to_native(node) + ->output_connections() + .size()); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_output_connection_node_at(const OakNodeNode *node, int index, - OakNodeNode **out_node) +int oaknode_node_output_connection_node_at(OakNodeNode node, int index, + OakNodeNode *out_node) { - if (!node || !out_node) { + if (!node.ctx || !out_node) { return OAKNODE_E_INVALID; } try { const olive::Node::OutputConnections &connections = - to_node(node)->output_connections(); + oaknode_c_api::to_native(node)->output_connections(); if (index < 0 || index >= static_cast(connections.size())) { return OAKNODE_E_NOT_FOUND; } - *out_node = from_node(connections[size_t(index)].second.node()); + *out_node = oaknode_c_api::make_handle( + connections[size_t(index)].second.node(), false, + &oaknode_c_api::delete_as); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_output_connection_input_id_at(const OakNodeNode *node, - int index, char *buf, - int buf_size) +int oaknode_node_output_connection_input_id_at(OakNodeNode node, int index, + char *buf, int buf_size) { - if (!node) { + if (!node.ctx) { return OAKNODE_E_INVALID; } try { const olive::Node::OutputConnections &connections = - to_node(node)->output_connections(); + oaknode_c_api::to_native(node)->output_connections(); if (index < 0 || index >= static_cast(connections.size())) { return OAKNODE_E_NOT_FOUND; } @@ -747,16 +801,16 @@ int oaknode_node_output_connection_input_id_at(const OakNodeNode *node, } } -int oaknode_node_output_connection_element_at(const OakNodeNode *node, - int index, int *out_element) +int oaknode_node_output_connection_element_at(OakNodeNode node, int index, + int *out_element) { - if (!node || !out_element) { + if (!node.ctx || !out_element) { return OAKNODE_E_INVALID; } try { const olive::Node::OutputConnections &connections = - to_node(node)->output_connections(); + oaknode_c_api::to_native(node)->output_connections(); if (index < 0 || index >= static_cast(connections.size())) { return OAKNODE_E_NOT_FOUND; } @@ -769,14 +823,16 @@ int oaknode_node_output_connection_element_at(const OakNodeNode *node, /* ---- Links --------------------------------------------------------------- */ -int oaknode_node_link(OakNodeNode *a, OakNodeNode *b, int *out_linked) +int oaknode_node_link(OakNodeNode a, OakNodeNode b, int *out_linked) { - if (!a || !b) { + if (!a.ctx || !b.ctx) { return OAKNODE_E_INVALID; } try { - bool linked = olive::Node::link(to_node(a), to_node(b)); + bool linked = olive::Node::link( + oaknode_c_api::to_native(a), + oaknode_c_api::to_native(b)); if (out_linked) { *out_linked = linked ? 1 : 0; } @@ -786,14 +842,16 @@ int oaknode_node_link(OakNodeNode *a, OakNodeNode *b, int *out_linked) } } -int oaknode_node_unlink(OakNodeNode *a, OakNodeNode *b, int *out_unlinked) +int oaknode_node_unlink(OakNodeNode a, OakNodeNode b, int *out_unlinked) { - if (!a || !b) { + if (!a.ctx || !b.ctx) { return OAKNODE_E_INVALID; } try { - bool unlinked = olive::Node::unlink(to_node(a), to_node(b)); + bool unlinked = olive::Node::unlink( + oaknode_c_api::to_native(a), + oaknode_c_api::to_native(b)); if (out_unlinked) { *out_unlinked = unlinked ? 1 : 0; } @@ -803,16 +861,18 @@ int oaknode_node_unlink(OakNodeNode *a, OakNodeNode *b, int *out_unlinked) } } -int oaknode_node_link_undoable(OakNodeNode *a, OakNodeNode *b, int link, +int oaknode_node_link_undoable(OakNodeNode a, OakNodeNode b, int link, OakUndoCommand *out_command) { - if (!a || !b || !out_command) { + if (!a.ctx || !b.ctx || !out_command) { return OAKNODE_E_INVALID; } try { OakUndoCommand handle = oaknode_c_api::wrap_command( - new olive::NodeLinkCommand(to_node(a), to_node(b), link != 0)); + new olive::NodeLinkCommand( + oaknode_c_api::to_native(a), + oaknode_c_api::to_native(b), link != 0)); if (!handle.ctx) { return OAKNODE_E_NOMEM; } @@ -823,17 +883,16 @@ int oaknode_node_link_undoable(OakNodeNode *a, OakNodeNode *b, int link, } } -int oaknode_node_are_linked(const OakNodeNode *a, const OakNodeNode *b, - int *out_value) +int oaknode_node_are_linked(OakNodeNode a, OakNodeNode b, int *out_value) { - if (!a || !b || !out_value) { + if (!a.ctx || !b.ctx || !out_value) { return OAKNODE_E_INVALID; } try { *out_value = olive::Node::are_linked( - const_cast(to_node(a)), - const_cast(to_node(b))) + oaknode_c_api::to_native(a), + oaknode_c_api::to_native(b)) ? 1 : 0; return OAKNODE_OK; @@ -842,33 +901,36 @@ int oaknode_node_are_linked(const OakNodeNode *a, const OakNodeNode *b, } } -int oaknode_node_link_count(const OakNodeNode *node, int *out_count) +int oaknode_node_link_count(OakNodeNode node, int *out_count) { - if (!node || !out_count) { + if (!node.ctx || !out_count) { return OAKNODE_E_INVALID; } try { - *out_count = static_cast(to_node(node)->links().size()); + *out_count = static_cast( + oaknode_c_api::to_native(node)->links().size()); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_link_at(const OakNodeNode *node, int index, - OakNodeNode **out_node) +int oaknode_node_link_at(OakNodeNode node, int index, OakNodeNode *out_node) { - if (!node || !out_node) { + if (!node.ctx || !out_node) { return OAKNODE_E_INVALID; } try { - const std::vector &links = to_node(node)->links(); + const std::vector &links = + oaknode_c_api::to_native(node)->links(); if (index < 0 || index >= static_cast(links.size())) { return OAKNODE_E_NOT_FOUND; } - *out_node = from_node(links[size_t(index)]); + *out_node = oaknode_c_api::make_handle( + links[size_t(index)], false, + &oaknode_c_api::delete_as); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; @@ -877,59 +939,65 @@ int oaknode_node_link_at(const OakNodeNode *node, int index, /* ---- Context positions ---------------------------------------------------- */ -int oaknode_node_context_count(const OakNodeNode *node, int *out_count) +int oaknode_node_context_count(OakNodeNode node, int *out_count) { - if (!node || !out_count) { + if (!node.ctx || !out_count) { return OAKNODE_E_INVALID; } try { - *out_count = static_cast(to_node(node)->get_context_positions().size()); + *out_count = static_cast( + oaknode_c_api::to_native(node) + ->get_context_positions() + .size()); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_context_node_at(const OakNodeNode *node, int index, - OakNodeNode **out_node) +int oaknode_node_context_node_at(OakNodeNode node, int index, + OakNodeNode *out_node) { - if (!node || !out_node) { + if (!node.ctx || !out_node) { return OAKNODE_E_INVALID; } try { const olive::Node::PositionMap &positions = - to_node(node)->get_context_positions(); + oaknode_c_api::to_native(node) + ->get_context_positions(); if (index < 0 || index >= static_cast(positions.size())) { return OAKNODE_E_NOT_FOUND; } auto it = positions.cbegin(); std::advance(it, index); - *out_node = from_node(it->first); + *out_node = oaknode_c_api::make_handle( + it->first, false, &oaknode_c_api::delete_as); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_get_context_position(const OakNodeNode *node, - OakNodeNode *context, double *out_x, - double *out_y, int *out_expanded) +int oaknode_node_get_context_position(OakNodeNode node, OakNodeNode context, + double *out_x, double *out_y, + int *out_expanded) { - if (!node || !context) { + if (!node.ctx || !context.ctx) { return OAKNODE_E_INVALID; } try { - const olive::Node *self = to_node(node); - if (!self->context_contains_node(to_node(context))) { + olive::Node *self = oaknode_c_api::to_native(node); + if (!self->context_contains_node( + oaknode_c_api::to_native(context))) { return OAKNODE_E_NOT_FOUND; } olive::Node::Position position = - const_cast(self)->get_node_position_data_in_context( - to_node(context)); + self->get_node_position_data_in_context( + oaknode_c_api::to_native(context)); if (out_x) { *out_x = position.position.x(); } @@ -945,28 +1013,30 @@ int oaknode_node_get_context_position(const OakNodeNode *node, } } -int oaknode_node_set_context_position(OakNodeNode *node, OakNodeNode *context, +int oaknode_node_set_context_position(OakNodeNode node, OakNodeNode context, double x, double y, int expanded) { - if (!node || !context) { + if (!node.ctx || !context.ctx) { return OAKNODE_E_INVALID; } try { olive::Node::Position position(olive::PointF(x, y), expanded != 0); - to_node(node)->set_node_position_in_context(to_node(context), position); + oaknode_c_api::to_native(node) + ->set_node_position_in_context( + oaknode_c_api::to_native(context), position); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_set_context_position_undoable(OakNodeNode *node, - OakNodeNode *context, double x, +int oaknode_node_set_context_position_undoable(OakNodeNode node, + OakNodeNode context, double x, double y, int expanded, OakUndoCommand *out_command) { - if (!node || !context || !out_command) { + if (!node.ctx || !context.ctx || !out_command) { return OAKNODE_E_INVALID; } @@ -978,8 +1048,9 @@ int oaknode_node_set_context_position_undoable(OakNodeNode *node, // so the command's (node, context) arguments are swapped relative // to this function's signature. OakUndoCommand handle = oaknode_c_api::wrap_command( - new olive::NodeSetPositionCommand(to_node(context), to_node(node), - position)); + new olive::NodeSetPositionCommand( + oaknode_c_api::to_native(context), + oaknode_c_api::to_native(node), position)); if (!handle.ctx) { return OAKNODE_E_NOMEM; } @@ -990,14 +1061,16 @@ int oaknode_node_set_context_position_undoable(OakNodeNode *node, } } -int oaknode_node_remove_from_context(OakNodeNode *node, OakNodeNode *context) +int oaknode_node_remove_from_context(OakNodeNode node, OakNodeNode context) { - if (!node || !context) { + if (!node.ctx || !context.ctx) { return OAKNODE_E_INVALID; } try { - if (!to_node(node)->remove_node_from_context(to_node(context))) { + if (!oaknode_c_api::to_native(node) + ->remove_node_from_context( + oaknode_c_api::to_native(context))) { return OAKNODE_E_NOT_FOUND; } return OAKNODE_OK; @@ -1008,210 +1081,211 @@ int oaknode_node_remove_from_context(OakNodeNode *node, OakNodeNode *context) /* ---- Lifetime --------------------------------------------------------------- */ -OakNodeNode *oaknode_node_create_copy(const OakNodeNode *node) +OakNodeNode oaknode_node_create_copy(OakNodeNode node) { - if (!node) { - return NULL; + if (!node.ctx) { + return OakNodeNode{}; } try { - olive::Node *copy = to_node(node)->copy(); - if (copy) { - oaknode_c_api::alive_inc(); - } - return from_node(copy); + return oaknode_c_api::make_handle( + oaknode_c_api::to_native(node)->copy(), true, + &oaknode_c_api::delete_as); } catch (...) { - return NULL; + return OakNodeNode{}; } } void oaknode_node_free(OakNodeNode *node) { - if (!node) { - return; - } - - try { - delete to_node(node); - oaknode_c_api::alive_dec(); - } catch (...) { - } + oaknode_c_api::free_handle(node); } -OakNodeNode *oaknode_node_copy_in_graph(OakNodeNode *node, - OakUndoCommand *out_command) +OakNodeNode oaknode_node_copy_in_graph(OakNodeNode node, + OakUndoCommand *out_command) { - if (!node || !out_command) { - return NULL; + if (!node.ctx || !out_command) { + return OakNodeNode{}; } try { auto *command = new olive::MultiUndoCommand(); - olive::Node *copy = - olive::Node::copy_node_in_graph(to_node(node), command); + olive::Node *copy = olive::Node::copy_node_in_graph( + oaknode_c_api::to_native(node), command); if (!copy) { delete command; - return NULL; + return OakNodeNode{}; } *out_command = oaknode_c_api::wrap_command(command); if (!out_command->ctx) { delete command; - return NULL; + return OakNodeNode{}; } - oaknode_c_api::alive_inc(); - return from_node(copy); + return oaknode_c_api::make_handle( + copy, true, &oaknode_c_api::delete_as); } catch (...) { - return NULL; + return OakNodeNode{}; } } -OakUndoCommand oaknode_command_create_remove_node(OakNodeNode *node) +OakUndoCommand oaknode_command_create_remove_node(OakNodeNode node) { - if (!node) { + if (!node.ctx) { return OakUndoCommand{}; } try { return oaknode_c_api::wrap_command( new olive::NodeRemoveWithExclusiveDependenciesAndDisconnect( - to_node(node))); + oaknode_c_api::to_native(node))); } catch (...) { return OakUndoCommand{}; } } -int oaknode_node_get_project(const OakNodeNode *node, OakNodeProject **out) +int oaknode_node_get_project(OakNodeNode node, OakNodeProject *out) { - if (!node || !out) { + if (!node.ctx || !out) { return OAKNODE_E_INVALID; } - *out = reinterpret_cast(to_node(node)->project()); + *out = oaknode_c_api::make_handle( + oaknode_c_api::to_native(node)->project(), false, + &oaknode_c_api::delete_as); return OAKNODE_OK; } -int oaknode_node_input_array_insert(OakNodeNode *node, const char *input_id, +int oaknode_node_input_array_insert(OakNodeNode node, const char *input_id, int index) { - if (!node || !input_id) { + if (!node.ctx || !input_id) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(node), input_id)) { + if (!has_input(oaknode_c_api::to_native(node), input_id)) { return OAKNODE_E_NOT_FOUND; } try { - to_node(node)->input_array_insert(input_id, index); + oaknode_c_api::to_native(node)->input_array_insert( + input_id, index); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_input_array_remove(OakNodeNode *node, const char *input_id, +int oaknode_node_input_array_remove(OakNodeNode node, const char *input_id, int index) { - if (!node || !input_id) { + if (!node.ctx || !input_id) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(node), input_id)) { + if (!has_input(oaknode_c_api::to_native(node), input_id)) { return OAKNODE_E_NOT_FOUND; } try { - to_node(node)->input_array_remove(input_id, index); + oaknode_c_api::to_native(node)->input_array_remove( + input_id, index); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_connect_element(OakNodeNode *output_node, - OakNodeNode *input_node, +int oaknode_node_connect_element(OakNodeNode output_node, + OakNodeNode input_node, const char *input_id, int element) { - if (!output_node || !input_node) { + if (!output_node.ctx || !input_node.ctx) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(input_node), input_id)) { + if (!has_input(oaknode_c_api::to_native(input_node), + input_id)) { return OAKNODE_E_NOT_FOUND; } try { olive::Node::connect_edge( - to_node(output_node), - olive::NodeInput(to_node(input_node), input_id, element)); + oaknode_c_api::to_native(output_node), + olive::NodeInput(oaknode_c_api::to_native(input_node), + input_id, element)); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_node_disconnect_element(OakNodeNode *input_node, +int oaknode_node_disconnect_element(OakNodeNode input_node, const char *input_id, int element) { - if (!input_node) { + if (!input_node.ctx) { return OAKNODE_E_INVALID; } - if (!has_input(to_node(input_node), input_id)) { + if (!has_input(oaknode_c_api::to_native(input_node), + input_id)) { return OAKNODE_E_NOT_FOUND; } try { - olive::Node *output = to_node(input_node)->get_connected_output( - input_id, element); + olive::Node *output = + oaknode_c_api::to_native(input_node) + ->get_connected_output(input_id, element); if (!output) { return OAKNODE_E_NOT_FOUND; } olive::Node::disconnect_edge( - output, olive::NodeInput(to_node(input_node), input_id, element)); + output, + olive::NodeInput(oaknode_c_api::to_native(input_node), + input_id, element)); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -OakUndoCommand oaknode_command_create_add_node(OakNodeProject *graph, - OakNodeNode *node) +OakUndoCommand oaknode_command_create_add_node(OakNodeProject graph, + OakNodeNode node) { - if (!graph || !node) { + if (!graph.ctx || !node.ctx) { return OakUndoCommand{}; } try { return oaknode_c_api::wrap_command( new olive::NodeAddCommand( - reinterpret_cast(graph), to_node(node))); + oaknode_c_api::to_native(graph), + oaknode_c_api::to_native(node))); } catch (...) { return OakUndoCommand{}; } } OakUndoCommand oaknode_command_create_set_position_recursive( - OakNodeNode *node, OakNodeNode *context, double x, double y) + OakNodeNode node, OakNodeNode context, double x, double y) { - if (!node || !context) { + if (!node.ctx || !context.ctx) { return OakUndoCommand{}; } try { return oaknode_c_api::wrap_command( new olive::NodeSetPositionAndDependenciesRecursivelyCommand( - to_node(node), to_node(context), + oaknode_c_api::to_native(node), + oaknode_c_api::to_native(context), olive::Node::Position(olive::PointF(x, y)))); } catch (...) { return OakUndoCommand{}; } } -int oaknode_node_get_markers(const OakNodeNode *node, - OakNodeMarkerList **out) +int oaknode_node_get_markers(OakNodeNode node, OakNodeMarkerList **out) { - if (!node || !out) { + if (!node.ctx || !out) { return OAKNODE_E_INVALID; } - const olive::Node *n = to_node(node); + const olive::Node *n = oaknode_c_api::to_native(node); if (auto *v = dynamic_cast(n)) { *out = reinterpret_cast(v->get_markers()); } else { @@ -1220,14 +1294,13 @@ int oaknode_node_get_markers(const OakNodeNode *node, return OAKNODE_OK; } -int oaknode_node_get_work_area(const OakNodeNode *node, - OakNodeWorkArea **out) +int oaknode_node_get_work_area(OakNodeNode node, OakNodeWorkArea **out) { - if (!node || !out) { + if (!node.ctx || !out) { return OAKNODE_E_INVALID; } - const olive::Node *n = to_node(node); + const olive::Node *n = oaknode_c_api::to_native(node); if (auto *v = dynamic_cast(n)) { *out = reinterpret_cast(v->get_work_area()); } else { @@ -1236,26 +1309,27 @@ int oaknode_node_get_work_area(const OakNodeNode *node, return OAKNODE_OK; } -int oaknode_node_get_video_frame_cache(const OakNodeNode *node, +int oaknode_node_get_video_frame_cache(OakNodeNode node, OakNodeFrameCache **out) { - if (!node || !out) { + if (!node.ctx || !out) { return OAKNODE_E_INVALID; } *out = reinterpret_cast( - to_node(node)->video_frame_cache()); + oaknode_c_api::to_native(node)->video_frame_cache()); return OAKNODE_OK; } -int oaknode_node_copy_inputs(OakNodeNode *dst, const OakNodeNode *src, +int oaknode_node_copy_inputs(OakNodeNode dst, OakNodeNode src, int include_connections) { - if (!dst || !src) { + if (!dst.ctx || !src.ctx) { return OAKNODE_E_INVALID; } try { - olive::Node::copy_inputs(to_node(src), to_node(dst), + olive::Node::copy_inputs(oaknode_c_api::to_native(src), + oaknode_c_api::to_native(dst), include_connections != 0); return OAKNODE_OK; } catch (...) { @@ -1263,16 +1337,15 @@ int oaknode_node_copy_inputs(OakNodeNode *dst, const OakNodeNode *src, } } -int oaknode_node_set_value_hint_track(OakNodeNode *node, - const char *input_id, +int oaknode_node_set_value_hint_track(OakNodeNode node, const char *input_id, int track_type, int track_index) { - if (!node || !input_id) { + if (!node.ctx || !input_id) { return OAKNODE_E_INVALID; } try { - to_node(node)->set_value_hint_for_input( + oaknode_c_api::to_native(node)->set_value_hint_for_input( input_id, olive::Node::ValueHint({ olive::NodeValue::k_texture }, olive::Track::Reference( @@ -1286,10 +1359,10 @@ int oaknode_node_set_value_hint_track(OakNodeNode *node, } } -int oaknode_viewer_set_video_params(OakNodeNode *viewer, +int oaknode_viewer_set_video_params(OakNodeNode viewer, const OakVideoParams *params) { - if (!viewer || !params || !params->ctx) { + if (!viewer.ctx || !params || !params->ctx) { return OAKNODE_E_INVALID; } @@ -1299,8 +1372,8 @@ int oaknode_viewer_set_video_params(OakNodeNode *viewer, return OAKNODE_E_INVALID; } - olive::ViewerOutput *v = - dynamic_cast(to_node(viewer)); + olive::ViewerOutput *v = dynamic_cast( + oaknode_c_api::to_native(viewer)); if (!v) { return OAKNODE_E_INVALID; } @@ -1313,15 +1386,15 @@ int oaknode_viewer_set_video_params(OakNodeNode *viewer, } } -int oaknode_viewer_set_audio_params(OakNodeNode *viewer, +int oaknode_viewer_set_audio_params(OakNodeNode viewer, const OakAudioParams *params) { - if (!viewer || !params) { + if (!viewer.ctx || !params) { return OAKNODE_E_INVALID; } - olive::ViewerOutput *v = - dynamic_cast(to_node(viewer)); + olive::ViewerOutput *v = dynamic_cast( + oaknode_c_api::to_native(viewer)); if (!v) { return OAKNODE_E_INVALID; } @@ -1335,19 +1408,21 @@ int oaknode_viewer_set_audio_params(OakNodeNode *viewer, } } -int oaknode_node_find_input_footage(const OakNodeNode *node, - OakNodeFootage **out) +int oaknode_node_find_input_footage(OakNodeNode node, OakNodeFootage *out) { - if (!node || !out) { + if (!node.ctx || !out) { return OAKNODE_E_INVALID; } - *out = NULL; + *out = OakNodeFootage{}; try { std::vector found = - to_node(node)->find_input_nodes(); + oaknode_c_api::to_native(node) + ->find_input_nodes(); if (!found.empty()) { - *out = reinterpret_cast(found.front()); + *out = oaknode_c_api::make_handle( + found.front(), false, + &oaknode_c_api::delete_as); } return OAKNODE_OK; } catch (...) { diff --git a/src/node/c_api/nodehandle.h b/src/node/c_api/nodehandle.h new file mode 100644 index 000000000..685ccc85d --- /dev/null +++ b/src/node/c_api/nodehandle.h @@ -0,0 +1,180 @@ +/*** + + Oak Video Editor - Non-Linear Video Editor + Copyright (C) 2026 Oak Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + the GNU General Public License. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef OAK_EDITOR_NODE_NODEHANDLE_H +#define OAK_EDITOR_NODE_NODEHANDLE_H + +#include +#include + +#include "node/error.h" + +#include "alivecount.h" + +/** + * @brief Internal control block behind every OakNode* value handle, + * shared between the c_api translation units. + * + * All oaknode public handle structs have the identical layout + * (ctx/addref/release/abi_version), so a single generic box and + * addref/release pair serves every family; `deleter` knows the + * concrete C++ type to destroy. + * + * `owns` is true for objects created through init/create/factory + * functions (releasing the last reference destroys the object through + * `deleter`) and false for references into library-owned graphs + * (children, tracks in a track list, nodes in a project): releasing + * those only destroys the box. Functions that insert an owned object + * into a graph (oaknode_project_add_node(), oaknode_tracklist_add_track(), + * the track block operations, ...) flip `owns` to false through + * mark_container_owned() once the graph assumes the lifetime. + * + * Live-object accounting: make_handle() counts every owned handle it + * creates (alive_inc) and handle_release() un-counts an owned object + * right after destroying it (alive_dec), keeping + * oaknode_debug_alive_count() meaningful for leak checking. + */ +struct OakNodeBox { + void *object; + bool owns; + std::atomic refs; + void (*deleter)(void *object); + + OakNodeBox(void *o, bool own, void (*del)(void *)) + : object(o) + , owns(own) + , refs(1) + , deleter(del) + { + } +}; + +namespace oaknode_c_api +{ + +inline void handle_addref(void *ctx) +{ + if (ctx) { + static_cast(ctx)->refs.fetch_add(1); + } +} + +inline void handle_release(void *ctx) +{ + if (!ctx) { + return; + } + OakNodeBox *box = static_cast(ctx); + if (box->refs.fetch_sub(1) == 1) { + if (box->owns) { + box->deleter(box->object); + alive_dec(); + } + delete box; + } +} + +/** + * @brief Wrap `object` in a value handle with reference count 1. + * + * `owns` selects whether the final release destroys the object through + * `deleter`. Returns an empty handle (ctx == nullptr) for a null + * object or on allocation failure (an owned object is destroyed via + * `deleter` in the latter case). + */ +template +inline Handle make_handle(void *object, bool owns, void (*deleter)(void *)) +{ + Handle handle = {}; + if (!object) { + return handle; + } + + OakNodeBox *box = new (std::nothrow) OakNodeBox(object, owns, deleter); + if (!box) { + if (owns && deleter) { + deleter(object); + } + return handle; + } + + if (owns) { + alive_inc(); + } + + handle.ctx = box; + handle.addref = handle_addref; + handle.release = handle_release; + handle.abi_version = OAKNODE_ABI_VERSION; + return handle; +} + +/** + * @brief Unwrap a value handle to the native C++ pointer (nullptr for + * an empty handle). + */ +template +inline T *to_native(Handle h) +{ + if (!h.ctx) { + return nullptr; + } + return static_cast(static_cast(h.ctx)->object); +} + +/** + * @brief Mark a handle's object as owned by a container (graph), so + * releasing the handle no longer destroys the object. + */ +template +inline void mark_container_owned(Handle h) +{ + if (h.ctx) { + static_cast(h.ctx)->owns = false; + } +} + +/** + * @brief Deleter callback stamping the concrete C++ type. + */ +template +inline void delete_as(void *object) +{ + delete static_cast(object); +} + +/** + * @brief Generic free() implementation for every oaknode family: + * release the caller's reference and null out the handle. NULL and + * ctx == NULL are no-ops. + */ +template +inline void free_handle(Handle *h) +{ + if (!h || !h->ctx) { + return; + } + h->release(h->ctx); + h->ctx = nullptr; +} + +} // namespace oaknode_c_api + +#endif // OAK_EDITOR_NODE_NODEHANDLE_H diff --git a/src/node/c_api/project.cpp b/src/node/c_api/project.cpp index 3d73d42b1..076cd8b7e 100644 --- a/src/node/c_api/project.cpp +++ b/src/node/c_api/project.cpp @@ -25,31 +25,17 @@ #include #include +#include "node/node.h" +#include "node/folder.h" + #include "../src/project.h" +#include "../src/project/folder/folder.h" + +#include "nodehandle.h" namespace { -olive::Project *to_cpp(OakNodeProject *project) -{ - return reinterpret_cast(project); -} - -const olive::Project *to_cpp(const OakNodeProject *project) -{ - return reinterpret_cast(project); -} - -olive::Node *to_cpp(OakNodeNode *node) -{ - return reinterpret_cast(node); -} - -OakNodeNode *to_c(olive::Node *node) -{ - return reinterpret_cast(node); -} - /** * @brief Shared two-stage string getter. * @@ -74,189 +60,202 @@ int copy_string(const std::string &value, char *buf, int buf_size) } // namespace -OakNodeProject *oaknode_project_init(void) +using oaknode_c_api::delete_as; +using oaknode_c_api::make_handle; +using oaknode_c_api::to_native; + +OakNodeProject oaknode_project_init(void) { try { - return reinterpret_cast( - new (std::nothrow) olive::Project()); + return make_handle(new (std::nothrow) olive::Project(), + true, + &delete_as); } catch (...) { - return NULL; + return OakNodeProject{}; } } void oaknode_project_free(OakNodeProject *project) { - delete to_cpp(project); + oaknode_c_api::free_handle(project); } -int oaknode_project_initialize(OakNodeProject *project) +int oaknode_project_initialize(OakNodeProject project) { - if (!project) { + if (!project.ctx) { return OAKNODE_E_INVALID; } try { - if (to_cpp(project)->root()) { + olive::Project *p = to_native(project); + if (p->root()) { return OAKNODE_E_STATE; } - to_cpp(project)->initialize(); + p->initialize(); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_project_clear(OakNodeProject *project) +int oaknode_project_clear(OakNodeProject project) { - if (!project) { + if (!project.ctx) { return OAKNODE_E_INVALID; } try { - to_cpp(project)->clear(); + to_native(project)->clear(); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -OakNodeFolder *oaknode_project_root(OakNodeProject *project) +OakNodeFolder oaknode_project_root(OakNodeProject project) { - if (!project) { - return NULL; + if (!project.ctx) { + return OakNodeFolder{}; } try { - return reinterpret_cast(to_cpp(project)->root()); + return make_handle( + to_native(project)->root(), false, + &delete_as); } catch (...) { - return NULL; + return OakNodeFolder{}; } } -int oaknode_project_name(const OakNodeProject *project, char *buf, int buf_size) +int oaknode_project_name(OakNodeProject project, char *buf, int buf_size) { - if (!project) { + if (!project.ctx) { return OAKNODE_E_INVALID; } try { - return copy_string(to_cpp(project)->name(), buf, buf_size); + return copy_string(to_native(project)->name(), buf, + buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_project_filename(const OakNodeProject *project, char *buf, +int oaknode_project_filename(OakNodeProject project, char *buf, int buf_size) { - if (!project) { + if (!project.ctx) { return OAKNODE_E_INVALID; } try { - return copy_string(to_cpp(project)->filename(), buf, buf_size); + return copy_string(to_native(project)->filename(), buf, + buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_project_pretty_filename(const OakNodeProject *project, char *buf, +int oaknode_project_pretty_filename(OakNodeProject project, char *buf, int buf_size) { - if (!project) { + if (!project.ctx) { return OAKNODE_E_INVALID; } try { - return copy_string(to_cpp(project)->pretty_filename(), buf, buf_size); + return copy_string(to_native(project)->pretty_filename(), + buf, buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_project_set_filename(OakNodeProject *project, const char *filename) +int oaknode_project_set_filename(OakNodeProject project, const char *filename) { - if (!project || !filename) { + if (!project.ctx || !filename) { return OAKNODE_E_INVALID; } try { - to_cpp(project)->set_filename(filename); + to_native(project)->set_filename(filename); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_project_is_modified(const OakNodeProject *project) +int oaknode_project_is_modified(OakNodeProject project) { - if (!project) { + if (!project.ctx) { return OAKNODE_E_INVALID; } - return to_cpp(project)->is_modified() ? 1 : 0; + return to_native(project)->is_modified() ? 1 : 0; } -int oaknode_project_set_modified(OakNodeProject *project, int modified) +int oaknode_project_set_modified(OakNodeProject project, int modified) { - if (!project) { + if (!project.ctx) { return OAKNODE_E_INVALID; } try { - to_cpp(project)->set_modified(modified != 0); + to_native(project)->set_modified(modified != 0); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_project_is_new(const OakNodeProject *project) +int oaknode_project_is_new(OakNodeProject project) { - if (!project) { + if (!project.ctx) { return OAKNODE_E_INVALID; } - return to_cpp(project)->is_new() ? 1 : 0; + return to_native(project)->is_new() ? 1 : 0; } -int oaknode_project_cache_path(const OakNodeProject *project, char *buf, +int oaknode_project_cache_path(OakNodeProject project, char *buf, int buf_size) { - if (!project) { + if (!project.ctx) { return OAKNODE_E_INVALID; } try { - return copy_string(to_cpp(project)->cache_path(), buf, buf_size); + return copy_string(to_native(project)->cache_path(), + buf, buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_project_get_cache_location_setting(const OakNodeProject *project) +int oaknode_project_get_cache_location_setting(OakNodeProject project) { - if (!project) { + if (!project.ctx) { return OAKNODE_E_INVALID; } try { - return static_cast(to_cpp(project)->get_cache_location_setting()); + return static_cast( + to_native(project)->get_cache_location_setting()); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_project_set_cache_location_setting(OakNodeProject *project, +int oaknode_project_set_cache_location_setting(OakNodeProject project, int setting) { - if (!project || setting < 0 || + if (!project.ctx || setting < 0 || setting > static_cast(olive::Project::k_cache_custom_path)) { return OAKNODE_E_INVALID; } try { - to_cpp(project)->set_cache_location_setting( + to_native(project)->set_cache_location_setting( static_cast(setting)); return OAKNODE_OK; } catch (...) { @@ -264,77 +263,85 @@ int oaknode_project_set_cache_location_setting(OakNodeProject *project, } } -int oaknode_project_get_custom_cache_path(const OakNodeProject *project, +int oaknode_project_get_custom_cache_path(OakNodeProject project, char *buf, int buf_size) { - if (!project) { + if (!project.ctx) { return OAKNODE_E_INVALID; } try { - return copy_string(to_cpp(project)->get_custom_cache_path(), buf, + return copy_string( + to_native(project)->get_custom_cache_path(), buf, + buf_size); + } catch (...) { + return OAKNODE_E_FAILED; + } +} + +int oaknode_project_set_custom_cache_path(OakNodeProject project, + const char *path) +{ + if (!project.ctx) { + return OAKNODE_E_INVALID; + } + + try { + to_native(project)->set_custom_cache_path( + path ? path : ""); + return OAKNODE_OK; + } catch (...) { + return OAKNODE_E_FAILED; + } +} + +int oaknode_project_get_uuid(OakNodeProject project, char *buf, + int buf_size) +{ + if (!project.ctx) { + return OAKNODE_E_INVALID; + } + + try { + return copy_string(to_native(project)->get_uuid(), buf, buf_size); } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_project_set_custom_cache_path(OakNodeProject *project, - const char *path) +int oaknode_project_add_node(OakNodeProject project, OakNodeNode node) { - if (!project) { + if (!project.ctx || !node.ctx) { return OAKNODE_E_INVALID; } try { - to_cpp(project)->set_custom_cache_path(path ? path : ""); + to_native(project)->add_node( + to_native(node)); + // The graph now owns the node; releasing `node` must not delete it. + oaknode_c_api::mark_container_owned(node); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_project_get_uuid(const OakNodeProject *project, char *buf, - int buf_size) +int oaknode_project_remove_node(OakNodeProject project, OakNodeNode node) { - if (!project) { + if (!project.ctx || !node.ctx) { return OAKNODE_E_INVALID; } try { - return copy_string(to_cpp(project)->get_uuid(), buf, buf_size); - } catch (...) { - return OAKNODE_E_FAILED; - } -} - -int oaknode_project_add_node(OakNodeProject *project, OakNodeNode *node) -{ - if (!project || !node) { - return OAKNODE_E_INVALID; - } - - try { - to_cpp(project)->add_node(to_cpp(node)); - return OAKNODE_OK; - } catch (...) { - return OAKNODE_E_FAILED; - } -} - -int oaknode_project_remove_node(OakNodeProject *project, OakNodeNode *node) -{ - if (!project || !node) { - return OAKNODE_E_INVALID; - } - - try { - olive::Project *p = to_cpp(project); - olive::Node *n = to_cpp(node); + olive::Project *p = to_native(project); + olive::Node *n = to_native(node); const auto &nodes = p->nodes(); if (std::find(nodes.begin(), nodes.end(), n) == nodes.end()) { return OAKNODE_E_NOT_FOUND; } + // Detach without deleting (legacy semantics); the caller's handle + // keeps its current ownership state. p->remove_node(n); return OAKNODE_OK; } catch (...) { @@ -342,45 +349,48 @@ int oaknode_project_remove_node(OakNodeProject *project, OakNodeNode *node) } } -int oaknode_project_node_count(const OakNodeProject *project) +int oaknode_project_node_count(OakNodeProject project) { - if (!project) { + if (!project.ctx) { return OAKNODE_E_INVALID; } try { - return static_cast(to_cpp(project)->nodes().size()); + return static_cast( + to_native(project)->nodes().size()); } catch (...) { return OAKNODE_E_FAILED; } } -OakNodeNode *oaknode_project_node_at(const OakNodeProject *project, int index) +OakNodeNode oaknode_project_node_at(OakNodeProject project, int index) { - if (!project || index < 0) { - return NULL; + if (!project.ctx || index < 0) { + return OakNodeNode{}; } try { - const auto &nodes = to_cpp(project)->nodes(); + const auto &nodes = to_native(project)->nodes(); if (static_cast(index) >= nodes.size()) { - return NULL; + return OakNodeNode{}; } - return to_c(nodes[static_cast(index)]); + return make_handle(nodes[static_cast(index)], + false, &delete_as); } catch (...) { - return NULL; + return OakNodeNode{}; } } -int oaknode_project_copy_settings(OakNodeProject *dst, - const OakNodeProject *src) +int oaknode_project_copy_settings(OakNodeProject dst, + OakNodeProject src) { - if (!dst || !src) { + if (!dst.ctx || !src.ctx) { return OAKNODE_E_INVALID; } try { - olive::Project::copy_settings(const_cast(to_cpp(src)), to_cpp(dst)); + olive::Project::copy_settings(to_native(src), + to_native(dst)); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; diff --git a/src/node/c_api/sequence.cpp b/src/node/c_api/sequence.cpp index 49392372d..5b3b27873 100644 --- a/src/node/c_api/sequence.cpp +++ b/src/node/c_api/sequence.cpp @@ -20,7 +20,7 @@ #include "node/sequence.h" -#include "alivecount.h" +#include "node/node.h" #include "node/track.h" #include "globals.h" @@ -28,14 +28,16 @@ #include "project/sequence/sequence.h" #include "videoparams.h" +#include "nodehandle.h" + +using oaknode_c_api::delete_as; +using oaknode_c_api::free_handle; +using oaknode_c_api::make_handle; +using oaknode_c_api::to_native; + namespace { -olive::Sequence *impl(OakNodeSequence *h) -{ - return reinterpret_cast(h); -} - int get_rational(const olive::core::Rational &r, int *numerator, int *denominator) { @@ -54,158 +56,165 @@ bool valid_track_type(int type) } // namespace -OakNodeSequence *oaknode_sequence_create(void) +OakNodeSequence oaknode_sequence_create(void) { try { - olive::Sequence *s = new olive::Sequence(); - oaknode_c_api::alive_inc(); - return reinterpret_cast(s); + return make_handle(new olive::Sequence(), true, + &delete_as); } catch (...) { - return nullptr; + return OakNodeSequence{}; } } void oaknode_sequence_free(OakNodeSequence *sequence) { - if (!sequence) { - return; - } - olive::Sequence *s = impl(sequence); - // ~Sequence() deletes the owned TrackLists - delete s; - oaknode_c_api::alive_dec(); + // The final release runs ~Sequence(), which deletes the owned TrackLists + free_handle(sequence); } -int oaknode_sequence_get_track_list(OakNodeSequence *sequence, int type, - OakNodeTrackList **out) +int oaknode_sequence_get_track_list(OakNodeSequence sequence, int type, + OakNodeTrackList *out) { - if (!sequence || !out) { + olive::Sequence *s = to_native(sequence); + if (!s || !out) { return OAKNODE_E_INVALID; } if (!valid_track_type(type)) { return OAKNODE_E_NOT_FOUND; } - *out = reinterpret_cast( - impl(sequence)->track_list(static_cast(type))); - return OAKNODE_OK; + // Borrowed; the track list stays owned by the sequence + *out = make_handle( + s->track_list(static_cast(type)), false, + &delete_as); + return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM; } -int oaknode_sequence_get_track_count(OakNodeSequence *sequence, int type, +int oaknode_sequence_get_track_count(OakNodeSequence sequence, int type, int *count) { - if (!sequence || !count) { + olive::Sequence *s = to_native(sequence); + if (!s || !count) { return OAKNODE_E_INVALID; } if (!valid_track_type(type)) { return OAKNODE_E_NOT_FOUND; } - *count = impl(sequence) - ->track_list(static_cast(type)) + *count = s->track_list(static_cast(type)) ->get_track_count(); return OAKNODE_OK; } -int oaknode_sequence_get_track_at(OakNodeSequence *sequence, int type, - int index, OakNodeTrack **out) +int oaknode_sequence_get_track_at(OakNodeSequence sequence, int type, + int index, OakNodeTrack *out) { - if (!sequence || !out || index < 0) { + olive::Sequence *s = to_native(sequence); + if (!s || !out || index < 0) { return OAKNODE_E_INVALID; } if (!valid_track_type(type)) { return OAKNODE_E_NOT_FOUND; } olive::TrackList *list = - impl(sequence)->track_list(static_cast(type)); + s->track_list(static_cast(type)); if (index >= list->get_track_count()) { return OAKNODE_E_NOT_FOUND; } - *out = reinterpret_cast(list->get_track_at(index)); + // Borrowed; the track stays owned by the list + *out = make_handle(list->get_track_at(index), false, + &delete_as); + return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM; +} + +int oaknode_sequence_get_all_track_count(OakNodeSequence sequence, int *count) +{ + olive::Sequence *s = to_native(sequence); + if (!s || !count) { + return OAKNODE_E_INVALID; + } + *count = int(s->get_tracks().size()); return OAKNODE_OK; } -int oaknode_sequence_get_all_track_count(OakNodeSequence *sequence, int *count) +int oaknode_sequence_get_all_track_at(OakNodeSequence sequence, int index, + OakNodeTrack *out) { - if (!sequence || !count) { + olive::Sequence *s = to_native(sequence); + if (!s || !out || index < 0) { return OAKNODE_E_INVALID; } - *count = int(impl(sequence)->get_tracks().size()); - return OAKNODE_OK; -} - -int oaknode_sequence_get_all_track_at(OakNodeSequence *sequence, int index, - OakNodeTrack **out) -{ - if (!sequence || !out || index < 0) { - return OAKNODE_E_INVALID; - } - const auto &tracks = impl(sequence)->get_tracks(); + const auto &tracks = s->get_tracks(); if (index >= int(tracks.size())) { return OAKNODE_E_NOT_FOUND; } - *out = reinterpret_cast(tracks.at(index)); - return OAKNODE_OK; + // Borrowed; the track stays owned by its list + *out = make_handle(tracks.at(index), false, + &delete_as); + return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM; } -int oaknode_sequence_get_playhead(OakNodeSequence *sequence, int *numerator, +int oaknode_sequence_get_playhead(OakNodeSequence sequence, int *numerator, int *denominator) { - if (!sequence) { + olive::Sequence *s = to_native(sequence); + if (!s) { return OAKNODE_E_INVALID; } - return get_rational(impl(sequence)->get_playhead(), numerator, denominator); + return get_rational(s->get_playhead(), numerator, denominator); } -int oaknode_sequence_set_playhead(OakNodeSequence *sequence, int numerator, +int oaknode_sequence_set_playhead(OakNodeSequence sequence, int numerator, int denominator) { - if (!sequence) { + olive::Sequence *s = to_native(sequence); + if (!s) { return OAKNODE_E_INVALID; } try { - impl(sequence)->set_playhead(olive::core::Rational(numerator, - denominator)); + s->set_playhead(olive::core::Rational(numerator, denominator)); } catch (...) { return OAKNODE_E_FAILED; } return OAKNODE_OK; } -int oaknode_sequence_get_length(OakNodeSequence *sequence, int *numerator, +int oaknode_sequence_get_length(OakNodeSequence sequence, int *numerator, int *denominator) { - if (!sequence) { + olive::Sequence *s = to_native(sequence); + if (!s) { return OAKNODE_E_INVALID; } - return get_rational(impl(sequence)->get_length(), numerator, denominator); + return get_rational(s->get_length(), numerator, denominator); } -int oaknode_sequence_get_video_length(OakNodeSequence *sequence, int *numerator, - int *denominator) +int oaknode_sequence_get_video_length(OakNodeSequence sequence, + int *numerator, int *denominator) { - if (!sequence) { + olive::Sequence *s = to_native(sequence); + if (!s) { return OAKNODE_E_INVALID; } - return get_rational(impl(sequence)->get_video_length(), numerator, - denominator); + return get_rational(s->get_video_length(), numerator, denominator); } -int oaknode_sequence_get_audio_length(OakNodeSequence *sequence, int *numerator, - int *denominator) +int oaknode_sequence_get_audio_length(OakNodeSequence sequence, + int *numerator, int *denominator) { - if (!sequence) { + olive::Sequence *s = to_native(sequence); + if (!s) { return OAKNODE_E_INVALID; } - return get_rational(impl(sequence)->get_audio_length(), numerator, - denominator); + return get_rational(s->get_audio_length(), numerator, denominator); } -int oaknode_sequence_verify_length(OakNodeSequence *sequence) +int oaknode_sequence_verify_length(OakNodeSequence sequence) { - if (!sequence) { + olive::Sequence *s = to_native(sequence); + if (!s) { return OAKNODE_E_INVALID; } try { - impl(sequence)->verify_length(); + s->verify_length(); } catch (...) { return OAKNODE_E_FAILED; } @@ -214,38 +223,40 @@ int oaknode_sequence_verify_length(OakNodeSequence *sequence) /* --------------------------------------------------- Video/audio params */ -int oaknode_sequence_get_video_stream_count(OakNodeSequence *sequence, +int oaknode_sequence_get_video_stream_count(OakNodeSequence sequence, int *count) { - if (!sequence || !count) { + olive::Sequence *s = to_native(sequence); + if (!s || !count) { return OAKNODE_E_INVALID; } - *count = impl(sequence)->get_video_stream_count(); + *count = s->get_video_stream_count(); return OAKNODE_OK; } -int oaknode_sequence_get_audio_stream_count(OakNodeSequence *sequence, +int oaknode_sequence_get_audio_stream_count(OakNodeSequence sequence, int *count) { - if (!sequence || !count) { + olive::Sequence *s = to_native(sequence); + if (!s || !count) { return OAKNODE_E_INVALID; } - *count = impl(sequence)->get_audio_stream_count(); + *count = s->get_audio_stream_count(); return OAKNODE_OK; } -int oaknode_sequence_get_video_params(OakNodeSequence *sequence, int index, +int oaknode_sequence_get_video_params(OakNodeSequence sequence, int index, OakVideoParams *out) { - if (!sequence || !out || index < 0) { + olive::Sequence *s = to_native(sequence); + if (!s || !out || index < 0) { return OAKNODE_E_INVALID; } - if (index >= impl(sequence)->get_video_stream_count()) { + if (index >= s->get_video_stream_count()) { return OAKNODE_E_NOT_FOUND; } try { - const olive::VideoParams params = - impl(sequence)->get_video_params(index); + const olive::VideoParams params = s->get_video_params(index); *out = oakcommon_videoparams_init_from_native(¶ms); } catch (...) { return OAKNODE_E_NOMEM; @@ -256,10 +267,11 @@ int oaknode_sequence_get_video_params(OakNodeSequence *sequence, int index, return OAKNODE_OK; } -int oaknode_sequence_set_video_params(OakNodeSequence *sequence, int index, +int oaknode_sequence_set_video_params(OakNodeSequence sequence, int index, OakVideoParams params) { - if (!sequence || index < 0) { + olive::Sequence *s = to_native(sequence); + if (!s || index < 0) { return OAKNODE_E_INVALID; } const olive::VideoParams *native = @@ -267,28 +279,29 @@ int oaknode_sequence_set_video_params(OakNodeSequence *sequence, int index, if (!native) { return OAKNODE_E_INVALID; } - if (index >= impl(sequence)->get_video_stream_count()) { + if (index >= s->get_video_stream_count()) { return OAKNODE_E_NOT_FOUND; } try { - impl(sequence)->set_video_params(*native, index); + s->set_video_params(*native, index); } catch (...) { return OAKNODE_E_FAILED; } return OAKNODE_OK; } -int oaknode_sequence_get_audio_params(OakNodeSequence *sequence, int index, +int oaknode_sequence_get_audio_params(OakNodeSequence sequence, int index, OakAudioParams **out) { - if (!sequence || !out || index < 0) { + olive::Sequence *s = to_native(sequence); + if (!s || !out || index < 0) { return OAKNODE_E_INVALID; } - if (index >= impl(sequence)->get_audio_stream_count()) { + if (index >= s->get_audio_stream_count()) { return OAKNODE_E_NOT_FOUND; } OakAudioParams *copy = - oakcore_audioparams_copy(impl(sequence)->get_audio_params(index).handle()); + oakcore_audioparams_copy(s->get_audio_params(index).handle()); if (!copy) { return OAKNODE_E_NOMEM; } @@ -296,13 +309,14 @@ int oaknode_sequence_get_audio_params(OakNodeSequence *sequence, int index, return OAKNODE_OK; } -int oaknode_sequence_set_audio_params(OakNodeSequence *sequence, int index, +int oaknode_sequence_set_audio_params(OakNodeSequence sequence, int index, const OakAudioParams *params) { - if (!sequence || !params || index < 0) { + olive::Sequence *s = to_native(sequence); + if (!s || !params || index < 0) { return OAKNODE_E_INVALID; } - if (index >= impl(sequence)->get_audio_stream_count()) { + if (index >= s->get_audio_stream_count()) { return OAKNODE_E_NOT_FOUND; } OakAudioParams *copy = oakcore_audioparams_copy(params); @@ -310,8 +324,7 @@ int oaknode_sequence_set_audio_params(OakNodeSequence *sequence, int index, return OAKNODE_E_NOMEM; } try { - impl(sequence)->set_audio_params( - olive::core::AudioParams::from_handle(copy), index); + s->set_audio_params(olive::core::AudioParams::from_handle(copy), index); } catch (...) { oakcore_audioparams_free(copy); return OAKNODE_E_FAILED; @@ -319,19 +332,22 @@ int oaknode_sequence_set_audio_params(OakNodeSequence *sequence, int index, return OAKNODE_OK; } -OakNodeNode *oaknode_sequence_as_node(OakNodeSequence *sequence) +OakNodeNode oaknode_sequence_as_node(OakNodeSequence sequence) { - return reinterpret_cast(sequence); + // Borrowed; releasing the result never destroys the sequence + return make_handle(to_native(sequence), + false, &delete_as); } -int oaknode_sequence_set_default_parameters(OakNodeSequence *sequence) +int oaknode_sequence_set_default_parameters(OakNodeSequence sequence) { - if (!sequence) { + olive::Sequence *s = to_native(sequence); + if (!s) { return OAKNODE_E_INVALID; } try { - impl(sequence)->set_default_parameters(); + s->set_default_parameters(); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; diff --git a/src/node/c_api/serializer.cpp b/src/node/c_api/serializer.cpp index 03cf8d208..be3ef5558 100644 --- a/src/node/c_api/serializer.cpp +++ b/src/node/c_api/serializer.cpp @@ -30,40 +30,13 @@ #include "../src/project/serializer/serializer.h" #include "xmlutils.h" -struct OakNodeSerializerSaveData { - olive::ProjectSerializer::SaveData impl; - - OakNodeSerializerSaveData(olive::ProjectSerializer::LoadType type, - olive::Project *project) - : impl(type, project) - { - } -}; - -struct OakNodeSerializerLoadData { - olive::ProjectSerializer::LoadData impl; -}; +#include "nodehandle.h" namespace { bool g_initialized = false; -olive::Project *to_cpp(OakNodeProject *project) -{ - return reinterpret_cast(project); -} - -olive::Node *to_cpp(OakNodeNode *node) -{ - return reinterpret_cast(node); -} - -OakNodeNode *to_c(olive::Node *node) -{ - return reinterpret_cast(node); -} - bool is_valid_load_type(int load_type) { return load_type >= static_cast(olive::ProjectSerializer::k_project) && @@ -126,31 +99,36 @@ void oaknode_serializer_shutdown(void) g_initialized = false; } -OakNodeSerializerSaveData *oaknode_serializer_savedata_create( - int load_type, OakNodeProject *project) +OakNodeSerializerSaveData oaknode_serializer_savedata_create( + int load_type, OakNodeProject project) { if (!is_valid_load_type(load_type)) { - return NULL; + return OakNodeSerializerSaveData{}; } try { - return new (std::nothrow) OakNodeSerializerSaveData( - static_cast(load_type), - to_cpp(project)); + return oaknode_c_api::make_handle( + new olive::ProjectSerializer::SaveData( + static_cast(load_type), + oaknode_c_api::to_native(project)), + true, + &oaknode_c_api::delete_as); } catch (...) { - return NULL; + return OakNodeSerializerSaveData{}; } } void oaknode_serializer_savedata_free(OakNodeSerializerSaveData *save_data) { - delete save_data; + oaknode_c_api::free_handle(save_data); } int oaknode_serializer_savedata_set_nodes( - OakNodeSerializerSaveData *save_data, OakNodeNode *const *nodes, int count) + OakNodeSerializerSaveData save_data, const OakNodeNode *nodes, int count) { - if (!save_data || !nodes || count < 0) { + olive::ProjectSerializer::SaveData *sd = + oaknode_c_api::to_native(save_data); + if (!sd || !nodes || count < 0) { return OAKNODE_E_INVALID; } @@ -158,13 +136,14 @@ int oaknode_serializer_savedata_set_nodes( std::vector cpp_nodes; cpp_nodes.reserve(static_cast(count)); for (int i = 0; i < count; i++) { - if (!nodes[i]) { + olive::Node *node = oaknode_c_api::to_native(nodes[i]); + if (!node) { return OAKNODE_E_INVALID; } - cpp_nodes.push_back(to_cpp(nodes[i])); + cpp_nodes.push_back(node); } - save_data->impl.set_only_serialize_nodes(cpp_nodes); + sd->set_only_serialize_nodes(cpp_nodes); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; @@ -172,28 +151,33 @@ int oaknode_serializer_savedata_set_nodes( } int oaknode_serializer_savedata_set_property( - OakNodeSerializerSaveData *save_data, OakNodeNode *node, const char *key, + OakNodeSerializerSaveData save_data, OakNodeNode node, const char *key, const char *value) { - if (!save_data || !node || !key || !value) { + olive::ProjectSerializer::SaveData *sd = + oaknode_c_api::to_native(save_data); + olive::Node *native_node = oaknode_c_api::to_native(node); + if (!sd || !native_node || !key || !value) { return OAKNODE_E_INVALID; } try { olive::ProjectSerializer::SerializedProperties properties = - save_data->impl.get_properties(); - properties[to_cpp(node)][key] = value; - save_data->impl.set_properties(properties); + sd->get_properties(); + properties[native_node][key] = value; + sd->set_properties(properties); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData *save_data, +int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData save_data, char *buf, int buf_size) { - if (!save_data) { + olive::ProjectSerializer::SaveData *sd = + oaknode_c_api::to_native(save_data); + if (!sd) { return OAKNODE_E_INVALID; } if (!g_initialized) { @@ -203,7 +187,7 @@ int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData *save_data, try { olive::XmlStreamWriter writer; olive::ProjectSerializer::Result result = - olive::ProjectSerializer::save(&writer, save_data->impl); + olive::ProjectSerializer::save(&writer, *sd); if (result != olive::ProjectSerializer::k_success) { return OAKNODE_E_FAILED; } @@ -213,9 +197,9 @@ int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData *save_data, } } -int oaknode_serializer_load_from_xml(OakNodeProject *project, const char *xml, +int oaknode_serializer_load_from_xml(OakNodeProject project, const char *xml, int load_type, int *out_result, - OakNodeSerializerLoadData **out_load_data, + OakNodeSerializerLoadData *out_load_data, char *details_buf, int details_buf_size) { if (!xml || !out_result || !is_valid_load_type(load_type)) { @@ -226,13 +210,13 @@ int oaknode_serializer_load_from_xml(OakNodeProject *project, const char *xml, } if (out_load_data) { - *out_load_data = NULL; + *out_load_data = OakNodeSerializerLoadData{}; } try { olive::XmlStreamReader reader(xml); olive::ProjectSerializer::Result result = olive::ProjectSerializer::load( - to_cpp(project), &reader, + oaknode_c_api::to_native(project), &reader, static_cast(load_type)); *out_result = static_cast(result.code()); @@ -242,12 +226,20 @@ int oaknode_serializer_load_from_xml(OakNodeProject *project, const char *xml, } if (result == olive::ProjectSerializer::k_success && out_load_data) { - auto *load_data = new (std::nothrow) OakNodeSerializerLoadData(); + auto *load_data = + new (std::nothrow) olive::ProjectSerializer::LoadData(); if (!load_data) { return OAKNODE_E_NOMEM; } - load_data->impl = result.get_load_data(); - *out_load_data = load_data; + *load_data = result.get_load_data(); + *out_load_data = + oaknode_c_api::make_handle( + load_data, true, + &oaknode_c_api::delete_as< + olive::ProjectSerializer::LoadData>); + if (!out_load_data->ctx) { + return OAKNODE_E_NOMEM; + } } return OAKNODE_OK; @@ -258,49 +250,59 @@ int oaknode_serializer_load_from_xml(OakNodeProject *project, const char *xml, void oaknode_serializer_loaddata_free(OakNodeSerializerLoadData *load_data) { - delete load_data; + oaknode_c_api::free_handle(load_data); } int oaknode_serializer_loaddata_node_count( - const OakNodeSerializerLoadData *load_data) + OakNodeSerializerLoadData load_data) { - if (!load_data) { + olive::ProjectSerializer::LoadData *ld = + oaknode_c_api::to_native(load_data); + if (!ld) { return OAKNODE_E_INVALID; } try { - return static_cast(load_data->impl.nodes.size()); + return static_cast(ld->nodes.size()); } catch (...) { return OAKNODE_E_FAILED; } } -OakNodeNode *oaknode_serializer_loaddata_node_at( - const OakNodeSerializerLoadData *load_data, int index) +OakNodeNode oaknode_serializer_loaddata_node_at( + OakNodeSerializerLoadData load_data, int index) { - if (!load_data || index < 0 || - static_cast(index) >= load_data->impl.nodes.size()) { - return NULL; + olive::ProjectSerializer::LoadData *ld = + oaknode_c_api::to_native(load_data); + if (!ld || index < 0 || + static_cast(index) >= ld->nodes.size()) { + return OakNodeNode{}; } try { - return to_c(load_data->impl.nodes[static_cast(index)]); + // Borrowed: the node is owned by the caller only in the sense of + // the documented adoption contract; see oaknode_project_add_node(). + return oaknode_c_api::make_handle( + ld->nodes[static_cast(index)], false, nullptr); } catch (...) { - return NULL; + return OakNodeNode{}; } } int oaknode_serializer_loaddata_get_property( - const OakNodeSerializerLoadData *load_data, OakNodeNode *node, - const char *key, char *buf, int buf_size) + OakNodeSerializerLoadData load_data, OakNodeNode node, const char *key, + char *buf, int buf_size) { - if (!load_data || !node || !key) { + olive::ProjectSerializer::LoadData *ld = + oaknode_c_api::to_native(load_data); + olive::Node *native_node = oaknode_c_api::to_native(node); + if (!ld || !native_node || !key) { return OAKNODE_E_INVALID; } try { - auto node_it = load_data->impl.properties.find(to_cpp(node)); - if (node_it == load_data->impl.properties.end()) { + auto node_it = ld->properties.find(native_node); + if (node_it == ld->properties.end()) { return OAKNODE_E_NOT_FOUND; } auto key_it = node_it->second.find(key); @@ -314,37 +316,43 @@ int oaknode_serializer_loaddata_get_property( } int oaknode_serializer_loaddata_connection_count( - const OakNodeSerializerLoadData *load_data) + OakNodeSerializerLoadData load_data) { - if (!load_data) { + olive::ProjectSerializer::LoadData *ld = + oaknode_c_api::to_native(load_data); + if (!ld) { return OAKNODE_E_INVALID; } try { - return static_cast(load_data->impl.promised_connections.size()); + return static_cast(ld->promised_connections.size()); } catch (...) { return OAKNODE_E_FAILED; } } int oaknode_serializer_loaddata_connection_at( - const OakNodeSerializerLoadData *load_data, int index, - OakNodeNode **out_output_node, OakNodeNode **out_input_node, + OakNodeSerializerLoadData load_data, int index, + OakNodeNode *out_output_node, OakNodeNode *out_input_node, char *input_id_buf, int input_id_buf_size, int *out_element) { - if (!load_data || !out_output_node || !out_input_node || !out_element) { + olive::ProjectSerializer::LoadData *ld = + oaknode_c_api::to_native(load_data); + if (!ld || !out_output_node || !out_input_node || !out_element) { return OAKNODE_E_INVALID; } - if (index < 0 || static_cast(index) >= - load_data->impl.promised_connections.size()) { + if (index < 0 || + static_cast(index) >= ld->promised_connections.size()) { return OAKNODE_E_NOT_FOUND; } try { const olive::Node::OutputConnection &connection = - load_data->impl.promised_connections[static_cast(index)]; - *out_output_node = to_c(connection.first); - *out_input_node = to_c(connection.second.node()); + ld->promised_connections[static_cast(index)]; + *out_output_node = oaknode_c_api::make_handle( + connection.first, false, nullptr); + *out_input_node = oaknode_c_api::make_handle( + connection.second.node(), false, nullptr); if (input_id_buf && input_id_buf_size > 0) { copy_string(connection.second.input(), input_id_buf, input_id_buf_size); @@ -383,11 +391,12 @@ int report_serializer_result(const olive::ProjectSerializer::Result &result, } // namespace -int oaknode_serializer_save_to_file(OakNodeProject *project, +int oaknode_serializer_save_to_file(OakNodeProject project, const char *filename, int use_compression, int *out_code, char *details, int details_size) { - if (!project || !filename) { + olive::Project *native = oaknode_c_api::to_native(project); + if (!native || !filename) { return OAKNODE_E_INVALID; } @@ -395,8 +404,7 @@ int oaknode_serializer_save_to_file(OakNodeProject *project, oaknode_serializer_initialize(); olive::ProjectSerializer::SaveData data( - olive::ProjectSerializer::k_project, - reinterpret_cast(project), filename); + olive::ProjectSerializer::k_project, native, filename); olive::ProjectSerializer::Result result = olive::ProjectSerializer::save(data, use_compression != 0); @@ -408,11 +416,12 @@ int oaknode_serializer_save_to_file(OakNodeProject *project, } } -int oaknode_serializer_load_from_file(OakNodeProject *project, +int oaknode_serializer_load_from_file(OakNodeProject project, const char *filename, int *out_code, char *details, int details_size) { - if (!project || !filename) { + olive::Project *native = oaknode_c_api::to_native(project); + if (!native || !filename) { return OAKNODE_E_INVALID; } @@ -420,9 +429,8 @@ int oaknode_serializer_load_from_file(OakNodeProject *project, oaknode_serializer_initialize(); olive::ProjectSerializer::Result result = - olive::ProjectSerializer::load( - reinterpret_cast(project), filename, - olive::ProjectSerializer::k_project); + olive::ProjectSerializer::load(native, filename, + olive::ProjectSerializer::k_project); return report_serializer_result(result, out_code, details, details_size); diff --git a/src/node/c_api/track.cpp b/src/node/c_api/track.cpp index cc577144c..e63a9849e 100644 --- a/src/node/c_api/track.cpp +++ b/src/node/c_api/track.cpp @@ -22,7 +22,9 @@ #include -#include "alivecount.h" +#include "node/block.h" +#include "node/node.h" +#include "node/sequence.h" #include "valueconvert.h" @@ -31,34 +33,17 @@ #include "output/track/tracklist.h" #include "project/sequence/sequence.h" +#include "nodehandle.h" + +using oaknode_c_api::delete_as; +using oaknode_c_api::free_handle; +using oaknode_c_api::make_handle; +using oaknode_c_api::mark_container_owned; +using oaknode_c_api::to_native; + namespace { -olive::Track *impl(OakNodeTrack *h) -{ - return reinterpret_cast(h); -} - -olive::TrackList *list_impl(OakNodeTrackList *h) -{ - return reinterpret_cast(h); -} - -olive::Block *block_impl(OakNodeBlock *h) -{ - return reinterpret_cast(h); -} - -OakNodeTrack *wrap(olive::Track *t) -{ - return reinterpret_cast(t); -} - -OakNodeBlock *wrap_block(olive::Block *b) -{ - return reinterpret_cast(b); -} - bool valid_type(int type) { return type >= OAKNODE_TRACK_TYPE_VIDEO && type < OAKNODE_TRACK_TYPE_COUNT; @@ -98,81 +83,82 @@ int get_rational(const olive::core::Rational &r, int *numerator, /* ---------------------------------------------------------------- Track */ -OakNodeTrack *oaknode_track_create(int type) +OakNodeTrack oaknode_track_create(int type) { if (!valid_type(type)) { - return nullptr; + return OakNodeTrack{}; } try { olive::Track *t = new olive::Track(); t->set_type(static_cast(type)); - oaknode_c_api::alive_inc(); - return wrap(t); + return make_handle(t, true, &delete_as); } catch (...) { - return nullptr; + return OakNodeTrack{}; } } void oaknode_track_free(OakNodeTrack *track) { - if (!track) { - return; - } - delete impl(track); - oaknode_c_api::alive_dec(); + free_handle(track); } -int oaknode_track_get_type(OakNodeTrack *track, int *type) +int oaknode_track_get_type(OakNodeTrack track, int *type) { - if (!track || !type) { + olive::Track *t = to_native(track); + if (!t || !type) { return OAKNODE_E_INVALID; } - *type = int(impl(track)->type()); + *type = int(t->type()); return OAKNODE_OK; } -int oaknode_track_set_type(OakNodeTrack *track, int type) +int oaknode_track_set_type(OakNodeTrack track, int type) { - if (!track || !valid_type(type)) { + olive::Track *t = to_native(track); + if (!t || !valid_type(type)) { return OAKNODE_E_INVALID; } - impl(track)->set_type(static_cast(type)); + t->set_type(static_cast(type)); return OAKNODE_OK; } -int oaknode_track_get_height(OakNodeTrack *track, double *height) +int oaknode_track_get_height(OakNodeTrack track, double *height) { - if (!track || !height) { + olive::Track *t = to_native(track); + if (!t || !height) { return OAKNODE_E_INVALID; } - *height = impl(track)->get_track_height(); + *height = t->get_track_height(); return OAKNODE_OK; } -int oaknode_track_set_height(OakNodeTrack *track, double height) +int oaknode_track_set_height(OakNodeTrack track, double height) { - if (!track) { + olive::Track *t = to_native(track); + if (!t) { return OAKNODE_E_INVALID; } - impl(track)->set_track_height(height); + t->set_track_height(height); return OAKNODE_OK; } -int oaknode_track_get_height_in_pixels(OakNodeTrack *track, int *height) +int oaknode_track_get_height_in_pixels(OakNodeTrack track, int *height) { - if (!track || !height) { + olive::Track *t = to_native(track); + if (!t || !height) { return OAKNODE_E_INVALID; } - *height = impl(track)->get_track_height_in_pixels(); + *height = t->get_track_height_in_pixels(); return OAKNODE_OK; } -int oaknode_track_set_height_in_pixels(OakNodeTrack *track, int height) +int oaknode_track_set_height_in_pixels(OakNodeTrack track, int height) { - if (!track) { + olive::Track *t = to_native(track); + if (!t) { return OAKNODE_E_INVALID; } - impl(track)->set_track_height_in_pixels(height); + t->set_track_height_in_pixels(height); return OAKNODE_OK; } @@ -186,223 +172,267 @@ int oaknode_track_get_minimum_height_in_pixels(void) return olive::Track::get_minimum_track_height_in_pixels(); } -int oaknode_track_get_index(OakNodeTrack *track, int *index) +int oaknode_track_get_index(OakNodeTrack track, int *index) { - if (!track || !index) { + olive::Track *t = to_native(track); + if (!t || !index) { return OAKNODE_E_INVALID; } - *index = impl(track)->index(); + *index = t->index(); return OAKNODE_OK; } -int oaknode_track_set_index(OakNodeTrack *track, int index) +int oaknode_track_set_index(OakNodeTrack track, int index) { - if (!track) { + olive::Track *t = to_native(track); + if (!t) { return OAKNODE_E_INVALID; } - impl(track)->set_index(index); + t->set_index(index); return OAKNODE_OK; } -int oaknode_track_get_muted(OakNodeTrack *track, int *muted) +int oaknode_track_get_muted(OakNodeTrack track, int *muted) { - if (!track || !muted) { + olive::Track *t = to_native(track); + if (!t || !muted) { return OAKNODE_E_INVALID; } - *muted = impl(track)->is_muted() ? 1 : 0; + *muted = t->is_muted() ? 1 : 0; return OAKNODE_OK; } -int oaknode_track_set_muted(OakNodeTrack *track, int muted) +int oaknode_track_set_muted(OakNodeTrack track, int muted) { - if (!track) { + olive::Track *t = to_native(track); + if (!t) { return OAKNODE_E_INVALID; } - impl(track)->set_muted(muted != 0); + t->set_muted(muted != 0); return OAKNODE_OK; } -int oaknode_track_get_locked(OakNodeTrack *track, int *locked) +int oaknode_track_get_locked(OakNodeTrack track, int *locked) { - if (!track || !locked) { + olive::Track *t = to_native(track); + if (!t || !locked) { return OAKNODE_E_INVALID; } - *locked = impl(track)->is_locked() ? 1 : 0; + *locked = t->is_locked() ? 1 : 0; return OAKNODE_OK; } -int oaknode_track_set_locked(OakNodeTrack *track, int locked) +int oaknode_track_set_locked(OakNodeTrack track, int locked) { - if (!track) { + olive::Track *t = to_native(track); + if (!t) { return OAKNODE_E_INVALID; } - impl(track)->set_locked(locked != 0); + t->set_locked(locked != 0); return OAKNODE_OK; } -int oaknode_track_get_reference(OakNodeTrack *track, int *type, int *index) +int oaknode_track_get_reference(OakNodeTrack track, int *type, int *index) { - if (!track || !type || !index) { + olive::Track *t = to_native(track); + if (!t || !type || !index) { return OAKNODE_E_INVALID; } - olive::Track::Reference ref = impl(track)->to_reference(); + olive::Track::Reference ref = t->to_reference(); *type = int(ref.type()); *index = ref.index(); return OAKNODE_OK; } -int oaknode_track_get_length(OakNodeTrack *track, int *numerator, +int oaknode_track_get_length(OakNodeTrack track, int *numerator, int *denominator) { - if (!track) { + olive::Track *t = to_native(track); + if (!t) { return OAKNODE_E_INVALID; } - return get_rational(impl(track)->track_length(), numerator, denominator); + return get_rational(t->track_length(), numerator, denominator); } -int oaknode_track_get_sequence(OakNodeTrack *track, OakNodeSequence **out) +int oaknode_track_get_sequence(OakNodeTrack track, OakNodeSequence *out) { - if (!track || !out) { + olive::Track *t = to_native(track); + if (!t || !out) { return OAKNODE_E_INVALID; } - *out = reinterpret_cast(impl(track)->sequence()); + // Borrowed (empty when the track is not in a sequence) + *out = make_handle(t->sequence(), false, + &delete_as); return OAKNODE_OK; } /* ------------------------------------------------------- Track blocks */ -int oaknode_track_get_block_count(OakNodeTrack *track, int *count) +int oaknode_track_get_block_count(OakNodeTrack track, int *count) { - if (!track || !count) { + olive::Track *t = to_native(track); + if (!t || !count) { return OAKNODE_E_INVALID; } - *count = int(impl(track)->blocks().size()); + *count = int(t->blocks().size()); return OAKNODE_OK; } -int oaknode_track_get_block_at(OakNodeTrack *track, int index, - OakNodeBlock **out) +int oaknode_track_get_block_at(OakNodeTrack track, int index, + OakNodeBlock *out) { - if (!track || !out || index < 0) { + olive::Track *t = to_native(track); + if (!t || !out || index < 0) { return OAKNODE_E_INVALID; } - const auto &blocks = impl(track)->blocks(); + const auto &blocks = t->blocks(); if (index >= int(blocks.size())) { return OAKNODE_E_NOT_FOUND; } - *out = wrap_block(blocks.at(index)); - return OAKNODE_OK; + // Borrowed; the block stays owned by the track + *out = make_handle(blocks.at(index), false, + &delete_as); + return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM; } -int oaknode_track_append_block(OakNodeTrack *track, OakNodeBlock *block) +int oaknode_track_append_block(OakNodeTrack track, OakNodeBlock block) { - if (!track || !block) { + olive::Track *t = to_native(track); + olive::Block *b = to_native(block); + if (!t || !b) { return OAKNODE_E_INVALID; } try { - impl(track)->append_block(block_impl(block)); + t->append_block(b); } catch (...) { return OAKNODE_E_FAILED; } - refresh_lengths(impl(track)); + // The track now owns the block; the caller's handle becomes a + // non-owning reference (its release no longer deletes) + mark_container_owned(block); + refresh_lengths(t); return OAKNODE_OK; } -int oaknode_track_prepend_block(OakNodeTrack *track, OakNodeBlock *block) +int oaknode_track_prepend_block(OakNodeTrack track, OakNodeBlock block) { - if (!track || !block) { + olive::Track *t = to_native(track); + olive::Block *b = to_native(block); + if (!t || !b) { return OAKNODE_E_INVALID; } try { - impl(track)->prepend_block(block_impl(block)); + t->prepend_block(b); } catch (...) { return OAKNODE_E_FAILED; } - refresh_lengths(impl(track)); + mark_container_owned(block); + refresh_lengths(t); return OAKNODE_OK; } -int oaknode_track_insert_block_at_index(OakNodeTrack *track, - OakNodeBlock *block, int index) +int oaknode_track_insert_block_at_index(OakNodeTrack track, + OakNodeBlock block, int index) { - if (!track || !block) { + olive::Track *t = to_native(track); + olive::Block *b = to_native(block); + if (!t || !b) { return OAKNODE_E_INVALID; } try { - impl(track)->insert_block_at_index(block_impl(block), index); + t->insert_block_at_index(b, index); } catch (...) { return OAKNODE_E_FAILED; } - refresh_lengths(impl(track)); + mark_container_owned(block); + refresh_lengths(t); return OAKNODE_OK; } -int oaknode_track_insert_block_after(OakNodeTrack *track, OakNodeBlock *block, - OakNodeBlock *before) +int oaknode_track_insert_block_after(OakNodeTrack track, OakNodeBlock block, + OakNodeBlock before) { - if (!track || !block || !before) { + olive::Track *t = to_native(track); + olive::Block *b = to_native(block); + olive::Block *bf = to_native(before); + if (!t || !b || !bf) { return OAKNODE_E_INVALID; } try { - impl(track)->insert_block_after(block_impl(block), block_impl(before)); + t->insert_block_after(b, bf); } catch (...) { return OAKNODE_E_FAILED; } - refresh_lengths(impl(track)); + mark_container_owned(block); + refresh_lengths(t); return OAKNODE_OK; } -int oaknode_track_insert_block_before(OakNodeTrack *track, OakNodeBlock *block, - OakNodeBlock *after) +int oaknode_track_insert_block_before(OakNodeTrack track, OakNodeBlock block, + OakNodeBlock after) { - if (!track || !block || !after) { + olive::Track *t = to_native(track); + olive::Block *b = to_native(block); + olive::Block *af = to_native(after); + if (!t || !b || !af) { return OAKNODE_E_INVALID; } try { - impl(track)->insert_block_before(block_impl(block), block_impl(after)); + t->insert_block_before(b, af); } catch (...) { return OAKNODE_E_FAILED; } - refresh_lengths(impl(track)); + mark_container_owned(block); + refresh_lengths(t); return OAKNODE_OK; } -int oaknode_track_ripple_remove_block(OakNodeTrack *track, OakNodeBlock *block) +int oaknode_track_ripple_remove_block(OakNodeTrack track, OakNodeBlock block) { - if (!track || !block) { + olive::Track *t = to_native(track); + olive::Block *b = to_native(block); + if (!t || !b) { return OAKNODE_E_INVALID; } try { - impl(track)->ripple_remove_block(block_impl(block)); + t->ripple_remove_block(b); } catch (...) { return OAKNODE_E_FAILED; } - refresh_lengths(impl(track)); + refresh_lengths(t); return OAKNODE_OK; } -int oaknode_track_replace_block(OakNodeTrack *track, OakNodeBlock *old_block, - OakNodeBlock *new_block) +int oaknode_track_replace_block(OakNodeTrack track, OakNodeBlock old_block, + OakNodeBlock new_block) { - if (!track || !old_block || !new_block) { + olive::Track *t = to_native(track); + olive::Block *old_b = to_native(old_block); + olive::Block *new_b = to_native(new_block); + if (!t || !old_b || !new_b) { return OAKNODE_E_INVALID; } try { - impl(track)->replace_block(block_impl(old_block), block_impl(new_block)); + t->replace_block(old_b, new_b); } catch (...) { return OAKNODE_E_FAILED; } - refresh_lengths(impl(track)); + // The track takes over the replacement; the caller's handle to it + // becomes non-owning + mark_container_owned(new_block); + refresh_lengths(t); return OAKNODE_OK; } -int oaknode_track_get_block_index(OakNodeTrack *track, OakNodeBlock *block, +int oaknode_track_get_block_index(OakNodeTrack track, OakNodeBlock block, int *index) { - if (!track || !block || !index) { + olive::Track *t = to_native(track); + olive::Block *b = to_native(block); + if (!t || !b || !index) { return OAKNODE_E_INVALID; } - int i = impl(track)->get_array_index_from_block(block_impl(block)); + int i = t->get_array_index_from_block(b); if (i < 0) { return OAKNODE_E_NOT_FOUND; } @@ -410,48 +440,51 @@ int oaknode_track_get_block_index(OakNodeTrack *track, OakNodeBlock *block, return OAKNODE_OK; } -int oaknode_track_get_block_containing_time(OakNodeTrack *track, int numerator, +int oaknode_track_get_block_containing_time(OakNodeTrack track, int numerator, int denominator, - OakNodeBlock **out) + OakNodeBlock *out) { - if (!track || !out) { + olive::Track *t = to_native(track); + if (!t || !out) { return OAKNODE_E_INVALID; } - olive::Block *b = impl(track)->block_containing_time( + olive::Block *b = t->block_containing_time( olive::core::Rational(numerator, denominator)); if (!b) { return OAKNODE_E_NOT_FOUND; } - *out = wrap_block(b); - return OAKNODE_OK; + *out = make_handle(b, false, &delete_as); + return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM; } -int oaknode_track_get_visible_block_at_time(OakNodeTrack *track, int numerator, +int oaknode_track_get_visible_block_at_time(OakNodeTrack track, int numerator, int denominator, - OakNodeBlock **out) + OakNodeBlock *out) { - if (!track || !out) { + olive::Track *t = to_native(track); + if (!t || !out) { return OAKNODE_E_INVALID; } - olive::Block *b = impl(track)->visible_block_at_time( + olive::Block *b = t->visible_block_at_time( olive::core::Rational(numerator, denominator)); if (!b) { return OAKNODE_E_NOT_FOUND; } - *out = wrap_block(b); - return OAKNODE_OK; + *out = make_handle(b, false, &delete_as); + return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM; } -int oaknode_track_is_range_free(OakNodeTrack *track, int in_num, int in_den, +int oaknode_track_is_range_free(OakNodeTrack track, int in_num, int in_den, int out_num, int out_den, int *is_free) { - if (!track || !is_free) { + olive::Track *t = to_native(track); + if (!t || !is_free) { return OAKNODE_E_INVALID; } - *is_free = impl(track)->is_range_free( - olive::core::TimeRange( - olive::core::Rational(in_num, in_den), - olive::core::Rational(out_num, out_den))) ? + *is_free = t->is_range_free( + olive::core::TimeRange( + olive::core::Rational(in_num, in_den), + olive::core::Rational(out_num, out_den))) ? 1 : 0; return OAKNODE_OK; @@ -459,63 +492,69 @@ int oaknode_track_is_range_free(OakNodeTrack *track, int in_num, int in_den, /* ------------------------------------------------------------ TrackList */ -int oaknode_tracklist_get_type(OakNodeTrackList *list, int *type) +int oaknode_tracklist_get_type(OakNodeTrackList list, int *type) { - if (!list || !type) { + olive::TrackList *l = to_native(list); + if (!l || !type) { return OAKNODE_E_INVALID; } - *type = int(list_impl(list)->type()); + *type = int(l->type()); return OAKNODE_OK; } -int oaknode_tracklist_get_track_count(OakNodeTrackList *list, int *count) +int oaknode_tracklist_get_track_count(OakNodeTrackList list, int *count) { - if (!list || !count) { + olive::TrackList *l = to_native(list); + if (!l || !count) { return OAKNODE_E_INVALID; } - *count = list_impl(list)->get_track_count(); + *count = l->get_track_count(); return OAKNODE_OK; } -int oaknode_tracklist_get_track_at(OakNodeTrackList *list, int index, - OakNodeTrack **out) +int oaknode_tracklist_get_track_at(OakNodeTrackList list, int index, + OakNodeTrack *out) { - if (!list || !out || index < 0) { + olive::TrackList *l = to_native(list); + if (!l || !out || index < 0) { return OAKNODE_E_INVALID; } - if (index >= list_impl(list)->get_track_count()) { + if (index >= l->get_track_count()) { return OAKNODE_E_NOT_FOUND; } - *out = wrap(list_impl(list)->get_track_at(index)); - return OAKNODE_OK; + // Borrowed; the track stays owned by the list + *out = make_handle(l->get_track_at(index), false, + &delete_as); + return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM; } -int oaknode_tracklist_get_total_length(OakNodeTrackList *list, int *numerator, +int oaknode_tracklist_get_total_length(OakNodeTrackList list, int *numerator, int *denominator) { - if (!list) { + olive::TrackList *l = to_native(list); + if (!l) { return OAKNODE_E_INVALID; } - return get_rational(list_impl(list)->get_total_length(), numerator, - denominator); + return get_rational(l->get_total_length(), numerator, denominator); } -int oaknode_tracklist_get_array_size(OakNodeTrackList *list, int *size) +int oaknode_tracklist_get_array_size(OakNodeTrackList list, int *size) { - if (!list || !size) { + olive::TrackList *l = to_native(list); + if (!l || !size) { return OAKNODE_E_INVALID; } - *size = list_impl(list)->array_size(); + *size = l->array_size(); return OAKNODE_OK; } -int oaknode_tracklist_add_track(OakNodeTrackList *list, OakNodeTrack *track) +int oaknode_tracklist_add_track(OakNodeTrackList list, OakNodeTrack track) { - if (!list || !track) { + olive::TrackList *l = to_native(list); + olive::Track *t = to_native(track); + if (!l || !t) { return OAKNODE_E_INVALID; } - olive::TrackList *l = list_impl(list); - olive::Track *t = impl(track); olive::Sequence *sequence = l->parent(); if (!sequence) { return OAKNODE_E_STATE; @@ -537,16 +576,19 @@ int oaknode_tracklist_add_track(OakNodeTrackList *list, OakNodeTrack *track) } catch (...) { return OAKNODE_E_FAILED; } + // The graph now owns the track; the caller's handle becomes a + // non-owning reference (its release no longer deletes) + mark_container_owned(track); return OAKNODE_OK; } -int oaknode_tracklist_remove_track(OakNodeTrackList *list, OakNodeTrack *track) +int oaknode_tracklist_remove_track(OakNodeTrackList list, OakNodeTrack track) { - if (!list || !track) { + olive::TrackList *l = to_native(list); + olive::Track *t = to_native(track); + if (!l || !t) { return OAKNODE_E_INVALID; } - olive::TrackList *l = list_impl(list); - olive::Track *t = impl(track); olive::Sequence *sequence = l->parent(); if (!sequence) { return OAKNODE_E_STATE; @@ -575,70 +617,78 @@ int oaknode_tracklist_remove_track(OakNodeTrackList *list, OakNodeTrack *track) return OAKNODE_OK; } -int oaknode_track_get_nearest_block_before_or_at(OakNodeTrack *track, - int numerator, int denominator, OakNodeBlock **out) +int oaknode_track_get_nearest_block_before_or_at(OakNodeTrack track, + int numerator, int denominator, OakNodeBlock *out) { - olive::Track *t = impl(track); + olive::Track *t = to_native(track); if (!t || !out) { return OAKNODE_E_INVALID; } - *out = wrap_block( - t->nearest_block_before_or_at(olive::core::Rational(numerator, denominator))); + // Borrowed (empty when there is no such block) + *out = make_handle( + t->nearest_block_before_or_at(olive::core::Rational(numerator, denominator)), + false, &delete_as); return OAKNODE_OK; } -int oaknode_track_get_nearest_block_after_or_at(OakNodeTrack *track, - int numerator, int denominator, OakNodeBlock **out) +int oaknode_track_get_nearest_block_after_or_at(OakNodeTrack track, + int numerator, int denominator, OakNodeBlock *out) { - olive::Track *t = impl(track); + olive::Track *t = to_native(track); if (!t || !out) { return OAKNODE_E_INVALID; } - *out = wrap_block( - t->nearest_block_after_or_at(olive::core::Rational(numerator, denominator))); + *out = make_handle( + t->nearest_block_after_or_at(olive::core::Rational(numerator, denominator)), + false, &delete_as); return OAKNODE_OK; } -int oaknode_tracklist_get_sequence(OakNodeTrackList *list, - OakNodeSequence **out) +int oaknode_tracklist_get_sequence(OakNodeTrackList list, + OakNodeSequence *out) { - if (!list || !out) { + olive::TrackList *l = to_native(list); + if (!l || !out) { return OAKNODE_E_INVALID; } - *out = reinterpret_cast(list_impl(list)->parent()); + // Borrowed; the sequence owns the list + *out = make_handle(l->parent(), false, + &delete_as); return OAKNODE_OK; } -int oaknode_tracklist_get_track_input_id(OakNodeTrackList *list, char *buf, +int oaknode_tracklist_get_track_input_id(OakNodeTrackList list, char *buf, int buf_size) { - if (!list) { + olive::TrackList *l = to_native(list); + if (!l) { return OAKNODE_E_INVALID; } - return oaknode_c_api::copy_string(list_impl(list)->track_input(), buf, - buf_size); + return oaknode_c_api::copy_string(l->track_input(), buf, buf_size); } -int oaknode_tracklist_array_append(OakNodeTrackList *list) +int oaknode_tracklist_array_append(OakNodeTrackList list) { - if (!list) { + olive::TrackList *l = to_native(list); + if (!l) { return OAKNODE_E_INVALID; } try { - list_impl(list)->array_append(); + l->array_append(); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; } } -int oaknode_tracklist_array_remove_last(OakNodeTrackList *list) +int oaknode_tracklist_array_remove_last(OakNodeTrackList list) { - if (!list) { + olive::TrackList *l = to_native(list); + if (!l) { return OAKNODE_E_INVALID; } try { - list_impl(list)->array_remove_last(); + l->array_remove_last(); return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; @@ -646,17 +696,19 @@ int oaknode_tracklist_array_remove_last(OakNodeTrackList *list) } int oaknode_tracklist_get_array_index_from_cache_index( - OakNodeTrackList *list, int cache_index, int *out_index) + OakNodeTrackList list, int cache_index, int *out_index) { - if (!list || !out_index) { + olive::TrackList *l = to_native(list); + if (!l || !out_index) { return OAKNODE_E_INVALID; } - *out_index = - list_impl(list)->get_array_index_from_cache_index(cache_index); + *out_index = l->get_array_index_from_cache_index(cache_index); return OAKNODE_OK; } -OakNodeNode *oaknode_track_as_node(OakNodeTrack *track) +OakNodeNode oaknode_track_as_node(OakNodeTrack track) { - return reinterpret_cast(track); + // Borrowed; releasing the result never destroys the track + return make_handle(to_native(track), false, + &delete_as); } diff --git a/src/node/c_api/traverser.cpp b/src/node/c_api/traverser.cpp index 2db94ff9f..4f0ef6c88 100644 --- a/src/node/c_api/traverser.cpp +++ b/src/node/c_api/traverser.cpp @@ -25,32 +25,22 @@ #include "traverser.h" #include "valuedatabase.h" +#include "nodehandle.h" #include "valueconvert.h" -struct OakNodeValueDatabase { - olive::NodeValueDatabase impl; -}; +using oaknode_c_api::make_handle; +using oaknode_c_api::to_native; namespace { -inline olive::NodeTraverser *to_traverser(OakNodeTraverser *traverser) -{ - return reinterpret_cast(traverser); -} - -inline olive::Node *to_node(OakNodeNode *node) -{ - return reinterpret_cast(node); -} - /** * @brief Find the table named `key`, or NULL when absent. */ -const olive::NodeValueTable *find_table(const OakNodeValueDatabase *db, +const olive::NodeValueTable *find_table(const olive::NodeValueDatabase *db, const char *key) { - for (auto it = db->impl.cbegin(); it != db->impl.cend(); ++it) { + for (auto it = db->cbegin(); it != db->cend(); ++it) { if (it->first == key) { return &it->second; } @@ -60,39 +50,34 @@ const olive::NodeValueTable *find_table(const OakNodeValueDatabase *db, } -OakNodeTraverser *oaknode_traverser_init(void) +OakNodeTraverser oaknode_traverser_init(void) { try { - olive::NodeTraverser *traverser = new (std::nothrow) olive::NodeTraverser(); - if (traverser) { - oaknode_c_api::alive_inc(); - } - return reinterpret_cast(traverser); + return make_handle( + new (std::nothrow) olive::NodeTraverser(), true, + oaknode_c_api::delete_as); } catch (...) { - return NULL; + return OakNodeTraverser{}; } } void oaknode_traverser_free(OakNodeTraverser *traverser) { - if (!traverser) { - return; - } - try { - delete to_traverser(traverser); - oaknode_c_api::alive_dec(); + oaknode_c_api::free_handle(traverser); } catch (...) { } } -int oaknode_traverser_generate_database(OakNodeTraverser *traverser, - OakNodeNode *node, int64_t in_num, +int oaknode_traverser_generate_database(OakNodeTraverser traverser, + OakNodeNode node, int64_t in_num, int64_t in_den, int64_t out_num, int64_t out_den, - OakNodeValueDatabase **out_db) + OakNodeValueDatabase *out_db) { - if (!traverser || !node || !out_db) { + olive::NodeTraverser *t = to_native(traverser); + olive::Node *n = to_native(node); + if (!t || !n || !out_db) { return OAKNODE_E_INVALID; } @@ -102,16 +87,19 @@ int oaknode_traverser_generate_database(OakNodeTraverser *traverser, olive::core::Rational out(static_cast(out_num), static_cast(out_den)); - OakNodeValueDatabase *db = new (std::nothrow) OakNodeValueDatabase(); + olive::NodeValueDatabase *db = + new (std::nothrow) olive::NodeValueDatabase(); if (!db) { return OAKNODE_E_NOMEM; } - db->impl = to_traverser(traverser)->generate_database( - to_node(node), olive::core::TimeRange(in, out)); + *db = t->generate_database(n, olive::core::TimeRange(in, out)); - *out_db = db; - oaknode_c_api::alive_inc(); + *out_db = make_handle( + db, true, oaknode_c_api::delete_as); + if (!out_db->ctx) { + return OAKNODE_E_NOMEM; + } return OAKNODE_OK; } catch (...) { return OAKNODE_E_FAILED; @@ -120,24 +108,24 @@ int oaknode_traverser_generate_database(OakNodeTraverser *traverser, void oaknode_traverser_database_free(OakNodeValueDatabase *db) { - if (!db) { - return; + try { + oaknode_c_api::free_handle(db); + } catch (...) { } - - delete db; - oaknode_c_api::alive_dec(); } -int oaknode_traverser_database_row_count(const OakNodeValueDatabase *db, +int oaknode_traverser_database_row_count(OakNodeValueDatabase db, int *out_count) { - if (!db || !out_count) { + const olive::NodeValueDatabase *impl = + to_native(db); + if (!impl || !out_count) { return OAKNODE_E_INVALID; } try { int count = 0; - for (auto it = db->impl.cbegin(); it != db->impl.cend(); ++it) { + for (auto it = impl->cbegin(); it != impl->cend(); ++it) { count++; } *out_count = count; @@ -147,10 +135,12 @@ int oaknode_traverser_database_row_count(const OakNodeValueDatabase *db, } } -int oaknode_traverser_database_row_key_at(const OakNodeValueDatabase *db, +int oaknode_traverser_database_row_key_at(OakNodeValueDatabase db, int index, char *buf, int buf_size) { - if (!db) { + const olive::NodeValueDatabase *impl = + to_native(db); + if (!impl) { return OAKNODE_E_INVALID; } @@ -158,10 +148,10 @@ int oaknode_traverser_database_row_key_at(const OakNodeValueDatabase *db, if (index < 0) { return OAKNODE_E_NOT_FOUND; } - auto it = db->impl.cbegin(); - for (int i = 0; i < index && it != db->impl.cend(); i++, ++it) { + auto it = impl->cbegin(); + for (int i = 0; i < index && it != impl->cend(); i++, ++it) { } - if (it == db->impl.cend()) { + if (it == impl->cend()) { return OAKNODE_E_NOT_FOUND; } return oaknode_c_api::copy_string(it->first, buf, buf_size); @@ -170,16 +160,18 @@ int oaknode_traverser_database_row_key_at(const OakNodeValueDatabase *db, } } -int oaknode_traverser_database_row_value_count(const OakNodeValueDatabase *db, +int oaknode_traverser_database_row_value_count(OakNodeValueDatabase db, const char *key, int *out_count) { - if (!db || !key || !out_count) { + const olive::NodeValueDatabase *impl = + to_native(db); + if (!impl || !key || !out_count) { return OAKNODE_E_INVALID; } try { - const olive::NodeValueTable *table = find_table(db, key); + const olive::NodeValueTable *table = find_table(impl, key); if (!table) { return OAKNODE_E_NOT_FOUND; } @@ -190,16 +182,18 @@ int oaknode_traverser_database_row_value_count(const OakNodeValueDatabase *db, } } -int oaknode_traverser_database_value_at(const OakNodeValueDatabase *db, +int oaknode_traverser_database_value_at(OakNodeValueDatabase db, const char *key, int index, oaknode_value *out) { - if (!db || !key || !out) { + const olive::NodeValueDatabase *impl = + to_native(db); + if (!impl || !key || !out) { return OAKNODE_E_INVALID; } try { - const olive::NodeValueTable *table = find_table(db, key); + const olive::NodeValueTable *table = find_table(impl, key); if (!table || index < 0 || index >= table->count()) { return OAKNODE_E_NOT_FOUND; } @@ -212,16 +206,18 @@ int oaknode_traverser_database_value_at(const OakNodeValueDatabase *db, } } -int oaknode_traverser_database_value_string_at(const OakNodeValueDatabase *db, +int oaknode_traverser_database_value_string_at(OakNodeValueDatabase db, const char *key, int index, char *buf, int buf_size) { - if (!db || !key) { + const olive::NodeValueDatabase *impl = + to_native(db); + if (!impl || !key) { return OAKNODE_E_INVALID; } try { - const olive::NodeValueTable *table = find_table(db, key); + const olive::NodeValueTable *table = find_table(impl, key); if (!table || index < 0 || index >= table->count()) { return OAKNODE_E_NOT_FOUND; } diff --git a/src/node/src/project/sequence/sequence.cpp b/src/node/src/project/sequence/sequence.cpp index 3302b728d..a3a39968e 100644 --- a/src/node/src/project/sequence/sequence.cpp +++ b/src/node/src/project/sequence/sequence.cpp @@ -84,10 +84,13 @@ Sequence::~Sequence() void Sequence::add_default_nodes(MultiUndoCommand *command) { // Create tracks and connect them to the viewer + // (borrowed handles: the track lists are owned by this sequence) UndoCommand *video_track_command = new TimelineAddTrackCommand( - reinterpret_cast(track_list(Track::k_video))); + oaknode_c_api::make_handle( + track_list(Track::k_video), false, nullptr)); UndoCommand *audio_track_command = new TimelineAddTrackCommand( - reinterpret_cast(track_list(Track::k_audio))); + oaknode_c_api::make_handle( + track_list(Track::k_audio), false, nullptr)); if (command) { command->add_child(video_track_command); diff --git a/src/node/tests/block_test.cpp b/src/node/tests/block_test.cpp index 34ff06c55..a029d2e0b 100644 --- a/src/node/tests/block_test.cpp +++ b/src/node/tests/block_test.cpp @@ -40,32 +40,33 @@ void expect_rational(int num, int den, int expected_num, int expected_den) TEST(BlockTest, CreateFreeClip) { int base = oaknode_debug_alive_count(); - OakNodeBlock *clip = oaknode_block_clip_create(); - ASSERT_NE(clip, nullptr); + OakNodeBlock clip = oaknode_block_clip_create(); + ASSERT_NE(clip.ctx, nullptr); EXPECT_EQ(oaknode_debug_alive_count(), base + 1); - oaknode_block_free(clip); + oaknode_block_free(&clip); EXPECT_EQ(oaknode_debug_alive_count(), base); + EXPECT_EQ(clip.ctx, nullptr); } TEST(BlockTest, CreateFreeGap) { - OakNodeBlock *gap = oaknode_block_gap_create(); - ASSERT_NE(gap, nullptr); - oaknode_block_free(gap); + OakNodeBlock gap = oaknode_block_gap_create(); + ASSERT_NE(gap.ctx, nullptr); + oaknode_block_free(&gap); } TEST(BlockTest, CreateTransitions) { - OakNodeBlock *cd = + OakNodeBlock cd = oaknode_block_transition_create(OAKNODE_TRANSITION_CROSS_DISSOLVE); - EXPECT_NE(cd, nullptr); - OakNodeBlock *dc = + EXPECT_NE(cd.ctx, nullptr); + OakNodeBlock dc = oaknode_block_transition_create(OAKNODE_TRANSITION_DIP_TO_COLOR); - EXPECT_NE(dc, nullptr); - EXPECT_EQ(oaknode_block_transition_create(99), nullptr); - EXPECT_EQ(oaknode_block_transition_create(-1), nullptr); - oaknode_block_free(cd); - oaknode_block_free(dc); + EXPECT_NE(dc.ctx, nullptr); + EXPECT_EQ(oaknode_block_transition_create(99).ctx, nullptr); + EXPECT_EQ(oaknode_block_transition_create(-1).ctx, nullptr); + oaknode_block_free(&cd); + oaknode_block_free(&dc); } TEST(BlockTest, FreeNullIsNoOp) @@ -76,23 +77,24 @@ TEST(BlockTest, FreeNullIsNoOp) TEST(BlockTest, NullHandleReturnsInvalid) { int num, den; - EXPECT_EQ(oaknode_block_get_in(nullptr, &num, &den), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_block_set_in(nullptr, 0, 1), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_block_get_length(nullptr, &num, &den), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_block_get_enabled(nullptr, &num), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_clip_get_speed(nullptr, nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_transition_is_dual(nullptr, &num), OAKNODE_E_INVALID); + OakNodeBlock empty = {}; + EXPECT_EQ(oaknode_block_get_in(empty, &num, &den), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_block_set_in(empty, 0, 1), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_block_get_length(empty, &num, &den), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_block_get_enabled(empty, &num), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_clip_get_speed(empty, nullptr), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_transition_is_dual(empty, &num), OAKNODE_E_INVALID); - OakNodeBlock *clip = oaknode_block_clip_create(); - ASSERT_NE(clip, nullptr); + OakNodeBlock clip = oaknode_block_clip_create(); + ASSERT_NE(clip.ctx, nullptr); EXPECT_EQ(oaknode_block_get_in(clip, nullptr, &den), OAKNODE_E_INVALID); - oaknode_block_free(clip); + oaknode_block_free(&clip); } TEST(BlockTest, InOutLength) { - OakNodeBlock *gap = oaknode_block_gap_create(); - ASSERT_NE(gap, nullptr); + OakNodeBlock gap = oaknode_block_gap_create(); + ASSERT_NE(gap.ctx, nullptr); int num, den; ASSERT_EQ(oaknode_block_set_length_and_media_out(gap, 2, 1), OAKNODE_OK); @@ -116,13 +118,13 @@ TEST(BlockTest, InOutLength) ASSERT_EQ(oaknode_block_get_length(gap, &num, &den), OAKNODE_OK); expect_rational(num, den, 1, 1); - oaknode_block_free(gap); + oaknode_block_free(&gap); } TEST(BlockTest, EnabledFlag) { - OakNodeBlock *gap = oaknode_block_gap_create(); - ASSERT_NE(gap, nullptr); + OakNodeBlock gap = oaknode_block_gap_create(); + ASSERT_NE(gap.ctx, nullptr); int enabled = 0; ASSERT_EQ(oaknode_block_get_enabled(gap, &enabled), OAKNODE_OK); @@ -132,32 +134,34 @@ TEST(BlockTest, EnabledFlag) ASSERT_EQ(oaknode_block_get_enabled(gap, &enabled), OAKNODE_OK); EXPECT_EQ(enabled, 0); - oaknode_block_free(gap); + oaknode_block_free(&gap); } TEST(BlockTest, TracklessBlockHasNoNeighbours) { - OakNodeBlock *gap = oaknode_block_gap_create(); - ASSERT_NE(gap, nullptr); + OakNodeBlock gap = oaknode_block_gap_create(); + ASSERT_NE(gap.ctx, nullptr); - OakNodeBlock *neighbour = reinterpret_cast(0x1); - OakNodeTrack *track = reinterpret_cast(0x1); + OakNodeBlock neighbour = {}; + neighbour.ctx = reinterpret_cast(0x1); + OakNodeTrack track = {}; + track.ctx = reinterpret_cast(0x1); ASSERT_EQ(oaknode_block_get_previous(gap, &neighbour), OAKNODE_OK); - EXPECT_EQ(neighbour, nullptr); + EXPECT_EQ(neighbour.ctx, nullptr); ASSERT_EQ(oaknode_block_get_next(gap, &neighbour), OAKNODE_OK); - EXPECT_EQ(neighbour, nullptr); + EXPECT_EQ(neighbour.ctx, nullptr); ASSERT_EQ(oaknode_block_get_track(gap, &track), OAKNODE_OK); - EXPECT_EQ(track, nullptr); + EXPECT_EQ(track.ctx, nullptr); - oaknode_block_free(gap); + oaknode_block_free(&gap); } TEST(BlockTest, LinkUnlink) { - OakNodeBlock *a = oaknode_block_clip_create(); - OakNodeBlock *b = oaknode_block_clip_create(); - ASSERT_NE(a, nullptr); - ASSERT_NE(b, nullptr); + OakNodeBlock a = oaknode_block_clip_create(); + OakNodeBlock b = oaknode_block_clip_create(); + ASSERT_NE(a.ctx, nullptr); + ASSERT_NE(b.ctx, nullptr); int linked = -1; ASSERT_EQ(oaknode_block_are_linked(a, b, &linked), OAKNODE_OK); @@ -174,9 +178,12 @@ TEST(BlockTest, LinkUnlink) ASSERT_EQ(oaknode_block_get_link_count(a, &count), OAKNODE_OK); EXPECT_EQ(count, 1); - OakNodeBlock *other = nullptr; + OakNodeBlock other = {}; ASSERT_EQ(oaknode_block_get_link_at(a, 0, &other), OAKNODE_OK); - EXPECT_EQ(other, b); + // The linked block is b: it is the only block linked to a + int linked_to_a = -1; + ASSERT_EQ(oaknode_block_are_linked(other, a, &linked_to_a), OAKNODE_OK); + EXPECT_EQ(linked_to_a, 1); EXPECT_EQ(oaknode_block_get_link_at(a, 1, &other), OAKNODE_E_NOT_FOUND); ASSERT_EQ(oaknode_block_unlink(a, b), OAKNODE_OK); @@ -184,16 +191,17 @@ TEST(BlockTest, LinkUnlink) ASSERT_EQ(oaknode_block_are_linked(a, b, &linked), OAKNODE_OK); EXPECT_EQ(linked, 0); - EXPECT_EQ(oaknode_block_link(a, nullptr), OAKNODE_E_INVALID); + OakNodeBlock empty = {}; + EXPECT_EQ(oaknode_block_link(a, empty), OAKNODE_E_INVALID); - oaknode_block_free(a); - oaknode_block_free(b); + oaknode_block_free(&a); + oaknode_block_free(&b); } TEST(BlockTest, ClipProperties) { - OakNodeBlock *clip = oaknode_block_clip_create(); - ASSERT_NE(clip, nullptr); + OakNodeBlock clip = oaknode_block_clip_create(); + ASSERT_NE(clip.ctx, nullptr); int num, den; ASSERT_EQ(oaknode_clip_set_media_in(clip, 3, 2), OAKNODE_OK); @@ -234,13 +242,13 @@ TEST(BlockTest, ClipProperties) ASSERT_EQ(oaknode_clip_get_track_type(clip, &type), OAKNODE_OK); EXPECT_EQ(type, OAKNODE_TRACK_TYPE_NONE); - oaknode_block_free(clip); + oaknode_block_free(&clip); } TEST(BlockTest, ClipApiRejectsNonClip) { - OakNodeBlock *gap = oaknode_block_gap_create(); - ASSERT_NE(gap, nullptr); + OakNodeBlock gap = oaknode_block_gap_create(); + ASSERT_NE(gap.ctx, nullptr); double speed; int num, den; @@ -248,14 +256,14 @@ TEST(BlockTest, ClipApiRejectsNonClip) EXPECT_EQ(oaknode_clip_set_media_in(gap, 1, 1), OAKNODE_E_INVALID); EXPECT_EQ(oaknode_clip_get_media_in(gap, &num, &den), OAKNODE_E_INVALID); - oaknode_block_free(gap); + oaknode_block_free(&gap); } TEST(BlockTest, TransitionOffsets) { - OakNodeBlock *t = + OakNodeBlock t = oaknode_block_transition_create(OAKNODE_TRANSITION_CROSS_DISSOLVE); - ASSERT_NE(t, nullptr); + ASSERT_NE(t.ctx, nullptr); int num, den; ASSERT_EQ(oaknode_transition_set_offsets_and_length(t, 1, 2, 1, 2), @@ -281,21 +289,23 @@ TEST(BlockTest, TransitionOffsets) ASSERT_EQ(oaknode_transition_is_dual(t, &dual), OAKNODE_OK); EXPECT_EQ(dual, 0); - OakNodeBlock *connected = reinterpret_cast(0x1); + OakNodeBlock connected = {}; + connected.ctx = reinterpret_cast(0x1); ASSERT_EQ(oaknode_transition_get_connected_out_block(t, &connected), OAKNODE_OK); - EXPECT_EQ(connected, nullptr); + EXPECT_EQ(connected.ctx, nullptr); + connected.ctx = reinterpret_cast(0x1); ASSERT_EQ(oaknode_transition_get_connected_in_block(t, &connected), OAKNODE_OK); - EXPECT_EQ(connected, nullptr); + EXPECT_EQ(connected.ctx, nullptr); - oaknode_block_free(t); + oaknode_block_free(&t); } TEST(BlockTest, TransitionApiRejectsNonTransition) { - OakNodeBlock *gap = oaknode_block_gap_create(); - ASSERT_NE(gap, nullptr); + OakNodeBlock gap = oaknode_block_gap_create(); + ASSERT_NE(gap.ctx, nullptr); int num, den; EXPECT_EQ(oaknode_transition_get_in_offset(gap, &num, &den), @@ -303,5 +313,5 @@ TEST(BlockTest, TransitionApiRejectsNonTransition) EXPECT_EQ(oaknode_transition_set_offset_center(gap, 1, 2), OAKNODE_E_INVALID); - oaknode_block_free(gap); + oaknode_block_free(&gap); } diff --git a/src/node/tests/colormanager_test.cpp b/src/node/tests/colormanager_test.cpp index fcbfef1f3..51a74867f 100644 --- a/src/node/tests/colormanager_test.cpp +++ b/src/node/tests/colormanager_test.cpp @@ -33,6 +33,8 @@ // object directly as scaffolding (borrowed OakNodeProject handle). #include "project.h" +#include "../c_api/nodehandle.h" + #ifndef OAK_OCIO_TEST_CONFIG #define OAK_OCIO_TEST_CONFIG "" #endif @@ -40,6 +42,25 @@ namespace { +/** + * @brief Wrap a native project in a borrowed (non-owning) value handle. + */ +OakNodeProject borrow_project(olive::Project *project) +{ + return oaknode_c_api::make_handle(project, false, nullptr); +} + +/** + * @brief Release the box of a borrowed handle produced by + * borrow_project(); the native object is not touched. + */ +void release_borrowed(OakNodeProject h) +{ + if (h.ctx) { + h.release(h.ctx); + } +} + /** * @brief Fixture ensuring the process-wide default OCIO config resolves * @@ -56,9 +77,9 @@ protected: } } - static std::string get_string(int (*fn)(OakNodeColorManager *, char *, + static std::string get_string(int (*fn)(OakNodeColorManager, char *, int), - OakNodeColorManager *m) + OakNodeColorManager m) { int needed = fn(m, nullptr, 0); EXPECT_GT(needed, 0); @@ -73,39 +94,43 @@ protected: TEST_F(ColorManagerTest, InitFree) { olive::Project project; + OakNodeProject ph = borrow_project(&project); int base = oaknode_debug_alive_count(); - OakNodeColorManager *m = - oaknode_colormanager_init(reinterpret_cast(&project)); - ASSERT_NE(m, nullptr); + OakNodeColorManager m = oaknode_colormanager_init(ph); + ASSERT_NE(m.ctx, nullptr); EXPECT_EQ(oaknode_debug_alive_count(), base + 1); - oaknode_colormanager_free(m); + oaknode_colormanager_free(&m); EXPECT_EQ(oaknode_debug_alive_count(), base); + EXPECT_EQ(m.ctx, nullptr); - EXPECT_EQ(oaknode_colormanager_init(nullptr), nullptr); + EXPECT_EQ(oaknode_colormanager_init(OakNodeProject{}).ctx, nullptr); oaknode_colormanager_free(nullptr); + + release_borrowed(ph); } TEST_F(ColorManagerTest, NullHandleReturnsInvalid) { char buf[64]; int count; - EXPECT_EQ(oaknode_colormanager_get_config_filename(nullptr, buf, + OakNodeColorManager empty = {}; + EXPECT_EQ(oaknode_colormanager_get_config_filename(empty, buf, sizeof(buf)), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_colormanager_initialize(nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_colormanager_get_display_count(nullptr, &count), + EXPECT_EQ(oaknode_colormanager_initialize(empty), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_colormanager_get_display_count(empty, &count), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_colormanager_set_config_filename(nullptr, "x"), + EXPECT_EQ(oaknode_colormanager_set_config_filename(empty, "x"), OAKNODE_E_INVALID); } TEST_F(ColorManagerTest, ConfigFilenameRoundTrip) { olive::Project project; - OakNodeColorManager *m = - oaknode_colormanager_init(reinterpret_cast(&project)); - ASSERT_NE(m, nullptr); + OakNodeProject ph = borrow_project(&project); + OakNodeColorManager m = oaknode_colormanager_init(ph); + ASSERT_NE(m.ctx, nullptr); ASSERT_EQ(oaknode_colormanager_set_config_filename(m, "/tmp/myconfig.ocio"), OAKNODE_OK); @@ -115,15 +140,16 @@ TEST_F(ColorManagerTest, ConfigFilenameRoundTrip) EXPECT_EQ(oaknode_colormanager_set_config_filename(m, nullptr), OAKNODE_E_INVALID); - oaknode_colormanager_free(m); + oaknode_colormanager_free(&m); + release_borrowed(ph); } TEST_F(ColorManagerTest, ProjectBackedColorSpaces) { olive::Project project; - OakNodeColorManager *m = - oaknode_colormanager_init(reinterpret_cast(&project)); - ASSERT_NE(m, nullptr); + OakNodeProject ph = borrow_project(&project); + OakNodeColorManager m = oaknode_colormanager_init(ph); + ASSERT_NE(m.ctx, nullptr); // Stored on the project: works even before a config is attached ASSERT_EQ(oaknode_colormanager_set_default_input_color_space(m, "Linear"), @@ -138,15 +164,16 @@ TEST_F(ColorManagerTest, ProjectBackedColorSpaces) get_string(oaknode_colormanager_get_reference_color_space, m); EXPECT_FALSE(ref.empty()); - oaknode_colormanager_free(m); + oaknode_colormanager_free(&m); + release_borrowed(ph); } TEST_F(ColorManagerTest, ConfigDependentCallsRequireConfig) { olive::Project project; - OakNodeColorManager *m = - oaknode_colormanager_init(reinterpret_cast(&project)); - ASSERT_NE(m, nullptr); + OakNodeProject ph = borrow_project(&project); + OakNodeColorManager m = oaknode_colormanager_init(ph); + ASSERT_NE(m.ctx, nullptr); // This manager never got initialize(): no config attached int count; @@ -167,15 +194,16 @@ TEST_F(ColorManagerTest, ConfigDependentCallsRequireConfig) sizeof(buf)), OAKNODE_E_STATE); - oaknode_colormanager_free(m); + oaknode_colormanager_free(&m); + release_borrowed(ph); } TEST_F(ColorManagerTest, InitializeAndQueryConfig) { olive::Project project; - OakNodeColorManager *m = - oaknode_colormanager_init(reinterpret_cast(&project)); - ASSERT_NE(m, nullptr); + OakNodeProject ph = borrow_project(&project); + OakNodeColorManager m = oaknode_colormanager_init(ph); + ASSERT_NE(m.ctx, nullptr); ASSERT_EQ(oaknode_colormanager_initialize(m), OAKNODE_OK); @@ -248,15 +276,16 @@ TEST_F(ColorManagerTest, InitializeAndQueryConfig) get_string(oaknode_colormanager_get_default_input_color_space, m)); - oaknode_colormanager_free(m); + oaknode_colormanager_free(&m); + release_borrowed(ph); } TEST_F(ColorManagerTest, CompliantColorTransform) { olive::Project project; - OakNodeColorManager *m = - oaknode_colormanager_init(reinterpret_cast(&project)); - ASSERT_NE(m, nullptr); + OakNodeProject ph = borrow_project(&project); + OakNodeColorManager m = oaknode_colormanager_init(ph); + ASSERT_NE(m.ctx, nullptr); ASSERT_EQ(oaknode_colormanager_initialize(m), OAKNODE_OK); std::string display = @@ -294,5 +323,6 @@ TEST_F(ColorManagerTest, CompliantColorTransform) &compliant), OAKNODE_E_INVALID); - oaknode_colormanager_free(m); + oaknode_colormanager_free(&m); + release_borrowed(ph); } diff --git a/src/node/tests/factory_test.cpp b/src/node/tests/factory_test.cpp index b29499d57..086088d2b 100644 --- a/src/node/tests/factory_test.cpp +++ b/src/node/tests/factory_test.cpp @@ -79,9 +79,9 @@ TEST_F(NodeFactoryTest, NodeAt) ASSERT_EQ(oaknode_factory_id_count(&count), OAKNODE_OK); ASSERT_GT(count, 0); - OakNodeNode *prototype = nullptr; + OakNodeNode prototype = {}; EXPECT_EQ(oaknode_factory_node_at(0, &prototype), OAKNODE_OK); - ASSERT_NE(prototype, nullptr); + ASSERT_NE(prototype.ctx, nullptr); // The prototype's id matches id_at(0). int required = oaknode_factory_id_at(0, nullptr, 0); @@ -93,6 +93,8 @@ TEST_F(NodeFactoryTest, NodeAt) ASSERT_GT(oaknode_node_get_id(prototype, buf, sizeof(buf)), 1); EXPECT_STREQ(buf, id.data()); + oaknode_node_free(&prototype); // borrowed: releases the handle only + EXPECT_EQ(oaknode_factory_node_at(count, &prototype), OAKNODE_E_NOT_FOUND); EXPECT_EQ(oaknode_factory_node_at(0, nullptr), OAKNODE_E_INVALID); } @@ -111,9 +113,9 @@ TEST_F(NodeFactoryTest, CreateFromId) name.data(), name_required), name_required); - OakNodeNode *node = + OakNodeNode node = oaknode_factory_create_from_id("org.olivevideoeditor.Olive.group"); - ASSERT_NE(node, nullptr); + ASSERT_NE(node.ctx, nullptr); EXPECT_EQ(oaknode_debug_alive_count(), alive_before + 1); char buf[256]; @@ -121,13 +123,17 @@ TEST_F(NodeFactoryTest, CreateFromId) EXPECT_STREQ(buf, "org.olivevideoeditor.Olive.group"); // The created instance is a group. - EXPECT_NE(oaknode_group_cast(node), nullptr); + OakNodeGroup as_group = oaknode_group_cast(node); + EXPECT_NE(as_group.ctx, nullptr); + oaknode_group_free(&as_group); - oaknode_node_free(node); + oaknode_node_free(&node); + EXPECT_EQ(node.ctx, nullptr); EXPECT_EQ(oaknode_debug_alive_count(), alive_before); - EXPECT_EQ(oaknode_factory_create_from_id("org.oak.DoesNotExist"), nullptr); - EXPECT_EQ(oaknode_factory_create_from_id(nullptr), nullptr); + EXPECT_EQ(oaknode_factory_create_from_id("org.oak.DoesNotExist").ctx, + nullptr); + EXPECT_EQ(oaknode_factory_create_from_id(nullptr).ctx, nullptr); } TEST(NodeFactoryUninitializedTest, StateErrors) @@ -138,7 +144,7 @@ TEST(NodeFactoryUninitializedTest, StateErrors) int count = 0; EXPECT_EQ(oaknode_factory_id_count(&count), OAKNODE_E_STATE); EXPECT_EQ(oaknode_factory_id_at(0, nullptr, 0), OAKNODE_E_STATE); - OakNodeNode *node = nullptr; + OakNodeNode node = {}; EXPECT_EQ(oaknode_factory_node_at(0, &node), OAKNODE_E_STATE); // Re-initialize for any subsequent test run. diff --git a/src/node/tests/folder_test.cpp b/src/node/tests/folder_test.cpp index 51000be7b..a7d8a982a 100644 --- a/src/node/tests/folder_test.cpp +++ b/src/node/tests/folder_test.cpp @@ -21,14 +21,27 @@ #include #include "node/folder.h" +#include "node/node.h" #include "node/project.h" +#include "../c_api/nodehandle.h" + namespace { -OakNodeNode *as_node(OakNodeFolder *folder) +OakNodeNode as_node(OakNodeFolder folder) { - return reinterpret_cast(folder); + return oaknode_folder_as_node(folder); +} + +/** + * @brief Identity compare for value handles (each handle has its own + * control block, so compare the wrapped objects). + */ +template +bool same_object(H1 a, H2 b) +{ + return oaknode_c_api::to_native(a) == oaknode_c_api::to_native(b); } /** @@ -40,15 +53,19 @@ OakNodeNode *as_node(OakNodeFolder *folder) * folder edges (upstream bug, reported to the parent agent). Removing * the edges first keeps teardown clean. */ -void detach_all(OakNodeFolder *root, OakNodeFolder *a, OakNodeFolder *b) +void detach_all(OakNodeFolder root, OakNodeFolder a, OakNodeFolder b) { - if (oaknode_folder_index_of_child(root, as_node(a)) >= 0) { + if (!root.ctx) { + return; + } + if (a.ctx && oaknode_folder_index_of_child(root, as_node(a)) >= 0) { oaknode_folder_remove_child(root, as_node(a)); } - if (oaknode_folder_index_of_child(root, as_node(b)) >= 0) { + if (b.ctx && oaknode_folder_index_of_child(root, as_node(b)) >= 0) { oaknode_folder_remove_child(root, as_node(b)); } - if (oaknode_folder_index_of_child(a, as_node(b)) >= 0) { + if (a.ctx && b.ctx && + oaknode_folder_index_of_child(a, as_node(b)) >= 0) { oaknode_folder_remove_child(a, as_node(b)); } } @@ -57,19 +74,19 @@ void detach_all(OakNodeFolder *root, OakNodeFolder *a, OakNodeFolder *b) TEST(NodeFolder, CreateAndHierarchy) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK); - OakNodeFolder *root = oaknode_project_root(project); - ASSERT_NE(root, nullptr); + OakNodeFolder root = oaknode_project_root(project); + ASSERT_NE(root.ctx, nullptr); // Creating a folder requires a project - EXPECT_EQ(oaknode_folder_create(nullptr), nullptr); + EXPECT_EQ(oaknode_folder_create(OakNodeProject{}).ctx, nullptr); - OakNodeFolder *a = oaknode_folder_create(project); - OakNodeFolder *b = oaknode_folder_create(project); - ASSERT_NE(a, nullptr); - ASSERT_NE(b, nullptr); + OakNodeFolder a = oaknode_folder_create(project); + OakNodeFolder b = oaknode_folder_create(project); + ASSERT_NE(a.ctx, nullptr); + ASSERT_NE(b.ctx, nullptr); EXPECT_EQ(oaknode_folder_child_count(root), 0); EXPECT_EQ(oaknode_folder_child_count(a), 0); @@ -77,9 +94,9 @@ TEST(NodeFolder, CreateAndHierarchy) // add_child builds the hierarchy EXPECT_EQ(oaknode_folder_add_child(root, as_node(a)), OAKNODE_OK); EXPECT_EQ(oaknode_folder_child_count(root), 1); - EXPECT_EQ(oaknode_folder_child_at(root, 0), as_node(a)); + EXPECT_TRUE(same_object(oaknode_folder_child_at(root, 0), as_node(a))); EXPECT_EQ(oaknode_folder_index_of_child(root, as_node(a)), 0); - EXPECT_EQ(oaknode_folder_parent_of(as_node(a)), root); + EXPECT_TRUE(same_object(oaknode_folder_parent_of(as_node(a)), root)); EXPECT_EQ(oaknode_folder_add_child(a, as_node(b)), OAKNODE_OK); EXPECT_EQ(oaknode_folder_has_child_recursive(root, as_node(b)), 1); @@ -89,32 +106,32 @@ TEST(NodeFolder, CreateAndHierarchy) // A node can only be in one folder at a time EXPECT_EQ(oaknode_folder_add_child(root, as_node(b)), OAKNODE_E_STATE); - // Out-of-range child access yields NULL - EXPECT_EQ(oaknode_folder_child_at(root, -1), nullptr); - EXPECT_EQ(oaknode_folder_child_at(root, 1), nullptr); + // Out-of-range child access yields an empty handle + EXPECT_EQ(oaknode_folder_child_at(root, -1).ctx, nullptr); + EXPECT_EQ(oaknode_folder_child_at(root, 1).ctx, nullptr); // index_of_child on a non-child is E_NOT_FOUND EXPECT_EQ(oaknode_folder_index_of_child(root, as_node(b)), OAKNODE_E_NOT_FOUND); detach_all(root, a, b); - oaknode_project_free(project); + oaknode_project_free(&project); } TEST(NodeFolder, RemoveChild) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK); - OakNodeFolder *root = oaknode_project_root(project); - OakNodeFolder *a = oaknode_folder_create(project); - ASSERT_NE(root, nullptr); - ASSERT_NE(a, nullptr); + OakNodeFolder root = oaknode_project_root(project); + OakNodeFolder a = oaknode_folder_create(project); + ASSERT_NE(root.ctx, nullptr); + ASSERT_NE(a.ctx, nullptr); ASSERT_EQ(oaknode_folder_add_child(root, as_node(a)), OAKNODE_OK); EXPECT_EQ(oaknode_folder_remove_child(root, as_node(a)), OAKNODE_OK); EXPECT_EQ(oaknode_folder_child_count(root), 0); - EXPECT_EQ(oaknode_folder_parent_of(as_node(a)), nullptr); + EXPECT_EQ(oaknode_folder_parent_of(as_node(a)).ctx, nullptr); // Removing a non-child is E_NOT_FOUND EXPECT_EQ(oaknode_folder_remove_child(root, as_node(a)), @@ -124,37 +141,37 @@ TEST(NodeFolder, RemoveChild) EXPECT_EQ(oaknode_folder_add_child(root, as_node(a)), OAKNODE_OK); EXPECT_EQ(oaknode_folder_child_count(root), 1); - detach_all(root, a, nullptr); - oaknode_project_free(project); + detach_all(root, a, OakNodeFolder{}); + oaknode_project_free(&project); } TEST(NodeFolder, MoveChildren) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK); - OakNodeFolder *root = oaknode_project_root(project); - OakNodeFolder *a = oaknode_folder_create(project); - OakNodeFolder *b = oaknode_folder_create(project); - OakNodeFolder *c = oaknode_folder_create(project); - ASSERT_NE(root, nullptr); - ASSERT_NE(a, nullptr); - ASSERT_NE(b, nullptr); - ASSERT_NE(c, nullptr); + OakNodeFolder root = oaknode_project_root(project); + OakNodeFolder a = oaknode_folder_create(project); + OakNodeFolder b = oaknode_folder_create(project); + OakNodeFolder c = oaknode_folder_create(project); + ASSERT_NE(root.ctx, nullptr); + ASSERT_NE(a.ctx, nullptr); + ASSERT_NE(b.ctx, nullptr); + ASSERT_NE(c.ctx, nullptr); ASSERT_EQ(oaknode_folder_add_child(root, as_node(a)), OAKNODE_OK); ASSERT_EQ(oaknode_folder_add_child(root, as_node(b)), OAKNODE_OK); ASSERT_EQ(oaknode_folder_add_child(a, as_node(c)), OAKNODE_OK); // Move b and c into a in one call - OakNodeNode *to_move[] = { as_node(b), as_node(c) }; + OakNodeNode to_move[] = { as_node(b), as_node(c) }; EXPECT_EQ(oaknode_folder_move_children(to_move, 2, a), OAKNODE_OK); EXPECT_EQ(oaknode_folder_child_count(root), 1); // c was already in a and is skipped; a ends up with {c, b} EXPECT_EQ(oaknode_folder_child_count(a), 2); - EXPECT_EQ(oaknode_folder_parent_of(as_node(b)), a); - EXPECT_EQ(oaknode_folder_parent_of(as_node(c)), a); + EXPECT_TRUE(same_object(oaknode_folder_parent_of(as_node(b)), a)); + EXPECT_TRUE(same_object(oaknode_folder_parent_of(as_node(c)), a)); EXPECT_EQ(oaknode_folder_index_of_child(a, as_node(c)), 0); EXPECT_EQ(oaknode_folder_index_of_child(a, as_node(b)), 1); @@ -164,29 +181,32 @@ TEST(NodeFolder, MoveChildren) // A node with no folder is simply appended EXPECT_EQ(oaknode_folder_remove_child(root, as_node(a)), OAKNODE_OK); - OakNodeNode *one[] = { as_node(a) }; + OakNodeNode one[] = { as_node(a) }; EXPECT_EQ(oaknode_folder_move_children(one, 1, root), OAKNODE_OK); - EXPECT_EQ(oaknode_folder_parent_of(as_node(a)), root); + EXPECT_TRUE(same_object(oaknode_folder_parent_of(as_node(a)), root)); // Detach every remaining edge before teardown (see detach_all). detach_all(root, a, b); detach_all(a, b, c); detach_all(b, a, c); - oaknode_project_free(project); + oaknode_project_free(&project); } TEST(NodeFolder, NullHandleErrors) { - EXPECT_EQ(oaknode_folder_child_count(nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_folder_child_at(nullptr, 0), nullptr); - EXPECT_EQ(oaknode_folder_add_child(nullptr, nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_folder_remove_child(nullptr, nullptr), + EXPECT_EQ(oaknode_folder_child_count(OakNodeFolder{}), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_folder_move_children(nullptr, 0, nullptr), + EXPECT_EQ(oaknode_folder_child_at(OakNodeFolder{}, 0).ctx, nullptr); + EXPECT_EQ(oaknode_folder_add_child(OakNodeFolder{}, OakNodeNode{}), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_folder_has_child_recursive(nullptr, nullptr), + EXPECT_EQ(oaknode_folder_remove_child(OakNodeFolder{}, OakNodeNode{}), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_folder_index_of_child(nullptr, nullptr), + EXPECT_EQ(oaknode_folder_move_children(nullptr, 0, OakNodeFolder{}), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_folder_parent_of(nullptr), nullptr); + EXPECT_EQ(oaknode_folder_has_child_recursive(OakNodeFolder{}, + OakNodeNode{}), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_folder_index_of_child(OakNodeFolder{}, OakNodeNode{}), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_folder_parent_of(OakNodeNode{}).ctx, nullptr); } diff --git a/src/node/tests/footage_test.cpp b/src/node/tests/footage_test.cpp index f20106261..94aff364a 100644 --- a/src/node/tests/footage_test.cpp +++ b/src/node/tests/footage_test.cpp @@ -33,8 +33,8 @@ namespace /** * @brief Two-stage string getter helper: query, then fetch. */ -std::string get_string(int (*fn)(const OakNodeFootage *, char *, int), - const OakNodeFootage *footage) +std::string get_string(int (*fn)(OakNodeFootage, char *, int), + OakNodeFootage footage) { int required = fn(footage, nullptr, 0); if (required <= 0) { @@ -49,14 +49,14 @@ std::string get_string(int (*fn)(const OakNodeFootage *, char *, int), TEST(NodeFootage, CreateAndFilename) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); // Creating footage requires a project - EXPECT_EQ(oaknode_footage_create(nullptr, nullptr), nullptr); + EXPECT_EQ(oaknode_footage_create(OakNodeProject{}, nullptr).ctx, nullptr); - OakNodeFootage *footage = oaknode_footage_create(project, nullptr); - ASSERT_NE(footage, nullptr); + OakNodeFootage footage = oaknode_footage_create(project, nullptr); + ASSERT_NE(footage.ctx, nullptr); EXPECT_EQ(get_string(oaknode_footage_filename, footage), ""); // A nonexistent path keeps the footage invalid but stores the name @@ -71,15 +71,15 @@ TEST(NodeFootage, CreateAndFilename) EXPECT_EQ(oaknode_footage_set_filename(footage, nullptr), OAKNODE_E_INVALID); - oaknode_project_free(project); + oaknode_project_free(&project); } TEST(NodeFootage, TimestampAndMetadataDefaults) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); - OakNodeFootage *footage = oaknode_footage_create(project, nullptr); - ASSERT_NE(footage, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); + OakNodeFootage footage = oaknode_footage_create(project, nullptr); + ASSERT_NE(footage.ctx, nullptr); int64_t timestamp = -1; EXPECT_EQ(oaknode_footage_timestamp(footage, ×tamp), OAKNODE_OK); @@ -102,15 +102,15 @@ TEST(NodeFootage, TimestampAndMetadataDefaults) OAKNODE_OK); EXPECT_EQ(numerator, 0); - oaknode_project_free(project); + oaknode_project_free(&project); } TEST(NodeFootage, Proxy) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); - OakNodeFootage *footage = oaknode_footage_create(project, nullptr); - ASSERT_NE(footage, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); + OakNodeFootage footage = oaknode_footage_create(project, nullptr); + ASSERT_NE(footage.ctx, nullptr); EXPECT_EQ(oaknode_footage_proxy_enabled(footage), 0); EXPECT_EQ(get_string(oaknode_footage_proxy_path, footage), ""); @@ -132,7 +132,7 @@ TEST(NodeFootage, Proxy) EXPECT_EQ(get_string(oaknode_footage_proxy_path, footage), ""); EXPECT_EQ(oaknode_footage_proxy_state(footage), 0); - oaknode_project_free(project); + oaknode_project_free(&project); } TEST(NodeFootage, NullHandleErrors) @@ -141,32 +141,40 @@ TEST(NodeFootage, NullHandleErrors) int num = 0; int den = 0; - EXPECT_EQ(oaknode_footage_filename(nullptr, nullptr, 0), + EXPECT_EQ(oaknode_footage_filename(OakNodeFootage{}, nullptr, 0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_set_filename(nullptr, "/tmp/x"), + EXPECT_EQ(oaknode_footage_set_filename(OakNodeFootage{}, "/tmp/x"), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_is_valid(nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_timestamp(nullptr, ×tamp), + EXPECT_EQ(oaknode_footage_is_valid(OakNodeFootage{}), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_footage_timestamp(OakNodeFootage{}, ×tamp), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_timestamp(nullptr, nullptr), + EXPECT_EQ(oaknode_footage_timestamp(OakNodeFootage{}, nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_set_timestamp(nullptr, 0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_decoder(nullptr, nullptr, 0), + EXPECT_EQ(oaknode_footage_set_timestamp(OakNodeFootage{}, 0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_total_stream_count(nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_video_stream_count(nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_audio_stream_count(nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_subtitle_stream_count(nullptr), + EXPECT_EQ(oaknode_footage_decoder(OakNodeFootage{}, nullptr, 0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_duration(nullptr, &num, &den), + EXPECT_EQ(oaknode_footage_total_stream_count(OakNodeFootage{}), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_proxy_enabled(nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_set_proxy_enabled(nullptr, 1), + EXPECT_EQ(oaknode_footage_video_stream_count(OakNodeFootage{}), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_proxy_path(nullptr, nullptr, 0), + EXPECT_EQ(oaknode_footage_audio_stream_count(OakNodeFootage{}), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_proxy_state(nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_set_proxy(nullptr, "/tmp/p", 2, 0, 1, 1), + EXPECT_EQ(oaknode_footage_subtitle_stream_count(OakNodeFootage{}), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_footage_duration(OakNodeFootage{}, &num, &den), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_footage_proxy_enabled(OakNodeFootage{}), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_footage_set_proxy_enabled(OakNodeFootage{}, 1), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_footage_proxy_path(OakNodeFootage{}, nullptr, 0), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_footage_proxy_state(OakNodeFootage{}), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_footage_set_proxy(OakNodeFootage{}, "/tmp/p", 2, 0, 1, + 1), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_footage_clear_proxy(OakNodeFootage{}), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_footage_clear_proxy(nullptr), OAKNODE_E_INVALID); } diff --git a/src/node/tests/group_test.cpp b/src/node/tests/group_test.cpp index ca0f522d1..fc94ba87a 100644 --- a/src/node/tests/group_test.cpp +++ b/src/node/tests/group_test.cpp @@ -26,6 +26,8 @@ #include "undo/undocommand.h" +#include "../c_api/nodehandle.h" +#include "../src/group/group.h" #include "testnode.h" namespace @@ -34,17 +36,28 @@ namespace using oaknode_test::TestNode; using oaknode_test::as_handle; +/** + * @brief Borrowed OakNodeNode view of a group handle (the group IS a + * node). Release with oaknode_node_free(). + */ +OakNodeNode group_as_node(OakNodeGroup group) +{ + return oaknode_c_api::make_handle( + oaknode_c_api::to_native(group), false, nullptr); +} + /** * @brief Create a group with `inner` registered in its context * (NodeGroup::add_input_passthrough() asserts context membership). */ -OakNodeGroup *make_group_with_inner(OakNodeNode *inner) +OakNodeGroup make_group_with_inner(OakNodeNode inner) { - OakNodeGroup *group = oaknode_group_create(); - EXPECT_NE(group, nullptr); - EXPECT_EQ(oaknode_node_set_context_position( - reinterpret_cast(group), inner, 0.0, 0.0, 0), + OakNodeGroup group = oaknode_group_create(); + EXPECT_NE(group.ctx, nullptr); + OakNodeNode node_view = group_as_node(group); + EXPECT_EQ(oaknode_node_set_context_position(node_view, inner, 0.0, 0.0, 0), OAKNODE_OK); + oaknode_node_free(&node_view); return group; } @@ -52,18 +65,24 @@ TEST(NodeGroupTest, CreateCastFree) { int alive_before = oaknode_debug_alive_count(); - OakNodeGroup *group = oaknode_group_create(); - ASSERT_NE(group, nullptr); + OakNodeGroup group = oaknode_group_create(); + ASSERT_NE(group.ctx, nullptr); EXPECT_EQ(oaknode_debug_alive_count(), alive_before + 1); - OakNodeNode *as_node = reinterpret_cast(group); - EXPECT_EQ(oaknode_group_cast(as_node), group); + OakNodeNode as_node = group_as_node(group); + OakNodeGroup casted = oaknode_group_cast(as_node); + ASSERT_NE(casted.ctx, nullptr); + EXPECT_EQ(oaknode_c_api::to_native(casted), + oaknode_c_api::to_native(group)); + oaknode_group_free(&casted); + oaknode_node_free(&as_node); TestNode plain; - EXPECT_EQ(oaknode_group_cast(as_handle(&plain)), nullptr); - EXPECT_EQ(oaknode_group_cast(nullptr), nullptr); + EXPECT_EQ(oaknode_group_cast(as_handle(&plain)).ctx, nullptr); + EXPECT_EQ(oaknode_group_cast(OakNodeNode{}).ctx, nullptr); - oaknode_group_free(group); + oaknode_group_free(&group); + EXPECT_EQ(group.ctx, nullptr); EXPECT_EQ(oaknode_debug_alive_count(), alive_before); oaknode_group_free(nullptr); // no crash @@ -72,10 +91,10 @@ TEST(NodeGroupTest, CreateCastFree) TEST(NodeGroupTest, PassthroughAddEnumerateRemove) { TestNode inner; - OakNodeNode *inner_handle = as_handle(&inner); - OakNodeGroup *group = make_group_with_inner(inner_handle); - ASSERT_NE(group, nullptr); - OakNodeNode *group_node = reinterpret_cast(group); + OakNodeNode inner_handle = as_handle(&inner); + OakNodeGroup group = make_group_with_inner(inner_handle); + ASSERT_NE(group.ctx, nullptr); + OakNodeNode group_node = group_as_node(group); // Two-stage: query the generated id size first. int float_required = oaknode_group_add_input_passthrough( @@ -102,15 +121,17 @@ TEST(NodeGroupTest, PassthroughAddEnumerateRemove) EXPECT_EQ(oaknode_node_input_get_type(group_node, buf, &type), OAKNODE_OK); EXPECT_EQ(type, OAKNODE_VALUE_FLOAT); - OakNodeNode *pt_node = nullptr; + OakNodeNode pt_node = {}; int pt_element = -2; char pt_id[64]; EXPECT_EQ(oaknode_group_passthrough_input_at(group, 0, &pt_node, pt_id, sizeof(pt_id), &pt_element), 9); - EXPECT_EQ(pt_node, inner_handle); + EXPECT_EQ(oaknode_c_api::to_native(pt_node), + oaknode_c_api::to_native(inner_handle)); EXPECT_STREQ(pt_id, "float_in"); EXPECT_EQ(pt_element, -1); + oaknode_node_free(&pt_node); EXPECT_EQ(oaknode_group_passthrough_input_at(group, 5, &pt_node, pt_id, sizeof(pt_id), &pt_element), OAKNODE_E_NOT_FOUND); @@ -123,18 +144,20 @@ TEST(NodeGroupTest, PassthroughAddEnumerateRemove) EXPECT_EQ(oaknode_group_remove_input_passthrough(group, inner_handle, "float_in", -1), OAKNODE_E_NOT_FOUND); - EXPECT_EQ(oaknode_group_remove_input_passthrough(nullptr, inner_handle, + EXPECT_EQ(oaknode_group_remove_input_passthrough(OakNodeGroup{}, + inner_handle, "float_in", -1), OAKNODE_E_INVALID); - oaknode_group_free(group); + oaknode_node_free(&group_node); + oaknode_group_free(&group); } TEST(NodeGroupTest, PassthroughAddUndoable) { TestNode inner; - OakNodeGroup *group = make_group_with_inner(as_handle(&inner)); - ASSERT_NE(group, nullptr); + OakNodeGroup group = make_group_with_inner(as_handle(&inner)); + ASSERT_NE(group.ctx, nullptr); OakUndoCommand command = {}; EXPECT_EQ(oaknode_group_add_input_passthrough_undoable( @@ -155,51 +178,56 @@ TEST(NodeGroupTest, PassthroughAddUndoable) EXPECT_EQ(count, 0); oakundo_command_free(&command); - oaknode_group_free(group); + oaknode_group_free(&group); } TEST(NodeGroupTest, OutputPassthroughLiveAndUndoable) { TestNode inner; - OakNodeNode *inner_handle = as_handle(&inner); - OakNodeGroup *group = make_group_with_inner(inner_handle); - ASSERT_NE(group, nullptr); + OakNodeNode inner_handle = as_handle(&inner); + OakNodeGroup group = make_group_with_inner(inner_handle); + ASSERT_NE(group.ctx, nullptr); - OakNodeNode *out = reinterpret_cast(uintptr_t(1)); + OakNodeNode out = {}; + out.ctx = reinterpret_cast(uintptr_t(1)); // sentinel: must be overwritten EXPECT_EQ(oaknode_group_get_output_passthrough(group, &out), OAKNODE_OK); - EXPECT_EQ(out, nullptr); + EXPECT_EQ(out.ctx, nullptr); EXPECT_EQ(oaknode_group_set_output_passthrough(group, inner_handle), OAKNODE_OK); EXPECT_EQ(oaknode_group_get_output_passthrough(group, &out), OAKNODE_OK); - EXPECT_EQ(out, inner_handle); + EXPECT_EQ(oaknode_c_api::to_native(out), + oaknode_c_api::to_native(inner_handle)); + oaknode_node_free(&out); OakUndoCommand command = {}; - EXPECT_EQ(oaknode_group_set_output_passthrough_undoable(group, nullptr, - &command), + EXPECT_EQ(oaknode_group_set_output_passthrough_undoable( + group, OakNodeNode{}, &command), OAKNODE_OK); ASSERT_NE(command.ctx, nullptr); EXPECT_EQ(oakundo_command_redo_now(command), OAKUNDO_OK); EXPECT_EQ(oaknode_group_get_output_passthrough(group, &out), OAKNODE_OK); - EXPECT_EQ(out, nullptr); + EXPECT_EQ(out.ctx, nullptr); EXPECT_EQ(oakundo_command_undo_now(command), OAKUNDO_OK); EXPECT_EQ(oaknode_group_get_output_passthrough(group, &out), OAKNODE_OK); - EXPECT_EQ(out, inner_handle); + EXPECT_EQ(oaknode_c_api::to_native(out), + oaknode_c_api::to_native(inner_handle)); + oaknode_node_free(&out); oakundo_command_free(&command); - EXPECT_EQ(oaknode_group_set_output_passthrough(nullptr, inner_handle), + EXPECT_EQ(oaknode_group_set_output_passthrough(OakNodeGroup{}, inner_handle), OAKNODE_E_INVALID); - oaknode_group_free(group); + oaknode_group_free(&group); } TEST(NodeGroupTest, ResolveInput) { TestNode inner; - OakNodeNode *inner_handle = as_handle(&inner); - OakNodeGroup *group = make_group_with_inner(inner_handle); - ASSERT_NE(group, nullptr); - OakNodeNode *group_node = reinterpret_cast(group); + OakNodeNode inner_handle = as_handle(&inner); + OakNodeGroup group = make_group_with_inner(inner_handle); + ASSERT_NE(group.ctx, nullptr); + OakNodeNode group_node = group_as_node(group); char id_buf[64]; int required = oaknode_group_add_input_passthrough(group, inner_handle, @@ -208,7 +236,7 @@ TEST(NodeGroupTest, ResolveInput) ASSERT_GT(required, 1); // Resolving the group's passthrough id yields the inner input. - OakNodeNode *resolved_node = nullptr; + OakNodeNode resolved_node = {}; int resolved_element = -2; char resolved_id[64]; EXPECT_EQ(oaknode_group_resolve_input(group_node, id_buf, -1, @@ -216,9 +244,11 @@ TEST(NodeGroupTest, ResolveInput) sizeof(resolved_id), &resolved_element), 9); - EXPECT_EQ(resolved_node, inner_handle); + EXPECT_EQ(oaknode_c_api::to_native(resolved_node), + oaknode_c_api::to_native(inner_handle)); EXPECT_STREQ(resolved_id, "float_in"); EXPECT_EQ(resolved_element, -1); + oaknode_node_free(&resolved_node); // A non-group input resolves to itself. EXPECT_EQ(oaknode_group_resolve_input(inner_handle, "float_in", -1, @@ -226,12 +256,15 @@ TEST(NodeGroupTest, ResolveInput) sizeof(resolved_id), &resolved_element), 9); - EXPECT_EQ(resolved_node, inner_handle); + EXPECT_EQ(oaknode_c_api::to_native(resolved_node), + oaknode_c_api::to_native(inner_handle)); EXPECT_STREQ(resolved_id, "float_in"); + oaknode_node_free(&resolved_node); // Error paths. - EXPECT_EQ(oaknode_group_resolve_input(nullptr, id_buf, -1, &resolved_node, - resolved_id, sizeof(resolved_id), + EXPECT_EQ(oaknode_group_resolve_input(OakNodeNode{}, id_buf, -1, + &resolved_node, resolved_id, + sizeof(resolved_id), &resolved_element), OAKNODE_E_INVALID); EXPECT_EQ(oaknode_group_resolve_input(group_node, nullptr, -1, @@ -240,7 +273,8 @@ TEST(NodeGroupTest, ResolveInput) &resolved_element), OAKNODE_E_INVALID); - oaknode_group_free(group); + oaknode_node_free(&group_node); + oaknode_group_free(&group); } } diff --git a/src/node/tests/keyframe_test.cpp b/src/node/tests/keyframe_test.cpp index 1fa5d84e9..af1f7cf2c 100644 --- a/src/node/tests/keyframe_test.cpp +++ b/src/node/tests/keyframe_test.cpp @@ -24,6 +24,7 @@ #include "undo/undocommand.h" +#include "../c_api/nodehandle.h" #include "testnode.h" namespace @@ -53,16 +54,16 @@ TEST(NodeKeyframeTest, EnumOrdinalsArePinned) TEST(NodeKeyframeTest, CreateAccessorsFree) { TestNode node; - OakNodeNode *node_handle = as_handle(&node); + OakNodeNode node_handle = as_handle(&node); int alive_before = oaknode_debug_alive_count(); oaknode_value value = make_float(1.5); - OakNodeKeyframe *key = oaknode_keyframe_create(2, 1, &value, - OAKNODE_KEYFRAME_LINEAR, 0, - -1, "float_in", - node_handle); - ASSERT_NE(key, nullptr); + OakNodeKeyframe key = oaknode_keyframe_create(2, 1, &value, + OAKNODE_KEYFRAME_LINEAR, 0, + -1, "float_in", + node_handle); + ASSERT_NE(key.ctx, nullptr); EXPECT_EQ(oaknode_debug_alive_count(), alive_before + 1); int64_t num = 0, den = 0; @@ -88,11 +89,14 @@ TEST(NodeKeyframeTest, CreateAccessorsFree) EXPECT_EQ(oaknode_keyframe_get_input(key, buf, sizeof(buf)), 9); EXPECT_STREQ(buf, "float_in"); - OakNodeNode *parent = nullptr; + OakNodeNode parent = {}; EXPECT_EQ(oaknode_keyframe_get_parent(key, &parent), OAKNODE_OK); - EXPECT_EQ(parent, node_handle); + EXPECT_EQ(oaknode_c_api::to_native(parent), + oaknode_c_api::to_native(node_handle)); + oaknode_node_free(&parent); - oaknode_keyframe_free(key); + oaknode_keyframe_free(&key); + EXPECT_EQ(key.ctx, nullptr); EXPECT_EQ(oaknode_debug_alive_count(), alive_before); oaknode_keyframe_free(nullptr); // no crash @@ -102,21 +106,25 @@ TEST(NodeKeyframeTest, CreateErrorPaths) { oaknode_value value = make_float(1.0); // Invalid interpolation type. - EXPECT_EQ(oaknode_keyframe_create(0, 1, &value, 99, 0, -1, "x", nullptr), + EXPECT_EQ(oaknode_keyframe_create(0, 1, &value, 99, 0, -1, "x", + OakNodeNode{}) + .ctx, nullptr); // STRING does not fit the POD. value.type = OAKNODE_VALUE_STRING; EXPECT_EQ(oaknode_keyframe_create(0, 1, &value, OAKNODE_KEYFRAME_LINEAR, 0, - -1, "x", nullptr), + -1, "x", OakNodeNode{}) + .ctx, nullptr); } TEST(NodeKeyframeTest, TimeLiveAndUndoable) { - OakNodeKeyframe *key = oaknode_keyframe_create(0, 1, nullptr, - OAKNODE_KEYFRAME_LINEAR, 0, - -1, "float_in", nullptr); - ASSERT_NE(key, nullptr); + OakNodeKeyframe key = oaknode_keyframe_create(0, 1, nullptr, + OAKNODE_KEYFRAME_LINEAR, 0, + -1, "float_in", + OakNodeNode{}); + ASSERT_NE(key.ctx, nullptr); EXPECT_EQ(oaknode_keyframe_set_time(key, 1, 2), OAKNODE_OK); int64_t num = 0, den = 0; @@ -139,19 +147,21 @@ TEST(NodeKeyframeTest, TimeLiveAndUndoable) EXPECT_EQ(num, 1); oakundo_command_free(&command); - oaknode_keyframe_free(key); + oaknode_keyframe_free(&key); - EXPECT_EQ(oaknode_keyframe_set_time(nullptr, 0, 1), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_keyframe_get_time(nullptr, &num, &den), + EXPECT_EQ(oaknode_keyframe_set_time(OakNodeKeyframe{}, 0, 1), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_keyframe_get_time(OakNodeKeyframe{}, &num, &den), OAKNODE_E_INVALID); } TEST(NodeKeyframeTest, ValueLiveAndUndoable) { - OakNodeKeyframe *key = oaknode_keyframe_create(0, 1, nullptr, - OAKNODE_KEYFRAME_LINEAR, 0, - -1, "float_in", nullptr); - ASSERT_NE(key, nullptr); + OakNodeKeyframe key = oaknode_keyframe_create(0, 1, nullptr, + OAKNODE_KEYFRAME_LINEAR, 0, + -1, "float_in", + OakNodeNode{}); + ASSERT_NE(key.ctx, nullptr); oaknode_value in = make_float(3.25); EXPECT_EQ(oaknode_keyframe_set_value(key, &in), OAKNODE_OK); @@ -180,15 +190,15 @@ TEST(NodeKeyframeTest, ValueLiveAndUndoable) EXPECT_EQ(oaknode_keyframe_set_value(key, &in), OAKNODE_E_INVALID); EXPECT_EQ(oaknode_keyframe_set_value(key, nullptr), OAKNODE_E_INVALID); - oaknode_keyframe_free(key); + oaknode_keyframe_free(&key); } TEST(NodeKeyframeTest, StringValueLiveAndUndoable) { - OakNodeKeyframe *key = oaknode_keyframe_create(0, 1, nullptr, - OAKNODE_KEYFRAME_HOLD, 0, -1, - "text_in", nullptr); - ASSERT_NE(key, nullptr); + OakNodeKeyframe key = oaknode_keyframe_create(0, 1, nullptr, + OAKNODE_KEYFRAME_HOLD, 0, -1, + "text_in", OakNodeNode{}); + ASSERT_NE(key.ctx, nullptr); EXPECT_EQ(oaknode_keyframe_set_value_string(key, "hello"), OAKNODE_OK); char buf[16]; @@ -210,18 +220,20 @@ TEST(NodeKeyframeTest, StringValueLiveAndUndoable) EXPECT_EQ(oaknode_keyframe_set_value_string(key, nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_keyframe_get_value_string(nullptr, buf, sizeof(buf)), + EXPECT_EQ(oaknode_keyframe_get_value_string(OakNodeKeyframe{}, buf, + sizeof(buf)), OAKNODE_E_INVALID); - oaknode_keyframe_free(key); + oaknode_keyframe_free(&key); } TEST(NodeKeyframeTest, TypeLiveAndUndoable) { - OakNodeKeyframe *key = oaknode_keyframe_create(0, 1, nullptr, - OAKNODE_KEYFRAME_LINEAR, 0, - -1, "float_in", nullptr); - ASSERT_NE(key, nullptr); + OakNodeKeyframe key = oaknode_keyframe_create(0, 1, nullptr, + OAKNODE_KEYFRAME_LINEAR, 0, + -1, "float_in", + OakNodeNode{}); + ASSERT_NE(key.ctx, nullptr); EXPECT_EQ(oaknode_keyframe_set_type(key, OAKNODE_KEYFRAME_HOLD), OAKNODE_OK); @@ -243,18 +255,20 @@ TEST(NodeKeyframeTest, TypeLiveAndUndoable) oakundo_command_free(&command); EXPECT_EQ(oaknode_keyframe_set_type(key, 99), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_keyframe_set_type(nullptr, OAKNODE_KEYFRAME_HOLD), + EXPECT_EQ(oaknode_keyframe_set_type(OakNodeKeyframe{}, + OAKNODE_KEYFRAME_HOLD), OAKNODE_E_INVALID); - oaknode_keyframe_free(key); + oaknode_keyframe_free(&key); } TEST(NodeKeyframeTest, BezierControlLiveAndUndoable) { - OakNodeKeyframe *key = oaknode_keyframe_create(0, 1, nullptr, - OAKNODE_KEYFRAME_BEZIER, 0, - -1, "float_in", nullptr); - ASSERT_NE(key, nullptr); + OakNodeKeyframe key = oaknode_keyframe_create(0, 1, nullptr, + OAKNODE_KEYFRAME_BEZIER, 0, + -1, "float_in", + OakNodeNode{}); + ASSERT_NE(key.ctx, nullptr); EXPECT_EQ(oaknode_keyframe_set_bezier_control( key, OAKNODE_KEYFRAME_IN_HANDLE, -1.0, 0.5), @@ -287,12 +301,12 @@ TEST(NodeKeyframeTest, BezierControlLiveAndUndoable) EXPECT_EQ(oaknode_keyframe_set_bezier_control(key, 99, 0.0, 0.0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_keyframe_get_bezier_control(nullptr, + EXPECT_EQ(oaknode_keyframe_get_bezier_control(OakNodeKeyframe{}, OAKNODE_KEYFRAME_IN_HANDLE, &x, &y), OAKNODE_E_INVALID); - oaknode_keyframe_free(key); + oaknode_keyframe_free(&key); } } diff --git a/src/node/tests/node_test.cpp b/src/node/tests/node_test.cpp index da00824c1..1a37a29c9 100644 --- a/src/node/tests/node_test.cpp +++ b/src/node/tests/node_test.cpp @@ -35,9 +35,9 @@ namespace using oaknode_test::TestNode; using oaknode_test::as_handle; +using oaknode_test::same_node; -std::string get_string(int (*fn)(const OakNodeNode *, char *, int), - const OakNodeNode *node) +std::string get_string(int (*fn)(OakNodeNode, char *, int), OakNodeNode node) { int required = fn(node, nullptr, 0); EXPECT_GT(required, 0); @@ -72,7 +72,7 @@ TEST(NodeValueMappingTest, OakEnumOrdinalsArePinned) TEST(NodeValueMappingTest, OliveToOakMappingIsPinned) { TestNode node; - OakNodeNode *handle = as_handle(&node); + OakNodeNode handle = as_handle(&node); struct { const char *input; @@ -90,12 +90,14 @@ TEST(NodeValueMappingTest, OliveToOakMappingIsPinned) OAKNODE_OK); EXPECT_EQ(type, c.expected) << c.input; } + + oaknode_node_free(&handle); } TEST(NodeMetadataTest, IdNameLabelRoundtrip) { TestNode node; - OakNodeNode *handle = as_handle(&node); + OakNodeNode handle = as_handle(&node); EXPECT_EQ(get_string(oaknode_node_get_id, handle), TestNode::k_id); EXPECT_EQ(get_string(oaknode_node_get_name, handle), "Test Node"); @@ -108,12 +110,14 @@ TEST(NodeMetadataTest, IdNameLabelRoundtrip) char small[3]; EXPECT_EQ(oaknode_node_get_label(handle, small, sizeof(small)), 5); EXPECT_STREQ(small, "he"); + + oaknode_node_free(&handle); } TEST(NodeMetadataTest, LabelUndoableSymmetry) { TestNode node; - OakNodeNode *handle = as_handle(&node); + OakNodeNode handle = as_handle(&node); EXPECT_EQ(oaknode_node_set_label(handle, "before"), OAKNODE_OK); @@ -129,12 +133,13 @@ TEST(NodeMetadataTest, LabelUndoableSymmetry) EXPECT_EQ(get_string(oaknode_node_get_label, handle), "before"); oakundo_command_free(&command); + oaknode_node_free(&handle); } TEST(NodeMetadataTest, OverrideColorLiveAndUndoable) { TestNode node; - OakNodeNode *handle = as_handle(&node); + OakNodeNode handle = as_handle(&node); int color = -2; EXPECT_EQ(oaknode_node_get_override_color(handle, &color), OAKNODE_OK); @@ -155,12 +160,13 @@ TEST(NodeMetadataTest, OverrideColorLiveAndUndoable) EXPECT_EQ(oaknode_node_get_override_color(handle, &color), OAKNODE_OK); EXPECT_EQ(color, 3); oakundo_command_free(&command); + oaknode_node_free(&handle); } TEST(NodeMetadataTest, EnabledLiveAndUndoable) { TestNode node; - OakNodeNode *handle = as_handle(&node); + OakNodeNode handle = as_handle(&node); int enabled = 0; EXPECT_EQ(oaknode_node_is_enabled(handle, &enabled), OAKNODE_OK); @@ -181,12 +187,13 @@ TEST(NodeMetadataTest, EnabledLiveAndUndoable) EXPECT_EQ(oaknode_node_is_enabled(handle, &enabled), OAKNODE_OK); EXPECT_EQ(enabled, 0); oakundo_command_free(&command); + oaknode_node_free(&handle); } TEST(NodeInputTest, EnumerateInputs) { TestNode node; - OakNodeNode *handle = as_handle(&node); + OakNodeNode handle = as_handle(&node); int count = 0; EXPECT_EQ(oaknode_node_input_count(handle, &count), OAKNODE_OK); @@ -212,12 +219,14 @@ TEST(NodeInputTest, EnumerateInputs) sizeof(buf)), 8); EXPECT_STREQ(buf, "Enabled"); + + oaknode_node_free(&handle); } TEST(NodeInputTest, ValueRoundtrip) { TestNode node; - OakNodeNode *handle = as_handle(&node); + OakNodeNode handle = as_handle(&node); oaknode_value out; EXPECT_EQ(oaknode_node_get_input(handle, "float_in", &out), OAKNODE_OK); @@ -268,15 +277,18 @@ TEST(NodeInputTest, ValueRoundtrip) EXPECT_EQ(out.type, OAKNODE_VALUE_COLOR); EXPECT_FLOAT_EQ(float(out.f[0]), 0.1f); EXPECT_FLOAT_EQ(float(out.f[3]), 0.4f); + + oaknode_node_free(&handle); } TEST(NodeInputTest, ValueErrorPaths) { TestNode node; - OakNodeNode *handle = as_handle(&node); + OakNodeNode handle = as_handle(&node); + OakNodeNode empty = {}; oaknode_value out; - EXPECT_EQ(oaknode_node_get_input(nullptr, "float_in", &out), + EXPECT_EQ(oaknode_node_get_input(empty, "float_in", &out), OAKNODE_E_INVALID); EXPECT_EQ(oaknode_node_get_input(handle, "unknown_in", &out), OAKNODE_E_NOT_FOUND); @@ -289,17 +301,19 @@ TEST(NodeInputTest, ValueErrorPaths) EXPECT_EQ(oaknode_node_set_input(handle, "int_in", &in), OAKNODE_E_INVALID); EXPECT_EQ(oaknode_node_set_input(handle, "unknown_in", &in), OAKNODE_E_NOT_FOUND); - EXPECT_EQ(oaknode_node_set_input(nullptr, "int_in", &in), + EXPECT_EQ(oaknode_node_set_input(empty, "int_in", &in), OAKNODE_E_INVALID); in.type = OAKNODE_VALUE_STRING; EXPECT_EQ(oaknode_node_set_input(handle, "text_in", &in), OAKNODE_E_INVALID); + + oaknode_node_free(&handle); } TEST(NodeInputTest, ValueUndoableSymmetry) { TestNode node; - OakNodeNode *handle = as_handle(&node); + OakNodeNode handle = as_handle(&node); oaknode_value in = make_float(9.0); OakUndoCommand command = {}; @@ -318,12 +332,13 @@ TEST(NodeInputTest, ValueUndoableSymmetry) EXPECT_EQ(oaknode_node_get_input(handle, "float_in", &out), OAKNODE_OK); EXPECT_DOUBLE_EQ(out.f[0], 0.0); oakundo_command_free(&command); + oaknode_node_free(&handle); } TEST(NodeInputTest, StringValueLiveAndUndoable) { TestNode node; - OakNodeNode *handle = as_handle(&node); + OakNodeNode handle = as_handle(&node); char buf[64]; EXPECT_EQ(oaknode_node_get_input_string(handle, "text_in", buf, @@ -359,14 +374,16 @@ TEST(NodeInputTest, StringValueLiveAndUndoable) 6); EXPECT_STREQ(buf, "hello"); oakundo_command_free(&command); + oaknode_node_free(&handle); } TEST(NodeGraphTest, ConnectDisconnectLive) { TestNode source; TestNode dest; - OakNodeNode *src = as_handle(&source); - OakNodeNode *dst = as_handle(&dest); + OakNodeNode src = as_handle(&source); + OakNodeNode dst = as_handle(&dest); + OakNodeNode empty = {}; int connected = -1; EXPECT_EQ(oaknode_node_input_is_connected(dst, "float_in", &connected), @@ -378,19 +395,19 @@ TEST(NodeGraphTest, ConnectDisconnectLive) OAKNODE_OK); EXPECT_EQ(connected, 1); - OakNodeNode *upstream = nullptr; + OakNodeNode upstream = {}; EXPECT_EQ(oaknode_node_input_get_connected_node(dst, "float_in", &upstream), OAKNODE_OK); - EXPECT_EQ(upstream, src); + EXPECT_TRUE(same_node(upstream, &source)); // Output side enumeration. int count = 0; EXPECT_EQ(oaknode_node_output_connection_count(src, &count), OAKNODE_OK); EXPECT_EQ(count, 1); - OakNodeNode *downstream = nullptr; + OakNodeNode downstream = {}; EXPECT_EQ(oaknode_node_output_connection_node_at(src, 0, &downstream), OAKNODE_OK); - EXPECT_EQ(downstream, dst); + EXPECT_TRUE(same_node(downstream, &dest)); char buf[64]; EXPECT_EQ(oaknode_node_output_connection_input_id_at(src, 0, buf, sizeof(buf)), @@ -414,15 +431,20 @@ TEST(NodeGraphTest, ConnectDisconnectLive) OAKNODE_OK); EXPECT_EQ(connected, 0); EXPECT_EQ(oaknode_node_disconnect(dst, "float_in"), OAKNODE_E_NOT_FOUND); - EXPECT_EQ(oaknode_node_disconnect(nullptr, "float_in"), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_node_disconnect(empty, "float_in"), OAKNODE_E_INVALID); + + oaknode_node_free(&downstream); + oaknode_node_free(&upstream); + oaknode_node_free(&dst); + oaknode_node_free(&src); } TEST(NodeGraphTest, ConnectDisconnectUndoable) { TestNode source; TestNode dest; - OakNodeNode *src = as_handle(&source); - OakNodeNode *dst = as_handle(&dest); + OakNodeNode src = as_handle(&source); + OakNodeNode dst = as_handle(&dest); OakUndoCommand add = {}; EXPECT_EQ(oaknode_node_connect_undoable(src, dst, "float_in", &add), @@ -460,14 +482,17 @@ TEST(NodeGraphTest, ConnectDisconnectUndoable) oakundo_command_free(&remove); oakundo_command_free(&add); + oaknode_node_free(&dst); + oaknode_node_free(&src); } TEST(NodeLinkTest, LinkUnlinkLiveAndUndoable) { TestNode a_node; TestNode b_node; - OakNodeNode *a = as_handle(&a_node); - OakNodeNode *b = as_handle(&b_node); + OakNodeNode a = as_handle(&a_node); + OakNodeNode b = as_handle(&b_node); + OakNodeNode empty = {}; int linked = -1; EXPECT_EQ(oaknode_node_are_linked(a, b, &linked), OAKNODE_OK); @@ -482,9 +507,9 @@ TEST(NodeLinkTest, LinkUnlinkLiveAndUndoable) int count = 0; EXPECT_EQ(oaknode_node_link_count(a, &count), OAKNODE_OK); EXPECT_EQ(count, 1); - OakNodeNode *other = nullptr; + OakNodeNode other = {}; EXPECT_EQ(oaknode_node_link_at(a, 0, &other), OAKNODE_OK); - EXPECT_EQ(other, b); + EXPECT_TRUE(same_node(other, &b_node)); EXPECT_EQ(oaknode_node_link_at(a, 1, &other), OAKNODE_E_NOT_FOUND); EXPECT_EQ(oaknode_node_unlink(a, b, &done), OAKNODE_OK); @@ -503,15 +528,19 @@ TEST(NodeLinkTest, LinkUnlinkLiveAndUndoable) EXPECT_EQ(linked, 0); oakundo_command_free(&command); - EXPECT_EQ(oaknode_node_link(nullptr, b, &done), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_node_link(empty, b, &done), OAKNODE_E_INVALID); + + oaknode_node_free(&other); + oaknode_node_free(&b); + oaknode_node_free(&a); } TEST(NodeContextTest, PositionsLiveAndUndoable) { TestNode node; TestNode context; - OakNodeNode *n = as_handle(&node); - OakNodeNode *ctx = as_handle(&context); + OakNodeNode n = as_handle(&node); + OakNodeNode ctx = as_handle(&context); double x = 0.0, y = 0.0; int expanded = -1; @@ -529,9 +558,9 @@ TEST(NodeContextTest, PositionsLiveAndUndoable) int count = 0; EXPECT_EQ(oaknode_node_context_count(n, &count), OAKNODE_OK); EXPECT_EQ(count, 1); - OakNodeNode *entry = nullptr; + OakNodeNode entry = {}; EXPECT_EQ(oaknode_node_context_node_at(n, 0, &entry), OAKNODE_OK); - EXPECT_EQ(entry, ctx); + EXPECT_TRUE(same_node(entry, &context)); EXPECT_EQ(oaknode_node_context_node_at(n, 1, &entry), OAKNODE_E_NOT_FOUND); OakUndoCommand command = {}; @@ -555,46 +584,54 @@ TEST(NodeContextTest, PositionsLiveAndUndoable) EXPECT_EQ(oaknode_node_remove_from_context(n, ctx), OAKNODE_E_NOT_FOUND); EXPECT_EQ(oaknode_node_context_count(n, &count), OAKNODE_OK); EXPECT_EQ(count, 0); + + oaknode_node_free(&entry); + oaknode_node_free(&ctx); + oaknode_node_free(&n); } TEST(NodeLifetimeTest, CopyAndFree) { TestNode node; + OakNodeNode handle = as_handle(&node); + OakNodeNode empty = {}; oaknode_value in = make_float(5.0); - EXPECT_EQ(oaknode_node_set_input(as_handle(&node), "float_in", &in), - OAKNODE_OK); + EXPECT_EQ(oaknode_node_set_input(handle, "float_in", &in), OAKNODE_OK); int alive_before = oaknode_debug_alive_count(); - OakNodeNode *copy = oaknode_node_create_copy(as_handle(&node)); - ASSERT_NE(copy, nullptr); + OakNodeNode copy = oaknode_node_create_copy(handle); + ASSERT_NE(copy.ctx, nullptr); EXPECT_EQ(oaknode_debug_alive_count(), alive_before + 1); // copy() clones the type, not the values. EXPECT_EQ(get_string(oaknode_node_get_id, copy), TestNode::k_id); - oaknode_node_free(copy); + oaknode_node_free(©); EXPECT_EQ(oaknode_debug_alive_count(), alive_before); oaknode_node_free(nullptr); // no crash - EXPECT_EQ(oaknode_node_create_copy(nullptr), nullptr); + EXPECT_EQ(oaknode_node_create_copy(empty).ctx, nullptr); + + oaknode_node_free(&handle); } TEST(NodeHandleTest, NullHandleErrors) { + OakNodeNode empty = {}; char buf[16]; int value = 0; - EXPECT_EQ(oaknode_node_get_id(nullptr, buf, sizeof(buf)), + EXPECT_EQ(oaknode_node_get_id(empty, buf, sizeof(buf)), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_node_set_label(nullptr, "x"), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_node_get_override_color(nullptr, &value), + EXPECT_EQ(oaknode_node_set_label(empty, "x"), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_node_get_override_color(empty, &value), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_node_is_enabled(nullptr, &value), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_node_input_count(nullptr, &value), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_node_are_linked(nullptr, nullptr, &value), + EXPECT_EQ(oaknode_node_is_enabled(empty, &value), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_node_input_count(empty, &value), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_node_are_linked(empty, empty, &value), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_node_context_count(nullptr, &value), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_node_output_connection_count(nullptr, &value), + EXPECT_EQ(oaknode_node_context_count(empty, &value), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_node_output_connection_count(empty, &value), OAKNODE_E_INVALID); } diff --git a/src/node/tests/project_test.cpp b/src/node/tests/project_test.cpp index 905f6d422..1c2fc7ba9 100644 --- a/src/node/tests/project_test.cpp +++ b/src/node/tests/project_test.cpp @@ -25,16 +25,19 @@ #include #include "node/folder.h" +#include "node/node.h" #include "node/project.h" +#include "../c_api/nodehandle.h" + namespace { /** * @brief Two-stage string getter helper: query, then fetch. */ -std::string get_string(int (*fn)(const OakNodeProject *, char *, int), - const OakNodeProject *project) +std::string get_string(int (*fn)(OakNodeProject, char *, int), + OakNodeProject project) { int required = fn(project, nullptr, 0); if (required <= 0) { @@ -45,23 +48,33 @@ std::string get_string(int (*fn)(const OakNodeProject *, char *, int), return std::string(buf.data()); } +/** + * @brief Identity compare for value handles (each handle has its own + * control block, so compare the wrapped objects). + */ +bool same_object(OakNodeNode a, OakNodeNode b) +{ + return oaknode_c_api::to_native(a) == oaknode_c_api::to_native(b); +} + } // namespace TEST(NodeProject, InitAndFree) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); // No root folder before initialize() - EXPECT_EQ(oaknode_project_root(project), nullptr); + EXPECT_EQ(oaknode_project_root(project).ctx, nullptr); EXPECT_EQ(oaknode_project_initialize(project), OAKNODE_OK); - EXPECT_NE(oaknode_project_root(project), nullptr); + EXPECT_NE(oaknode_project_root(project).ctx, nullptr); // Initializing twice is a state error EXPECT_EQ(oaknode_project_initialize(project), OAKNODE_E_STATE); - oaknode_project_free(project); + oaknode_project_free(&project); + EXPECT_EQ(project.ctx, nullptr); // NULL free is a no-op (must not crash) oaknode_project_free(nullptr); @@ -69,39 +82,45 @@ TEST(NodeProject, InitAndFree) TEST(NodeProject, NullHandleErrors) { - EXPECT_EQ(oaknode_project_initialize(nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_clear(nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_root(nullptr), nullptr); - EXPECT_EQ(oaknode_project_name(nullptr, nullptr, 0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_filename(nullptr, nullptr, 0), + EXPECT_EQ(oaknode_project_initialize(OakNodeProject{}), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_pretty_filename(nullptr, nullptr, 0), + EXPECT_EQ(oaknode_project_clear(OakNodeProject{}), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_project_root(OakNodeProject{}).ctx, nullptr); + EXPECT_EQ(oaknode_project_name(OakNodeProject{}, nullptr, 0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_set_filename(nullptr, "/tmp/x.ove"), + EXPECT_EQ(oaknode_project_filename(OakNodeProject{}, nullptr, 0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_is_modified(nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_set_modified(nullptr, 1), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_is_new(nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_cache_path(nullptr, nullptr, 0), + EXPECT_EQ(oaknode_project_pretty_filename(OakNodeProject{}, nullptr, 0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_get_cache_location_setting(nullptr), + EXPECT_EQ(oaknode_project_set_filename(OakNodeProject{}, "/tmp/x.ove"), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_set_cache_location_setting(nullptr, 0), + EXPECT_EQ(oaknode_project_is_modified(OakNodeProject{}), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_get_custom_cache_path(nullptr, nullptr, 0), + EXPECT_EQ(oaknode_project_set_modified(OakNodeProject{}, 1), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_set_custom_cache_path(nullptr, "/tmp"), + EXPECT_EQ(oaknode_project_is_new(OakNodeProject{}), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_project_cache_path(OakNodeProject{}, nullptr, 0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_get_uuid(nullptr, nullptr, 0), + EXPECT_EQ(oaknode_project_get_cache_location_setting(OakNodeProject{}), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_node_count(nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_node_at(nullptr, 0), nullptr); + EXPECT_EQ(oaknode_project_set_cache_location_setting(OakNodeProject{}, 0), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_project_get_custom_cache_path(OakNodeProject{}, nullptr, + 0), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_project_set_custom_cache_path(OakNodeProject{}, "/tmp"), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_project_get_uuid(OakNodeProject{}, nullptr, 0), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_project_node_count(OakNodeProject{}), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_project_node_at(OakNodeProject{}, 0).ctx, nullptr); } TEST(NodeProject, NameAndFilename) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); // Untitled project EXPECT_EQ(get_string(oaknode_project_name, project), "(untitled)"); @@ -121,13 +140,13 @@ TEST(NodeProject, NameAndFilename) EXPECT_EQ(oaknode_project_set_filename(project, nullptr), OAKNODE_E_INVALID); - oaknode_project_free(project); + oaknode_project_free(&project); } TEST(NodeProject, ModifiedAndIsNew) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); EXPECT_EQ(oaknode_project_is_modified(project), 0); EXPECT_EQ(oaknode_project_is_new(project), 1); @@ -144,13 +163,13 @@ TEST(NodeProject, ModifiedAndIsNew) OAKNODE_OK); EXPECT_EQ(oaknode_project_is_new(project), 0); - oaknode_project_free(project); + oaknode_project_free(&project); } TEST(NodeProject, CachePathSettings) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); // Default location setting EXPECT_EQ(oaknode_project_get_cache_location_setting(project), 0); @@ -178,24 +197,24 @@ TEST(NodeProject, CachePathSettings) OAKNODE_OK); EXPECT_EQ(get_string(oaknode_project_get_custom_cache_path, project), ""); - oaknode_project_free(project); + oaknode_project_free(&project); } TEST(NodeProject, Uuid) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); std::string uuid = get_string(oaknode_project_get_uuid, project); EXPECT_FALSE(uuid.empty()); - oaknode_project_free(project); + oaknode_project_free(&project); } TEST(NodeProject, AddRemoveNode) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK); // The root folder is the only node after initialize() @@ -203,17 +222,17 @@ TEST(NodeProject, AddRemoveNode) EXPECT_EQ(base_count, 1); // folder_create already adds the node to the graph - OakNodeFolder *folder = oaknode_folder_create(project); - ASSERT_NE(folder, nullptr); + OakNodeFolder folder = oaknode_folder_create(project); + ASSERT_NE(folder.ctx, nullptr); EXPECT_EQ(oaknode_project_node_count(project), base_count + 1); - // node_at returns the same node; out-of-range yields NULL - OakNodeNode *as_node = nullptr; + // node_at returns the same node; out-of-range yields an empty handle + OakNodeNode as_node = {}; bool found = false; for (int i = 0; i < oaknode_project_node_count(project); i++) { - OakNodeNode *n = oaknode_project_node_at(project, i); - ASSERT_NE(n, nullptr); - if (n == reinterpret_cast(folder)) { + OakNodeNode n = oaknode_project_node_at(project, i); + ASSERT_NE(n.ctx, nullptr); + if (same_object(n, oaknode_folder_as_node(folder))) { found = true; } if (i == 0) { @@ -221,13 +240,15 @@ TEST(NodeProject, AddRemoveNode) } } EXPECT_TRUE(found); - EXPECT_EQ(oaknode_project_node_at(project, -1), nullptr); + EXPECT_NE(as_node.ctx, nullptr); + EXPECT_EQ(oaknode_project_node_at(project, -1).ctx, nullptr); EXPECT_EQ(oaknode_project_node_at(project, - oaknode_project_node_count(project)), + oaknode_project_node_count(project)) + .ctx, nullptr); // Remove detaches without deleting - OakNodeNode *folder_node = reinterpret_cast(folder); + OakNodeNode folder_node = oaknode_folder_as_node(folder); EXPECT_EQ(oaknode_project_remove_node(project, folder_node), OAKNODE_OK); EXPECT_EQ(oaknode_project_node_count(project), base_count); @@ -239,20 +260,21 @@ TEST(NodeProject, AddRemoveNode) EXPECT_EQ(oaknode_project_add_node(project, folder_node), OAKNODE_OK); EXPECT_EQ(oaknode_project_node_count(project), base_count + 1); - // NULL args - EXPECT_EQ(oaknode_project_add_node(project, nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_project_remove_node(project, nullptr), + // Empty handle args + EXPECT_EQ(oaknode_project_add_node(project, OakNodeNode{}), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_project_remove_node(project, OakNodeNode{}), OAKNODE_E_INVALID); - oaknode_project_free(project); + oaknode_project_free(&project); } TEST(NodeProject, Clear) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK); - ASSERT_NE(oaknode_folder_create(project), nullptr); + ASSERT_NE(oaknode_folder_create(project).ctx, nullptr); EXPECT_GE(oaknode_project_node_count(project), 2); EXPECT_EQ(oaknode_project_clear(project), OAKNODE_OK); @@ -262,5 +284,5 @@ TEST(NodeProject, Clear) EXPECT_EQ(oaknode_project_initialize(project), OAKNODE_OK); EXPECT_GE(oaknode_project_node_count(project), 1); - oaknode_project_free(project); + oaknode_project_free(&project); } diff --git a/src/node/tests/sequence_test.cpp b/src/node/tests/sequence_test.cpp index 6dd4e5565..b111d31fb 100644 --- a/src/node/tests/sequence_test.cpp +++ b/src/node/tests/sequence_test.cpp @@ -43,39 +43,41 @@ void expect_rational(int num, int den, int expected_num, int expected_den) TEST(SequenceTest, CreateFree) { int base = oaknode_debug_alive_count(); - OakNodeSequence *seq = oaknode_sequence_create(); - ASSERT_NE(seq, nullptr); + OakNodeSequence seq = oaknode_sequence_create(); + ASSERT_NE(seq.ctx, nullptr); EXPECT_EQ(oaknode_debug_alive_count(), base + 1); - oaknode_sequence_free(seq); + oaknode_sequence_free(&seq); EXPECT_EQ(oaknode_debug_alive_count(), base); + EXPECT_EQ(seq.ctx, nullptr); oaknode_sequence_free(nullptr); } TEST(SequenceTest, NullHandleReturnsInvalid) { int v; - OakNodeTrackList *list; - EXPECT_EQ(oaknode_sequence_get_track_list(nullptr, OAKNODE_TRACK_TYPE_VIDEO, + OakNodeSequence empty = {}; + OakNodeTrackList list = {}; + EXPECT_EQ(oaknode_sequence_get_track_list(empty, OAKNODE_TRACK_TYPE_VIDEO, &list), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_sequence_get_track_count(nullptr, 0, &v), + EXPECT_EQ(oaknode_sequence_get_track_count(empty, 0, &v), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_sequence_get_playhead(nullptr, &v, &v), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_sequence_get_video_params(nullptr, 0, nullptr), + EXPECT_EQ(oaknode_sequence_get_playhead(empty, &v, &v), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_sequence_get_video_params(empty, 0, nullptr), OAKNODE_E_INVALID); } TEST(SequenceTest, TrackLists) { - OakNodeSequence *seq = oaknode_sequence_create(); - ASSERT_NE(seq, nullptr); + OakNodeSequence seq = oaknode_sequence_create(); + ASSERT_NE(seq.ctx, nullptr); for (int type = OAKNODE_TRACK_TYPE_VIDEO; type < OAKNODE_TRACK_TYPE_COUNT; type++) { - OakNodeTrackList *list = nullptr; + OakNodeTrackList list = {}; ASSERT_EQ(oaknode_sequence_get_track_list(seq, type, &list), OAKNODE_OK); - ASSERT_NE(list, nullptr); + ASSERT_NE(list.ctx, nullptr); int list_type = -1; ASSERT_EQ(oaknode_tracklist_get_type(list, &list_type), OAKNODE_OK); @@ -86,7 +88,7 @@ TEST(SequenceTest, TrackLists) EXPECT_EQ(count, 0); } - OakNodeTrackList *list; + OakNodeTrackList list = {}; EXPECT_EQ(oaknode_sequence_get_track_list(seq, OAKNODE_TRACK_TYPE_NONE, &list), OAKNODE_E_NOT_FOUND); @@ -94,23 +96,26 @@ TEST(SequenceTest, TrackLists) nullptr), OAKNODE_E_INVALID); - oaknode_sequence_free(seq); + oaknode_sequence_free(&seq); } TEST(SequenceTest, AddAndRemoveTracks) { - OakNodeSequence *seq = oaknode_sequence_create(); - ASSERT_NE(seq, nullptr); + OakNodeSequence seq = oaknode_sequence_create(); + ASSERT_NE(seq.ctx, nullptr); - OakNodeTrackList *video = nullptr; + OakNodeTrackList video = {}; ASSERT_EQ(oaknode_sequence_get_track_list(seq, OAKNODE_TRACK_TYPE_VIDEO, &video), OAKNODE_OK); - OakNodeTrack *t1 = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); - OakNodeTrack *t2 = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); - ASSERT_NE(t1, nullptr); - ASSERT_NE(t2, nullptr); + OakNodeTrack t1 = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); + OakNodeTrack t2 = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); + ASSERT_NE(t1.ctx, nullptr); + ASSERT_NE(t2.ctx, nullptr); + + // Marker distinguishing t2 from t1 in the identity checks below + ASSERT_EQ(oaknode_track_set_muted(t2, 1), OAKNODE_OK); ASSERT_EQ(oaknode_tracklist_add_track(video, t1), OAKNODE_OK); ASSERT_EQ(oaknode_tracklist_add_track(video, t2), OAKNODE_OK); @@ -139,18 +144,25 @@ TEST(SequenceTest, AddAndRemoveTracks) ASSERT_EQ(oaknode_tracklist_get_array_size(video, &array_size), OAKNODE_OK); EXPECT_EQ(array_size, 2); - // Sequence back-pointer and reference - OakNodeSequence *owner = nullptr; + // Sequence back-pointer: mutating through the borrowed handle must be + // visible through the owning sequence handle (same object) + OakNodeSequence owner = {}; ASSERT_EQ(oaknode_track_get_sequence(t1, &owner), OAKNODE_OK); - EXPECT_EQ(owner, seq); + ASSERT_NE(owner.ctx, nullptr); + ASSERT_EQ(oaknode_sequence_set_playhead(owner, 7, 3), OAKNODE_OK); + int pnum = -1, pden = -1; + ASSERT_EQ(oaknode_sequence_get_playhead(seq, &pnum, &pden), OAKNODE_OK); + expect_rational(pnum, pden, 7, 3); // Flat cache spans all types int all = 0; ASSERT_EQ(oaknode_sequence_get_all_track_count(seq, &all), OAKNODE_OK); EXPECT_EQ(all, 2); - OakNodeTrack *at = nullptr; + OakNodeTrack at = {}; + int muted = -1; ASSERT_EQ(oaknode_sequence_get_all_track_at(seq, 1, &at), OAKNODE_OK); - EXPECT_EQ(at, t2); + ASSERT_EQ(oaknode_track_get_muted(at, &muted), OAKNODE_OK); + EXPECT_EQ(muted, 1); // at is t2 EXPECT_EQ(oaknode_sequence_get_all_track_at(seq, 2, &at), OAKNODE_E_NOT_FOUND); @@ -158,7 +170,8 @@ TEST(SequenceTest, AddAndRemoveTracks) ASSERT_EQ(oaknode_sequence_get_track_at(seq, OAKNODE_TRACK_TYPE_VIDEO, 0, &at), OAKNODE_OK); - EXPECT_EQ(at, t1); + ASSERT_EQ(oaknode_track_get_muted(at, &muted), OAKNODE_OK); + EXPECT_EQ(muted, 0); // at is t1 EXPECT_EQ(oaknode_sequence_get_track_at(seq, OAKNODE_TRACK_TYPE_AUDIO, 0, &at), OAKNODE_E_NOT_FOUND); @@ -172,7 +185,8 @@ TEST(SequenceTest, AddAndRemoveTracks) ASSERT_EQ(oaknode_sequence_get_track_at(seq, OAKNODE_TRACK_TYPE_VIDEO, 0, &at), OAKNODE_OK); - EXPECT_EQ(at, t2); + ASSERT_EQ(oaknode_track_get_muted(at, &muted), OAKNODE_OK); + EXPECT_EQ(muted, 1); // at is t2 ASSERT_EQ(oaknode_track_get_index(t2, &index), OAKNODE_OK); EXPECT_EQ(index, 0); @@ -180,26 +194,26 @@ TEST(SequenceTest, AddAndRemoveTracks) EXPECT_EQ(oaknode_tracklist_remove_track(video, t1), OAKNODE_E_NOT_FOUND); ASSERT_EQ(oaknode_tracklist_remove_track(video, t2), OAKNODE_OK); - oaknode_track_free(t1); - oaknode_track_free(t2); - oaknode_sequence_free(seq); + oaknode_track_free(&t1); + oaknode_track_free(&t2); + oaknode_sequence_free(&seq); } TEST(SequenceTest, TrackLengthFlowsIntoSequence) { - OakNodeSequence *seq = oaknode_sequence_create(); - ASSERT_NE(seq, nullptr); + OakNodeSequence seq = oaknode_sequence_create(); + ASSERT_NE(seq.ctx, nullptr); - OakNodeTrackList *video = nullptr; + OakNodeTrackList video = {}; ASSERT_EQ(oaknode_sequence_get_track_list(seq, OAKNODE_TRACK_TYPE_VIDEO, &video), OAKNODE_OK); - OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); - ASSERT_NE(t, nullptr); + OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); + ASSERT_NE(t.ctx, nullptr); ASSERT_EQ(oaknode_tracklist_add_track(video, t), OAKNODE_OK); - OakNodeBlock *gap = oaknode_block_gap_create(); - ASSERT_NE(gap, nullptr); + OakNodeBlock gap = oaknode_block_gap_create(); + ASSERT_NE(gap.ctx, nullptr); ASSERT_EQ(oaknode_block_set_length_and_media_out(gap, 5, 1), OAKNODE_OK); ASSERT_EQ(oaknode_track_append_block(t, gap), OAKNODE_OK); @@ -215,16 +229,16 @@ TEST(SequenceTest, TrackLengthFlowsIntoSequence) expect_rational(num, den, 5, 1); ASSERT_EQ(oaknode_track_ripple_remove_block(t, gap), OAKNODE_OK); - oaknode_block_free(gap); + oaknode_block_free(&gap); ASSERT_EQ(oaknode_tracklist_remove_track(video, t), OAKNODE_OK); - oaknode_track_free(t); - oaknode_sequence_free(seq); + oaknode_track_free(&t); + oaknode_sequence_free(&seq); } TEST(SequenceTest, Playhead) { - OakNodeSequence *seq = oaknode_sequence_create(); - ASSERT_NE(seq, nullptr); + OakNodeSequence seq = oaknode_sequence_create(); + ASSERT_NE(seq.ctx, nullptr); int num = -1, den = -1; ASSERT_EQ(oaknode_sequence_get_playhead(seq, &num, &den), OAKNODE_OK); @@ -233,13 +247,13 @@ TEST(SequenceTest, Playhead) ASSERT_EQ(oaknode_sequence_get_playhead(seq, &num, &den), OAKNODE_OK); expect_rational(num, den, 7, 2); - oaknode_sequence_free(seq); + oaknode_sequence_free(&seq); } TEST(SequenceTest, VideoParamsRoundTrip) { - OakNodeSequence *seq = oaknode_sequence_create(); - ASSERT_NE(seq, nullptr); + OakNodeSequence seq = oaknode_sequence_create(); + ASSERT_NE(seq.ctx, nullptr); int count = 0; ASSERT_EQ(oaknode_sequence_get_video_stream_count(seq, &count), @@ -284,13 +298,13 @@ TEST(SequenceTest, VideoParamsRoundTrip) expect_rational(tb_num, tb_den, 1, 25); oakcommon_videoparams_free(&readback); - oaknode_sequence_free(seq); + oaknode_sequence_free(&seq); } TEST(SequenceTest, AudioParamsRoundTrip) { - OakNodeSequence *seq = oaknode_sequence_create(); - ASSERT_NE(seq, nullptr); + OakNodeSequence seq = oaknode_sequence_create(); + ASSERT_NE(seq.ctx, nullptr); int count = 0; ASSERT_EQ(oaknode_sequence_get_audio_stream_count(seq, &count), @@ -316,5 +330,5 @@ TEST(SequenceTest, AudioParamsRoundTrip) EXPECT_EQ(oaknode_sequence_set_audio_params(seq, 0, nullptr), OAKNODE_E_INVALID); - oaknode_sequence_free(seq); + oaknode_sequence_free(&seq); } diff --git a/src/node/tests/serializer_test.cpp b/src/node/tests/serializer_test.cpp index 6e2133bce..f6075aeea 100644 --- a/src/node/tests/serializer_test.cpp +++ b/src/node/tests/serializer_test.cpp @@ -33,7 +33,7 @@ namespace /** * @brief Serialize `save_data` to an XML string using the two-stage getter. */ -std::string save_to_string(OakNodeSerializerSaveData *save_data) +std::string save_to_string(OakNodeSerializerSaveData save_data) { int required = oaknode_serializer_save_to_xml(save_data, nullptr, 0); if (required <= 0) { @@ -53,12 +53,12 @@ TEST(NodeSerializer, SaveWithoutInitializeFails) { oaknode_serializer_shutdown(); - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); - OakNodeSerializerSaveData *save_data = oaknode_serializer_savedata_create( + OakNodeSerializerSaveData save_data = oaknode_serializer_savedata_create( OAKNODE_SERIALIZER_LOAD_ONLY_NODES, project); - ASSERT_NE(save_data, nullptr); + ASSERT_NE(save_data.ctx, nullptr); EXPECT_EQ(oaknode_serializer_save_to_xml(save_data, nullptr, 0), OAKNODE_E_STATE); @@ -68,8 +68,8 @@ TEST(NodeSerializer, SaveWithoutInitializeFails) &result, nullptr, nullptr, 0), OAKNODE_E_STATE); - oaknode_serializer_savedata_free(save_data); - oaknode_project_free(project); + oaknode_serializer_savedata_free(&save_data); + oaknode_project_free(&project); } TEST(NodeSerializer, InitializeIsIdempotent) @@ -83,22 +83,21 @@ TEST(NodeSerializer, NodeGraphCopyPasteRoundTrip) ASSERT_EQ(oaknode_serializer_initialize(), OAKNODE_OK); // Source project with a folder under the root - OakNodeProject *source = oaknode_project_init(); - ASSERT_NE(source, nullptr); + OakNodeProject source = oaknode_project_init(); + ASSERT_NE(source.ctx, nullptr); ASSERT_EQ(oaknode_project_initialize(source), OAKNODE_OK); - OakNodeFolder *root = oaknode_project_root(source); - OakNodeFolder *folder = oaknode_folder_create(source); - ASSERT_NE(root, nullptr); - ASSERT_NE(folder, nullptr); - ASSERT_EQ(oaknode_folder_add_child( - root, reinterpret_cast(folder)), + OakNodeFolder root = oaknode_project_root(source); + OakNodeFolder folder = oaknode_folder_create(source); + ASSERT_NE(root.ctx, nullptr); + ASSERT_NE(folder.ctx, nullptr); + ASSERT_EQ(oaknode_folder_add_child(root, oaknode_folder_as_node(folder)), OAKNODE_OK); // "Copy": serialize the folder with a custom property - OakNodeSerializerSaveData *save_data = oaknode_serializer_savedata_create( + OakNodeSerializerSaveData save_data = oaknode_serializer_savedata_create( OAKNODE_SERIALIZER_LOAD_ONLY_NODES, source); - ASSERT_NE(save_data, nullptr); - OakNodeNode *nodes[] = { reinterpret_cast(folder) }; + ASSERT_NE(save_data.ctx, nullptr); + OakNodeNode nodes[] = { oaknode_folder_as_node(folder) }; EXPECT_EQ(oaknode_serializer_savedata_set_nodes(save_data, nodes, 1), OAKNODE_OK); EXPECT_EQ(oaknode_serializer_savedata_set_property( @@ -108,29 +107,29 @@ TEST(NodeSerializer, NodeGraphCopyPasteRoundTrip) std::string xml = save_to_string(save_data); EXPECT_FALSE(xml.empty()); EXPECT_NE(xml.find("(folder)), + EXPECT_EQ(oaknode_folder_remove_child(root, + oaknode_folder_as_node(folder)), OAKNODE_OK); - oaknode_project_free(target); - oaknode_project_free(source); + oaknode_project_free(&target); + oaknode_project_free(&source); } TEST(NodeSerializer, LoadInvalidXmlReportsSerializerError) { ASSERT_EQ(oaknode_serializer_initialize(), OAKNODE_OK); - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); int result = -1; - OakNodeSerializerLoadData *load_data = nullptr; + OakNodeSerializerLoadData load_data = {}; char details[256] = { 0 }; EXPECT_EQ(oaknode_serializer_load_from_xml( project, "this is not xml", @@ -190,9 +189,9 @@ TEST(NodeSerializer, LoadInvalidXmlReportsSerializerError) OAKNODE_OK); // Not an oak document: the format version cannot be determined EXPECT_EQ(result, OAKNODE_SERIALIZER_UNKNOWN_VERSION); - EXPECT_EQ(load_data, nullptr); + EXPECT_EQ(load_data.ctx, nullptr); - oaknode_project_free(project); + oaknode_project_free(&project); } TEST(NodeSerializer, NullAndInvalidArgs) @@ -201,37 +200,48 @@ TEST(NodeSerializer, NullAndInvalidArgs) int result = -1; - EXPECT_EQ(oaknode_serializer_savedata_create(-1, nullptr), nullptr); - EXPECT_EQ(oaknode_serializer_savedata_create(99, nullptr), nullptr); + EXPECT_EQ(oaknode_serializer_savedata_create(-1, OakNodeProject{}).ctx, + nullptr); + EXPECT_EQ(oaknode_serializer_savedata_create(99, OakNodeProject{}).ctx, + nullptr); - EXPECT_EQ(oaknode_serializer_savedata_set_nodes(nullptr, nullptr, 0), + EXPECT_EQ(oaknode_serializer_savedata_set_nodes( + OakNodeSerializerSaveData{}, nullptr, 0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_serializer_savedata_set_property(nullptr, nullptr, - nullptr, nullptr), + EXPECT_EQ(oaknode_serializer_savedata_set_property( + OakNodeSerializerSaveData{}, OakNodeNode{}, nullptr, nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_serializer_save_to_xml(nullptr, nullptr, 0), + EXPECT_EQ(oaknode_serializer_save_to_xml(OakNodeSerializerSaveData{}, + nullptr, 0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_serializer_load_from_xml(nullptr, nullptr, 1, &result, - nullptr, nullptr, 0), - OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_serializer_load_from_xml(nullptr, "", 99, + EXPECT_EQ(oaknode_serializer_load_from_xml(OakNodeProject{}, nullptr, 1, &result, nullptr, nullptr, 0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_serializer_load_from_xml(nullptr, "", 1, + EXPECT_EQ(oaknode_serializer_load_from_xml(OakNodeProject{}, "", 99, + &result, nullptr, nullptr, 0), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_serializer_load_from_xml(OakNodeProject{}, "", 1, nullptr, nullptr, nullptr, 0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_serializer_loaddata_node_count(nullptr), + EXPECT_EQ(oaknode_serializer_loaddata_node_count( + OakNodeSerializerLoadData{}), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_serializer_loaddata_node_at(nullptr, 0), nullptr); - EXPECT_EQ(oaknode_serializer_loaddata_get_property(nullptr, nullptr, - nullptr, nullptr, 0), + EXPECT_EQ(oaknode_serializer_loaddata_node_at(OakNodeSerializerLoadData{}, + 0) + .ctx, + nullptr); + EXPECT_EQ(oaknode_serializer_loaddata_get_property( + OakNodeSerializerLoadData{}, OakNodeNode{}, nullptr, nullptr, + 0), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_serializer_loaddata_connection_count(nullptr), + EXPECT_EQ(oaknode_serializer_loaddata_connection_count( + OakNodeSerializerLoadData{}), OAKNODE_E_INVALID); EXPECT_EQ(oaknode_serializer_loaddata_connection_at( - nullptr, 0, nullptr, nullptr, nullptr, 0, nullptr), + OakNodeSerializerLoadData{}, 0, nullptr, nullptr, nullptr, 0, + nullptr), OAKNODE_E_INVALID); } diff --git a/src/node/tests/testnode.h b/src/node/tests/testnode.h index 550fbc58f..b7d0804cd 100644 --- a/src/node/tests/testnode.h +++ b/src/node/tests/testnode.h @@ -25,6 +25,7 @@ // one input per POD-carrying value type, so the C API tests can exercise // value mapping without depending on NodeFactory. +#include "../c_api/nodehandle.h" #include "../src/node.h" namespace oaknode_test @@ -73,9 +74,23 @@ public: inline const char *TestNode::k_id = "org.oak.TestNode"; -inline OakNodeNode *as_handle(olive::Node *node) +/** + * @brief Borrowed handle to a test-owned node; releasing it only releases + * the handle, never the node. + */ +inline OakNodeNode as_handle(olive::Node *node) { - return reinterpret_cast(node); + return oaknode_c_api::make_handle( + node, false, &oaknode_c_api::delete_as); +} + +/** + * @brief Identity comparison for handles: two handles refer to the same + * node when they wrap the same native object. + */ +inline bool same_node(OakNodeNode a, olive::Node *node) +{ + return oaknode_c_api::to_native(a) == node; } } diff --git a/src/node/tests/track_test.cpp b/src/node/tests/track_test.cpp index 2a304b206..a3f0f32da 100644 --- a/src/node/tests/track_test.cpp +++ b/src/node/tests/track_test.cpp @@ -22,15 +22,16 @@ #include "node/block.h" #include "node/error.h" +#include "node/sequence.h" #include "node/track.h" namespace { -OakNodeBlock *make_gap(int length_num, int length_den) +OakNodeBlock make_gap(int length_num, int length_den) { - OakNodeBlock *gap = oaknode_block_gap_create(); - EXPECT_NE(gap, nullptr); + OakNodeBlock gap = oaknode_block_gap_create(); + EXPECT_NE(gap.ctx, nullptr); EXPECT_EQ(oaknode_block_set_length_and_media_out(gap, length_num, length_den), OAKNODE_OK); @@ -43,19 +44,40 @@ void expect_rational(int num, int den, int expected_num, int expected_den) EXPECT_EQ(den, expected_den); } +/** + * @brief Identity probe: block handles are distinct boxes per accessor + * call, so "is the same object" is checked through per-object state + * (length / in point / enabled) instead of pointer equality. + */ +void expect_block_length(OakNodeBlock block, int expected_num, + int expected_den) +{ + int num = -1, den = -1; + ASSERT_EQ(oaknode_block_get_length(block, &num, &den), OAKNODE_OK); + expect_rational(num, den, expected_num, expected_den); +} + +void expect_block_in(OakNodeBlock block, int expected_num, int expected_den) +{ + int num = -1, den = -1; + ASSERT_EQ(oaknode_block_get_in(block, &num, &den), OAKNODE_OK); + expect_rational(num, den, expected_num, expected_den); +} + } // namespace TEST(TrackTest, CreateFree) { - OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); - ASSERT_NE(t, nullptr); + OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); + ASSERT_NE(t.ctx, nullptr); int type = -1; ASSERT_EQ(oaknode_track_get_type(t, &type), OAKNODE_OK); EXPECT_EQ(type, OAKNODE_TRACK_TYPE_VIDEO); - oaknode_track_free(t); + oaknode_track_free(&t); + EXPECT_EQ(t.ctx, nullptr); - EXPECT_EQ(oaknode_track_create(OAKNODE_TRACK_TYPE_NONE), nullptr); - EXPECT_EQ(oaknode_track_create(OAKNODE_TRACK_TYPE_COUNT), nullptr); + EXPECT_EQ(oaknode_track_create(OAKNODE_TRACK_TYPE_NONE).ctx, nullptr); + EXPECT_EQ(oaknode_track_create(OAKNODE_TRACK_TYPE_COUNT).ctx, nullptr); oaknode_track_free(nullptr); } @@ -63,20 +85,24 @@ TEST(TrackTest, NullHandleReturnsInvalid) { int v; double d; - EXPECT_EQ(oaknode_track_get_type(nullptr, &v), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_track_set_type(nullptr, OAKNODE_TRACK_TYPE_VIDEO), + OakNodeTrack empty_track = {}; + OakNodeBlock empty_block = {}; + OakNodeTrackList empty_list = {}; + EXPECT_EQ(oaknode_track_get_type(empty_track, &v), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_track_set_type(empty_track, OAKNODE_TRACK_TYPE_VIDEO), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_track_get_height(nullptr, &d), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_track_set_muted(nullptr, 1), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_track_append_block(nullptr, nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_tracklist_get_track_count(nullptr, &v), + EXPECT_EQ(oaknode_track_get_height(empty_track, &d), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_track_set_muted(empty_track, 1), OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_track_append_block(empty_track, empty_block), + OAKNODE_E_INVALID); + EXPECT_EQ(oaknode_tracklist_get_track_count(empty_list, &v), OAKNODE_E_INVALID); } TEST(TrackTest, TypeHeightIndexFlags) { - OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_AUDIO); - ASSERT_NE(t, nullptr); + OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_AUDIO); + ASSERT_NE(t.ctx, nullptr); ASSERT_EQ(oaknode_track_set_type(t, OAKNODE_TRACK_TYPE_SUBTITLE), OAKNODE_OK); @@ -122,24 +148,25 @@ TEST(TrackTest, TypeHeightIndexFlags) EXPECT_EQ(flag, 1); // Fresh track has no sequence - OakNodeSequence *seq = reinterpret_cast(0x1); + OakNodeSequence seq = {}; + seq.ctx = reinterpret_cast(0x1); ASSERT_EQ(oaknode_track_get_sequence(t, &seq), OAKNODE_OK); - EXPECT_EQ(seq, nullptr); + EXPECT_EQ(seq.ctx, nullptr); - oaknode_track_free(t); + oaknode_track_free(&t); } TEST(TrackTest, AppendAndQueryBlocks) { - OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); - ASSERT_NE(t, nullptr); + OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); + ASSERT_NE(t.ctx, nullptr); int count = -1; ASSERT_EQ(oaknode_track_get_block_count(t, &count), OAKNODE_OK); EXPECT_EQ(count, 0); - OakNodeBlock *a = make_gap(1, 1); - OakNodeBlock *b = make_gap(2, 1); + OakNodeBlock a = make_gap(1, 1); + OakNodeBlock b = make_gap(2, 1); ASSERT_EQ(oaknode_track_append_block(t, a), OAKNODE_OK); ASSERT_EQ(oaknode_track_append_block(t, b), OAKNODE_OK); @@ -155,15 +182,22 @@ TEST(TrackTest, AppendAndQueryBlocks) ASSERT_EQ(oaknode_block_get_out(b, &num, &den), OAKNODE_OK); expect_rational(num, den, 3, 1); - // Adjacency and back-pointer - OakNodeBlock *nb = nullptr; + // Adjacency (identified by their distinct lengths: a = 1, b = 2) + OakNodeBlock nb = {}; ASSERT_EQ(oaknode_block_get_next(a, &nb), OAKNODE_OK); - EXPECT_EQ(nb, b); + expect_block_length(nb, 2, 1); // nb is b ASSERT_EQ(oaknode_block_get_previous(b, &nb), OAKNODE_OK); - EXPECT_EQ(nb, a); - OakNodeTrack *owner = nullptr; + expect_block_length(nb, 1, 1); // nb is a + + // Back-pointer: mutating through the borrowed track handle must be + // visible through the owning track handle (same object) + OakNodeTrack owner = {}; ASSERT_EQ(oaknode_block_get_track(a, &owner), OAKNODE_OK); - EXPECT_EQ(owner, t); + ASSERT_NE(owner.ctx, nullptr); + ASSERT_EQ(oaknode_track_set_muted(owner, 1), OAKNODE_OK); + int muted = -1; + ASSERT_EQ(oaknode_track_get_muted(t, &muted), OAKNODE_OK); + EXPECT_EQ(muted, 1); // Length of the whole track ASSERT_EQ(oaknode_track_get_length(t, &num, &den), OAKNODE_OK); @@ -174,17 +208,17 @@ TEST(TrackTest, AppendAndQueryBlocks) ASSERT_EQ(oaknode_track_get_block_index(t, b, &index), OAKNODE_OK); EXPECT_EQ(index, 1); - OakNodeBlock *at = nullptr; + OakNodeBlock at = {}; ASSERT_EQ(oaknode_track_get_block_at(t, 0, &at), OAKNODE_OK); - EXPECT_EQ(at, a); + expect_block_length(at, 1, 1); // at is a EXPECT_EQ(oaknode_track_get_block_at(t, 2, &at), OAKNODE_E_NOT_FOUND); ASSERT_EQ(oaknode_track_get_block_containing_time(t, 1, 2, &at), OAKNODE_OK); - EXPECT_EQ(at, a); + expect_block_length(at, 1, 1); // at is a ASSERT_EQ(oaknode_track_get_block_containing_time(t, 3, 2, &at), OAKNODE_OK); - EXPECT_EQ(at, b); + expect_block_length(at, 2, 1); // at is b // Boundary time is not "contained" EXPECT_EQ(oaknode_track_get_block_containing_time(t, 1, 1, &at), OAKNODE_E_NOT_FOUND); @@ -204,20 +238,20 @@ TEST(TrackTest, AppendAndQueryBlocks) // Tear down manually (no project graph owns these) ASSERT_EQ(oaknode_track_ripple_remove_block(t, a), OAKNODE_OK); ASSERT_EQ(oaknode_track_ripple_remove_block(t, b), OAKNODE_OK); - oaknode_block_free(a); - oaknode_block_free(b); - oaknode_track_free(t); + oaknode_block_free(&a); + oaknode_block_free(&b); + oaknode_track_free(&t); } TEST(TrackTest, PrependInsertAndRippleRemove) { - OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); - ASSERT_NE(t, nullptr); + OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); + ASSERT_NE(t.ctx, nullptr); - OakNodeBlock *a = make_gap(1, 1); - OakNodeBlock *b = make_gap(1, 1); - OakNodeBlock *c = make_gap(1, 1); - OakNodeBlock *d = make_gap(1, 1); + OakNodeBlock a = make_gap(1, 1); + OakNodeBlock b = make_gap(1, 1); + OakNodeBlock c = make_gap(1, 1); + OakNodeBlock d = make_gap(1, 1); ASSERT_EQ(oaknode_track_append_block(t, c), OAKNODE_OK); ASSERT_EQ(oaknode_track_prepend_block(t, a), OAKNODE_OK); @@ -228,13 +262,15 @@ TEST(TrackTest, PrependInsertAndRippleRemove) ASSERT_EQ(oaknode_track_get_block_count(t, &count), OAKNODE_OK); EXPECT_EQ(count, 4); - OakNodeBlock *at = nullptr; + // All gaps have equal lengths; identity is checked through the + // per-object in point (a at 0, b at 1, d at 3) + OakNodeBlock at = {}; ASSERT_EQ(oaknode_track_get_block_at(t, 0, &at), OAKNODE_OK); - EXPECT_EQ(at, a); + expect_block_in(at, 0, 1); // at is a ASSERT_EQ(oaknode_track_get_block_at(t, 1, &at), OAKNODE_OK); - EXPECT_EQ(at, b); + expect_block_in(at, 1, 1); // at is b ASSERT_EQ(oaknode_track_get_block_at(t, 3, &at), OAKNODE_OK); - EXPECT_EQ(at, d); + expect_block_in(at, 3, 1); // at is d // Ripple-removing b shifts c back to t=1 ASSERT_EQ(oaknode_track_ripple_remove_block(t, b), OAKNODE_OK); @@ -247,67 +283,72 @@ TEST(TrackTest, PrependInsertAndRippleRemove) ASSERT_EQ(oaknode_track_ripple_remove_block(t, a), OAKNODE_OK); ASSERT_EQ(oaknode_track_ripple_remove_block(t, c), OAKNODE_OK); ASSERT_EQ(oaknode_track_ripple_remove_block(t, d), OAKNODE_OK); - oaknode_block_free(a); - oaknode_block_free(b); - oaknode_block_free(c); - oaknode_block_free(d); - oaknode_track_free(t); + oaknode_block_free(&a); + oaknode_block_free(&b); + oaknode_block_free(&c); + oaknode_block_free(&d); + oaknode_track_free(&t); } TEST(TrackTest, InsertBefore) { - OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); - ASSERT_NE(t, nullptr); + OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); + ASSERT_NE(t.ctx, nullptr); - OakNodeBlock *a = make_gap(1, 1); - OakNodeBlock *b = make_gap(1, 1); + OakNodeBlock a = make_gap(1, 1); + OakNodeBlock b = make_gap(1, 1); ASSERT_EQ(oaknode_track_append_block(t, b), OAKNODE_OK); ASSERT_EQ(oaknode_track_insert_block_before(t, a, b), OAKNODE_OK); - OakNodeBlock *at = nullptr; + OakNodeBlock at = {}; ASSERT_EQ(oaknode_track_get_block_at(t, 0, &at), OAKNODE_OK); - EXPECT_EQ(at, a); + expect_block_in(at, 0, 1); // at is a ASSERT_EQ(oaknode_track_ripple_remove_block(t, a), OAKNODE_OK); ASSERT_EQ(oaknode_track_ripple_remove_block(t, b), OAKNODE_OK); - oaknode_block_free(a); - oaknode_block_free(b); - oaknode_track_free(t); + oaknode_block_free(&a); + oaknode_block_free(&b); + oaknode_track_free(&t); } TEST(TrackTest, ReplaceBlock) { - OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); - ASSERT_NE(t, nullptr); + OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); + ASSERT_NE(t.ctx, nullptr); + + OakNodeBlock old_gap = make_gap(2, 1); + OakNodeBlock new_gap = make_gap(2, 1); + // Marker distinguishing new_gap from the equal-length old_gap + ASSERT_EQ(oaknode_block_set_enabled(new_gap, 0), OAKNODE_OK); - OakNodeBlock *old_gap = make_gap(2, 1); - OakNodeBlock *new_gap = make_gap(2, 1); ASSERT_EQ(oaknode_track_append_block(t, old_gap), OAKNODE_OK); ASSERT_EQ(oaknode_track_replace_block(t, old_gap, new_gap), OAKNODE_OK); int count = 0; ASSERT_EQ(oaknode_track_get_block_count(t, &count), OAKNODE_OK); EXPECT_EQ(count, 1); - OakNodeBlock *at = nullptr; + OakNodeBlock at = {}; ASSERT_EQ(oaknode_track_get_block_at(t, 0, &at), OAKNODE_OK); - EXPECT_EQ(at, new_gap); + int enabled = -1; + ASSERT_EQ(oaknode_block_get_enabled(at, &enabled), OAKNODE_OK); + EXPECT_EQ(enabled, 0); // at is new_gap int num, den; ASSERT_EQ(oaknode_track_get_length(t, &num, &den), OAKNODE_OK); expect_rational(num, den, 2, 1); ASSERT_EQ(oaknode_track_ripple_remove_block(t, new_gap), OAKNODE_OK); - oaknode_block_free(old_gap); - oaknode_block_free(new_gap); - oaknode_track_free(t); + oaknode_block_free(&old_gap); + oaknode_block_free(&new_gap); + oaknode_track_free(&t); } TEST(TrackTest, RangeOccupiedByClipIsNotFree) { - OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); - ASSERT_NE(t, nullptr); + OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); + ASSERT_NE(t.ctx, nullptr); - OakNodeBlock *clip = oaknode_block_clip_create(); - ASSERT_NE(clip, nullptr); + OakNodeBlock clip = oaknode_block_clip_create(); + ASSERT_NE(clip.ctx, nullptr); ASSERT_EQ(oaknode_block_set_length_and_media_out(clip, 2, 1), OAKNODE_OK); ASSERT_EQ(oaknode_track_append_block(t, clip), OAKNODE_OK); @@ -320,14 +361,14 @@ TEST(TrackTest, RangeOccupiedByClipIsNotFree) EXPECT_EQ(free_range, 1); ASSERT_EQ(oaknode_track_ripple_remove_block(t, clip), OAKNODE_OK); - oaknode_block_free(clip); - oaknode_track_free(t); + oaknode_block_free(&clip); + oaknode_track_free(&t); } TEST(TrackTest, Reference) { - OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_AUDIO); - ASSERT_NE(t, nullptr); + OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_AUDIO); + ASSERT_NE(t.ctx, nullptr); ASSERT_EQ(oaknode_track_set_index(t, 2), OAKNODE_OK); int type = -1, index = -1; @@ -335,5 +376,5 @@ TEST(TrackTest, Reference) EXPECT_EQ(type, OAKNODE_TRACK_TYPE_AUDIO); EXPECT_EQ(index, 2); - oaknode_track_free(t); + oaknode_track_free(&t); } diff --git a/src/node/tests/traverser_test.cpp b/src/node/tests/traverser_test.cpp index 250fcad8f..00ac7c80b 100644 --- a/src/node/tests/traverser_test.cpp +++ b/src/node/tests/traverser_test.cpp @@ -36,11 +36,12 @@ TEST(NodeTraverserTest, InitFree) { int alive_before = oaknode_debug_alive_count(); - OakNodeTraverser *traverser = oaknode_traverser_init(); - ASSERT_NE(traverser, nullptr); + OakNodeTraverser traverser = oaknode_traverser_init(); + ASSERT_NE(traverser.ctx, nullptr); EXPECT_EQ(oaknode_debug_alive_count(), alive_before + 1); - oaknode_traverser_free(traverser); + oaknode_traverser_free(&traverser); + EXPECT_EQ(traverser.ctx, nullptr); EXPECT_EQ(oaknode_debug_alive_count(), alive_before); oaknode_traverser_free(nullptr); // no crash @@ -55,16 +56,16 @@ TEST(NodeTraverserTest, GenerateDatabaseAndEnumerate) ASSERT_EQ(oaknode_node_set_input(as_handle(&node), "float_in", &in), OAKNODE_OK); - OakNodeTraverser *traverser = oaknode_traverser_init(); - ASSERT_NE(traverser, nullptr); + OakNodeTraverser traverser = oaknode_traverser_init(); + ASSERT_NE(traverser.ctx, nullptr); int alive_before = oaknode_debug_alive_count(); - OakNodeValueDatabase *db = nullptr; + OakNodeValueDatabase db = {}; EXPECT_EQ(oaknode_traverser_generate_database(traverser, as_handle(&node), 0, 1, 1, 1, &db), OAKNODE_OK); - ASSERT_NE(db, nullptr); + ASSERT_NE(db.ctx, nullptr); EXPECT_EQ(oaknode_debug_alive_count(), alive_before + 1); int rows = 0; @@ -115,28 +116,30 @@ TEST(NodeTraverserTest, GenerateDatabaseAndEnumerate) oaknode_value out; EXPECT_EQ(oaknode_traverser_database_value_at(db, "nope", 0, &out), OAKNODE_E_NOT_FOUND); - EXPECT_EQ(oaknode_traverser_database_value_at(nullptr, "float_in", 0, - &out), + EXPECT_EQ(oaknode_traverser_database_value_at(OakNodeValueDatabase{}, + "float_in", 0, &out), OAKNODE_E_INVALID); - oaknode_traverser_database_free(db); + oaknode_traverser_database_free(&db); + EXPECT_EQ(db.ctx, nullptr); EXPECT_EQ(oaknode_debug_alive_count(), alive_before); oaknode_traverser_database_free(nullptr); // no crash - oaknode_traverser_free(traverser); + oaknode_traverser_free(&traverser); } TEST(NodeTraverserTest, InvalidArguments) { - OakNodeValueDatabase *db = nullptr; - EXPECT_EQ(oaknode_traverser_generate_database(nullptr, nullptr, 0, 1, 1, 1, + OakNodeValueDatabase db = {}; + EXPECT_EQ(oaknode_traverser_generate_database(OakNodeTraverser{}, + OakNodeNode{}, 0, 1, 1, 1, &db), OAKNODE_E_INVALID); - OakNodeTraverser *traverser = oaknode_traverser_init(); - ASSERT_NE(traverser, nullptr); - EXPECT_EQ(oaknode_traverser_generate_database(traverser, nullptr, 0, 1, 1, - 1, &db), + OakNodeTraverser traverser = oaknode_traverser_init(); + ASSERT_NE(traverser.ctx, nullptr); + EXPECT_EQ(oaknode_traverser_generate_database(traverser, OakNodeNode{}, 0, + 1, 1, 1, &db), OAKNODE_E_INVALID); TestNode node; @@ -144,10 +147,11 @@ TEST(NodeTraverserTest, InvalidArguments) 0, 1, 1, 1, nullptr), OAKNODE_E_INVALID); - EXPECT_EQ(oaknode_traverser_database_row_count(nullptr, nullptr), + EXPECT_EQ(oaknode_traverser_database_row_count(OakNodeValueDatabase{}, + nullptr), OAKNODE_E_INVALID); - oaknode_traverser_free(traverser); + oaknode_traverser_free(&traverser); } } diff --git a/src/render/c_api/copier.cpp b/src/render/c_api/copier.cpp index e41754269..843e03ed3 100644 --- a/src/render/c_api/copier.cpp +++ b/src/render/c_api/copier.cpp @@ -22,6 +22,7 @@ #include +#include "../../node/c_api/nodehandle.h" #include "../src/projectcopier.h" namespace @@ -50,36 +51,41 @@ void oakrender_project_copier_free(OakRenderProjectCopier *copier) } int oakrender_project_copier_set_project(OakRenderProjectCopier *copier, - OakNodeProject *project) + OakNodeProject project) { - if (!copier || !project) { + if (!copier || !project.ctx) { return OAKRENDER_E_INVALID; } try { impl(copier)->set_project( - reinterpret_cast(project)); + oaknode_c_api::to_native(project)); return OAKRENDER_OK; } catch (...) { return OAKRENDER_E_FAILED; } } -OakNodeNode *oakrender_project_copier_get_copy( - OakRenderProjectCopier *copier, OakNodeNode *original) +OakNodeNode oakrender_project_copier_get_copy( + OakRenderProjectCopier *copier, OakNodeNode original) { - if (!copier || !original) { - return NULL; + if (!copier || !original.ctx) { + return OakNodeNode{}; } - return reinterpret_cast( - impl(copier)->get_copy(reinterpret_cast(original))); + // Borrowed handle: releasing it only destroys the handle box, never + // the copied node (owned by the copier's copied project). + return oaknode_c_api::make_handle( + impl(copier)->get_copy(oaknode_c_api::to_native(original)), + false, &oaknode_c_api::delete_as); } -OakNodeProject *oakrender_project_copier_get_copied_project( +OakNodeProject oakrender_project_copier_get_copied_project( OakRenderProjectCopier *copier) { if (!copier) { - return NULL; + return OakNodeProject{}; } - return reinterpret_cast( - impl(copier)->get_copied_project()); + // Borrowed handle: the copied project is owned by the copier. + return oaknode_c_api::make_handle( + impl(copier)->get_copied_project(), false, + &oaknode_c_api::delete_as); } diff --git a/src/render/c_api/manager.cpp b/src/render/c_api/manager.cpp index 1e7af5037..0070db49b 100644 --- a/src/render/c_api/manager.cpp +++ b/src/render/c_api/manager.cpp @@ -30,6 +30,8 @@ #include "alivecount.h" #include "internalhandles.h" +#include "../../node/c_api/nodehandle.h" + #include "diskmanager.h" #include "output/viewer/viewer.h" #include "previewautocacher.h" @@ -75,10 +77,10 @@ void oakrender_manager_shutdown(void) } } -int64_t oakrender_request_frame(OakNodeNode *viewer, int64_t ts, +int64_t oakrender_request_frame(OakNodeNode viewer, int64_t ts, oakrender_frame_ready_fn cb, void *userdata) { - if (!viewer || !cb) { + if (!viewer.ctx || !cb) { return OAKRENDER_E_INVALID; } olive::RenderManager *manager = olive::RenderManager::instance(); @@ -86,7 +88,7 @@ int64_t oakrender_request_frame(OakNodeNode *viewer, int64_t ts, return OAKRENDER_E_STATE; } auto *v = dynamic_cast( - reinterpret_cast(viewer)); + oaknode_c_api::to_native(viewer)); if (!v) { return OAKRENDER_E_INVALID; } @@ -157,14 +159,16 @@ int oakrender_cancel_request(int64_t request_id) return OAKRENDER_OK; } -int oakrender_set_cacher_multicam(OakNodeNode *multicam_or_NULL) +int oakrender_set_cacher_multicam(OakNodeNode multicam_or_NULL) { olive::RenderManager *manager = olive::RenderManager::instance(); if (!manager) { return OAKRENDER_E_STATE; } manager->get_cacher()->set_multicam_node( - reinterpret_cast(multicam_or_NULL)); + multicam_or_NULL.ctx ? + oaknode_c_api::to_native(multicam_or_NULL) : + nullptr); return OAKRENDER_OK; } diff --git a/src/render/c_api/ticket.cpp b/src/render/c_api/ticket.cpp index 35f777786..936896d31 100644 --- a/src/render/c_api/ticket.cpp +++ b/src/render/c_api/ticket.cpp @@ -26,6 +26,7 @@ #include "../src/framehashcache.h" #include "../src/rendermanager.h" #include "../src/renderticket.h" +#include "../../node/c_api/nodehandle.h" #include "internalhandles.h" namespace @@ -59,9 +60,9 @@ OakRenderTicket *wrap(olive::RenderTicketWatcher *w, return reinterpret_cast(h); } -olive::Node *to_node(OakNodeNode *n) +olive::Node *to_node(OakNodeNode n) { - return reinterpret_cast(n); + return oaknode_c_api::to_native(n); } } // namespace @@ -70,7 +71,7 @@ OakRenderTicket *oakrender_ticket_render_frame( const oakrender_video_ticket_params *params, oakrender_ticket_finished_fn cb, void *userdata) { - if (!params || !params->output_node) { + if (!params || !params->output_node.ctx) { return NULL; } @@ -96,7 +97,10 @@ OakRenderTicket *oakrender_ticket_render_frame( : olive::AudioParams(), olive::core::Rational(int(params->time_num), int(params->time_den)), - reinterpret_cast(params->color_manager), + params->color_manager.ctx ? + oaknode_c_api::to_native( + params->color_manager) : + nullptr, static_cast(params->mode)); rvp.force_size = @@ -161,11 +165,11 @@ OakRenderTicket *oakrender_ticket_render_frame( } OakRenderTicket *oakrender_ticket_render_audio( - OakNodeNode *output_node, int64_t in_num, int64_t in_den, + OakNodeNode output_node, int64_t in_num, int64_t in_den, int64_t out_num, int64_t out_den, const OakAudioParams *params, int mode, oakrender_ticket_finished_fn cb, void *userdata) { - if (!output_node || !params) { + if (!output_node.ctx || !params) { return NULL; } diff --git a/src/render/tests/manager_test.cpp b/src/render/tests/manager_test.cpp index 581c9dbe5..82c042a5f 100644 --- a/src/render/tests/manager_test.cpp +++ b/src/render/tests/manager_test.cpp @@ -57,30 +57,31 @@ TEST(OakRenderManagerTest, InitShutdown) TEST(OakRenderManagerTest, RequestFrameRequiresManager) { ASSERT_EQ(oaknode_factory_initialize(), OAKNODE_OK); - OakNodeNode *viewer = oaknode_factory_create_from_id( + OakNodeNode viewer = oaknode_factory_create_from_id( "org.olivevideoeditor.Olive.vieweroutput"); - ASSERT_NE(viewer, nullptr); + ASSERT_NE(viewer.ctx, nullptr); // No oakrender_manager_init() in this process: E_STATE EXPECT_EQ(oakrender_request_frame(viewer, 0, noop_frame_ready, nullptr), int64_t(OAKRENDER_E_STATE)); - oaknode_node_free(viewer); + oaknode_node_free(&viewer); oaknode_factory_destroy(); } TEST(OakRenderManagerTest, RequestFrameInvalidArgs) { - EXPECT_EQ(oakrender_request_frame(nullptr, 0, noop_frame_ready, nullptr), + EXPECT_EQ(oakrender_request_frame(OakNodeNode{}, 0, noop_frame_ready, + nullptr), int64_t(OAKRENDER_E_INVALID)); ASSERT_EQ(oaknode_factory_initialize(), OAKNODE_OK); - OakNodeNode *viewer = oaknode_factory_create_from_id( + OakNodeNode viewer = oaknode_factory_create_from_id( "org.olivevideoeditor.Olive.vieweroutput"); - ASSERT_NE(viewer, nullptr); + ASSERT_NE(viewer.ctx, nullptr); EXPECT_EQ(oakrender_request_frame(viewer, 0, nullptr, nullptr), int64_t(OAKRENDER_E_INVALID)); - oaknode_node_free(viewer); + oaknode_node_free(&viewer); oaknode_factory_destroy(); } @@ -98,7 +99,7 @@ TEST(OakRenderManagerTest, CancelUnknownRequest) TEST(OakRenderManagerTest, CacherSettersRequireManager) { - EXPECT_EQ(oakrender_set_cacher_multicam(nullptr), OAKRENDER_E_STATE); + EXPECT_EQ(oakrender_set_cacher_multicam(OakNodeNode{}), OAKRENDER_E_STATE); EXPECT_EQ(oakrender_set_display_color_processor(nullptr), OAKRENDER_E_STATE); } diff --git a/src/task/c_api/project.cpp b/src/task/c_api/project.cpp index c73c221d2..17b6f2b68 100644 --- a/src/task/c_api/project.cpp +++ b/src/task/c_api/project.cpp @@ -66,20 +66,20 @@ OakTaskTask *oaktask_create_project_load(const char *filename) } } -OakNodeProject *oaktask_load_take_project(OakTaskTask *t) +OakNodeProject oaktask_load_take_project(OakTaskTask *t) { olive::ProjectLoadBaseTask *task = load_impl(t); if (!task) { - return NULL; + return OakNodeProject{}; } return task->take_project(); } -OakTaskTask *oaktask_create_project_save(OakNodeProject *project, +OakTaskTask *oaktask_create_project_save(OakNodeProject project, const char *filename_or_NULL, int use_compression) { - if (!project) { + if (!project.ctx) { return NULL; } try { @@ -94,12 +94,13 @@ OakTaskTask *oaktask_create_project_save(OakNodeProject *project, } } -OakTaskTask *oaktask_create_project_import(OakNodeFolder *folder, - OakNodeProject *project, +OakTaskTask *oaktask_create_project_import(OakNodeFolder folder, + OakNodeProject project, const char *const *urls, int url_count) { - if (!folder || !project || (!urls && url_count > 0) || url_count < 0) { + if (!folder.ctx || !project.ctx || (!urls && url_count > 0) || + url_count < 0) { return NULL; } try { @@ -136,14 +137,18 @@ int oaktask_import_footage_count(OakTaskTask *t) return int(task->get_imported_footage().size()); } -OakNodeFootage *oaktask_import_footage_at(OakTaskTask *t, int index) +OakNodeFootage oaktask_import_footage_at(OakTaskTask *t, int index) { olive::ProjectImportTask *task = import_impl(t); if (!task || index < 0 || index >= int(task->get_imported_footage().size())) { - return NULL; + return OakNodeFootage{}; } - return task->get_imported_footage()[size_t(index)]; + OakNodeFootage footage = task->get_imported_footage()[size_t(index)]; + if (footage.ctx) { + footage.addref(footage.ctx); + } + return footage; } int oaktask_import_invalid_count(OakTaskTask *t) @@ -183,10 +188,10 @@ void oaktask_import_set_image_sequence_confirm_cb( }); } -OakTaskTask *oaktask_create_precache(OakNodeFootage *footage, int index, - OakNodeSequence *sequence) +OakTaskTask *oaktask_create_precache(OakNodeFootage footage, int index, + OakNodeSequence sequence) { - if (!footage || !sequence) { + if (!footage.ctx || !sequence.ctx) { return NULL; } try { @@ -196,11 +201,11 @@ OakTaskTask *oaktask_create_precache(OakNodeFootage *footage, int index, } } -OakTaskTask *oaktask_create_export(OakNodeNode *viewer, - OakNodeColorManager *color_manager, +OakTaskTask *oaktask_create_export(OakNodeNode viewer, + OakNodeColorManager color_manager, const oakcodec_encoding_params *params) { - if (!viewer || !params) { + if (!viewer.ctx || !params) { return NULL; } try { @@ -222,19 +227,19 @@ OakTaskTask *oaktask_create_project_load_otio(const char *filename) } } -OakNodeProject *oaktask_load_otio_take_project(OakTaskTask *t) +OakNodeProject oaktask_load_otio_take_project(OakTaskTask *t) { olive::ProjectLoadBaseTask *task = load_impl(t); if (!task) { - return NULL; + return OakNodeProject{}; } return task->take_project(); } -OakTaskTask *oaktask_create_project_save_otio(OakNodeProject *project, +OakTaskTask *oaktask_create_project_save_otio(OakNodeProject project, const char *filename) { - if (!project || !filename) { + if (!project.ctx || !filename) { return NULL; } try { diff --git a/src/task/src/CMakeLists.txt b/src/task/src/CMakeLists.txt index d670cbcfa..223a60cf0 100644 --- a/src/task/src/CMakeLists.txt +++ b/src/task/src/CMakeLists.txt @@ -24,6 +24,7 @@ target_include_directories(oaktask PUBLIC ${OAK_REPO_ROOT}/include ${OAK_REPO_ROOT}/src/common/src ${OAK_REPO_ROOT}/src/undo/src + ${OAK_REPO_ROOT}/src/node/c_api ${OAK_REPO_ROOT}/core/include ${OAK_REPO_ROOT}/otio-install/include ) diff --git a/src/task/src/export/export.cpp b/src/task/src/export/export.cpp index 275e678a2..cf4c14c49 100644 --- a/src/task/src/export/export.cpp +++ b/src/task/src/export/export.cpp @@ -31,13 +31,15 @@ #include "olive/core/util/timecodefunctions.h" #include "render/color.h" +#include "nodehandle.h" + namespace olive { namespace { -std::string node_label(OakNodeNode *node) +std::string node_label(OakNodeNode node) { int needed = oaknode_node_get_label(node, nullptr, 0); if (needed <= 0) { @@ -128,13 +130,23 @@ OakFrame copy_frame_to_codec(OakCodecFrame *render_frame) return out; } +/** + * @brief Borrowed sequence alias of a viewer node handle (same underlying + * node; releasing the alias only frees its handle box). + */ +OakNodeSequence sequence_alias_of(OakNodeNode node) +{ + return oaknode_c_api::make_handle( + oaknode_c_api::to_native(node), false, nullptr); +} + } // namespace -ExportTask::ExportTask(OakNodeNode *viewer_node, - OakNodeColorManager *color_manager, +ExportTask::ExportTask(OakNodeNode viewer_node, + OakNodeColorManager color_manager, const oakcodec_encoding_params ¶ms) : copier_(nullptr) - , color_manager_(nullptr) + , color_manager_({}) , params_(params) , encoder_({}) , subtitle_encoder_({}) @@ -146,24 +158,26 @@ ExportTask::ExportTask(OakNodeNode *viewer_node, (void)color_manager; // Create a copy of the project - OakNodeProject *source_project = nullptr; + OakNodeProject source_project = {}; oaknode_node_get_project(viewer_node, &source_project); copier_ = oakrender_project_copier_create(); - if (copier_ && source_project) { + if (copier_ && source_project.ctx) { oakrender_project_copier_set_project(copier_, source_project); } + oaknode_project_free(&source_project); set_viewer(oakrender_project_copier_get_copy(copier_, viewer_node)); - OakNodeProject *copied_project = + OakNodeProject copied_project = oakrender_project_copier_get_copied_project(copier_); color_manager_ = oaknode_colormanager_init(copied_project); + oaknode_project_free(&copied_project); // Adjust video params to have no divider + OakNodeSequence viewer_sequence = sequence_alias_of(viewer_node); OakVideoParams vp = {}; - oaknode_sequence_get_video_params( - reinterpret_cast(viewer_node), 0, &vp); + oaknode_sequence_get_video_params(viewer_sequence, 0, &vp); oakcommon_videoparams_set_divider(vp, 1); oakcommon_videoparams_set_time_base(vp, params_.video_time_base_num, params_.video_time_base_den); @@ -173,10 +187,9 @@ ExportTask::ExportTask(OakNodeNode *viewer_node, oakcommon_videoparams_free(&vp); OakAudioParams *audio_params = nullptr; - oaknode_sequence_get_audio_params( - reinterpret_cast(viewer_node), 0, - &audio_params); + oaknode_sequence_get_audio_params(viewer_sequence, 0, &audio_params); set_audio_params(audio_params); + oaknode_sequence_free(&viewer_sequence); set_title("Exporting \"" + node_label(viewer_node) + "\""); set_native_progress_signalling_enabled(false); @@ -193,9 +206,7 @@ ExportTask::~ExportTask() if (color_processor_) { oakrender_color_processor_free(color_processor_); } - if (color_manager_) { - oaknode_colormanager_free(color_manager_); - } + oaknode_colormanager_free(&color_manager_); oakrender_project_copier_free(copier_); if (audio_params()) { oakcore_audioparams_free(audio_params()); @@ -282,8 +293,9 @@ bool ExportTask::run() } else { // Render entire sequence int len_n = 0, len_d = 1; - oaknode_sequence_get_length( - reinterpret_cast(viewer()), &len_n, &len_d); + OakNodeSequence viewer_sequence = sequence_alias_of(viewer()); + oaknode_sequence_get_length(viewer_sequence, &len_n, &len_d); + oaknode_sequence_free(&viewer_sequence); export_range_ = TimeRange(Rational(0), Rational(len_n, len_d)); } @@ -474,7 +486,7 @@ bool ExportTask::audio_downloaded(const TimeRange &range, return true; } -bool ExportTask::encode_subtitle(OakNodeBlock *sub) +bool ExportTask::encode_subtitle(OakNodeBlock sub) { // The subtitle block's text is its standard "text" input char text[8192]; diff --git a/src/task/src/export/export.h b/src/task/src/export/export.h index 5d500b08b..e9c0d8c66 100644 --- a/src/task/src/export/export.h +++ b/src/task/src/export/export.h @@ -41,7 +41,7 @@ namespace olive */ class ExportTask : public RenderTask { public: - ExportTask(OakNodeNode *viewer_node, OakNodeColorManager *color_manager, + ExportTask(OakNodeNode viewer_node, OakNodeColorManager color_manager, const oakcodec_encoding_params ¶ms); virtual ~ExportTask() override; @@ -55,7 +55,7 @@ protected: virtual bool audio_downloaded(const TimeRange &range, OakSampleBuffer *samples) override; - virtual bool encode_subtitle(OakNodeBlock *sub) override; + virtual bool encode_subtitle(OakNodeBlock sub) override; private: bool write_audio_loop(const TimeRange &time, OakSampleBuffer *samples); @@ -72,7 +72,8 @@ private: }; std::map audio_map_; - OakNodeColorManager *color_manager_; + /** Owned handle (created on the copied project). */ + OakNodeColorManager color_manager_; oakcodec_encoding_params params_; diff --git a/src/task/src/precache/precachetask.cpp b/src/task/src/precache/precachetask.cpp index 717d4471c..3594c0246 100644 --- a/src/task/src/precache/precachetask.cpp +++ b/src/task/src/precache/precachetask.cpp @@ -31,6 +31,8 @@ #include "rendermodes.h" #include "timeline/workarea.h" +#include "nodehandle.h" + namespace olive { @@ -41,10 +43,10 @@ const char *k_viewer_output_id = "org.olivevideoeditor.Olive.vieweroutput"; } // namespace -PreCacheTask::PreCacheTask(OakNodeFootage *footage, int index, - OakNodeSequence *sequence) - : project_(nullptr) - , footage_(nullptr) +PreCacheTask::PreCacheTask(OakNodeFootage footage, int index, + OakNodeSequence sequence) + : project_({}) + , footage_({}) , audio_params_(nullptr) { // Set video and audio params @@ -71,31 +73,35 @@ PreCacheTask::PreCacheTask(OakNodeFootage *footage, int index, } // Copy project config nodes - OakNodeProject *source_project = nullptr; + OakNodeProject source_project = {}; oaknode_node_get_project(oaknode_footage_as_node(footage), &source_project); - if (source_project) { + if (source_project.ctx) { oaknode_project_copy_settings(project_, source_project); + oaknode_project_free(&source_project); } // Copy footage node so it can precache without any modifications from the user screwing it up - OakNodeNode *footage_copy = + OakNodeNode footage_copy = oaknode_node_create_copy(oaknode_footage_as_node(footage)); - footage_ = reinterpret_cast( - oaknode_block_from_node(footage_copy)); - if (!footage_) { - footage_ = reinterpret_cast(footage_copy); - } oaknode_project_add_node(project_, footage_copy); oaknode_node_copy_inputs(footage_copy, oaknode_footage_as_node(footage), 0); + // Borrowed footage alias of the copied node (releasing it only frees + // the handle box; the graph owns the node). + footage_ = oaknode_c_api::make_handle( + oaknode_c_api::to_native(footage_copy), false, nullptr); + oaknode_node_connect(footage_copy, viewer(), OAKNODE_SEQUENCE_TEXTURE_INPUT); oaknode_node_set_value_hint_track(viewer(), OAKNODE_SEQUENCE_TEXTURE_INPUT, OAKNODE_TRACK_TYPE_VIDEO, index); + // The graph owns the copy now; release our handle box. + oaknode_node_free(&footage_copy); + char filename[1024]; if (oaknode_footage_filename(footage, filename, sizeof(filename)) <= 0) { @@ -108,7 +114,10 @@ PreCacheTask::PreCacheTask(OakNodeFootage *footage, int index, PreCacheTask::~PreCacheTask() { // This should delete the footage we copied and the viewer we created - oaknode_project_free(project_); + oaknode_project_free(&project_); + // Release the borrowed alias box (the footage itself died with the + // project above). + oaknode_c_api::free_handle(&footage_); if (audio_params_) { oakcore_audioparams_free(audio_params_); } @@ -167,13 +176,13 @@ bool PreCacheTask::run() } } - OakNodeColorManager *color_manager = + OakNodeColorManager color_manager = oaknode_colormanager_init(project_); render(color_manager, video_range, TimeRangeList(), TimeRange(), 0 /* RenderMode::k_online */, cache, ForceParams()); - oaknode_colormanager_free(color_manager); + oaknode_colormanager_free(&color_manager); return true; } diff --git a/src/task/src/precache/precachetask.h b/src/task/src/precache/precachetask.h index 424fd2cfd..6b0112fad 100644 --- a/src/task/src/precache/precachetask.h +++ b/src/task/src/precache/precachetask.h @@ -31,8 +31,8 @@ namespace olive class PreCacheTask : public RenderTask { public: - PreCacheTask(OakNodeFootage *footage, int index, - OakNodeSequence *sequence); + PreCacheTask(OakNodeFootage footage, int index, + OakNodeSequence sequence); virtual ~PreCacheTask() override; @@ -46,9 +46,12 @@ protected: OakSampleBuffer *samples) override; private: - OakNodeProject *project_; + /** Owned handle; the project owns the whole graph (including the + * copied footage and the viewer), so freeing it tears them down. */ + OakNodeProject project_; - OakNodeFootage *footage_; + /** Borrowed alias of the copied footage node inside `project_`. */ + OakNodeFootage footage_; OakAudioParams *audio_params_; }; diff --git a/src/task/src/project/import/import.cpp b/src/task/src/project/import/import.cpp index ea395023a..3a601fa10 100644 --- a/src/task/src/project/import/import.cpp +++ b/src/task/src/project/import/import.cpp @@ -63,7 +63,7 @@ std::string basename_of(const std::string &path) ProjectImportTask::ImageSequenceConfirmFn ProjectImportTask::confirm_callback_; ProjectImportTask::ProjectImportTask( - OakNodeFolder *folder, OakNodeProject *project, + OakNodeFolder folder, OakNodeProject project, const std::vector &filenames) : command_({}) , folder_(folder) @@ -83,6 +83,13 @@ ProjectImportTask::~ProjectImportTask() if (command_.ctx) { oakundo_command_free(&command_); } + // Borrowed handles: releasing them only frees the handle boxes (the + // footage nodes are owned by the project). + for (OakNodeFootage &footage : imported_footage_) { + if (footage.ctx) { + footage.release(footage.ctx); + } + } } const int &ProjectImportTask::get_file_count() const @@ -110,7 +117,7 @@ bool ProjectImportTask::run() return true; } -void ProjectImportTask::import(OakNodeFolder *folder, +void ProjectImportTask::import(OakNodeFolder folder, const std::vector &entries, int &counter, OakUndoCommand parent_command) { @@ -133,8 +140,8 @@ void ProjectImportTask::import(OakNodeFolder *folder, } if (!entry_list.empty()) { - OakNodeFolder *f = oaknode_folder_create(project_); - if (!f) { + OakNodeFolder f = oaknode_folder_create(project_); + if (!f.ctx) { continue; } @@ -149,9 +156,9 @@ void ProjectImportTask::import(OakNodeFolder *folder, } } else { - OakNodeFootage *footage = + OakNodeFootage footage = oaknode_footage_create(project_, nullptr); - if (!footage) { + if (!footage.ctx) { continue; } @@ -181,9 +188,15 @@ void ProjectImportTask::import(OakNodeFolder *folder, // Add to list so we can tell the user about it later invalid_files_.push_back(file_path); - oaknode_project_remove_node(project_, - oaknode_footage_as_node(footage)); - oaknode_node_free(oaknode_footage_as_node(footage)); + // Remove the invalid footage from the graph; the remove + // command takes ownership on redo and deletes the node + // when the command is destroyed. + OakUndoCommand remove = oaknode_command_create_remove_node( + oaknode_footage_as_node(footage)); + if (remove.ctx) { + oakundo_command_redo_now(remove); + oakundo_command_free(&remove); + } } counter++; @@ -195,7 +208,7 @@ void ProjectImportTask::import(OakNodeFolder *folder, } void ProjectImportTask::validate_image_sequence( - OakNodeFootage *footage, std::vector &info_list, + OakNodeFootage footage, std::vector &info_list, size_t index) { char filename[1024]; @@ -245,15 +258,15 @@ void ProjectImportTask::validate_image_sequence( oakcodec_decoder_transform_image_sequence_file_name(filename, ind + 1, next_fn, sizeof(next_fn)); - OakNodeFootage *previous_file = + OakNodeFootage previous_file = oaknode_footage_create(project_, prev_fn); - OakNodeFootage *next_file = oaknode_footage_create(project_, next_fn); + OakNodeFootage next_file = oaknode_footage_create(project_, next_fn); bool prev_matches = - previous_file && oaknode_footage_is_valid(previous_file) && + previous_file.ctx && oaknode_footage_is_valid(previous_file) && compare_still_image_size(previous_file, width, height); bool next_matches = - next_file && oaknode_footage_is_valid(next_file) && + next_file.ctx && oaknode_footage_is_valid(next_file) && compare_still_image_size(next_file, width, height); if (prev_matches || next_matches) { @@ -322,20 +335,23 @@ void ProjectImportTask::validate_image_sequence( oakcommon_videoparams_free(&video_stream); - if (previous_file) { - oaknode_project_remove_node(project_, - oaknode_footage_as_node(previous_file)); - oaknode_node_free(oaknode_footage_as_node(previous_file)); - } - if (next_file) { - oaknode_project_remove_node(project_, - oaknode_footage_as_node(next_file)); - oaknode_node_free(oaknode_footage_as_node(next_file)); + // The probe footage above was only created for comparison; remove it + // from the graph again (the remove command deletes the node, see + // import()). + for (OakNodeFootage probe : { previous_file, next_file }) { + if (probe.ctx) { + OakUndoCommand remove = oaknode_command_create_remove_node( + oaknode_footage_as_node(probe)); + if (remove.ctx) { + oakundo_command_redo_now(remove); + oakundo_command_free(&remove); + } + } } } -void ProjectImportTask::add_item_to_folder(OakNodeFolder *folder, - OakNodeNode *item, +void ProjectImportTask::add_item_to_folder(OakNodeFolder folder, + OakNodeNode item, OakUndoCommand command) { OakUndoCommand child = @@ -346,7 +362,7 @@ void ProjectImportTask::add_item_to_folder(OakNodeFolder *folder, } bool ProjectImportTask::item_is_still_image_footage_only( - OakNodeFootage *footage) + OakNodeFootage footage) { if (oaknode_footage_total_stream_count(footage) != 1) { // Footage with more than one stream (usually video+audio) most likely isn't an image sequence @@ -368,7 +384,7 @@ bool ProjectImportTask::item_is_still_image_footage_only( return valid && video_type == OAKCOMMON_VIDEO_TYPE_STILL; } -bool ProjectImportTask::compare_still_image_size(OakNodeFootage *footage, +bool ProjectImportTask::compare_still_image_size(OakNodeFootage footage, int width, int height) { if (!item_is_still_image_footage_only(footage)) { diff --git a/src/task/src/project/import/import.h b/src/task/src/project/import/import.h index 5a8e2e58c..5d451a9fe 100644 --- a/src/task/src/project/import/import.h +++ b/src/task/src/project/import/import.h @@ -37,7 +37,7 @@ namespace olive class ProjectImportTask : public Task { public: - ProjectImportTask(OakNodeFolder *folder, OakNodeProject *project, + ProjectImportTask(OakNodeFolder folder, OakNodeProject project, const std::vector &filenames); ~ProjectImportTask() override; @@ -62,7 +62,7 @@ public: return !invalid_files_.empty(); } - const std::vector &get_imported_footage() const + const std::vector &get_imported_footage() const { return imported_footage_; } @@ -85,20 +85,20 @@ protected: virtual bool run() override; private: - void import(OakNodeFolder *folder, + void import(OakNodeFolder folder, const std::vector &entries, int &counter, OakUndoCommand parent_command); - void validate_image_sequence(OakNodeFootage *footage, + void validate_image_sequence(OakNodeFootage footage, std::vector &info_list, size_t index); - void add_item_to_folder(OakNodeFolder *folder, OakNodeNode *item, + void add_item_to_folder(OakNodeFolder folder, OakNodeNode item, OakUndoCommand command); - static bool item_is_still_image_footage_only(OakNodeFootage *footage); + static bool item_is_still_image_footage_only(OakNodeFootage footage); - static bool compare_still_image_size(OakNodeFootage *footage, int width, + static bool compare_still_image_size(OakNodeFootage footage, int width, int height); static int64_t get_image_sequence_limit(const std::string &start_fn, @@ -106,9 +106,10 @@ private: OakUndoCommand command_; - OakNodeFolder *folder_; + /** Borrowed handles; the caller keeps ownership of both. */ + OakNodeFolder folder_; - OakNodeProject *project_; + OakNodeProject project_; std::vector filenames_; @@ -118,7 +119,8 @@ private: std::vector image_sequence_ignore_files_; - std::vector imported_footage_; + /** Borrowed footage handles; the project owns the footage nodes. */ + std::vector imported_footage_; static ImageSequenceConfirmFn confirm_callback_; }; diff --git a/src/task/src/project/load/load.cpp b/src/task/src/project/load/load.cpp index 420c91639..52057df60 100644 --- a/src/task/src/project/load/load.cpp +++ b/src/task/src/project/load/load.cpp @@ -27,7 +27,7 @@ namespace olive { ProjectLoadBaseTask::ProjectLoadBaseTask(const std::string &filename) - : project_(nullptr) + : project_({}) , filename_(filename) { set_title("Loading '" + filename + "'"); @@ -41,7 +41,7 @@ ProjectLoadTask::ProjectLoadTask(const std::string &filename) bool ProjectLoadTask::run() { project_ = oaknode_project_init(); - if (!project_) { + if (!project_.ctx) { set_error("Failed to create project"); return false; } @@ -92,8 +92,7 @@ bool ProjectLoadTask::run() return true; } - oaknode_project_free(project_); - project_ = nullptr; + oaknode_project_free(&project_); return false; } diff --git a/src/task/src/project/load/load.h b/src/task/src/project/load/load.h index a37075e5c..59a46acff 100644 --- a/src/task/src/project/load/load.h +++ b/src/task/src/project/load/load.h @@ -38,13 +38,13 @@ public: ProjectLoadBaseTask(const std::string &filename); /** - * @brief Take the loaded project (ownership transfer). NULL if the - * task has not succeeded. + * @brief Take the loaded project (ownership transfer). Empty handle + * (ctx == NULL) if the task has not succeeded. */ - OakNodeProject *take_project() + OakNodeProject take_project() { - OakNodeProject *p = project_; - project_ = nullptr; + OakNodeProject p = project_; + project_ = OakNodeProject{}; return p; } @@ -54,7 +54,7 @@ public: } protected: - OakNodeProject *project_; + OakNodeProject project_; private: std::string filename_; diff --git a/src/task/src/project/loadotio/loadotio.cpp b/src/task/src/project/loadotio/loadotio.cpp index c6da224e3..46dc93830 100644 --- a/src/task/src/project/loadotio/loadotio.cpp +++ b/src/task/src/project/loadotio/loadotio.cpp @@ -58,7 +58,7 @@ const char *k_sequence_id = "org.olivevideoeditor.Olive.sequence"; const char *k_transform_id = "org.olivevideoeditor.Olive.transform"; const char *k_volume_id = "org.olivevideoeditor.Olive.volume"; -void set_own_context_position(OakNodeNode *node, double x, double y) +void set_own_context_position(OakNodeNode node, double x, double y) { oaknode_node_set_context_position(node, node, x, y, 0); } @@ -87,7 +87,7 @@ bool LoadOTIOTask::run() } project_ = oaknode_project_init(); - if (!project_) { + if (!project_.ctx) { set_error("Failed to create project"); return false; } @@ -113,14 +113,13 @@ bool LoadOTIOTask::run() } else { // Unknown root, we don't know what to do with this set_error("Unknown OpenTimelineIO root element"); - oaknode_project_free(project_); - project_ = nullptr; + oaknode_project_free(&project_); return false; } // Keep track of imported footage - std::map imported_footage; - std::map timeline_sequence_map; + std::map imported_footage; + std::map timeline_sequence_map; // Variables used for loading bar float number_of_clips = 0; @@ -130,8 +129,8 @@ bool LoadOTIOTask::run() // Assumes each timeline has a unique name. int unnamed_sequence_count = 0; for (auto timeline : timelines) { - OakNodeSequence *sequence = oaknode_sequence_create(); - if (!sequence) { + OakNodeSequence sequence = oaknode_sequence_create(); + if (!sequence.ctx) { continue; } @@ -180,16 +179,16 @@ bool LoadOTIOTask::run() if (!accepted) { // Cancel to indicate to caller that this task did not complete and to simply dispose of it cancel(); - for (const auto &pair : timeline_sequence_map) { - oaknode_sequence_free(pair.second); + for (auto &pair : timeline_sequence_map) { + oaknode_sequence_free(&pair.second); } return true; } for (const auto &pair : timeline_sequence_map) { OTIO::Timeline *timeline = pair.first; - OakNodeSequence *sequence = pair.second; - OakNodeNode *sequence_node = oaknode_sequence_as_node(sequence); + OakNodeSequence sequence = pair.second; + OakNodeNode sequence_node = oaknode_sequence_as_node(sequence); oaknode_project_add_node(project_, sequence_node); OakUndoCommand add_seq = oaknode_command_create_folder_add_child( @@ -200,9 +199,9 @@ bool LoadOTIOTask::run() } // Create a folder for this sequence's footage - OakNodeFolder *sequence_footage = + OakNodeFolder sequence_footage = oaknode_folder_create(project_); - if (sequence_footage) { + if (sequence_footage.ctx) { oaknode_node_set_label(oaknode_folder_as_node(sequence_footage), timeline->name().c_str()); OakUndoCommand add_folder = @@ -220,7 +219,7 @@ bool LoadOTIOTask::run() auto otio_track = static_cast(c.value); // Create a new track - OakNodeTrack *track = nullptr; + OakNodeTrack track = {}; // Determine what kind of track it is int track_type = OAKNODE_TRACK_TYPE_NONE; @@ -235,7 +234,7 @@ bool LoadOTIOTask::run() } { - OakNodeTrackList *track_list = nullptr; + OakNodeTrackList track_list = {}; oaknode_sequence_get_track_list(sequence, track_type, &track_list); OakUndoCommand add_track = @@ -253,20 +252,20 @@ bool LoadOTIOTask::run() } } - if (!track) { + if (!track.ctx) { continue; } // Get clips from track auto clip_map = otio_track->children(); - OakNodeBlock *previous_block = nullptr; + OakNodeBlock previous_block = {}; bool prev_block_transition = false; for (auto otio_block_retainer : clip_map) { auto otio_block = otio_block_retainer.value; - OakNodeBlock *block = nullptr; + OakNodeBlock block = {}; if (otio_block->schema_name() == "Clip") { block = oaknode_block_clip_create(); @@ -286,7 +285,7 @@ bool LoadOTIOTask::run() block = oaknode_block_gap_create(); } - if (!block) { + if (!block.ctx) { continue; } @@ -347,7 +346,7 @@ bool LoadOTIOTask::run() block, in_offset.numerator(), in_offset.denominator(), out_offset.numerator(), out_offset.denominator()); - if (previous_block) { + if (previous_block.ctx) { oaknode_node_connect( oaknode_block_as_node(previous_block), oaknode_block_as_node(block), @@ -382,7 +381,7 @@ bool LoadOTIOTask::run() otio_clip->media_reference()) ->target_url(); - OakNodeFootage *probed_item = nullptr; + OakNodeFootage probed_item = {}; auto it = imported_footage.find(footage_url); if (it != imported_footage.end()) { @@ -390,7 +389,7 @@ bool LoadOTIOTask::run() } else { probed_item = oaknode_footage_create( project_, footage_url.c_str()); - if (probed_item) { + if (probed_item.ctx) { imported_footage.insert( { footage_url, probed_item }); @@ -402,7 +401,7 @@ bool LoadOTIOTask::run() oaknode_footage_as_node(probed_item), label.c_str()); - if (sequence_footage) { + if (sequence_footage.ctx) { OakUndoCommand add_footage = oaknode_command_create_folder_add_child( sequence_footage, @@ -416,7 +415,7 @@ bool LoadOTIOTask::run() } } - if (probed_item) { + if (probed_item.ctx) { // Position clip in its own context set_own_context_position( oaknode_block_as_node(block), 0, 0); @@ -428,10 +427,10 @@ bool LoadOTIOTask::run() 0); if (track_type == OAKNODE_TRACK_TYPE_VIDEO) { - OakNodeNode *transform = + OakNodeNode transform = oaknode_factory_create_from_id( k_transform_id); - if (transform) { + if (transform.ctx) { oaknode_project_add_node(project_, transform); @@ -448,10 +447,10 @@ bool LoadOTIOTask::run() transform, -1, 0, 0); } } else { - OakNodeNode *volume_node = + OakNodeNode volume_node = oaknode_factory_create_from_id( k_volume_id); - if (volume_node) { + if (volume_node.ctx) { oaknode_project_add_node(project_, volume_node); diff --git a/src/task/src/project/save/save.cpp b/src/task/src/project/save/save.cpp index 9724ce737..31761344e 100644 --- a/src/task/src/project/save/save.cpp +++ b/src/task/src/project/save/save.cpp @@ -29,7 +29,7 @@ namespace olive namespace { -std::string project_filename(OakNodeProject *project) +std::string project_filename(OakNodeProject project) { int needed = oaknode_project_filename(project, nullptr, 0); if (needed <= 0) { @@ -43,7 +43,7 @@ std::string project_filename(OakNodeProject *project) } // namespace -ProjectSaveTask::ProjectSaveTask(OakNodeProject *project, +ProjectSaveTask::ProjectSaveTask(OakNodeProject project, bool use_compression) : project_(project) , use_compression_(use_compression) diff --git a/src/task/src/project/save/save.h b/src/task/src/project/save/save.h index 1cc8c5422..465b087ae 100644 --- a/src/task/src/project/save/save.h +++ b/src/task/src/project/save/save.h @@ -32,9 +32,9 @@ namespace olive class ProjectSaveTask : public Task { public: - ProjectSaveTask(OakNodeProject *project, bool use_compression); + ProjectSaveTask(OakNodeProject project, bool use_compression); - OakNodeProject *get_project() const + OakNodeProject get_project() const { return project_; } @@ -48,7 +48,8 @@ protected: virtual bool run() override; private: - OakNodeProject *project_; + /** Borrowed handle; the caller keeps ownership of the project. */ + OakNodeProject project_; std::string override_filename_; diff --git a/src/task/src/project/saveotio/saveotio.cpp b/src/task/src/project/saveotio/saveotio.cpp index 0e34928e5..194727d0c 100644 --- a/src/task/src/project/saveotio/saveotio.cpp +++ b/src/task/src/project/saveotio/saveotio.cpp @@ -44,7 +44,7 @@ namespace olive namespace { -std::string node_label_of(OakNodeNode *node) +std::string node_label_of(OakNodeNode node) { char buf[256]; if (oaknode_node_get_label(node, buf, sizeof(buf)) <= 0) { @@ -53,21 +53,21 @@ std::string node_label_of(OakNodeNode *node) return buf; } -Rational block_in_of(OakNodeBlock *b) +Rational block_in_of(OakNodeBlock b) { int n = 0, d = 1; oaknode_block_get_in(b, &n, &d); return Rational(n, d); } -Rational block_length_of(OakNodeBlock *b) +Rational block_length_of(OakNodeBlock b) { int n = 0, d = 1; oaknode_block_get_length(b, &n, &d); return Rational(n, d); } -Rational track_length_of(OakNodeTrack *t) +Rational track_length_of(OakNodeTrack t) { int n = 0, d = 1; oaknode_track_get_length(t, &n, &d); @@ -76,7 +76,7 @@ Rational track_length_of(OakNodeTrack *t) } // namespace -SaveOTIOTask::SaveOTIOTask(OakNodeProject *project, +SaveOTIOTask::SaveOTIOTask(OakNodeProject project, const std::string &filename) : project_(project) , filename_(filename) @@ -88,18 +88,18 @@ bool SaveOTIOTask::run() { // Collect sequences from the root folder (non-recursive, matching the // original list_children_of_type behavior closely enough for OTIO) - std::vector sequences; + std::vector sequences; - OakNodeFolder *root = oaknode_project_root(project_); - if (!root) { + OakNodeFolder root = oaknode_project_root(project_); + if (!root.ctx) { set_error("Project contains no sequences to export."); return false; } int child_count = oaknode_folder_child_count(root); for (int i = 0; i < child_count; i++) { - OakNodeNode *child = oaknode_folder_child_at(root, i); - if (!child) { + OakNodeNode child = oaknode_folder_child_at(root, i); + if (!child.ctx) { continue; } @@ -108,8 +108,14 @@ bool SaveOTIOTask::run() continue; } if (std::string(id) == "org.olivevideoeditor.Olive.sequence") { - sequences.push_back( - reinterpret_cast(child)); + // Borrowed sequence alias of the child node handle (all + // oaknode handles share the same box layout). + OakNodeSequence sequence = {}; + sequence.ctx = child.ctx; + sequence.addref = child.addref; + sequence.release = child.release; + sequence.abi_version = child.abi_version; + sequences.push_back(sequence); } } @@ -120,7 +126,7 @@ bool SaveOTIOTask::run() std::vector serialized; - for (OakNodeSequence *seq : sequences) { + for (OakNodeSequence seq : sequences) { auto otio_timeline = serialize_timeline(seq); if (otio_timeline) { @@ -163,7 +169,7 @@ bool SaveOTIOTask::run() return (es.outcome == OTIO::ErrorStatus::Outcome::OK); } -OTIO::Timeline *SaveOTIOTask::serialize_timeline(OakNodeSequence *sequence) +OTIO::Timeline *SaveOTIOTask::serialize_timeline(OakNodeSequence sequence) { auto otio_timeline = new OTIO::Timeline( node_label_of(oaknode_sequence_as_node(sequence))); @@ -189,8 +195,8 @@ OTIO::Timeline *SaveOTIOTask::serialize_timeline(OakNodeSequence *sequence) return nullptr; } - OakNodeTrackList *video_list = nullptr; - OakNodeTrackList *audio_list = nullptr; + OakNodeTrackList video_list = {}; + OakNodeTrackList audio_list = {}; oaknode_sequence_get_track_list(sequence, OAKNODE_TRACK_TYPE_VIDEO, &video_list); oaknode_sequence_get_track_list(sequence, OAKNODE_TRACK_TYPE_AUDIO, @@ -205,7 +211,7 @@ OTIO::Timeline *SaveOTIOTask::serialize_timeline(OakNodeSequence *sequence) return otio_timeline; } -OTIO::Track *SaveOTIOTask::serialize_track(OakNodeTrack *track, +OTIO::Track *SaveOTIOTask::serialize_track(OakNodeTrack track, double sequence_rate, Rational max_track_length) { @@ -234,9 +240,9 @@ OTIO::Track *SaveOTIOTask::serialize_track(OakNodeTrack *track, oaknode_track_get_block_count(track, &block_count); for (int i = 0; i < block_count; i++) { - OakNodeBlock *block = nullptr; + OakNodeBlock block = {}; oaknode_track_get_block_at(track, i, &block); - if (!block) { + if (!block.ctx) { continue; } @@ -253,10 +259,10 @@ OTIO::Track *SaveOTIOTask::serialize_track(OakNodeTrack *track, block_in_of(block).toRationalTime(sequence_rate), block_length_of(block).toRationalTime(sequence_rate))); - OakNodeFootage *media = nullptr; + OakNodeFootage media = {}; oaknode_node_find_input_footage( oaknode_block_as_node(block), &media); - if (media) { + if (media.ctx) { OTIO::TimeRange available_range; if (track_type == OAKNODE_TRACK_TYPE_VIDEO) { // OTIO ExternalReference uses the source clips frame rate (or sample rate) as opposed to @@ -353,11 +359,11 @@ fail: return nullptr; } -bool SaveOTIOTask::serialize_track_list(OakNodeTrackList *list, +bool SaveOTIOTask::serialize_track_list(OakNodeTrackList list, OTIO::Timeline *otio_timeline, double sequence_rate) { - if (!list) { + if (!list.ctx) { return true; } @@ -369,17 +375,17 @@ bool SaveOTIOTask::serialize_track_list(OakNodeTrackList *list, oaknode_tracklist_get_track_count(list, &track_count); for (int i = 0; i < track_count; i++) { - OakNodeTrack *track = nullptr; + OakNodeTrack track = {}; oaknode_tracklist_get_track_at(list, i, &track); - if (track && track_length_of(track) > max_track_length) { + if (track.ctx && track_length_of(track) > max_track_length) { max_track_length = track_length_of(track); } } for (int i = 0; i < track_count; i++) { - OakNodeTrack *track = nullptr; + OakNodeTrack track = {}; oaknode_tracklist_get_track_at(list, i, &track); - if (!track) { + if (!track.ctx) { continue; } diff --git a/src/task/src/project/saveotio/saveotio.h b/src/task/src/project/saveotio/saveotio.h index 01ac8a582..59ef536ef 100644 --- a/src/task/src/project/saveotio/saveotio.h +++ b/src/task/src/project/saveotio/saveotio.h @@ -43,22 +43,23 @@ using core::Rational; class SaveOTIOTask : public Task { public: - SaveOTIOTask(OakNodeProject *project, const std::string &filename); + SaveOTIOTask(OakNodeProject project, const std::string &filename); protected: virtual bool run() override; private: - OTIO::Timeline *serialize_timeline(OakNodeSequence *sequence); + OTIO::Timeline *serialize_timeline(OakNodeSequence sequence); - OTIO::Track *serialize_track(OakNodeTrack *track, double sequence_rate, + OTIO::Track *serialize_track(OakNodeTrack track, double sequence_rate, Rational max_track_length); - bool serialize_track_list(OakNodeTrackList *list, + bool serialize_track_list(OakNodeTrackList list, OTIO::Timeline *otio_timeline, double sequence_rate); - OakNodeProject *project_; + /** Borrowed handle; the caller keeps ownership of the project. */ + OakNodeProject project_; std::string filename_; }; diff --git a/src/task/src/render/render.cpp b/src/task/src/render/render.cpp index f9b51b290..a4ef70801 100644 --- a/src/task/src/render/render.cpp +++ b/src/task/src/render/render.cpp @@ -27,20 +27,22 @@ #include "node/sequence.h" #include "node/track.h" +#include "nodehandle.h" + namespace olive { namespace { -Rational task_block_in(OakNodeBlock *b) +Rational task_block_in(OakNodeBlock b) { int n = 0, d = 1; oaknode_block_get_in(b, &n, &d); return Rational(n, d); } -Rational task_block_out(OakNodeBlock *b) +Rational task_block_out(OakNodeBlock b) { int n = 0, d = 1; oaknode_block_get_out(b, &n, &d); @@ -50,7 +52,7 @@ Rational task_block_out(OakNodeBlock *b) } // namespace RenderTask::RenderTask() - : viewer_(nullptr) + : viewer_({}) , video_params_({}) , audio_params_(nullptr) , running_tickets_(0) @@ -61,6 +63,9 @@ RenderTask::RenderTask() RenderTask::~RenderTask() { + // Borrowed/owned indifferent: releasing the viewer handle only frees + // the handle box once the node lives in a graph. + oaknode_node_free(&viewer_); if (video_params_.ctx) { oakcommon_videoparams_free(&video_params_); } @@ -75,15 +80,15 @@ void RenderTask::on_ticket_finished(OakRenderTicket *ticket) finished_mutex_.unlock(); } -bool RenderTask::start_video_ticket(OakNodeColorManager *manager, +bool RenderTask::start_video_ticket(OakNodeColorManager manager, const Rational &time, int mode, OakNodeFrameCache *cache, const ForceParams &force) { - OakNodeNode *output_node = nullptr; + OakNodeNode output_node = {}; oaknode_node_input_get_connected_node( viewer_, OAKNODE_SEQUENCE_TEXTURE_INPUT, &output_node); - if (!output_node) { + if (!output_node.ctx) { return false; } @@ -113,6 +118,9 @@ bool RenderTask::start_video_ticket(OakNodeColorManager *manager, static_cast(userdata)->on_ticket_finished(t); }, this); + // Per-frame call: release the borrowed handle box (the ticket keeps + // the native node, not the handle). + oaknode_node_free(&output_node); if (!ticket) { return false; } @@ -124,7 +132,7 @@ bool RenderTask::start_video_ticket(OakNodeColorManager *manager, return true; } -bool RenderTask::render(OakNodeColorManager *manager, +bool RenderTask::render(OakNodeColorManager manager, const TimeRangeList &video_range, const TimeRangeList &audio_range, const TimeRange &subtitle_range, int render_mode, @@ -137,10 +145,10 @@ bool RenderTask::render(OakNodeColorManager *manager, // Queue audio jobs for (const TimeRange &range : audio_range) { - OakNodeNode *output_node = nullptr; + OakNodeNode output_node = {}; oaknode_node_input_get_connected_node( viewer_, OAKNODE_SEQUENCE_SAMPLES_INPUT, &output_node); - if (!output_node) { + if (!output_node.ctx) { continue; } @@ -158,6 +166,7 @@ bool RenderTask::render(OakNodeColorManager *manager, running_tickets_++; finished_mutex_.unlock(); } + oaknode_node_free(&output_node); } // Frame timestamps @@ -198,13 +207,16 @@ bool RenderTask::render(OakNodeColorManager *manager, // Subtitle loop, loops over all blocks in sequence on all tracks if (!subtitle_range.length().isNull()) { - OakNodeSequence *sequence = - reinterpret_cast(viewer_); - OakNodeTrackList *list = nullptr; + // Borrowed sequence alias of the viewer handle (same underlying + // node; releasing it only frees the handle box). + OakNodeSequence sequence = oaknode_c_api::make_handle< + OakNodeSequence>(oaknode_c_api::to_native(viewer_), false, + nullptr); + OakNodeTrackList list = {}; oaknode_sequence_get_track_list( sequence, OAKNODE_TRACK_TYPE_SUBTITLE, &list); - if (list) { + if (list.ctx) { int track_count = 0; oaknode_tracklist_get_track_count(list, &track_count); @@ -214,9 +226,9 @@ bool RenderTask::render(OakNodeColorManager *manager, tracks_to_push.clear(); for (int i = 0; i < track_count; i++) { - OakNodeTrack *this_track = nullptr; + OakNodeTrack this_track = {}; oaknode_tracklist_get_track_at(list, i, &this_track); - if (!this_track) { + if (!this_track.ctx) { continue; } @@ -234,27 +246,27 @@ bool RenderTask::render(OakNodeColorManager *manager, continue; } - OakNodeBlock *this_block = nullptr; + OakNodeBlock this_block = {}; oaknode_track_get_block_at(this_track, this_block_index, &this_block); - OakNodeTrack *compare_track = nullptr; + OakNodeTrack compare_track = {}; if (!tracks_to_push.empty()) { oaknode_tracklist_get_track_at( list, tracks_to_push.front(), &compare_track); } - OakNodeBlock *compare_block = nullptr; - if (compare_track) { + OakNodeBlock compare_block = {}; + if (compare_track.ctx) { oaknode_track_get_block_at( compare_track, block_indexes[size_t( tracks_to_push.front())], &compare_block); } - if (!compare_track || + if (!compare_track.ctx || task_block_out(compare_block) >= task_block_in(this_block)) { - if (compare_track && + if (compare_track.ctx && task_block_in(compare_block) != task_block_in(this_block)) { tracks_to_push.clear(); @@ -264,17 +276,17 @@ bool RenderTask::render(OakNodeColorManager *manager, } for (int i : tracks_to_push) { - OakNodeTrack *this_track = nullptr; + OakNodeTrack this_track = {}; oaknode_tracklist_get_track_at(list, i, &this_track); - OakNodeBlock *this_block = nullptr; + OakNodeBlock this_block = {}; oaknode_track_get_block_at( this_track, block_indexes[size_t(i)], &this_block); int kind = OAKNODE_BLOCK_OTHER; - if (this_block) { + if (this_block.ctx) { oaknode_block_get_kind(this_block, &kind); } - if (this_block && kind != OAKNODE_BLOCK_GAP) { + if (this_block.ctx && kind != OAKNODE_BLOCK_GAP) { int enabled = 0; oaknode_block_get_enabled(this_block, &enabled); if (enabled) { @@ -289,6 +301,8 @@ bool RenderTask::render(OakNodeColorManager *manager, } } while (!tracks_to_push.empty()); } + + oaknode_sequence_free(&sequence); } std::unique_lock loop_lock(finished_mutex_); @@ -400,7 +414,7 @@ bool RenderTask::download_frame(OakCodecFrame *frame, const Rational &time) return true; } -bool RenderTask::encode_subtitle(OakNodeBlock *subtitle) +bool RenderTask::encode_subtitle(OakNodeBlock subtitle) { (void)subtitle; return true; diff --git a/src/task/src/render/render.h b/src/task/src/render/render.h index f3ce6083b..6914d2f0d 100644 --- a/src/task/src/render/render.h +++ b/src/task/src/render/render.h @@ -69,7 +69,7 @@ protected: OakColorTransform color_transform = {}; /**< empty ctx = default */ }; - bool render(OakNodeColorManager *manager, + bool render(OakNodeColorManager manager, const TimeRangeList &video_range, const TimeRangeList &audio_range, const TimeRange &subtitle_range, int render_mode, @@ -84,14 +84,14 @@ protected: virtual bool audio_downloaded(const TimeRange &range, OakSampleBuffer *samples) = 0; - virtual bool encode_subtitle(OakNodeBlock *subtitle); + virtual bool encode_subtitle(OakNodeBlock subtitle); - OakNodeNode *viewer() const + OakNodeNode viewer() const { return viewer_; } - void set_viewer(OakNodeNode *v) + void set_viewer(OakNodeNode v) { viewer_ = v; } @@ -162,12 +162,12 @@ private: void on_ticket_finished(OakRenderTicket *ticket); - bool start_video_ticket(OakNodeColorManager *manager, + bool start_video_ticket(OakNodeColorManager manager, const Rational &time, int mode, OakNodeFrameCache *cache, const ForceParams &force); - OakNodeNode *viewer_; + OakNodeNode viewer_; OakVideoParams video_params_; diff --git a/src/task/tests/task_test.cpp b/src/task/tests/task_test.cpp index e7f8bddd8..c021c4b3b 100644 --- a/src/task/tests/task_test.cpp +++ b/src/task/tests/task_test.cpp @@ -41,16 +41,16 @@ protected: void SetUp() override { project_ = oaknode_project_init(); - ASSERT_NE(project_, nullptr); + ASSERT_NE(project_.ctx, nullptr); ASSERT_EQ(oaknode_project_initialize(project_), OAKNODE_OK); } void TearDown() override { - oaknode_project_free(project_); + oaknode_project_free(&project_); } - OakNodeProject *project_ = nullptr; + OakNodeProject project_ = {}; }; // ---- task family ---------------------------------------------------------- @@ -104,7 +104,7 @@ TEST_F(OakTaskFixture, LoadMissingFileFails) char err[256]; EXPECT_GT(oaktask_task_error(t, err, sizeof(err)), 0); - EXPECT_EQ(oaktask_load_take_project(t), nullptr); + EXPECT_EQ(oaktask_load_take_project(t).ctx, nullptr); oaktask_task_free(t); EXPECT_EQ(oaktask_create_project_load(nullptr), nullptr); @@ -126,11 +126,11 @@ TEST_F(OakTaskFixture, SaveLoadRoundTrip) ASSERT_NE(load, nullptr); ASSERT_EQ(oaktask_task_start_sync(load), 1); - OakNodeProject *loaded = oaktask_load_take_project(load); - ASSERT_NE(loaded, nullptr); - EXPECT_EQ(oaktask_load_take_project(load), nullptr); + OakNodeProject loaded = oaktask_load_take_project(load); + ASSERT_NE(loaded.ctx, nullptr); + EXPECT_EQ(oaktask_load_take_project(load).ctx, nullptr); - oaknode_project_free(loaded); + oaknode_project_free(&loaded); oaktask_task_free(load); std::filesystem::remove(path); @@ -138,8 +138,8 @@ TEST_F(OakTaskFixture, SaveLoadRoundTrip) TEST_F(OakTaskFixture, ImportDemoFootage) { - OakNodeFolder *folder = oaknode_folder_create(project_); - ASSERT_NE(folder, nullptr); + OakNodeFolder folder = oaknode_folder_create(project_); + ASSERT_NE(folder.ctx, nullptr); const char *urls[] = { OAK_REPO_ROOT "/tests/demo.mp4" }; OakTaskTask *t = @@ -157,8 +157,12 @@ TEST_F(OakTaskFixture, ImportDemoFootage) } EXPECT_EQ(oaktask_import_footage_count(t), 1); - EXPECT_NE(oaktask_import_footage_at(t, 0), nullptr); - EXPECT_EQ(oaktask_import_footage_at(t, 5), nullptr); + OakNodeFootage footage = oaktask_import_footage_at(t, 0); + EXPECT_NE(footage.ctx, nullptr); + if (footage.ctx) { + footage.release(footage.ctx); + } + EXPECT_EQ(oaktask_import_footage_at(t, 5).ctx, nullptr); EXPECT_EQ(oaktask_import_invalid_count(t), 0); EXPECT_EQ(oaktask_import_invalid_at(t, 0, nullptr, 0), OAKTASK_E_NOT_FOUND); @@ -179,8 +183,9 @@ TEST_F(OakTaskFixture, ImportDemoFootage) oakundo_command_free(&cmd); oaktask_task_free(t); - EXPECT_EQ(oaktask_create_project_import(nullptr, project_, urls, 1), - nullptr); + EXPECT_EQ( + oaktask_create_project_import(OakNodeFolder{}, project_, urls, 1), + nullptr); EXPECT_EQ(oaktask_import_footage_count(nullptr), OAKTASK_E_INVALID); EXPECT_EQ(oaktask_debug_alive_count(), 0); @@ -210,8 +215,8 @@ TEST(OakTaskManager, AsyncStartAndSubscribe) { ASSERT_EQ(oaktask_manager_init(), OAKTASK_OK); - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK); std::string path = @@ -258,7 +263,7 @@ TEST(OakTaskManager, AsyncStartAndSubscribe) std::filesystem::remove(path); oaktask_task_free(t); - oaknode_project_free(project); + oaknode_project_free(&project); oaktask_manager_shutdown(); EXPECT_EQ(oaktask_debug_alive_count(), 0); @@ -319,29 +324,34 @@ TEST(OakTaskConform, SubmittedConformProducesPcm) TEST(OakTaskRenderFamily, FactoryErrorPaths) { - EXPECT_EQ(oaktask_create_precache(nullptr, 0, nullptr), nullptr); + EXPECT_EQ(oaktask_create_precache(OakNodeFootage{}, 0, OakNodeSequence{}), + nullptr); oakcodec_encoding_params params = {}; - EXPECT_EQ(oaktask_create_export(nullptr, nullptr, ¶ms), nullptr); - EXPECT_EQ(oaktask_create_export(nullptr, nullptr, nullptr), nullptr); + EXPECT_EQ(oaktask_create_export(OakNodeNode{}, OakNodeColorManager{}, + ¶ms), + nullptr); + EXPECT_EQ(oaktask_create_export(OakNodeNode{}, OakNodeColorManager{}, + nullptr), + nullptr); } TEST(OakTaskRenderFamily, ExportTaskConstruction) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK); // A sequence gives us a viewer with tracks; the factory should wrap // the task successfully (no render manager needed for construction) - OakNodeSequence *sequence = oaknode_sequence_create(); - ASSERT_NE(sequence, nullptr); + OakNodeSequence sequence = oaknode_sequence_create(); + ASSERT_NE(sequence.ctx, nullptr); ASSERT_EQ(oaknode_project_add_node(project, oaknode_sequence_as_node(sequence)), OAKNODE_OK); - OakNodeColorManager *cm = oaknode_colormanager_init(project); - ASSERT_NE(cm, nullptr); + OakNodeColorManager cm = oaknode_colormanager_init(project); + ASSERT_NE(cm.ctx, nullptr); oakcodec_encoding_params params = {}; strncpy(params.filename, "/tmp/oaktask_export_test.mp4", @@ -357,8 +367,8 @@ TEST(OakTaskRenderFamily, ExportTaskConstruction) // Running needs a render manager; not available in this binary oaktask_task_free(t); - oaknode_colormanager_free(cm); - oaknode_project_free(project); + oaknode_colormanager_free(&cm); + oaknode_project_free(&project); EXPECT_EQ(oaktask_debug_alive_count(), 0); } @@ -367,13 +377,13 @@ TEST(OakTaskRenderFamily, ExportTaskConstruction) TEST(OakTaskOTIO, SaveLoadRoundTrip) { - OakNodeProject *project = oaknode_project_init(); - ASSERT_NE(project, nullptr); + OakNodeProject project = oaknode_project_init(); + ASSERT_NE(project.ctx, nullptr); ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK); // A sequence with a name so save has something to serialize - OakNodeSequence *sequence = oaknode_sequence_create(); - ASSERT_NE(sequence, nullptr); + OakNodeSequence sequence = oaknode_sequence_create(); + ASSERT_NE(sequence.ctx, nullptr); ASSERT_EQ(oaknode_project_add_node(project, oaknode_sequence_as_node(sequence)), OAKNODE_OK); @@ -383,8 +393,8 @@ TEST(OakTaskOTIO, SaveLoadRoundTrip) "OTIO Test Sequence"), OAKNODE_OK); - OakNodeFolder *root = oaknode_project_root(project); - ASSERT_NE(root, nullptr); + OakNodeFolder root = oaknode_project_root(project); + ASSERT_NE(root.ctx, nullptr); OakUndoCommand add_seq = oaknode_command_create_folder_add_child( root, oaknode_sequence_as_node(sequence)); ASSERT_NE(add_seq.ctx, nullptr); @@ -409,16 +419,17 @@ TEST(OakTaskOTIO, SaveLoadRoundTrip) ASSERT_NE(load, nullptr); ASSERT_EQ(oaktask_task_start_sync(load), 1); - OakNodeProject *loaded = oaktask_load_otio_take_project(load); - ASSERT_NE(loaded, nullptr); - oaknode_project_free(loaded); + OakNodeProject loaded = oaktask_load_otio_take_project(load); + ASSERT_NE(loaded.ctx, nullptr); + oaknode_project_free(&loaded); oaktask_task_free(load); std::filesystem::remove(path, ec); - oaknode_project_free(project); + oaknode_project_free(&project); EXPECT_EQ(oaktask_debug_alive_count(), 0); EXPECT_EQ(oaktask_create_project_load_otio(nullptr), nullptr); - EXPECT_EQ(oaktask_create_project_save_otio(nullptr, "x"), nullptr); + EXPECT_EQ(oaktask_create_project_save_otio(OakNodeProject{}, "x"), + nullptr); } diff --git a/src/timeline/c_api/edit.cpp b/src/timeline/c_api/edit.cpp index 8dcdbe808..a7a413843 100644 --- a/src/timeline/c_api/edit.cpp +++ b/src/timeline/c_api/edit.cpp @@ -52,9 +52,9 @@ olive::Timeline::MovementMode to_mode(int mode) } // namespace -OakUndoCommand oaktimeline_add_track_command(OakNodeTrackList *list) +OakUndoCommand oaktimeline_add_track_command(OakNodeTrackList list) { - if (!list) { + if (!list.ctx) { return OakUndoCommand{}; } @@ -65,9 +65,9 @@ OakUndoCommand oaktimeline_add_track_command(OakNodeTrackList *list) } } -OakUndoCommand oaktimeline_remove_track_command(OakNodeTrack *track) +OakUndoCommand oaktimeline_remove_track_command(OakNodeTrack track) { - if (!track) { + if (!track.ctx) { return OakUndoCommand{}; } @@ -78,13 +78,13 @@ OakUndoCommand oaktimeline_remove_track_command(OakNodeTrack *track) } } -OakUndoCommand oaktimeline_place_block_command(OakNodeTrackList *list, +OakUndoCommand oaktimeline_place_block_command(OakNodeTrackList list, int track_index, - OakNodeBlock *block, + OakNodeBlock block, int64_t in_num, int64_t in_den) { - if (!list || !block) { + if (!list.ctx || !block.ctx) { return OakUndoCommand{}; } @@ -97,9 +97,9 @@ OakUndoCommand oaktimeline_place_block_command(OakNodeTrackList *list, } OakUndoCommand oaktimeline_replace_block_with_gap_command( - OakNodeTrack *track, OakNodeBlock *block) + OakNodeTrack track, OakNodeBlock block) { - if (!track || !block) { + if (!track.ctx || !block.ctx) { return OakUndoCommand{}; } @@ -111,13 +111,13 @@ OakUndoCommand oaktimeline_replace_block_with_gap_command( } } -OakUndoCommand oaktimeline_trim_command(OakNodeTrack *track, - OakNodeBlock *block, +OakUndoCommand oaktimeline_trim_command(OakNodeTrack track, + OakNodeBlock block, int64_t new_length_num, int64_t new_length_den, int mode) { - if (!track || !block || (mode != OAKTIMELINE_MOVEMENT_TRIM_IN && - mode != OAKTIMELINE_MOVEMENT_TRIM_OUT)) { + if (!track.ctx || !block.ctx || (mode != OAKTIMELINE_MOVEMENT_TRIM_IN && + mode != OAKTIMELINE_MOVEMENT_TRIM_OUT)) { return OakUndoCommand{}; } @@ -131,7 +131,7 @@ OakUndoCommand oaktimeline_trim_command(OakNodeTrack *track, } } -OakUndoCommand oaktimeline_split_command(OakNodeBlock *const *blocks, +OakUndoCommand oaktimeline_split_command(const OakNodeBlock *blocks, int count, int64_t point_num, int64_t point_den) { @@ -142,7 +142,7 @@ OakUndoCommand oaktimeline_split_command(OakNodeBlock *const *blocks, try { auto *multi = new olive::MultiUndoCommand(); for (int i = 0; i < count; i++) { - if (blocks[i]) { + if (blocks[i].ctx) { multi->add_child(new olive::BlockSplitCommand( blocks[i], rat(point_num, point_den))); } @@ -154,7 +154,7 @@ OakUndoCommand oaktimeline_split_command(OakNodeBlock *const *blocks, } OakUndoCommand oaktimeline_split_preserving_links_command( - OakNodeBlock *const *blocks, int count, const int64_t *point_nums, + const OakNodeBlock *blocks, int count, const int64_t *point_nums, const int64_t *point_dens, int time_count) { if (!blocks || count <= 0 || !point_nums || !point_dens || @@ -163,7 +163,7 @@ OakUndoCommand oaktimeline_split_preserving_links_command( } try { - std::vector block_vec(blocks, blocks + count); + std::vector block_vec(blocks, blocks + count); std::vector times; times.reserve(time_count); for (int i = 0; i < time_count; i++) { @@ -177,11 +177,11 @@ OakUndoCommand oaktimeline_split_preserving_links_command( } OakUndoCommand oaktimeline_ripple_delete_gaps_command( - OakNodeSequence *sequence, const int64_t *in_nums, + OakNodeSequence sequence, const int64_t *in_nums, const int64_t *in_dens, const int64_t *out_nums, - const int64_t *out_dens, OakNodeTrack *const *tracks, int range_count) + const int64_t *out_dens, const OakNodeTrack *tracks, int range_count) { - if (!sequence || !in_nums || !in_dens || !out_nums || !out_dens || + if (!sequence.ctx || !in_nums || !in_dens || !out_nums || !out_dens || !tracks || range_count <= 0) { return OakUndoCommand{}; } @@ -204,16 +204,16 @@ OakUndoCommand oaktimeline_ripple_delete_gaps_command( } OakUndoCommand oaktimeline_slide_command( - OakNodeTrack *track, OakNodeBlock *const *blocks, int block_count, - OakNodeBlock *in_adjacent, OakNodeBlock *out_adjacent, + OakNodeTrack track, const OakNodeBlock *blocks, int block_count, + OakNodeBlock in_adjacent, OakNodeBlock out_adjacent, int64_t movement_num, int64_t movement_den) { - if (!track || !blocks || block_count <= 0) { + if (!track.ctx || !blocks || block_count <= 0) { return OakUndoCommand{}; } try { - std::vector block_vec(blocks, blocks + block_count); + std::vector block_vec(blocks, blocks + block_count); return wrap_command(new olive::TrackSlideCommand( track, block_vec, in_adjacent, out_adjacent, rat(movement_num, movement_den))); @@ -223,10 +223,10 @@ OakUndoCommand oaktimeline_slide_command( } OakUndoCommand oaktimeline_ripple_remove_area_command( - OakNodeTrack *track, int64_t in_num, int64_t in_den, int64_t out_num, + OakNodeTrack track, int64_t in_num, int64_t in_den, int64_t out_num, int64_t out_den) { - if (!track) { + if (!track.ctx) { return OakUndoCommand{}; } @@ -239,13 +239,13 @@ OakUndoCommand oaktimeline_ripple_remove_area_command( } } -OakUndoCommand oaktimeline_insert_gaps_command(OakNodeTrackList *list, +OakUndoCommand oaktimeline_insert_gaps_command(OakNodeTrackList list, int64_t point_num, int64_t point_den, int64_t length_num, int64_t length_den) { - if (!list) { + if (!list.ctx) { return OakUndoCommand{}; } diff --git a/src/timeline/c_api/marker.cpp b/src/timeline/c_api/marker.cpp index cb02008f8..38b4de056 100644 --- a/src/timeline/c_api/marker.cpp +++ b/src/timeline/c_api/marker.cpp @@ -66,9 +66,9 @@ olive::TimelineMarker *marker_at(OakTimelineMarkerList *list, int index) } // namespace -OakTimelineMarkerList *oaktimeline_marker_list_of(OakNodeNode *owner) +OakTimelineMarkerList *oaktimeline_marker_list_of(OakNodeNode owner) { - if (!owner) { + if (!owner.ctx) { return NULL; } diff --git a/src/timeline/c_api/workarea.cpp b/src/timeline/c_api/workarea.cpp index 773f0ae0e..b901e3dba 100644 --- a/src/timeline/c_api/workarea.cpp +++ b/src/timeline/c_api/workarea.cpp @@ -54,9 +54,9 @@ olive::core::Rational rat(int64_t n, int64_t d) } // namespace -OakTimelineWorkArea *oaktimeline_workarea_of(OakNodeNode *owner) +OakTimelineWorkArea *oaktimeline_workarea_of(OakNodeNode owner) { - if (!owner) { + if (!owner.ctx) { return NULL; } diff --git a/src/timeline/src/timelineundocommon.h b/src/timeline/src/timelineundocommon.h index e90ca6220..4436697a2 100644 --- a/src/timeline/src/timelineundocommon.h +++ b/src/timeline/src/timelineundocommon.h @@ -28,36 +28,36 @@ namespace olive { -inline bool node_can_be_removed(OakNodeNode *n) +inline bool node_can_be_removed(OakNodeNode n) { int count = 0; oaknode_node_output_connection_count(n, &count); return count == 0; } -inline bool node_can_be_removed(OakNodeBlock *b) +inline bool node_can_be_removed(OakNodeBlock b) { return node_can_be_removed(oaknode_block_as_node(b)); } -inline OakUndoCommand create_remove_command(OakNodeNode *n) +inline OakUndoCommand create_remove_command(OakNodeNode n) { return oaknode_command_create_remove_node(n); } -inline OakUndoCommand create_remove_command(OakNodeBlock *b) +inline OakUndoCommand create_remove_command(OakNodeBlock b) { return oaknode_command_create_remove_node(oaknode_block_as_node(b)); } -inline OakUndoCommand create_and_run_remove_command(OakNodeNode *n) +inline OakUndoCommand create_and_run_remove_command(OakNodeNode n) { OakUndoCommand command = create_remove_command(n); oakundo_command_redo_now(command); return command; } -inline OakUndoCommand create_and_run_remove_command(OakNodeBlock *b) +inline OakUndoCommand create_and_run_remove_command(OakNodeBlock b) { return create_and_run_remove_command(oaknode_block_as_node(b)); } diff --git a/src/timeline/src/timelineundogeneral.cpp b/src/timeline/src/timelineundogeneral.cpp index dbec7469c..6e4779b8c 100644 --- a/src/timeline/src/timelineundogeneral.cpp +++ b/src/timeline/src/timelineundogeneral.cpp @@ -104,7 +104,7 @@ void BlockSetMediaInCommand::undo() // // TimelineAddTrackCommand // -TimelineAddTrackCommand::TimelineAddTrackCommand(OakNodeTrackList *timeline) +TimelineAddTrackCommand::TimelineAddTrackCommand(OakNodeTrackList timeline) : TimelineAddTrackCommand( timeline, oakcommon_config_get_bool(NULL, "AutoMergeTracks", 0) != 0) @@ -112,10 +112,10 @@ TimelineAddTrackCommand::TimelineAddTrackCommand(OakNodeTrackList *timeline) } TimelineAddTrackCommand::TimelineAddTrackCommand( - OakNodeTrackList *timeline, bool automerge_tracks) + OakNodeTrackList timeline, bool automerge_tracks) : timeline_(timeline) - , track_(nullptr) - , merge_(nullptr) + , track_{} + , merge_{} , position_command_({}) , automerge_tracks_(automerge_tracks) , track_orphaned_(false) @@ -136,7 +136,7 @@ TimelineAddTrackCommand::TimelineAddTrackCommand( // If we have an input to connect to, check if something is already connected if (!direct_input_.empty() && automerge_tracks_) { - OakNodeSequence *sequence = nullptr; + OakNodeSequence sequence = {}; oaknode_tracklist_get_sequence(timeline_, &sequence); int connected = 0; @@ -155,7 +155,7 @@ TimelineAddTrackCommand::TimelineAddTrackCommand( blend_input_ = k_math_param_b_input; } - if (merge_) { + if (merge_.ctx) { merge_orphaned_ = true; } } @@ -167,27 +167,27 @@ TimelineAddTrackCommand::~TimelineAddTrackCommand() if (position_command_.ctx) { oakundo_command_free(&position_command_); } - if (track_orphaned_ && track_) { - oaknode_track_free(track_); + if (track_orphaned_) { + free_detached_handle(&track_); } - if (merge_orphaned_ && merge_) { - oaknode_node_free(merge_); + if (merge_orphaned_) { + free_detached_handle(&merge_); } } void TimelineAddTrackCommand::redo() { // Get sequence - OakNodeSequence *sequence = nullptr; + OakNodeSequence sequence = {}; oaknode_tracklist_get_sequence(timeline_, &sequence); - OakNodeNode *sequence_node = oaknode_sequence_as_node(sequence); - OakNodeNode *track_node = oaknode_track_as_node(track_); + OakNodeNode sequence_node = oaknode_sequence_as_node(sequence); + OakNodeNode track_node = oaknode_track_as_node(track_); - OakNodeProject *project = nullptr; + OakNodeProject project = {}; oaknode_node_get_project(sequence_node, &project); // Add track to sequence's graph - if (project) { + if (project.ctx) { oaknode_project_add_node(project, track_node); } track_orphaned_ = false; @@ -195,10 +195,10 @@ void TimelineAddTrackCommand::redo() int track_count = 0; oaknode_tracklist_get_track_count(timeline_, &track_count); if (track_count > 0) { - OakNodeTrack *last = nullptr; + OakNodeTrack last = {}; oaknode_tracklist_get_track_at(timeline_, track_count - 1, &last); double height = 0; - if (last && oaknode_track_get_height(last, &height) == OAKNODE_OK) { + if (last.ctx && oaknode_track_get_height(last, &height) == OAKNODE_OK) { oaknode_track_set_height(track_, height); } } @@ -228,15 +228,15 @@ void TimelineAddTrackCommand::redo() } // Add merge if applicable - if (merge_) { + if (merge_.ctx) { // Determine what was previously connected - OakNodeNode *previous_connection = nullptr; + OakNodeNode previous_connection = {}; oaknode_node_input_get_connected_node(sequence_node, direct_input_.c_str(), &previous_connection); // Add merge to graph - if (project) { + if (project.ctx) { oaknode_project_add_node(project, merge_); } merge_orphaned_ = false; @@ -244,7 +244,7 @@ void TimelineAddTrackCommand::redo() // Connect merge between what used to be here oaknode_node_disconnect(sequence_node, direct_input_.c_str()); oaknode_node_connect(merge_, sequence_node, direct_input_.c_str()); - if (previous_connection) { + if (previous_connection.ctx) { oaknode_node_connect(previous_connection, merge_, base_input_.c_str()); } @@ -315,38 +315,38 @@ void TimelineAddTrackCommand::undo() oakundo_command_undo_now(position_command_); } - OakNodeSequence *sequence = nullptr; + OakNodeSequence sequence = {}; oaknode_tracklist_get_sequence(timeline_, &sequence); - OakNodeNode *sequence_node = oaknode_sequence_as_node(sequence); - OakNodeNode *track_node = oaknode_track_as_node(track_); + OakNodeNode sequence_node = oaknode_sequence_as_node(sequence); + OakNodeNode track_node = oaknode_track_as_node(track_); - OakNodeProject *project = nullptr; + OakNodeProject project = {}; oaknode_node_get_project(sequence_node, &project); // Remove merge if applicable - if (merge_) { - OakNodeNode *previous_connection = nullptr; + if (merge_.ctx) { + OakNodeNode previous_connection = {}; oaknode_node_input_get_connected_node(merge_, base_input_.c_str(), &previous_connection); oaknode_node_disconnect(merge_, blend_input_.c_str()); oaknode_node_disconnect(merge_, base_input_.c_str()); oaknode_node_disconnect(sequence_node, direct_input_.c_str()); - if (previous_connection) { + if (previous_connection.ctx) { oaknode_node_connect(previous_connection, sequence_node, direct_input_.c_str()); } - if (project) { + if (project.ctx) { oaknode_project_remove_node(project, merge_); } merge_orphaned_ = true; } else if (!direct_input_.empty()) { - OakNodeNode *connected_output = nullptr; + OakNodeNode connected_output = {}; oaknode_node_input_get_connected_node(sequence_node, direct_input_.c_str(), &connected_output); - if (connected_output == track_node) { + if (same_node(connected_output, track_node)) { oaknode_node_disconnect(sequence_node, direct_input_.c_str()); } } @@ -360,7 +360,7 @@ void TimelineAddTrackCommand::undo() oaknode_node_disconnect_element(sequence_node, input_id, array_size - 1); oaknode_tracklist_array_remove_last(timeline_); - if (project) { + if (project.ctx) { oaknode_project_remove_node(project, track_node); } track_orphaned_ = true; @@ -384,24 +384,24 @@ void TransitionRemoveCommand::redo() int n, d; - if (in_block_) { + if (in_block_.ctx) { oaknode_transition_get_in_offset(block_, &n, &d); block_set_length_and_media_in( in_block_, block_length(in_block_) + Rational(n, d)); } - if (out_block_) { + if (out_block_.ctx) { oaknode_transition_get_out_offset(block_, &n, &d); block_set_length_and_media_out( out_block_, block_length(out_block_) + Rational(n, d)); } - if (in_block_) { + if (in_block_.ctx) { oaknode_node_disconnect(oaknode_block_as_node(block_), OAKNODE_TRANSITION_IN_BLOCK_INPUT); } - if (out_block_) { + if (out_block_.ctx) { oaknode_node_disconnect(oaknode_block_as_node(block_), OAKNODE_TRANSITION_OUT_BLOCK_INPUT); } @@ -423,19 +423,19 @@ void TransitionRemoveCommand::undo() oakundo_command_undo_now(remove_command_); } - if (in_block_) { + if (in_block_.ctx) { oaknode_track_insert_block_before(track_, block_, in_block_); } else { oaknode_track_insert_block_after(track_, block_, out_block_); } - if (in_block_) { + if (in_block_.ctx) { oaknode_node_connect(oaknode_block_as_node(in_block_), oaknode_block_as_node(block_), OAKNODE_TRANSITION_IN_BLOCK_INPUT); } - if (out_block_) { + if (out_block_.ctx) { oaknode_node_connect(oaknode_block_as_node(out_block_), oaknode_block_as_node(block_), OAKNODE_TRANSITION_OUT_BLOCK_INPUT); @@ -446,13 +446,13 @@ void TransitionRemoveCommand::undo() // These if statements must be separated because in_offset and out_offset report different things // if only one block is connected vs two. So we have to connect the blocks first before we have // an accurate return value from these offset functions. - if (in_block_) { + if (in_block_.ctx) { oaknode_transition_get_in_offset(block_, &n, &d); block_set_length_and_media_in( in_block_, block_length(in_block_) - Rational(n, d)); } - if (out_block_) { + if (out_block_.ctx) { oaknode_transition_get_out_offset(block_, &n, &d); block_set_length_and_media_out( out_block_, block_length(out_block_) - Rational(n, d)); @@ -466,8 +466,8 @@ TrackListInsertGaps::~TrackListInsertGaps() { delete split_command_; for (AddGap &add_gap : gaps_added_) { - if (add_gap.orphaned && add_gap.gap) { - oaknode_block_free(add_gap.gap); + if (add_gap.orphaned) { + free_detached_handle(&add_gap.gap); } } } @@ -478,9 +478,9 @@ void TrackListInsertGaps::prepare() int track_count = 0; oaknode_tracklist_get_track_count(track_list_, &track_count); for (int i = 0; i < track_count; i++) { - OakNodeTrack *track = nullptr; + OakNodeTrack track = {}; oaknode_tracklist_get_track_at(track_list_, i, &track); - if (!track) { + if (!track.ctx) { continue; } @@ -493,17 +493,17 @@ void TrackListInsertGaps::prepare() working_tracks_.push_back(track); } - std::vector blocks_to_split; - std::vector blocks_to_append_gap_to; - std::vector tracks_to_append_gap_to; + std::vector blocks_to_split; + std::vector blocks_to_append_gap_to; + std::vector tracks_to_append_gap_to; - for (OakNodeTrack *track : working_tracks_) { + for (OakNodeTrack track : working_tracks_) { int block_count = 0; oaknode_track_get_block_count(track, &block_count); for (int i = 0; i < block_count; i++) { - OakNodeBlock *b = nullptr; + OakNodeBlock b = {}; oaknode_track_get_block_at(track, i, &b); - if (!b) { + if (!b.ctx) { continue; } @@ -521,11 +521,11 @@ void TrackListInsertGaps::prepare() if (block_in(b) == point_) { // The only reason we should be here is if this block is at the start of the track, // in which case no split needs to occur - b = nullptr; + b = OakNodeBlock{}; } else if (block_out(b) > point_) { // Block must be split as well as having a gap appended to it blocks_to_split.push_back(b); - } else if (!block_next(b)) { + } else if (!block_next(b).ctx) { // At the end of a track, no gap needs to be added at all append_gap = false; } @@ -545,7 +545,7 @@ void TrackListInsertGaps::prepare() } for (size_t i = 0; i < blocks_to_append_gap_to.size(); i++) { - OakNodeBlock *gap = oaknode_block_gap_create(); + OakNodeBlock gap = oaknode_block_gap_create(); block_set_length_and_media_out(gap, length_); gaps_added_.push_back({ gap, true, blocks_to_append_gap_to.at(i), tracks_to_append_gap_to.at(i) }); @@ -554,7 +554,7 @@ void TrackListInsertGaps::prepare() void TrackListInsertGaps::redo() { - for (OakNodeBlock *gap : gaps_to_extend_) { + for (OakNodeBlock gap : gaps_to_extend_) { block_set_length_and_media_out(gap, block_length(gap) + length_); } @@ -574,8 +574,8 @@ void TrackListInsertGaps::undo() { // Remove added gaps for (AddGap &add_gap : gaps_added_) { - OakNodeTrack *t = block_track(add_gap.gap); - if (t) { + OakNodeTrack t = block_track(add_gap.gap); + if (t.ctx) { oaknode_track_ripple_remove_block(t, add_gap.gap); block_remove_from_graph(add_gap.gap, t); } @@ -588,7 +588,7 @@ void TrackListInsertGaps::undo() } // Restore original length of gaps - for (OakNodeBlock *gap : gaps_to_extend_) { + for (OakNodeBlock gap : gaps_to_extend_) { block_set_length_and_media_out(gap, block_length(gap) - length_); } } @@ -601,11 +601,11 @@ TrackReplaceBlockWithGapCommand::~TrackReplaceBlockWithGapCommand() for (TransitionRemoveCommand *c : transition_remove_commands_) { delete c; } - if (our_gap_ && our_gap_orphaned_) { - oaknode_block_free(our_gap_); + if (our_gap_orphaned_) { + free_detached_handle(&our_gap_); } - if (existing_merged_gap_ && merged_gap_orphaned_) { - oaknode_block_free(existing_merged_gap_); + if (merged_gap_orphaned_) { + free_detached_handle(&existing_merged_gap_); } } @@ -620,19 +620,19 @@ void TrackReplaceBlockWithGapCommand::redo() c->redo_now(); } - if (block_next(block_)) { + if (block_next(block_).ctx) { // Block has a next, which means it's NOT at the end of the sequence and thus requires a gap Rational new_gap_length = block_length(block_); - OakNodeBlock *previous = block_previous(block_); - OakNodeBlock *next = block_next(block_); + OakNodeBlock previous = block_previous(block_); + OakNodeBlock next = block_next(block_); int prev_kind = OAKNODE_BLOCK_OTHER; int next_kind = OAKNODE_BLOCK_OTHER; - if (previous) { + if (previous.ctx) { oaknode_block_get_kind(previous, &prev_kind); } - if (next) { + if (next.ctx) { oaknode_block_get_kind(next, &next_kind); } bool previous_is_a_gap = (prev_kind == OAKNODE_BLOCK_GAP); @@ -655,16 +655,16 @@ void TrackReplaceBlockWithGapCommand::redo() existing_gap_ = next; } - if (existing_gap_) { + if (existing_gap_.ctx) { // Extend an existing gap new_gap_length += block_length(existing_gap_); block_set_length_and_media_out(existing_gap_, new_gap_length); oaknode_track_ripple_remove_block(track_, block_); - existing_gap_precedes_ = (existing_gap_ == previous); + existing_gap_precedes_ = same_block(existing_gap_, previous); } else { // No gap exists to fill this space, create a new one and swap it in - if (!our_gap_) { + if (!our_gap_.ctx) { our_gap_ = oaknode_block_gap_create(); block_set_length_and_media_out(our_gap_, new_gap_length); our_gap_orphaned_ = true; @@ -677,12 +677,12 @@ void TrackReplaceBlockWithGapCommand::redo() } else { // Block is at the end of the track, simply remove it - OakNodeBlock *preceding = block_previous(block_); + OakNodeBlock preceding = block_previous(block_); oaknode_track_ripple_remove_block(track_, block_); // Determine if it's preceded by a gap, and remove that gap if so int kind = OAKNODE_BLOCK_OTHER; - if (preceding) { + if (preceding.ctx) { oaknode_block_get_kind(preceding, &kind); } if (kind == OAKNODE_BLOCK_GAP) { @@ -697,8 +697,8 @@ void TrackReplaceBlockWithGapCommand::redo() void TrackReplaceBlockWithGapCommand::undo() { - if (our_gap_ || existing_gap_) { - if (our_gap_) { + if (our_gap_.ctx || existing_gap_.ctx) { + if (our_gap_.ctx) { // We made this gap, simply swap our gap back oaknode_track_replace_block(track_, our_gap_, block_); block_remove_from_graph(our_gap_, track_); @@ -710,13 +710,13 @@ void TrackReplaceBlockWithGapCommand::undo() block_length(existing_gap_) - block_length(block_); // If we merged two gaps together, restore the second one now - if (existing_merged_gap_) { + if (existing_merged_gap_.ctx) { original_gap_length -= block_length(existing_merged_gap_); block_add_to_graph(existing_merged_gap_, track_); oaknode_track_insert_block_after(track_, existing_merged_gap_, existing_gap_); merged_gap_orphaned_ = false; - existing_merged_gap_ = nullptr; + existing_merged_gap_ = OakNodeBlock{}; } // Restore original block @@ -732,7 +732,7 @@ void TrackReplaceBlockWithGapCommand::undo() block_set_length_and_media_out(existing_gap_, original_gap_length); - existing_gap_ = nullptr; + existing_gap_ = OakNodeBlock{}; } } else { @@ -740,11 +740,11 @@ void TrackReplaceBlockWithGapCommand::undo() // required no gap extension/replacement // However, we may have removed an unnecessary gap that preceded it - if (existing_merged_gap_) { + if (existing_merged_gap_.ctx) { block_add_to_graph(existing_merged_gap_, track_); oaknode_track_append_block(track_, existing_merged_gap_); merged_gap_orphaned_ = false; - existing_merged_gap_ = nullptr; + existing_merged_gap_ = OakNodeBlock{}; } // Restore block @@ -760,25 +760,25 @@ void TrackReplaceBlockWithGapCommand::undo() void TrackReplaceBlockWithGapCommand::create_remove_transition_command_if_necessary( bool next) { - OakNodeBlock *relevant_block = + OakNodeBlock relevant_block = next ? block_next(block_) : block_previous(block_); int kind = OAKNODE_BLOCK_OTHER; - if (relevant_block) { + if (relevant_block.ctx) { oaknode_block_get_kind(relevant_block, &kind); } if (kind != OAKNODE_BLOCK_TRANSITION) { return; } - OakNodeBlock *connected_out = nullptr; - OakNodeBlock *connected_in = nullptr; + OakNodeBlock connected_out = {}; + OakNodeBlock connected_in = {}; oaknode_transition_get_connected_out_block(relevant_block, &connected_out); oaknode_transition_get_connected_in_block(relevant_block, &connected_in); - if ((next && connected_out == block_ && !connected_in) || - (!next && connected_in == block_ && !connected_out)) { + if ((next && same_block(connected_out, block_) && !connected_in.ctx) || + (!next && same_block(connected_in, block_) && !connected_out.ctx)) { transition_remove_commands_.push_back( new TransitionRemoveCommand(relevant_block, true)); } @@ -796,7 +796,7 @@ TimelineRemoveTrackCommand::~TimelineRemoveTrackCommand() void TimelineRemoveTrackCommand::prepare() { - OakNodeSequence *sequence = nullptr; + OakNodeSequence sequence = {}; oaknode_track_get_sequence(track_, &sequence); int type = OAKNODE_TRACK_TYPE_NONE; @@ -815,7 +815,7 @@ void TimelineRemoveTrackCommand::redo() { oakundo_command_redo_now(remove_command_); - OakNodeSequence *sequence = nullptr; + OakNodeSequence sequence = {}; oaknode_tracklist_get_sequence(list_, &sequence); char input_id[64]; @@ -826,7 +826,7 @@ void TimelineRemoveTrackCommand::redo() void TimelineRemoveTrackCommand::undo() { - OakNodeSequence *sequence = nullptr; + OakNodeSequence sequence = {}; oaknode_tracklist_get_sequence(list_, &sequence); char input_id[64]; @@ -860,16 +860,16 @@ std::string config_get_string(const char *key) void TimelineAddDefaultTransitionCommand::prepare() { - for (OakNodeBlock *c : clips_) { - OakNodeBlock *previous = block_previous(c); - OakNodeBlock *next = block_next(c); + for (OakNodeBlock c : clips_) { + OakNodeBlock previous = block_previous(c); + OakNodeBlock next = block_next(c); - auto is_clip_in_selection = [this](OakNodeBlock *b) { - if (!b) { + auto is_clip_in_selection = [this](OakNodeBlock b) { + if (!b.ctx) { return false; } - for (OakNodeBlock *clip : clips_) { - if (clip == b) { + for (OakNodeBlock clip : clips_) { + if (same_block(clip, b)) { return true; } } @@ -878,17 +878,17 @@ void TimelineAddDefaultTransitionCommand::prepare() int prev_kind = OAKNODE_BLOCK_OTHER; int next_kind = OAKNODE_BLOCK_OTHER; - if (previous) { + if (previous.ctx) { oaknode_block_get_kind(previous, &prev_kind); } - if (next) { + if (next.ctx) { oaknode_block_get_kind(next, &next_kind); } // Handle in transition if (is_clip_in_selection(previous)) { // Do nothing, assume this will be handled by a dual transition from that clip - } else if (prev_kind == OAKNODE_BLOCK_GAP || !previous) { + } else if (prev_kind == OAKNODE_BLOCK_GAP || !previous.ctx) { // Create in transition add_transition(c, k_in); } @@ -896,7 +896,7 @@ void TimelineAddDefaultTransitionCommand::prepare() // Handle out transition if (is_clip_in_selection(next)) { add_transition(c, k_out_dual); - } else if (next_kind == OAKNODE_BLOCK_GAP || !next) { + } else if (next_kind == OAKNODE_BLOCK_GAP || !next.ctx) { // Create out transition add_transition(c, k_out); } @@ -904,17 +904,17 @@ void TimelineAddDefaultTransitionCommand::prepare() } void TimelineAddDefaultTransitionCommand::add_transition( - OakNodeBlock *c, CreateTransitionMode mode) + OakNodeBlock c, CreateTransitionMode mode) { - OakNodeTrack *t = block_track(c); - if (!t) { + OakNodeTrack t = block_track(c); + if (!t.ctx) { return; } int type = OAKNODE_TRACK_TYPE_NONE; oaknode_track_get_type(t, &type); - OakNodeNode *p = nullptr; + OakNodeNode p = {}; if (type == OAKNODE_TRACK_TYPE_VIDEO) { std::string id = config_get_string("DefaultVideoTransition"); if (!id.empty()) { @@ -964,20 +964,20 @@ void TimelineAddDefaultTransitionCommand::add_transition( } if (transition_length <= 0) { - if (p) { - oaknode_node_free(p); + if (p.ctx) { + oaknode_node_free(&p); } return; } - OakNodeBlock *transition = oaknode_block_from_node(p); + OakNodeBlock transition = oaknode_block_from_node(p); int kind = OAKNODE_BLOCK_OTHER; - if (transition) { + if (transition.ctx) { oaknode_block_get_kind(transition, &kind); } if (kind != OAKNODE_BLOCK_TRANSITION) { - if (p) { - oaknode_node_free(p); + if (p.ctx) { + oaknode_node_free(&p); } return; } @@ -985,13 +985,13 @@ void TimelineAddDefaultTransitionCommand::add_transition( block_set_length_and_media_out(transition, transition_length); // Add transition - OakNodeProject *project = nullptr; + OakNodeProject project = {}; oaknode_node_get_project(oaknode_block_as_node(c), &project); commands_.push_back( new CHandleCommandWrapper(oaknode_command_create_add_node(project, p))); // Insert block - OakNodeBlock *insert_after = (mode == k_in) ? block_previous(c) : c; + OakNodeBlock insert_after = (mode == k_in) ? block_previous(c) : c; commands_.push_back( new TrackInsertBlockAfterCommand(t, transition, insert_after)); @@ -1027,7 +1027,7 @@ void TimelineAddDefaultTransitionCommand::add_transition( } void TimelineAddDefaultTransitionCommand::adjust_clip_length( - OakNodeBlock *c, const Rational &transition_length, bool out) + OakNodeBlock c, const Rational &transition_length, bool out) { Rational cur_len = lengths_.count(c) ? lengths_[c] : block_length(c); Rational new_len = cur_len - transition_length; @@ -1040,9 +1040,9 @@ void TimelineAddDefaultTransitionCommand::adjust_clip_length( } void TimelineAddDefaultTransitionCommand::validate_transition_length( - OakNodeBlock *c, Rational &transition_length) + OakNodeBlock c, Rational &transition_length) { - if (!c) { + if (!c.ctx) { return; } Rational cur_len = lengths_.count(c) ? lengths_[c] : block_length(c); diff --git a/src/timeline/src/timelineundogeneral.h b/src/timeline/src/timelineundogeneral.h index ac5b7ecfb..039ba35fa 100644 --- a/src/timeline/src/timelineundogeneral.h +++ b/src/timeline/src/timelineundogeneral.h @@ -29,9 +29,11 @@ #include #include "node/block.h" +#include "node/node.h" #include "node/sequence.h" #include "node/track.h" #include "timelineundosplit.h" +#include "timelineutil.h" #include "undocommand.h" using namespace olive::core; @@ -41,7 +43,7 @@ namespace olive class BlockResizeCommand : public UndoCommand { public: - BlockResizeCommand(OakNodeBlock *block, Rational new_length) + BlockResizeCommand(OakNodeBlock block, Rational new_length) : block_(block) , new_length_(new_length) { @@ -52,14 +54,14 @@ protected: virtual void undo() override; private: - OakNodeBlock *block_; + OakNodeBlock block_; Rational old_length_; Rational new_length_; }; class BlockResizeWithMediaInCommand : public UndoCommand { public: - BlockResizeWithMediaInCommand(OakNodeBlock *block, Rational new_length) + BlockResizeWithMediaInCommand(OakNodeBlock block, Rational new_length) : block_(block) , new_length_(new_length) { @@ -70,14 +72,14 @@ protected: virtual void undo(); private: - OakNodeBlock *block_; + OakNodeBlock block_; Rational old_length_; Rational new_length_; }; class BlockSetMediaInCommand : public UndoCommand { public: - BlockSetMediaInCommand(OakNodeBlock *block, Rational new_media_in) + BlockSetMediaInCommand(OakNodeBlock block, Rational new_media_in) : block_(block) , new_media_in_(new_media_in) { @@ -88,34 +90,34 @@ protected: virtual void undo(); private: - OakNodeBlock *block_; + OakNodeBlock block_; Rational old_media_in_; Rational new_media_in_; }; class TimelineAddTrackCommand : public UndoCommand { public: - TimelineAddTrackCommand(OakNodeTrackList *timeline); - TimelineAddTrackCommand(OakNodeTrackList *timeline, bool automerge_tracks); + TimelineAddTrackCommand(OakNodeTrackList timeline); + TimelineAddTrackCommand(OakNodeTrackList timeline, bool automerge_tracks); virtual ~TimelineAddTrackCommand() override; - static OakNodeTrack *run_immediately(OakNodeTrackList *timeline) + static OakNodeTrack run_immediately(OakNodeTrackList timeline) { TimelineAddTrackCommand c(timeline); c.redo(); return c.track(); } - static OakNodeTrack *run_immediately(OakNodeTrackList *timeline, - bool automerge) + static OakNodeTrack run_immediately(OakNodeTrackList timeline, + bool automerge) { TimelineAddTrackCommand c(timeline, automerge); c.redo(); return c.track(); } - OakNodeTrack *track() const + OakNodeTrack track() const { return track_; } @@ -126,10 +128,10 @@ protected: virtual void undo() override; private: - OakNodeTrackList *timeline_; + OakNodeTrackList timeline_; - OakNodeTrack *track_; - OakNodeNode *merge_; + OakNodeTrack track_; + OakNodeNode merge_; std::string base_input_; std::string blend_input_; @@ -147,9 +149,9 @@ private: class TimelineRemoveTrackCommand : public UndoCommand { public: - TimelineRemoveTrackCommand(OakNodeTrack *track) + TimelineRemoveTrackCommand(OakNodeTrack track) : track_(track) - , list_(nullptr) + , list_{} , index_(0) , remove_command_({}) { @@ -165,9 +167,9 @@ protected: virtual void undo() override; private: - OakNodeTrack *track_; + OakNodeTrack track_; - OakNodeTrackList *list_; + OakNodeTrackList list_; int index_; @@ -176,11 +178,11 @@ private: class TransitionRemoveCommand : public UndoCommand { public: - TransitionRemoveCommand(OakNodeBlock *block, bool remove_from_graph) + TransitionRemoveCommand(OakNodeBlock block, bool remove_from_graph) : block_(block) - , track_(nullptr) - , out_block_(nullptr) - , in_block_(nullptr) + , track_{} + , out_block_{} + , in_block_{} , remove_from_graph_(remove_from_graph) , remove_command_({}) { @@ -194,12 +196,12 @@ protected: virtual void undo() override; private: - OakNodeBlock *block_; + OakNodeBlock block_; - OakNodeTrack *track_; + OakNodeTrack track_; - OakNodeBlock *out_block_; - OakNodeBlock *in_block_; + OakNodeBlock out_block_; + OakNodeBlock in_block_; bool remove_from_graph_; OakUndoCommand remove_command_; @@ -207,14 +209,14 @@ private: class TrackReplaceBlockWithGapCommand : public UndoCommand { public: - TrackReplaceBlockWithGapCommand(OakNodeTrack *track, OakNodeBlock *block, + TrackReplaceBlockWithGapCommand(OakNodeTrack track, OakNodeBlock block, bool handle_transitions = true) : track_(track) , block_(block) - , existing_gap_(nullptr) - , existing_merged_gap_(nullptr) + , existing_gap_{} + , existing_merged_gap_{} , existing_gap_precedes_(false) - , our_gap_(nullptr) + , our_gap_{} , handle_transitions_(handle_transitions) , our_gap_orphaned_(false) , merged_gap_orphaned_(false) @@ -231,13 +233,13 @@ protected: private: void create_remove_transition_command_if_necessary(bool next); - OakNodeTrack *track_; - OakNodeBlock *block_; + OakNodeTrack track_; + OakNodeBlock block_; - OakNodeBlock *existing_gap_; - OakNodeBlock *existing_merged_gap_; + OakNodeBlock existing_gap_; + OakNodeBlock existing_merged_gap_; bool existing_gap_precedes_; - OakNodeBlock *our_gap_; + OakNodeBlock our_gap_; bool handle_transitions_; @@ -249,7 +251,7 @@ private: class BlockEnableDisableCommand : public UndoCommand { public: - BlockEnableDisableCommand(OakNodeBlock *block, bool enabled) + BlockEnableDisableCommand(OakNodeBlock block, bool enabled) : block_(block) , new_enabled_(enabled) { @@ -269,7 +271,7 @@ protected: } private: - OakNodeBlock *block_; + OakNodeBlock block_; int old_enabled_; @@ -278,7 +280,7 @@ private: class TrackListInsertGaps : public UndoCommand { public: - TrackListInsertGaps(OakNodeTrackList *track_list, const Rational &point, + TrackListInsertGaps(OakNodeTrackList track_list, const Rational &point, const Rational &length) : track_list_(track_list) , point_(point) @@ -297,21 +299,21 @@ protected: virtual void undo() override; private: - OakNodeTrackList *track_list_; + OakNodeTrackList track_list_; Rational point_; Rational length_; - std::vector working_tracks_; + std::vector working_tracks_; - std::vector gaps_to_extend_; + std::vector gaps_to_extend_; struct AddGap { - OakNodeBlock *gap; + OakNodeBlock gap; bool orphaned; - OakNodeBlock *before; - OakNodeTrack *track; + OakNodeBlock before; + OakNodeTrack track; }; std::vector gaps_added_; @@ -322,7 +324,7 @@ private: class TimelineAddDefaultTransitionCommand : public UndoCommand { public: TimelineAddDefaultTransitionCommand( - const std::vector &clips, const Rational &timebase) + const std::vector &clips, const Rational &timebase) : clips_(clips) , timebase_(timebase) { @@ -355,17 +357,17 @@ protected: private: enum CreateTransitionMode { k_in, k_out, k_out_dual }; - void add_transition(OakNodeBlock *c, CreateTransitionMode mode); - void adjust_clip_length(OakNodeBlock *c, const Rational &transition_length, + void add_transition(OakNodeBlock c, CreateTransitionMode mode); + void adjust_clip_length(OakNodeBlock c, const Rational &transition_length, bool out); - void validate_transition_length(OakNodeBlock *c, + void validate_transition_length(OakNodeBlock c, Rational &transition_length); - std::vector clips_; + std::vector clips_; Rational timebase_; std::vector commands_; - std::map lengths_; + std::map lengths_; }; } diff --git a/src/timeline/src/timelineundopointer.cpp b/src/timeline/src/timelineundopointer.cpp index 462cbbf3a..0fd0169e8 100644 --- a/src/timeline/src/timelineundopointer.cpp +++ b/src/timeline/src/timelineundopointer.cpp @@ -36,8 +36,8 @@ BlockTrimCommand::~BlockTrimCommand() if (deleted_adjacent_command_.ctx) { free_command_handle(&deleted_adjacent_command_); } - if (adjacent_orphaned_ && adjacent_) { - oaknode_block_free(adjacent_); + if (adjacent_orphaned_) { + free_detached_handle(&adjacent_); } } @@ -148,7 +148,7 @@ void BlockTrimCommand::prepare() // Will be POSITIVE if trimming shorter and NEGATIVE if trimming longer trim_diff_ = old_length_ - new_length_; - // Retrieve our adjacent block (or nullptr if none) + // Retrieve our adjacent block (or an empty handle if none) if (mode_ == Timeline::k_trim_in) { adjacent_ = block_previous(block_); } else { @@ -157,16 +157,16 @@ void BlockTrimCommand::prepare() // Ignore when trimming the out with no adjacent, because the user must have trimmed the end // of the last block in the track, so we don't need to do anything elses - needs_adjacent_ = (mode_ == Timeline::k_trim_in || adjacent_); + needs_adjacent_ = (mode_ == Timeline::k_trim_in || adjacent_.ctx); if (needs_adjacent_) { // If we're trimming shorter, we need an adjacent, so check if we have a viable one. int adjacent_kind = OAKNODE_BLOCK_OTHER; - if (adjacent_) { + if (adjacent_.ctx) { oaknode_block_get_kind(adjacent_, &adjacent_kind); } we_created_adjacent_ = - (trim_diff_ > 0 && (!adjacent_ || (adjacent_kind != + (trim_diff_ > 0 && (!adjacent_.ctx || (adjacent_kind != OAKNODE_BLOCK_GAP && !trim_is_a_roll_edit_))); @@ -194,11 +194,11 @@ TrackSlideCommand::~TrackSlideCommand() if (out_adjacent_remove_command_.ctx) { free_command_handle(&out_adjacent_remove_command_); } - if (in_adjacent_orphaned_ && in_adjacent_) { - oaknode_block_free(in_adjacent_); + if (in_adjacent_orphaned_) { + free_detached_handle(&in_adjacent_); } - if (out_adjacent_orphaned_ && out_adjacent_) { - oaknode_block_free(out_adjacent_); + if (out_adjacent_orphaned_) { + free_detached_handle(&out_adjacent_); } } @@ -232,7 +232,7 @@ void TrackSlideCommand::redo() } // We may not have an out adjacent if the slide was at the end of the track - if (out_adjacent_) { + if (out_adjacent_.ctx) { if (we_created_out_adjacent_) { // We created out adjacent, so we just have to insert it block_add_to_graph(out_adjacent_, track_); @@ -283,7 +283,7 @@ void TrackSlideCommand::undo() in_adjacent_, block_length(in_adjacent_) - movement_); } - if (out_adjacent_) { + if (out_adjacent_.ctx) { if (we_created_out_adjacent_) { // We created this, so we can remove it now oaknode_track_ripple_remove_block(track_, out_adjacent_); @@ -307,7 +307,7 @@ void TrackSlideCommand::undo() void TrackSlideCommand::prepare() { - if (!in_adjacent_) { + if (!in_adjacent_.ctx) { in_adjacent_ = oaknode_block_gap_create(); block_set_length_and_media_out(in_adjacent_, movement_); in_adjacent_orphaned_ = true; @@ -316,7 +316,7 @@ void TrackSlideCommand::prepare() we_created_in_adjacent_ = false; } - if (!out_adjacent_ && block_next(blocks_.back())) { + if (!out_adjacent_.ctx && block_next(blocks_.back()).ctx) { out_adjacent_ = oaknode_block_gap_create(); block_set_length_and_media_out(out_adjacent_, -movement_); out_adjacent_orphaned_ = true; @@ -335,8 +335,8 @@ TrackPlaceBlockCommand::~TrackPlaceBlockCommand() for (TimelineAddTrackCommand *c : add_track_commands_) { delete c; } - if (gap_orphaned_ && gap_) { - oaknode_block_free(gap_); + if (gap_orphaned_) { + free_detached_handle(&gap_); } } @@ -362,7 +362,7 @@ void TrackPlaceBlockCommand::redo() } } - OakNodeTrack *track = nullptr; + OakNodeTrack track = {}; oaknode_tracklist_get_track_at(timeline_, track_index_, &track); bool append = (in_ >= track_length(track)); @@ -371,7 +371,7 @@ void TrackPlaceBlockCommand::redo() if (append) { if (in_ > track_length(track)) { // If so, insert a gap here - if (!gap_) { + if (!gap_.ctx) { gap_ = oaknode_block_gap_create(); block_set_length_and_media_out(gap_, in_ - track_length(track)); @@ -398,7 +398,7 @@ void TrackPlaceBlockCommand::redo() void TrackPlaceBlockCommand::undo() { - OakNodeTrack *t = nullptr; + OakNodeTrack t = {}; oaknode_tracklist_get_track_at(timeline_, track_index_, &t); // Firstly, remove our insert @@ -407,7 +407,7 @@ void TrackPlaceBlockCommand::undo() if (ripple_remove_command_) { // If we ripple removed, just undo that ripple_remove_command_->undo_now(); - } else if (gap_) { + } else if (gap_.ctx) { oaknode_track_ripple_remove_block(t, gap_); block_remove_from_graph(gap_, t); gap_orphaned_ = true; diff --git a/src/timeline/src/timelineundopointer.h b/src/timeline/src/timelineundopointer.h index 6b879f528..aa9072484 100644 --- a/src/timeline/src/timelineundopointer.h +++ b/src/timeline/src/timelineundopointer.h @@ -50,7 +50,7 @@ namespace olive */ class BlockTrimCommand : public UndoCommand { public: - BlockTrimCommand(OakNodeTrack *track, OakNodeBlock *block, + BlockTrimCommand(OakNodeTrack track, OakNodeBlock block, Rational new_length, Timeline::MovementMode mode) : track_(track) , block_(block) @@ -60,7 +60,7 @@ public: , trim_is_a_roll_edit_(false) , remove_block_from_graph_(true) , doing_nothing_(false) - , adjacent_(nullptr) + , adjacent_{} , needs_adjacent_(false) , we_created_adjacent_(false) , we_removed_adjacent_(false) @@ -99,13 +99,13 @@ private: bool doing_nothing_; Rational trim_diff_; - OakNodeTrack *track_; - OakNodeBlock *block_; + OakNodeTrack track_; + OakNodeBlock block_; Rational old_length_; Rational new_length_; Timeline::MovementMode mode_; - OakNodeBlock *adjacent_; + OakNodeBlock adjacent_; bool needs_adjacent_; bool we_created_adjacent_; bool we_removed_adjacent_; @@ -120,9 +120,9 @@ private: class TrackSlideCommand : public UndoCommand { public: - TrackSlideCommand(OakNodeTrack *track, - const std::vector &moving_blocks, - OakNodeBlock *in_adjacent, OakNodeBlock *out_adjacent, + TrackSlideCommand(OakNodeTrack track, + const std::vector &moving_blocks, + OakNodeBlock in_adjacent, OakNodeBlock out_adjacent, const Rational &movement) : track_(track) , blocks_(moving_blocks) @@ -150,18 +150,18 @@ protected: virtual void undo() override; private: - OakNodeTrack *track_; - std::vector blocks_; + OakNodeTrack track_; + std::vector blocks_; Rational movement_; bool we_created_in_adjacent_; bool we_removed_in_adjacent_; - OakNodeBlock *in_adjacent_; + OakNodeBlock in_adjacent_; OakUndoCommand in_adjacent_remove_command_; bool in_adjacent_orphaned_; bool we_created_out_adjacent_; bool we_removed_out_adjacent_; - OakNodeBlock *out_adjacent_; + OakNodeBlock out_adjacent_; OakUndoCommand out_adjacent_remove_command_; bool out_adjacent_orphaned_; }; @@ -175,12 +175,12 @@ private: */ class TrackPlaceBlockCommand : public UndoCommand { public: - TrackPlaceBlockCommand(OakNodeTrackList *timeline, int track, - OakNodeBlock *block, Rational in) + TrackPlaceBlockCommand(OakNodeTrackList timeline, int track, + OakNodeBlock block, Rational in) : timeline_(timeline) , track_index_(track) , in_(in) - , gap_(nullptr) + , gap_{} , gap_orphaned_(false) , insert_(block) , ripple_remove_command_(nullptr) @@ -195,12 +195,12 @@ protected: virtual void undo() override; private: - OakNodeTrackList *timeline_; + OakNodeTrackList timeline_; int track_index_; Rational in_; - OakNodeBlock *gap_; + OakNodeBlock gap_; bool gap_orphaned_; - OakNodeBlock *insert_; + OakNodeBlock insert_; std::vector add_track_commands_; TrackRippleRemoveAreaCommand *ripple_remove_command_; }; diff --git a/src/timeline/src/timelineundoripple.cpp b/src/timeline/src/timelineundoripple.cpp index 1028a822b..517e4bdfb 100644 --- a/src/timeline/src/timelineundoripple.cpp +++ b/src/timeline/src/timelineundoripple.cpp @@ -35,15 +35,15 @@ namespace olive // TrackRippleRemoveAreaCommand // TrackRippleRemoveAreaCommand::TrackRippleRemoveAreaCommand( - OakNodeTrack *track, const TimeRange &range) + OakNodeTrack track, const TimeRange &range) : track_(track) , range_(range) - , insert_previous_(nullptr) + , insert_previous_{} , allow_splitting_gaps_(false) , splice_split_command_(nullptr) { - trim_out_.block = nullptr; - trim_in_.block = nullptr; + trim_out_.block = OakNodeBlock{}; + trim_in_.block = OakNodeBlock{}; } TrackRippleRemoveAreaCommand::~TrackRippleRemoveAreaCommand() @@ -60,10 +60,10 @@ void TrackRippleRemoveAreaCommand::prepare() rat_nd(range_.in(), &n, &d); // Determine precisely what will be happening to these tracks - OakNodeBlock *first_block = nullptr; + OakNodeBlock first_block = {}; oaknode_track_get_nearest_block_before_or_at(track_, n, d, &first_block); - if (!first_block) { + if (!first_block.ctx) { // No blocks at this time, nothing to be done on this track return; } @@ -111,7 +111,7 @@ void TrackRippleRemoveAreaCommand::prepare() // If the first block is getting in trimmed, we're already at the end of our range if (!first_block_is_in_trimmed) { // Loop through the rest of the blocks and determine what to do with those - for (OakNodeBlock *next = block_next(first_block); next; + for (OakNodeBlock next = block_next(first_block); next.ctx; next = block_next(next)) { bool trimming = (block_out(next) > range_.out()); @@ -140,16 +140,16 @@ void TrackRippleRemoveAreaCommand::redo() splice_split_command_->redo_now(); // Trim the in of the split - OakNodeBlock *split = splice_split_command_->new_block(); + OakNodeBlock split = splice_split_command_->new_block(); block_set_length_and_media_in( split, block_length(split) - (range_.out() - block_in(split))); } else { - if (trim_out_.block) { + if (trim_out_.block.ctx) { block_set_length_and_media_out(trim_out_.block, trim_out_.new_length); } - if (trim_in_.block) { + if (trim_in_.block.ctx) { block_set_length_and_media_in(trim_in_.block, trim_in_.new_length); } @@ -182,12 +182,12 @@ void TrackRippleRemoveAreaCommand::undo() if (splice_split_command_) { splice_split_command_->undo_now(); } else { - if (trim_out_.block) { + if (trim_out_.block.ctx) { block_set_length_and_media_out(trim_out_.block, trim_out_.old_length); } - if (trim_in_.block) { + if (trim_in_.block.ctx) { block_set_length_and_media_in(trim_in_.block, trim_in_.old_length); } @@ -212,9 +212,9 @@ void TrackListRippleRemoveAreaCommand::prepare() oaknode_tracklist_get_track_count(list_, &count); for (int i = 0; i < count; i++) { - OakNodeTrack *track = nullptr; + OakNodeTrack track = {}; oaknode_tracklist_get_track_at(list_, i, &track); - if (!track) { + if (!track.ctx) { continue; } @@ -249,12 +249,12 @@ void TrackListRippleRemoveAreaCommand::undo() // TimelineRippleRemoveAreaCommand // TimelineRippleRemoveAreaCommand::TimelineRippleRemoveAreaCommand( - OakNodeSequence *timeline, Rational in, Rational out) + OakNodeSequence timeline, Rational in, Rational out) { for (int i = 0; i < OAKNODE_TRACK_TYPE_COUNT; i++) { - OakNodeTrackList *list = nullptr; + OakNodeTrackList list = {}; oaknode_sequence_get_track_list(timeline, i, &list); - if (list) { + if (list.ctx) { add_child(new TrackListRippleRemoveAreaCommand(list, in, out)); } } @@ -264,8 +264,8 @@ TimelineRippleRemoveAreaCommand::TimelineRippleRemoveAreaCommand( // TrackListRippleToolCommand // TrackListRippleToolCommand::TrackListRippleToolCommand( - OakNodeTrackList *track_list, - const std::map &info, + OakNodeTrackList track_list, + const std::map &info, const Rational &ripple_movement, const Timeline::MovementMode &movement_mode) : track_list_(track_list) @@ -280,11 +280,11 @@ TrackListRippleToolCommand::~TrackListRippleToolCommand() // Free any gaps still owned by this command (detached from the graph) for (auto &pair : working_data_) { WorkingData &wd = pair.second; - if (wd.created_gap && wd.created_gap_orphaned) { - oaknode_block_free(wd.created_gap); + if (wd.created_gap_orphaned) { + free_detached_handle(&wd.created_gap); } - if (wd.removed_gap && wd.removed_gap_orphaned) { - oaknode_block_free(wd.removed_gap); + if (wd.removed_gap_orphaned) { + free_detached_handle(&wd.removed_gap); } } } @@ -304,10 +304,10 @@ void TrackListRippleToolCommand::ripple(bool redo) // Make timeline changes for (const auto &pair : info_) { - OakNodeTrack *track = pair.first; + OakNodeTrack track = pair.first; const RippleInfo &info = pair.second; WorkingData working_data = working_data_[track]; - OakNodeBlock *b = info.block; + OakNodeBlock b = info.block; // Generate block length Rational new_block_length; @@ -321,7 +321,7 @@ void TrackListRippleToolCommand::ripple(bool redo) operation_movement = -operation_movement; } - if (b) { + if (b.ctx) { new_block_length = block_length(b) + operation_movement; } @@ -330,10 +330,10 @@ void TrackListRippleToolCommand::ripple(bool redo) if (info.append_gap) { // Rather than rippling the referenced block, we'll insert a gap and ripple with that - OakNodeBlock *gap = working_data.created_gap; + OakNodeBlock gap = working_data.created_gap; if (redo) { - if (!gap) { + if (!gap.ctx) { gap = oaknode_block_gap_create(); block_set_length_and_media_out( gap, ripple_movement_ < Rational(0) @@ -362,7 +362,7 @@ void TrackListRippleToolCommand::ripple(bool redo) } } else if ((redo && new_block_length.isNull()) || - (!redo && !block_track(b))) { + (!redo && !block_track(b).ctx)) { // The ripple is the length of this block. We assume that for this to happen, it must have // been a gap that we will now remove. @@ -445,9 +445,9 @@ void TrackListRippleToolCommand::ripple(bool redo) namespace { -bool is_gap(OakNodeBlock *b) +bool is_gap(OakNodeBlock b) { - if (!b) { + if (!b.ctx) { return false; } int kind = OAKNODE_BLOCK_OTHER; @@ -460,16 +460,17 @@ bool is_gap(OakNodeBlock *b) void TimelineRippleDeleteGapsAtRegionsCommand::prepare() { size_t max_gaps = 0; - std::map> requested_gaps; + std::map, TrackHandleLess> + requested_gaps; // Convert regions to gaps for (const auto ®ion : regions_) { - OakNodeTrack *track = region.first; + OakNodeTrack track = region.first; const TimeRange &range = region.second; int n, d; rat_nd(range.in(), &n, &d); - OakNodeBlock *block = nullptr; + OakNodeBlock block = {}; oaknode_track_get_nearest_block_before_or_at(track, n, d, &block); if (is_gap(block)) { @@ -499,7 +500,7 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare() // For each gap on each track, find a corresponding gap on every other track (which may include // a requested gap) to ripple in order to keep everything synchronized - std::map gap_lengths; + std::map gap_lengths; for (size_t gap_index = 0; gap_index < max_gaps; gap_index++) { Rational earliest_point = RATIONAL_MAX; Rational ripple_length = RATIONAL_MAX; @@ -516,14 +517,14 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare() } // Determine which gaps will be involved in this operation - std::vector gaps; + std::vector gaps; int track_count = 0; oaknode_sequence_get_all_track_count(timeline_, &track_count); for (int ti = 0; ti < track_count; ti++) { - OakNodeTrack *track = nullptr; + OakNodeTrack track = {}; oaknode_sequence_get_all_track_at(timeline_, ti, &track); - if (!track) { + if (!track.ctx) { continue; } @@ -538,7 +539,7 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare() const std::vector &requested_gaps_on_track = req_it != requested_gaps.end() ? req_it->second : empty_list; - OakNodeBlock *gap = nullptr; + OakNodeBlock gap = {}; if (gap_index < requested_gaps_on_track.size()) { // A requested gap was at this index, use it gap = requested_gaps_on_track.at(gap_index).gap; @@ -546,24 +547,24 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare() // No requested gap was at this index, find one int n, d; rat_nd(earliest_point, &n, &d); - OakNodeBlock *block = nullptr; + OakNodeBlock block = {}; oaknode_track_get_nearest_block_after_or_at(track, n, d, &block); - if (block) { + if (block.ctx) { // Found a block, test if it's a gap if (is_gap(block)) { gap = block; } else { if (block_in(block) == earliest_point) { - OakNodeBlock *next = block_next(block); + OakNodeBlock next = block_next(block); if (is_gap(next)) { gap = next; } else { ripple_length = 0; } } else { - OakNodeBlock *prev = block_previous(block); + OakNodeBlock prev = block_previous(block); if (is_gap(prev)) { gap = prev; } else { @@ -576,7 +577,7 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare() } } - if (gap) { + if (gap.ctx) { gaps.push_back(gap); if (!gap_lengths.count(gap)) { @@ -592,7 +593,7 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare() } if (ripple_length > 0) { - for (OakNodeBlock *gap : gaps) { + for (OakNodeBlock gap : gaps) { if (gap_lengths[gap] == ripple_length) { commands_.push_back(new TrackRippleRemoveBlockCommand( block_track(gap), gap)); diff --git a/src/timeline/src/timelineundoripple.h b/src/timeline/src/timelineundoripple.h index 433bee909..5f97965dd 100644 --- a/src/timeline/src/timelineundoripple.h +++ b/src/timeline/src/timelineundoripple.h @@ -51,25 +51,25 @@ namespace olive */ class TrackRippleRemoveAreaCommand : public UndoCommand { public: - TrackRippleRemoveAreaCommand(OakNodeTrack *track, const TimeRange &range); + TrackRippleRemoveAreaCommand(OakNodeTrack track, const TimeRange &range); virtual ~TrackRippleRemoveAreaCommand() override; /** * @brief Block to insert after if you want to insert something between this ripple */ - OakNodeBlock *get_insertion_index() const + OakNodeBlock get_insertion_index() const { return insert_previous_; } - OakNodeBlock *get_spliced_block() const + OakNodeBlock get_spliced_block() const { if (splice_split_command_) { return splice_split_command_->new_block(); } - return nullptr; + return OakNodeBlock{}; } void set_allow_splitting_gaps(bool e) @@ -86,23 +86,23 @@ protected: private: struct TrimOperation { - OakNodeBlock *block; + OakNodeBlock block; Rational old_length; Rational new_length; }; struct RemoveOperation { - OakNodeBlock *block; - OakNodeBlock *before; + OakNodeBlock block; + OakNodeBlock before; }; - OakNodeTrack *track_; + OakNodeTrack track_; TimeRange range_; TrimOperation trim_out_; std::vector removals_; TrimOperation trim_in_; - OakNodeBlock *insert_previous_; + OakNodeBlock insert_previous_; bool allow_splitting_gaps_; BlockSplitCommand *splice_split_command_; @@ -111,7 +111,7 @@ private: class TrackListRippleRemoveAreaCommand : public UndoCommand { public: - TrackListRippleRemoveAreaCommand(OakNodeTrackList *list, Rational in, + TrackListRippleRemoveAreaCommand(OakNodeTrackList list, Rational in, Rational out) : list_(list) , range_(in, out) @@ -133,9 +133,9 @@ protected: virtual void undo() override; private: - OakNodeTrackList *list_; + OakNodeTrackList list_; - std::vector working_tracks_; + std::vector working_tracks_; TimeRange range_; @@ -144,20 +144,20 @@ private: class TimelineRippleRemoveAreaCommand : public MultiUndoCommand { public: - TimelineRippleRemoveAreaCommand(OakNodeSequence *timeline, Rational in, + TimelineRippleRemoveAreaCommand(OakNodeSequence timeline, Rational in, Rational out); }; class TrackListRippleToolCommand : public UndoCommand { public: struct RippleInfo { - OakNodeBlock *block; + OakNodeBlock block; bool append_gap; }; TrackListRippleToolCommand( - OakNodeTrackList *track_list, - const std::map &info, + OakNodeTrackList track_list, + const std::map &info, const Rational &ripple_movement, const Timeline::MovementMode &movement_mode); @@ -177,30 +177,30 @@ protected: private: void ripple(bool redo); - OakNodeTrackList *track_list_; + OakNodeTrackList track_list_; - std::map info_; + std::map info_; Rational ripple_movement_; Timeline::MovementMode movement_mode_; struct WorkingData { - OakNodeBlock *created_gap = nullptr; + OakNodeBlock created_gap{}; bool created_gap_orphaned = false; - OakNodeBlock *removed_gap = nullptr; + OakNodeBlock removed_gap{}; bool removed_gap_orphaned = false; - OakNodeBlock *removed_gap_after = nullptr; + OakNodeBlock removed_gap_after{}; Rational old_length; Rational earliest_point_of_change; }; - std::map working_data_; + std::map working_data_; }; class TimelineRippleDeleteGapsAtRegionsCommand : public UndoCommand { public: - using RangeList = std::vector>; + using RangeList = std::vector>; - TimelineRippleDeleteGapsAtRegionsCommand(OakNodeSequence *vo, + TimelineRippleDeleteGapsAtRegionsCommand(OakNodeSequence vo, const RangeList ®ions) : timeline_(vo) , regions_(regions) @@ -227,13 +227,13 @@ protected: virtual void undo() override; private: - OakNodeSequence *timeline_; + OakNodeSequence timeline_; RangeList regions_; std::vector commands_; struct RemovalRequest { - OakNodeBlock *gap; + OakNodeBlock gap; TimeRange range; }; }; diff --git a/src/timeline/src/timelineundosplit.cpp b/src/timeline/src/timelineundosplit.cpp index 15609991b..8f5375027 100644 --- a/src/timeline/src/timelineundosplit.cpp +++ b/src/timeline/src/timelineundosplit.cpp @@ -24,6 +24,7 @@ #include #include "node/node.h" +#include "timelineutil.h" #include "undo/undostack.h" namespace olive @@ -32,19 +33,13 @@ namespace olive namespace { -void rat_nd(const Rational &r, int *n, int *d) -{ - *n = r.numerator(); - *d = r.denominator(); -} - /** * @brief oaknode-block-link undo command (replaces NodeLinkCommand from * oaknode's nodeundo, which does not cross the module boundary) */ class BlockLinkUndoCommand : public UndoCommand { public: - BlockLinkUndoCommand(OakNodeBlock *a, OakNodeBlock *b, bool link) + BlockLinkUndoCommand(OakNodeBlock a, OakNodeBlock b, bool link) : a_(a) , b_(b) , link_(link) @@ -71,8 +66,8 @@ protected: } private: - OakNodeBlock *a_; - OakNodeBlock *b_; + OakNodeBlock a_; + OakNodeBlock b_; bool link_; }; @@ -90,7 +85,7 @@ BlockSplitCommand::~BlockSplitCommand() void BlockSplitCommand::prepare() { - OakNodeNode *copy = oaknode_node_copy_in_graph( + OakNodeNode copy = oaknode_node_copy_in_graph( oaknode_block_as_node(block_), &reconnect_tree_command_); new_block_ = oaknode_block_from_node(copy); } @@ -119,7 +114,7 @@ void BlockSplitCommand::redo() Rational new_part_length = block_out - point_; // Begin an operation - OakNodeTrack *track = nullptr; + OakNodeTrack track = {}; oaknode_block_get_track(block_, &track); // Set lengths @@ -134,13 +129,13 @@ void BlockSplitCommand::redo() oaknode_clip_add_cache_passthrough_from(new_block_, block_); // If the block had an out transition, we move it to the new block - moved_transition_ = nullptr; + moved_transition_ = OakNodeBlock{}; moved_transition_input_.clear(); - OakNodeBlock *next = nullptr; + OakNodeBlock next = {}; oaknode_block_get_next(new_block_, &next); int next_kind = OAKNODE_BLOCK_OTHER; - if (next) { + if (next.ctx) { oaknode_block_get_kind(next, &next_kind); } if (next_kind == OAKNODE_BLOCK_TRANSITION) { @@ -149,10 +144,10 @@ void BlockSplitCommand::redo() oaknode_node_output_connection_count( oaknode_block_as_node(block_), &conn_count); for (int i = 0; i < conn_count; i++) { - OakNodeNode *conn_node = nullptr; + OakNodeNode conn_node = {}; oaknode_node_output_connection_node_at( oaknode_block_as_node(block_), i, &conn_node); - if (oaknode_block_from_node(conn_node) != next) { + if (!same_block(oaknode_block_from_node(conn_node), next)) { continue; } @@ -176,10 +171,10 @@ void BlockSplitCommand::redo() void BlockSplitCommand::undo() { - OakNodeTrack *track = nullptr; + OakNodeTrack track = {}; oaknode_block_get_track(block_, &track); - if (moved_transition_) { + if (moved_transition_.ctx) { oaknode_node_disconnect(oaknode_block_as_node(moved_transition_), moved_transition_input_.c_str()); oaknode_node_connect(oaknode_block_as_node(block_), @@ -208,19 +203,19 @@ BlockSplitPreservingLinksCommand::~BlockSplitPreservingLinksCommand() } } -OakNodeBlock * -BlockSplitPreservingLinksCommand::get_split(OakNodeBlock *original, +OakNodeBlock +BlockSplitPreservingLinksCommand::get_split(OakNodeBlock original, int time_index) const { if (time_index >= 0 && time_index < int(times_.size())) { for (size_t i = 0; i < blocks_.size(); i++) { - if (blocks_[i] == original) { + if (same_block(blocks_[i], original)) { return splits_.at(time_index).at(i); } } } - return nullptr; + return OakNodeBlock{}; } void BlockSplitPreservingLinksCommand::prepare() @@ -235,10 +230,10 @@ void BlockSplitPreservingLinksCommand::prepare() // if this ever becomes an issue. assert(i == 0 || time > times_.at(i - 1)); - std::vector splits(blocks_.size(), nullptr); + std::vector splits(blocks_.size(), OakNodeBlock{}); for (size_t j = 0; j < blocks_.size(); j++) { - OakNodeBlock *b = blocks_.at(j); + OakNodeBlock b = blocks_.at(j); int in_n, in_d, out_n, out_d; oaknode_block_get_in(b, &in_n, &in_d); @@ -260,22 +255,22 @@ void BlockSplitPreservingLinksCommand::prepare() // Now that we've determined all the splits, we can relink everything for (size_t i = 0; i < blocks_.size(); i++) { - OakNodeBlock *a = blocks_.at(i); + OakNodeBlock a = blocks_.at(i); for (size_t j = 0; j < blocks_.size(); j++) { if (i == j) { continue; } - OakNodeBlock *b = blocks_.at(j); + OakNodeBlock b = blocks_.at(j); int linked = 0; oaknode_block_are_linked(a, b, &linked); if (linked) { // These blocks are linked, ensure all the splits are linked too - for (const std::vector &split_list : + for (const std::vector &split_list : splits_) { - if (!split_list.at(i) || !split_list.at(j)) { + if (!split_list.at(i).ctx || !split_list.at(j).ctx) { continue; } BlockLinkUndoCommand *blc = new BlockLinkUndoCommand( @@ -297,10 +292,10 @@ void TrackSplitAtTimeCommand::prepare() int n, d; rat_nd(point_, &n, &d); - OakNodeBlock *b = nullptr; + OakNodeBlock b = {}; oaknode_track_get_block_containing_time(track_, n, d, &b); - if (b) { + if (b.ctx) { command_ = new BlockSplitCommand(b, point_); } } diff --git a/src/timeline/src/timelineundosplit.h b/src/timeline/src/timelineundosplit.h index 5d8083281..3601a0026 100644 --- a/src/timeline/src/timelineundosplit.h +++ b/src/timeline/src/timelineundosplit.h @@ -44,12 +44,12 @@ namespace olive */ class BlockSplitCommand : public UndoCommand { public: - BlockSplitCommand(OakNodeBlock *block, Rational point) + BlockSplitCommand(OakNodeBlock block, Rational point) : block_(block) - , new_block_(nullptr) + , new_block_{} , point_(point) , reconnect_tree_command_({}) - , moved_transition_(nullptr) + , moved_transition_{} { } @@ -58,7 +58,7 @@ public: /** * @brief Access the second block created as a result. Only valid after redo(). */ - OakNodeBlock *new_block() + OakNodeBlock new_block() { return new_block_; } @@ -71,22 +71,22 @@ protected: virtual void undo() override; private: - OakNodeBlock *block_; - OakNodeBlock *new_block_; + OakNodeBlock block_; + OakNodeBlock new_block_; Rational old_length_; Rational point_; OakUndoCommand reconnect_tree_command_; - OakNodeBlock *moved_transition_; + OakNodeBlock moved_transition_; std::string moved_transition_input_; }; class BlockSplitPreservingLinksCommand : public UndoCommand { public: BlockSplitPreservingLinksCommand( - const std::vector &blocks, + const std::vector &blocks, const std::vector ×) : blocks_(blocks) , times_(times) @@ -95,7 +95,7 @@ public: virtual ~BlockSplitPreservingLinksCommand() override; - OakNodeBlock *get_split(OakNodeBlock *original, int time_index) const; + OakNodeBlock get_split(OakNodeBlock original, int time_index) const; protected: virtual void prepare() override; @@ -115,18 +115,18 @@ protected: } private: - std::vector blocks_; + std::vector blocks_; std::vector times_; std::vector commands_; - std::vector> splits_; + std::vector> splits_; }; class TrackSplitAtTimeCommand : public UndoCommand { public: - TrackSplitAtTimeCommand(OakNodeTrack *track, Rational point) + TrackSplitAtTimeCommand(OakNodeTrack track, Rational point) : track_(track) , point_(point) , command_(nullptr) @@ -156,7 +156,7 @@ protected: } private: - OakNodeTrack *track_; + OakNodeTrack track_; Rational point_; diff --git a/src/timeline/src/timelineundotrack.h b/src/timeline/src/timelineundotrack.h index 8aee94829..816bd76a3 100644 --- a/src/timeline/src/timelineundotrack.h +++ b/src/timeline/src/timelineundotrack.h @@ -37,10 +37,10 @@ namespace olive */ class TrackRippleRemoveBlockCommand : public UndoCommand { public: - TrackRippleRemoveBlockCommand(OakNodeTrack *track, OakNodeBlock *block) + TrackRippleRemoveBlockCommand(OakNodeTrack track, OakNodeBlock block) : track_(track) , block_(block) - , before_(nullptr) + , before_{} { } @@ -57,16 +57,16 @@ protected: } private: - OakNodeTrack *track_; + OakNodeTrack track_; - OakNodeBlock *block_; + OakNodeBlock block_; - OakNodeBlock *before_; + OakNodeBlock before_; }; class TrackPrependBlockCommand : public UndoCommand { public: - TrackPrependBlockCommand(OakNodeTrack *track, OakNodeBlock *block) + TrackPrependBlockCommand(OakNodeTrack track, OakNodeBlock block) : track_(track) , block_(block) { @@ -84,14 +84,14 @@ protected: } private: - OakNodeTrack *track_; - OakNodeBlock *block_; + OakNodeTrack track_; + OakNodeBlock block_; }; class TrackInsertBlockAfterCommand : public UndoCommand { public: - TrackInsertBlockAfterCommand(OakNodeTrack *track, OakNodeBlock *block, - OakNodeBlock *before) + TrackInsertBlockAfterCommand(OakNodeTrack track, OakNodeBlock block, + OakNodeBlock before) : track_(track) , block_(block) , before_(before) @@ -110,11 +110,11 @@ protected: } private: - OakNodeTrack *track_; + OakNodeTrack track_; - OakNodeBlock *block_; + OakNodeBlock block_; - OakNodeBlock *before_; + OakNodeBlock before_; }; /** @@ -124,8 +124,8 @@ private: */ class TrackReplaceBlockCommand : public UndoCommand { public: - TrackReplaceBlockCommand(OakNodeTrack *track, OakNodeBlock *old_block, - OakNodeBlock *replace) + TrackReplaceBlockCommand(OakNodeTrack track, OakNodeBlock old_block, + OakNodeBlock replace) : track_(track) , old_(old_block) , replace_(replace) @@ -144,9 +144,9 @@ protected: } private: - OakNodeTrack *track_; - OakNodeBlock *old_; - OakNodeBlock *replace_; + OakNodeTrack track_; + OakNodeBlock old_; + OakNodeBlock replace_; }; } diff --git a/src/timeline/src/timelineutil.h b/src/timeline/src/timelineutil.h index 46a347f98..ab06cd6f0 100644 --- a/src/timeline/src/timelineutil.h +++ b/src/timeline/src/timelineutil.h @@ -25,13 +25,22 @@ #include #include "node/block.h" +#include "node/node.h" #include "node/project.h" #include "node/sequence.h" #include "node/track.h" +#include "../../node/c_api/nodehandle.h" + namespace olive { +class Block; +class Node; +class Project; +class Sequence; +class Track; + /** * @brief Small helpers translating between olive::core::Rational and the * oaknode C ABI's numerator/denominator pairs @@ -42,28 +51,92 @@ inline void rat_nd(const olive::core::Rational &r, int *n, int *d) *d = r.denominator(); } -inline olive::core::Rational block_in(OakNodeBlock *b) +/** + * @brief Identity comparison for value handles: two handles refer to the + * same object when they wrap the same native pointer (borrowed accessors + * hand out a fresh handle box per call, so comparing ctx is NOT an + * identity check) + */ +inline bool same_block(OakNodeBlock a, OakNodeBlock b) +{ + return oaknode_c_api::to_native(a) == + oaknode_c_api::to_native(b); +} + +inline bool same_track(OakNodeTrack a, OakNodeTrack b) +{ + return oaknode_c_api::to_native(a) == + oaknode_c_api::to_native(b); +} + +inline bool same_node(OakNodeNode a, OakNodeNode b) +{ + return oaknode_c_api::to_native(a) == + oaknode_c_api::to_native(b); +} + +/** + * @brief Handle-map comparators ordering by the wrapped native object + * (identity), so lookups work across freshly-boxed borrowed handles + */ +struct BlockHandleLess { + bool operator()(OakNodeBlock a, OakNodeBlock b) const + { + return oaknode_c_api::to_native(a) < + oaknode_c_api::to_native(b); + } +}; + +struct TrackHandleLess { + bool operator()(OakNodeTrack a, OakNodeTrack b) const + { + return oaknode_c_api::to_native(a) < + oaknode_c_api::to_native(b); + } +}; + +/** + * @brief Free a handle whose object is currently detached from any graph + * or container. + * + * Insertion into a graph/track flips the handle to non-owning, so a + * plain free() would only release the handle box and leak the detached + * object. This helper re-takes ownership (the caller guarantees the + * object is detached, as with the old raw-pointer delete semantics) and + * then releases, destroying the object. + */ +template +inline void free_detached_handle(Handle *h) +{ + if (!h || !h->ctx) { + return; + } + static_cast(h->ctx)->owns = true; + oaknode_c_api::free_handle(h); +} + +inline olive::core::Rational block_in(OakNodeBlock b) { int n, d; oaknode_block_get_in(b, &n, &d); return olive::core::Rational(n, d); } -inline olive::core::Rational block_out(OakNodeBlock *b) +inline olive::core::Rational block_out(OakNodeBlock b) { int n, d; oaknode_block_get_out(b, &n, &d); return olive::core::Rational(n, d); } -inline olive::core::Rational block_length(OakNodeBlock *b) +inline olive::core::Rational block_length(OakNodeBlock b) { int n, d; oaknode_block_get_length(b, &n, &d); return olive::core::Rational(n, d); } -inline void block_set_length_and_media_out(OakNodeBlock *b, +inline void block_set_length_and_media_out(OakNodeBlock b, const olive::core::Rational &len) { int n, d; @@ -71,7 +144,7 @@ inline void block_set_length_and_media_out(OakNodeBlock *b, oaknode_block_set_length_and_media_out(b, n, d); } -inline void block_set_length_and_media_in(OakNodeBlock *b, +inline void block_set_length_and_media_in(OakNodeBlock b, const olive::core::Rational &len) { int n, d; @@ -79,30 +152,30 @@ inline void block_set_length_and_media_in(OakNodeBlock *b, oaknode_block_set_length_and_media_in(b, n, d); } -inline olive::core::Rational track_length(OakNodeTrack *t) +inline olive::core::Rational track_length(OakNodeTrack t) { int n, d; oaknode_track_get_length(t, &n, &d); return olive::core::Rational(n, d); } -inline OakNodeBlock *block_previous(OakNodeBlock *b) +inline OakNodeBlock block_previous(OakNodeBlock b) { - OakNodeBlock *out = nullptr; + OakNodeBlock out = {}; oaknode_block_get_previous(b, &out); return out; } -inline OakNodeBlock *block_next(OakNodeBlock *b) +inline OakNodeBlock block_next(OakNodeBlock b) { - OakNodeBlock *out = nullptr; + OakNodeBlock out = {}; oaknode_block_get_next(b, &out); return out; } -inline OakNodeTrack *block_track(OakNodeBlock *b) +inline OakNodeTrack block_track(OakNodeBlock b) { - OakNodeTrack *out = nullptr; + OakNodeTrack out = {}; oaknode_block_get_track(b, &out); return out; } @@ -111,30 +184,30 @@ inline OakNodeTrack *block_track(OakNodeBlock *b) * @brief Attach/detach a block to/from the project graph that owns a * track (replaces the original setParent(graph) / setParent(memory)) */ -inline OakNodeProject *track_project(OakNodeTrack *track) +inline OakNodeProject track_project(OakNodeTrack track) { - OakNodeSequence *sequence = nullptr; + OakNodeSequence sequence = {}; if (oaknode_track_get_sequence(track, &sequence) != OAKNODE_OK || - !sequence) { - return nullptr; + !sequence.ctx) { + return OakNodeProject{}; } - OakNodeProject *project = nullptr; + OakNodeProject project = {}; oaknode_node_get_project(oaknode_sequence_as_node(sequence), &project); return project; } -inline void block_add_to_graph(OakNodeBlock *b, OakNodeTrack *track) +inline void block_add_to_graph(OakNodeBlock b, OakNodeTrack track) { - OakNodeProject *project = track_project(track); - if (project) { + OakNodeProject project = track_project(track); + if (project.ctx) { oaknode_project_add_node(project, oaknode_block_as_node(b)); } } -inline void block_remove_from_graph(OakNodeBlock *b, OakNodeTrack *track) +inline void block_remove_from_graph(OakNodeBlock b, OakNodeTrack track) { - OakNodeProject *project = track_project(track); - if (project) { + OakNodeProject project = track_project(track); + if (project.ctx) { oaknode_project_remove_node(project, oaknode_block_as_node(b)); } } diff --git a/src/timeline/tests/timeline_test.cpp b/src/timeline/tests/timeline_test.cpp index 294a32229..5e84eeacf 100644 --- a/src/timeline/tests/timeline_test.cpp +++ b/src/timeline/tests/timeline_test.cpp @@ -28,6 +28,8 @@ #include "timeline/workarea.h" #include "undo/undocommand.h" +#include "../../node/c_api/nodehandle.h" + namespace { @@ -36,13 +38,13 @@ protected: void SetUp() override { project_ = oaknode_project_init(); - ASSERT_NE(project_, nullptr); + ASSERT_NE(project_.ctx, nullptr); sequence_ = oaknode_sequence_create(); - ASSERT_NE(sequence_, nullptr); + ASSERT_NE(sequence_.ctx, nullptr); node_ = oaknode_sequence_as_node(sequence_); - OakNodeProject *owner = nullptr; + OakNodeProject owner = {}; ASSERT_EQ(oaknode_node_get_project(node_, &owner), OAKNODE_OK); - if (!owner) { + if (!owner.ctx) { ASSERT_EQ(oaknode_project_add_node(project_, node_), OAKNODE_OK); } @@ -51,12 +53,12 @@ protected: void TearDown() override { // The project owns the sequence and everything in its graph - oaknode_project_free(project_); + oaknode_project_free(&project_); } - OakNodeProject *project_ = nullptr; - OakNodeSequence *sequence_ = nullptr; - OakNodeNode *node_ = nullptr; + OakNodeProject project_ = {}; + OakNodeSequence sequence_ = {}; + OakNodeNode node_ = {}; }; // ---- marker --------------------------------------------------------------- @@ -64,7 +66,7 @@ protected: TEST_F(TimelineSequenceFixture, MarkerListOfReturnsList) { EXPECT_NE(oaktimeline_marker_list_of(node_), nullptr); - EXPECT_EQ(oaktimeline_marker_list_of(nullptr), nullptr); + EXPECT_EQ(oaktimeline_marker_list_of(OakNodeNode{}), nullptr); } TEST_F(TimelineSequenceFixture, MarkerAddCountAtRemove) @@ -196,8 +198,8 @@ TEST_F(TimelineSequenceFixture, MarkerListXmlRoundTrip) oakcommon_xml_writer_free(&writer); // Load into a fresh sequence's marker list - OakNodeSequence *seq2 = oaknode_sequence_create(); - ASSERT_NE(seq2, nullptr); + OakNodeSequence seq2 = oaknode_sequence_create(); + ASSERT_NE(seq2.ctx, nullptr); ASSERT_EQ(oaknode_project_add_node(project_, oaknode_sequence_as_node(seq2)), OAKNODE_OK); @@ -237,7 +239,7 @@ TEST_F(TimelineSequenceFixture, WorkareaGetSetLive) { OakTimelineWorkArea *w = oaktimeline_workarea_of(node_); ASSERT_NE(w, nullptr); - EXPECT_EQ(oaktimeline_workarea_of(nullptr), nullptr); + EXPECT_EQ(oaktimeline_workarea_of(OakNodeNode{}), nullptr); int in_n = 0, in_d = 0, out_n = 0, out_d = 0, enabled = -1; EXPECT_EQ(oaktimeline_workarea_get(w, &in_n, &in_d, &out_n, &out_d, @@ -343,8 +345,8 @@ TEST_F(TimelineSequenceFixture, WorkareaXmlRoundTrip) ASSERT_LT(needed, int(sizeof(xml))); oakcommon_xml_writer_free(&writer); - OakNodeSequence *seq2 = oaknode_sequence_create(); - ASSERT_NE(seq2, nullptr); + OakNodeSequence seq2 = oaknode_sequence_create(); + ASSERT_NE(seq2.ctx, nullptr); ASSERT_EQ(oaknode_project_add_node(project_, oaknode_sequence_as_node(seq2)), OAKNODE_OK); @@ -374,11 +376,11 @@ TEST_F(TimelineSequenceFixture, WorkareaXmlRoundTrip) TEST_F(TimelineSequenceFixture, AddAndRemoveTrackCommands) { - OakNodeTrackList *list = nullptr; + OakNodeTrackList list = {}; ASSERT_EQ(oaknode_sequence_get_track_list( sequence_, OAKNODE_TRACK_TYPE_VIDEO, &list), OAKNODE_OK); - ASSERT_NE(list, nullptr); + ASSERT_NE(list.ctx, nullptr); int count = -1; EXPECT_EQ(oaknode_tracklist_get_track_count(list, &count), OAKNODE_OK); @@ -391,10 +393,10 @@ TEST_F(TimelineSequenceFixture, AddAndRemoveTrackCommands) EXPECT_EQ(oaknode_tracklist_get_track_count(list, &count), OAKNODE_OK); EXPECT_EQ(count, before + 1); - OakNodeTrack *track = nullptr; + OakNodeTrack track = {}; EXPECT_EQ(oaknode_tracklist_get_track_at(list, before, &track), OAKNODE_OK); - ASSERT_NE(track, nullptr); + ASSERT_NE(track.ctx, nullptr); OakUndoCommand rm = oaktimeline_remove_track_command(track); ASSERT_NE(rm.ctx, nullptr); @@ -414,26 +416,26 @@ TEST_F(TimelineSequenceFixture, AddAndRemoveTrackCommands) oakundo_command_free(&add); oakundo_command_free(&rm); - EXPECT_EQ(oaktimeline_add_track_command(nullptr).ctx, nullptr); - EXPECT_EQ(oaktimeline_remove_track_command(nullptr).ctx, nullptr); + EXPECT_EQ(oaktimeline_add_track_command(OakNodeTrackList{}).ctx, nullptr); + EXPECT_EQ(oaktimeline_remove_track_command(OakNodeTrack{}).ctx, nullptr); } TEST_F(TimelineSequenceFixture, PlaceTrimSplitRemoveAreaCommands) { - OakNodeTrackList *list = nullptr; + OakNodeTrackList list = {}; ASSERT_EQ(oaknode_sequence_get_track_list( sequence_, OAKNODE_TRACK_TYPE_VIDEO, &list), OAKNODE_OK); - OakNodeTrack *track = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); - ASSERT_NE(track, nullptr); + OakNodeTrack track = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); + ASSERT_NE(track.ctx, nullptr); ASSERT_EQ(oaknode_project_add_node(project_, oaknode_track_as_node(track)), OAKNODE_OK); ASSERT_EQ(oaknode_tracklist_add_track(list, track), OAKNODE_OK); - OakNodeBlock *clip = oaknode_block_clip_create(); - ASSERT_NE(clip, nullptr); + OakNodeBlock clip = oaknode_block_clip_create(); + ASSERT_NE(clip.ctx, nullptr); EXPECT_EQ(oaknode_block_set_length_and_media_out(clip, 10, 1), OAKNODE_OK); ASSERT_EQ(oaknode_project_add_node(project_, @@ -466,7 +468,7 @@ TEST_F(TimelineSequenceFixture, PlaceTrimSplitRemoveAreaCommands) oakundo_command_free(&trim); // Split at 5: two blocks of 5 - OakNodeBlock *blocks[] = { clip }; + OakNodeBlock blocks[] = { clip }; OakUndoCommand split = oaktimeline_split_command(blocks, 1, 5, 1); ASSERT_NE(split.ctx, nullptr); @@ -495,9 +497,9 @@ TEST_F(TimelineSequenceFixture, PlaceTrimSplitRemoveAreaCommands) EXPECT_EQ(oaknode_block_get_length(clip, &n, &d), OAKNODE_OK); EXPECT_EQ(n, 3); - OakNodeBlock *second = nullptr; + OakNodeBlock second = {}; EXPECT_EQ(oaknode_track_get_block_at(track, 1, &second), OAKNODE_OK); - ASSERT_NE(second, nullptr); + ASSERT_NE(second.ctx, nullptr); EXPECT_EQ(oaknode_block_get_length(second, &n, &d), OAKNODE_OK); EXPECT_EQ(n, 5); @@ -512,32 +514,32 @@ TEST_F(TimelineSequenceFixture, PlaceTrimSplitRemoveAreaCommands) EXPECT_EQ(block_count, 0); oakundo_command_free(&place); - EXPECT_EQ(oaktimeline_place_block_command(nullptr, 0, clip, 0, 1) + EXPECT_EQ(oaktimeline_place_block_command(OakNodeTrackList{}, 0, clip, 0, 1) .ctx, nullptr); EXPECT_EQ(oaktimeline_trim_command(track, clip, 1, 1, 99).ctx, nullptr); EXPECT_EQ(oaktimeline_split_command(blocks, 0, 1, 1).ctx, nullptr); - EXPECT_EQ(oaktimeline_ripple_remove_area_command(nullptr, 0, 1, 1, 1) + EXPECT_EQ(oaktimeline_ripple_remove_area_command(OakNodeTrack{}, 0, 1, 1, 1) .ctx, nullptr); } TEST_F(TimelineSequenceFixture, ReplaceWithGapCommand) { - OakNodeTrackList *list = nullptr; + OakNodeTrackList list = {}; ASSERT_EQ(oaknode_sequence_get_track_list( sequence_, OAKNODE_TRACK_TYPE_VIDEO, &list), OAKNODE_OK); - OakNodeTrack *track = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); - ASSERT_NE(track, nullptr); + OakNodeTrack track = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO); + ASSERT_NE(track.ctx, nullptr); ASSERT_EQ(oaknode_project_add_node(project_, oaknode_track_as_node(track)), OAKNODE_OK); ASSERT_EQ(oaknode_tracklist_add_track(list, track), OAKNODE_OK); - OakNodeBlock *clip = oaknode_block_clip_create(); - ASSERT_NE(clip, nullptr); + OakNodeBlock clip = oaknode_block_clip_create(); + ASSERT_NE(clip.ctx, nullptr); EXPECT_EQ(oaknode_block_set_length_and_media_out(clip, 6, 1), OAKNODE_OK); ASSERT_EQ(oaknode_project_add_node(project_, @@ -546,8 +548,8 @@ TEST_F(TimelineSequenceFixture, ReplaceWithGapCommand) EXPECT_EQ(oaknode_track_append_block(track, clip), OAKNODE_OK); // Second clip so the first has a next (gap required) - OakNodeBlock *clip2 = oaknode_block_clip_create(); - ASSERT_NE(clip2, nullptr); + OakNodeBlock clip2 = oaknode_block_clip_create(); + ASSERT_NE(clip2.ctx, nullptr); EXPECT_EQ(oaknode_block_set_length_and_media_out(clip2, 6, 1), OAKNODE_OK); ASSERT_EQ(oaknode_project_add_node(project_, @@ -565,7 +567,7 @@ TEST_F(TimelineSequenceFixture, ReplaceWithGapCommand) OAKNODE_OK); ASSERT_EQ(block_count, 2); - OakNodeBlock *first = nullptr; + OakNodeBlock first = {}; EXPECT_EQ(oaknode_track_get_block_at(track, 0, &first), OAKNODE_OK); int kind = OAKNODE_BLOCK_OTHER; EXPECT_EQ(oaknode_block_get_kind(first, &kind), OAKNODE_OK); @@ -573,10 +575,13 @@ TEST_F(TimelineSequenceFixture, ReplaceWithGapCommand) oakundo_command_undo_now(replace); EXPECT_EQ(oaknode_track_get_block_at(track, 0, &first), OAKNODE_OK); - EXPECT_EQ(first, clip); + // Borrowed handles get a fresh box per call, so compare the wrapped + // native objects + EXPECT_EQ(oaknode_c_api::to_native(first), + oaknode_c_api::to_native(clip)); oakundo_command_free(&replace); - EXPECT_EQ(oaktimeline_replace_block_with_gap_command(nullptr, clip) + EXPECT_EQ(oaktimeline_replace_block_with_gap_command(OakNodeTrack{}, clip) .ctx, nullptr); @@ -586,17 +591,17 @@ TEST_F(TimelineSequenceFixture, ReplaceWithGapCommand) TEST_F(TimelineSequenceFixture, SlideAndInsertGapsAndRippleDeleteGapsFactories) { - OakNodeBlock *clip = oaknode_block_clip_create(); - EXPECT_EQ(oaktimeline_slide_command(nullptr, &clip, 1, nullptr, - nullptr, 1, 1) + OakNodeBlock clip = oaknode_block_clip_create(); + EXPECT_EQ(oaktimeline_slide_command(OakNodeTrack{}, &clip, 1, + OakNodeBlock{}, OakNodeBlock{}, 1, 1) .ctx, nullptr); - OakNodeTrackList *list = nullptr; + OakNodeTrackList list = {}; ASSERT_EQ(oaknode_sequence_get_track_list( sequence_, OAKNODE_TRACK_TYPE_VIDEO, &list), OAKNODE_OK); - EXPECT_EQ(oaktimeline_insert_gaps_command(nullptr, 0, 1, 1, 1).ctx, + EXPECT_EQ(oaktimeline_insert_gaps_command(OakNodeTrackList{}, 0, 1, 1, 1).ctx, nullptr); EXPECT_EQ(oaktimeline_ripple_delete_gaps_command( @@ -604,7 +609,7 @@ TEST_F(TimelineSequenceFixture, SlideAndInsertGapsAndRippleDeleteGapsFactories) .ctx, nullptr); - oaknode_block_free(clip); + oaknode_block_free(&clip); } } // namespace