Files
oak-editor/crates/oaktimeline
Mike-Solar aa5fcef66e fix(oakengine): facade bugs found by integration tests
undo:
- NULL/empty label no longer crosses to oakundo as a dangling 0x1
  pointer (push, group_begin/end) — fixed SIGSEGV
- group_abort now undoes each executed child in reverse order

task:
- create_project_import addrefs the borrowed project handle instead of
  freeing it under the async task — fixed UAF/SIGSEGV

timeline:
- toggle_enabled/delete_clips guard NULL+0 slices — fixed SIGABRT
- BlockSplitCommand halves placed correctly (oaktimeline undosplit)
- PreservingLinks / ripple remove / ripple delete-gaps commands
  self-prepare on first redo — fixes silent no-op split/ripple
- trim_clips_to targets the block containing the point, not the track
- delete_empty_tracks applies the live track removal
- ripple facades no longer free borrowed track handles still referenced
  by commands — fixed UAF

node:
- project_add_node releases the factory handle — fixes per-call leak
- inputs_from(recursive=0) matches direct feeders (BFS off-by-one)
- group passthrough id/resolve treat two-stage string length as success
- node_connect(_command) reject duplicate connects with E_STATE
- folder_add_child enforces one-folder-per-node
- value_split_to_tracks splits vector/color per component
- set_context_position/expanded establish the first entry
- node_get_flags on an empty box returns 0, not u64::MAX
- footage_borrow addrefs its wrapper — fixes double-free

render:
- renderer_create rejects invalid pixel formats (real range check)
- render_frame forwards renderer width/height to the ticket

tests: repro #[ignore]s removed, bug-behavior assertions corrected,
it_undo global-stack tests serialized with a shared lock
2026-08-11 01:30:18 +08:00
..

oaktimeline Rust crate (declaration draft, for review)

Status: declaration draft. Signatures + doc comments are the spec; every body is todo!(). Not wired into any build. The crate template (FFI discipline, testing layers) follows crates/oakplugin/README.md and crates/oaknode/README.md.

Scope

Replaces the C++ oaktimeline module (src/timeline/src): timeline markers and work areas, the timeline undo-command family (add/remove tracks, place/trim/split blocks, ripple edits, slide, gap insertion), and the shared Timeline namespace / utility helpers.

Public contract: include/timeline/*.h (error.h, displaymode.h, marker.h, workarea.h, edit.h) — frozen, implemented verbatim by src/ffi.rs. The timeline value handles (OakTimelineMarkerList, OakTimelineWorkArea) and every edit command are exported through this ABI only; consumers (the facade/app, the oaknode crate) never see the internal Rust types.

Key architectural decisions (C++ → Rust mapping)

  1. Domain modules mirror the C++ header files. The C++ module is a flat set of headers, not a deep class hierarchy, so the Rust crate keeps one module per C++ header family (common, marker, workarea, undocommon, undotrack, undogeneral, undopointer, undoripple, undosplit, util). COVERAGE.md maps every C++ type to its Rust home; review that first.

  2. No C++ UndoCommand subclass hierarchy. Following the oaknode crate decision (#4), each undo command is a plain Rust struct that exposes prepare() / redo() / undo() (all todo!() here) and is surfaced to the world through the oakundo C ABI vtable (bridge::undo::oakundo_command_init, Rust callbacks as userdata). Every command struct carries a to_command() -> CHandle factory doc comment describing the wiring.

  3. Value types come from oakcore-rs. Markers and work areas are built on Rational/TimeRange, so the crate depends on oakcore-rs (crates/oakcore) exactly like oaknode does; no pixel/sample formats are involved here.

  4. All cross-module access goes through the C ABI. Per the project rule (no cross-module C++ member calls), timeline commands touch the node graph exclusively through the oaknode C ABI (bridge::node), undo through the oakundo C ABI (bridge::undo), and XML/config through the oakcommon C ABI (bridge::common). The C++ internal helpers oakundo_capi::make_command_handle / oaknode_c_api::to_native are not replicated in Rust — their role is subsumed by vtable commands and by value handles treated as opaque.

  5. Handles. handle.rs provides the shared RefBox/CHandle scaffolding (duplicated per crate, as in oaknode) with OAKTIMELINE_ABI_VERSION = 1. Borrowed handles into node-owned objects and owning handles created by *_create share one box layout {ctx, addref, release, abi_version}.

Layout

src/
  lib.rs         crate doc + module map
  error.rs       error codes (mirrors include/timeline/error.h)
  handle.rs      refcounted-handle scaffolding (same pattern as node)
  common.rs      Timeline namespace (MovementMode/ThumbnailMode/
                 WaveformMode, EditToInfo) — timelinecommon.h
  marker.rs      TimelineMarker/MarkerList + 5 marker commands
  workarea.rs    TimelineWorkArea + 2 workarea commands
  undocommon.rs  node/block remove helpers + CHandleCommandWrapper
  undotrack.rs   track ripple/prepend/insert-after/replace commands
  undogeneral.rs resize/media-in/add/remove-track/transition/gap/
                 enable-disable/insert-gaps/default-transition commands
  undopointer.rs BlockTrimCommand/TrackSlideCommand/TrackPlaceBlockCommand
  undoripple.rs  ripple remove-area / ripple-tool / delete-gaps commands
  undosplit.rs   BlockSplitCommand/BlockSplitPreservingLinksCommand/
                 TrackSplitAtTimeCommand
  util.rs        timelineutil.h inline helpers (rat_nd, same_*,
                 free_detached_handle, block/track queries)
  bridge/        C ABI imports: node.rs, undo.rs, common.rs
  ffi.rs         include/timeline/*.h export layer
tests/           contract + golden tests (see README test section)

Hard rules for the implementer

  1. Every extern "C" body goes through handle::guard*; no panic crosses FFI.
  2. Timeline objects never outlive their owning node; borrowed handles are created under a guard that owns the node reference.
  3. Behavior parity with C++ is proven by the C ABI test-suite (src/timeline/tests, unchanged) plus the golden tests in tests/ (XML save/load formats captured verbatim from src/timeline/src/timelinemarker.cpp / timelineworkarea.cpp).
  4. Where C++ behavior is genuinely load-bearing but ugly (e.g. marker list kept sorted by time, ripple's compensation gap rules), port the behavior, not the aesthetics; leave a // CPP-PARITY: comment with the C++ file:line.

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.