- oakengine_sequence_move_clip implemented for real (oaktimeline TrackMoveBlockCommand; fixes the graph-ownership/gap-anchor/ripple trim bugs the stub was hiding); same-track via the frozen C ABI, cross-track supported by the module command - oaknode clip blocks now declare a tex_in texture input and set effect_input to it, so timeline clips can host effect chains; facade test covers effect insert/remove on a real clip - oakffmpeg-link: FFMPEG_DIR is now mandatory with a clear panic (a Homebrew upgrade left the system ffmpeg .pc pointing at a deleted dav1d Cellar path, breaking links); reads a git-ignored workspace .env for IDEs that cannot inject env vars (RustRover); links the C++ stdlib for C++ codec libs (svt-av1) - oakengine re-exports oaknode so tests share one crate instance; it_node uses the direct instance's value type where it calls the module FFI (the --workspace dev-dependency feature split builds oaknode twice)
oakundo Rust crate
Status: implemented. Ports the C++ oakundo module (
src/undo/src) to Rust behind its frozen C ABI (include/undo/*.h). Template followscrates/oakplugin.
Scope
Replaces the C++ oakundo module (src/undo/src): undoable commands
and the undo/redo history stack. Public contract: include/undo/*.h
(3 headers: error.h, undocommand.h, undostack.h) — frozen,
implemented verbatim by src/ffi.rs.
Architectural decisions
- Vtable-command pattern is the centerpiece. In C++ other modules
subclass
olive::UndoCommand(redo()/undo()overrides) and plug themselves in polymorphically. Rust has no inheritance, so the C ABI already models exactly this withOakUndoCommandVtable { redo, undo, free_fn }plus a caller-owneduserdatapointer. The safe layer's [undocommand::CommandKind] is the direct analog: either a caller-defined vtable command (function pointers + userdata) or a [undocommand::MultiUndoCommand] composite. Domain logic dispatches on the vtable the same way the C++ virtual dispatch does. - Modified-state callbacks are intentionally not part of the C ABI.
The C++
UndoCommand::redo_and_set_modifiedpair records/restores a project dirty flag viastd::functionaccessors. The public headers expose none of this; the stack drives state viadone_on the safe type instead, and the flag callbacks are left as a documented future extension. UndoStackstate machine is modeled directly on the C++: two deques —commands_(done, oldest at front) andundone_commands_(most-recently-undone at front);pushclears any redoable tail, executes redo, and drops the oldest when the cap (200) is exceeded;jumpclamps and walks viaundo/redo. The fresh stack holds a single "New/Open Project" empty command socan_undois false at the bottom (perundostack.cpp).- No merge semantics.
include/undo/*.handsrc/undo/src/*define nomerge_with/can_merge; commands are never coalesced. Tests reflect this (no merge tests).
Layout
src/
lib.rs crate doc + module map
error.rs error codes (include/undo/error.h)
handle.rs refcounted-handle scaffolding (OAKUNDO_ABI_VERSION=1)
undocommand.rs UndoCommand / vtable command / MultiUndoCommand
undostack.rs UndoStack + empty bottom command
ffi.rs export layer (one submodule per public header)
tests/ contract tests per module
error.h exports macros only and is folded into ffi.rs's preamble
(no own submodule), matching the codec crate convention.
Dependency policy
Prefer mature third-party crates (MIT/Apache-2.0/BSD, GPL-compatible) over hand-rolling; register each addition (name + reason) here. Large existing C++ libraries (OTIO, OCIO, OIIO, FFmpeg) are NEVER rewritten — they are consumed through their C ABI / bridge layers.