From f8540e38921a3651ec97bd2910a71da8f64f70e1 Mon Sep 17 00:00:00 2001 From: Mike Solar Date: Mon, 10 Aug 2026 19:11:25 +0800 Subject: [PATCH] =?UTF-8?q?refactor(engine):=20single-lib=20phase=203=20?= =?UTF-8?q?=E2=80=94=20plugin/timeline/task/codec=20bridges=20converted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - oakplugin bridges -> direct (dlsym 28->0; OFX suites untouched) - oaktimeline bridges -> direct (teststubs slimmed to pure-Rust mocks) - oaktask bridges -> direct; facade EncodingParamsPOD -> oakcodec alias - dlsym total 102->24 (all in documented dead/host directions) - All crates build+test green; dylib exports unchanged --- docs/zh/plans/riir/single-lib.md | 69 +- src/codec/rust/src/bridge/common.rs | 66 +- src/codec/rust/src/bridge/render.rs | 66 +- src/engine/rust/Cargo.lock | 12 + src/engine/rust/src/bridge/audio.rs | 23 +- src/engine/rust/src/bridge/codec.rs | 109 +- src/engine/rust/src/bridge/task.rs | 17 +- src/engine/rust/src/codec.rs | 22 +- src/engine/rust/src/task.rs | 6 +- src/oakcore-rs/src/handle.rs | 2 +- src/plugin/rust/Cargo.lock | 1622 ++++++++++++++++++- src/plugin/rust/Cargo.toml | 11 +- src/plugin/rust/src/bridge/mod.rs | 63 +- src/plugin/rust/src/bridge/node.rs | 47 +- src/plugin/rust/src/bridge/render.rs | 187 +-- src/plugin/rust/src/bridge/undo.rs | 41 +- src/plugin/rust/tests/ffi_instance_test.rs | 8 +- src/plugin/rust/tests/render_driver_test.rs | 5 +- src/task/rust/Cargo.lock | 218 ++- src/task/rust/Cargo.toml | 17 +- src/task/rust/src/bridge/codec.rs | 333 ++-- src/task/rust/src/bridge/common.rs | 207 ++- src/task/rust/src/bridge/node.rs | 1121 +++++++++---- src/task/rust/src/bridge/render.rs | 424 ++--- src/task/rust/src/bridge/timeline.rs | 24 +- src/task/rust/src/bridge/undo.rs | 81 +- src/task/rust/src/codecbridge.rs | 2 +- src/task/rust/src/conform.rs | 2 +- src/task/rust/src/customcache.rs | 4 +- src/task/rust/src/export.rs | 2 +- src/task/rust/src/ffi/project.rs | 5 +- src/task/rust/src/precache.rs | 2 +- src/task/rust/src/project/import.rs | 2 +- src/task/rust/src/render.rs | 4 +- src/task/rust/src/task.rs | 10 +- src/task/rust/tests/common/mod.rs | 52 +- src/task/rust/tests/render_loop_test.rs | 4 +- src/task/rust/tests/task_run_test.rs | 4 +- src/timeline/rust/Cargo.lock | 559 +++++++ src/timeline/rust/Cargo.toml | 11 +- src/timeline/rust/src/bridge/common.rs | 290 +++- src/timeline/rust/src/bridge/mod.rs | 11 +- src/timeline/rust/src/bridge/node.rs | 800 +++++++-- src/timeline/rust/src/bridge/teststubs.rs | 264 ++- src/timeline/rust/src/bridge/undo.rs | 126 +- src/timeline/rust/src/ffi.rs | 14 +- src/timeline/rust/src/util.rs | 2 +- src/timeline/rust/tests/bridge_test.rs | 14 +- src/timeline/rust/tests/cov_bridge.rs | 6 +- 49 files changed, 5164 insertions(+), 1827 deletions(-) diff --git a/docs/zh/plans/riir/single-lib.md b/docs/zh/plans/riir/single-lib.md index e8bacdb37..105e854ee 100644 --- a/docs/zh/plans/riir/single-lib.md +++ b/docs/zh/plans/riir/single-lib.md @@ -415,19 +415,64 @@ encoding-params POD(`oakcodec_encoding_params` 等)在 4 个 crate 各有 `oakcodec_decoder_probe` 直接调用,测试的 `is_err()` 断言仍成立 (真实 codec 对不存在文件返回错误句柄)。 -### 11.4 未完成(后续步骤) +### 11.4 第三轮完成(2026-08-10) -- **模块 bridge 其余方向**(步骤 b 剩余):oakplugin `bridge/{node, - render,undo}`(28 处 dlsym + 状态化 test-stubs 迁移)、oaktimeline - `bridge/{node,undo,common}`(teststubs.rs 52921 行 + 16 个测试文件)、 - oaktask `bridge/*`(7 文件 + tests/common 的 C ABI 桩)、oakcodec - `bridge/common`(`OakVideoParams` 类型从桩句柄对齐到真实 CHandle, - 波及 frame.rs 等内部类型)。每个方向模式已由 node/render/audio 证明; - 工作量集中在测试侧(桩删除 + 迁移 + 类型对齐)。 -- **dlsym 删除**(步骤 c):node 20 处、render 4 处(均在死/宿主方向 - bridge)、plugin 28 处,在各自 bridge 转换完成后清除。 -- **facade 侧后续**:`linkage.rs` 注释更新;EncodingParamsPOD 等 POD - 统一到 oakcore-rs。 +本轮完成的方向(每步 `cargo build` + 该 crate `cargo test` 绿): + +- **oakplugin `bridge/{node,render,undo}` → 直接调用**(28 处 dlsym → 0): + oaknode/oakrender/oakundo 加入依赖;28 个桥函数直接调 `oaknode::ffi`/ + `oakrender::ffi`/`oakundo::ffi`。`Value` 桥接用布局一致的指针 cast + (避免 49 处 `r#type`→`kind` 改名);`VideoParams` 别名到 + `oakrender::ffi::OakRenderVideoParams`。`test-stubs` 特性保留,桩改为 + 纯 Rust(无 `#[no_mangle]`,与真实 crate 导出不冲突);`dlsym` 模块 + 删除。测试迁移:两个"符号缺失"单测改为空/NULL 句柄经真实 crate 的 + 容错路径(`texture_is_dummy` 空句柄 0、`instance_render` 空 dst 被 + 插件层 E_INVALID 拒绝、`render_job` 同);OFX suites 层未触碰。 +- **oaktimeline `bridge/{node,undo,common}` → 直接调用**:oaknode/ + oakundo/oakcommon 加入依赖;81 个桥函数直接调用。`teststubs.rs` + (1658 行)改为纯 Rust mock(去 `#[no_mangle]`),桥函数加 + `#[cfg(any(test, feature="test-stubs"))]` mock 变体 / 真实变体, + 测试二进制(cfg(test) 对依赖关)经 `--features test-stubs` 解析 + `bridge::teststubs`——与真实 crate 导出共存无冲突。修复桥签名漂移: + `oakcommon_xml_reader_read_next_start_element` 2 参(found 标志, + 调用点 ffi.rs/tests 同步);`oakundo_stack_push`→真实 + `oakundo_undostack_push`。260 测试绿。 +- **oaktask `bridge/*` → 直接调用**(240 个桥函数):oaknode/oakundo/ + oakcommon/oakcodec/oakrender/oaktimeline 加入依赖(oakrender 由可选 + 转必选,`real-oakrender` 特性改为标记)。镜像类型对齐真实 crate: + `OakCancelAtom`/`OakRenderTicket`/`OakRenderCache`/`OakColorProcessor` + 等 → CHandle 别名,`OakCodecEncodingParams`/`OakCodecProxyParams`/ + `OakCodecTaskRequest`/`OakRenderVideoTicketParams`/`OakUndoCommandVtable` + → 真实 crate 类型;`from_chandle`/`to_chandle` 助手删除(恒等)。 + 测试侧:encoding-params 结构从 `[c_char;N]`→`[u8;N]`,`write_cstr` + 改 u8,`fake_atom` 等 fixtures 仍可用(真实 crate 的 cancel 路径由 + task 内部创建真实 atom)。90 测试绿。 +- **oakcodec `bridge/{common,render}` 句柄类型对齐**:`OakVideoParams`/ + `OakAudioParams`/`OakSubtitleParams`/`OakNodeBlock`/`OakRenderTexture`/ + `OakCancelAtom`/`OakRenderRenderer`/`OakCodecFrame` → CHandle 别名 + (删除其上的 `unsafe impl Send/Sync` 与固有 impl);共享 + `oakcore_rs::handle::CHandle` 增补 `PartialEq, Eq`(codec 的 + `CodecStream` 派生比较)。 +- **facade EncodingParamsPOD 统一**(步骤 c 的 POD 部分):facade 的 + `EncodingParamsPOD`(41 字段 c_char 镜像)→ 别名 + `oakcodec::ffi::encoder::oakcodec_encoding_params`(u8);`zeroed()` + → `zeroed_encoding_params()` 自由函数;`write_field`/`field_str` 改 + u8;`oakcodec_encoder_init`/`oaktask_create_export`/ + `oakaudio_manager_start_recording` 三个 encoding-params link-time + extern 全部改直接调用(audio 的 `EncodingParams` 同别名后打通)。 + facade bridge 仅剩宿主 `oakcore_audioparams_*` extern(C++ lib,非 + Rust crate)。 + +### 11.5 未完成(后续步骤) + +- **dlsym 残留**(node 20、render 4):全在死/宿主方向——node + `bridge/core.rs`(`oakcore_audioparams_*` 宿主符号)、 + `bridge/render.rs`+`bridge/timeline.rs`(死方向,node 测试 + `ffi_timeline_test` 使用)、render `bridge/node.rs`(copier 死方向, + `copier_test` 使用)。这些是文档化的"保留 dlsym"(死/宿主),转换 + 需同步迁移对应测试;`linkage.rs` 锚定注释可更新。 +- `oakcodec::bridge::render` 的 cancel-atom C 边界(设计 §4.2)保持。 +- OFX suites 层保持 C(冻结,未触碰)。 ## 10. 风险与回退 diff --git a/src/codec/rust/src/bridge/common.rs b/src/codec/rust/src/bridge/common.rs index a92b34378..27cab20e0 100644 --- a/src/codec/rust/src/bridge/common.rs +++ b/src/codec/rust/src/bridge/common.rs @@ -35,74 +35,28 @@ use std::ffi::{c_char, c_int, c_void}; +use crate::handle::CHandle; + /// `OakVideoParams` — refcounted video-parameter handle. -#[derive(Clone, Debug, PartialEq, Eq)] -#[repr(C)] -pub struct OakVideoParams { - /// Opaque object pointer. - pub ctx: *mut c_void, - /// Atomically increments the reference count. - pub addref: Option, - /// Decrements the count, destroys at zero. - pub release: Option, - /// ABI version (see [`OAKCODEC_ABI_VERSION`]). - pub abi_version: u32, -} +pub type OakVideoParams = CHandle; + /// `OakAudioParams` — refcounted audio-parameter handle. -#[derive(Clone, Debug, PartialEq, Eq)] -#[repr(C)] -pub struct OakAudioParams { - /// Opaque object pointer. - pub ctx: *mut c_void, - /// Atomically increments the reference count. - pub addref: Option, - /// Decrements the count, destroys at zero. - pub release: Option, - /// ABI version. - pub abi_version: u32, -} +pub type OakAudioParams = CHandle; + /// `OakSubtitleParams` — refcounted subtitle-parameter handle. -#[derive(Clone, Debug, PartialEq, Eq)] -#[repr(C)] -pub struct OakSubtitleParams { - /// Opaque object pointer. - pub ctx: *mut c_void, - /// Atomically increments the reference count. - pub addref: Option, - /// Decrements the count, destroys at zero. - pub release: Option, - /// ABI version. - pub abi_version: u32, -} +pub type OakSubtitleParams = CHandle; + /// `OakNodeBlock` — opaque node-block handle (owned elsewhere; codec /// only stores and forwards it). -#[derive(Clone, Debug, PartialEq, Eq)] -#[repr(C)] -pub struct OakNodeBlock { - /// Opaque object pointer. - pub ctx: *mut c_void, - /// Atomically increments the reference count. - pub addref: Option, - /// Decrements the count, destroys at zero. - pub release: Option, - /// ABI version. - pub abi_version: u32, -} +pub type OakNodeBlock = CHandle; + // The handle structs are opaque refcounted handles pointing into a C // library; the boxed objects are independently synchronized there, so // moving a handle between threads is sound. -unsafe impl Send for OakVideoParams {} -unsafe impl Sync for OakVideoParams {} -unsafe impl Send for OakAudioParams {} -unsafe impl Sync for OakAudioParams {} -unsafe impl Send for OakSubtitleParams {} -unsafe impl Sync for OakSubtitleParams {} -unsafe impl Send for OakNodeBlock {} -unsafe impl Sync for OakNodeBlock {} extern "C" { /// `oakcommon_videoparams_init`. diff --git a/src/codec/rust/src/bridge/render.rs b/src/codec/rust/src/bridge/render.rs index 9352c8eac..4d1923b62 100644 --- a/src/codec/rust/src/bridge/render.rs +++ b/src/codec/rust/src/bridge/render.rs @@ -23,71 +23,25 @@ use std::ffi::{c_char, c_int, c_void}; +use crate::handle::CHandle; + /// `OakRenderTexture` — refcounted GPU texture handle. -#[derive(Clone, Debug, PartialEq, Eq)] -#[repr(C)] -pub struct OakRenderTexture { - /// Opaque object pointer. - pub ctx: *mut c_void, - /// Atomically increments the reference count. - pub addref: Option, - /// Decrements the count, destroys at zero. - pub release: Option, - /// ABI version. - pub abi_version: u32, -} +pub type OakRenderTexture = CHandle; + /// `OakCancelAtom` — refcounted cancellation atom handle. -#[derive(Clone, Debug, PartialEq, Eq)] -#[repr(C)] -pub struct OakCancelAtom { - /// Opaque object pointer. - pub ctx: *mut c_void, - /// Atomically increments the reference count. - pub addref: Option, - /// Decrements the count, destroys at zero. - pub release: Option, - /// ABI version. - pub abi_version: u32, -} +pub type OakCancelAtom = CHandle; + /// `OakRenderRenderer` — refcounted display-renderer handle. -#[derive(Clone, Debug, PartialEq, Eq)] -#[repr(C)] -pub struct OakRenderRenderer { - /// Opaque object pointer. - pub ctx: *mut c_void, - /// Atomically increments the reference count. - pub addref: Option, - /// Decrements the count, destroys at zero. - pub release: Option, - /// ABI version. - pub abi_version: u32, -} +pub type OakRenderRenderer = CHandle; + /// `OakCodecFrame` — refcounted CPU-frame handle shared with oakrender. -#[derive(Clone, Debug, PartialEq, Eq)] -#[repr(C)] -pub struct OakCodecFrame { - /// Opaque object pointer. - pub ctx: *mut c_void, - /// Atomically increments the reference count. - pub addref: Option, - /// Decrements the count, destroys at zero. - pub release: Option, - /// ABI version. - pub abi_version: u32, -} +pub type OakCodecFrame = CHandle; + // Refcounted opaque handles; thread-safe in the C library. -unsafe impl Send for OakRenderTexture {} -unsafe impl Sync for OakRenderTexture {} -unsafe impl Send for OakCancelAtom {} -unsafe impl Sync for OakCancelAtom {} -unsafe impl Send for OakRenderRenderer {} -unsafe impl Sync for OakRenderRenderer {} -unsafe impl Send for OakCodecFrame {} -unsafe impl Sync for OakCodecFrame {} /// `oakrender_video_params` — flattened POD of `olive::VideoParams` /// passed into oakrender; see `include/render/renderer.h`. diff --git a/src/engine/rust/Cargo.lock b/src/engine/rust/Cargo.lock index 18c500b47..d8ea00f60 100644 --- a/src/engine/rust/Cargo.lock +++ b/src/engine/rust/Cargo.lock @@ -830,6 +830,9 @@ version = "0.1.0" dependencies = [ "cc", "oakcore-rs", + "oaknode", + "oakrender", + "oakundo", ] [[package]] @@ -846,15 +849,24 @@ dependencies = [ name = "oaktask" version = "0.1.0" dependencies = [ + "oakcodec", + "oakcommon", "oakcore-rs", + "oaknode", "oakotio", + "oakrender", + "oaktimeline", + "oakundo", ] [[package]] name = "oaktimeline" version = "0.1.0" dependencies = [ + "oakcommon", "oakcore-rs", + "oaknode", + "oakundo", ] [[package]] diff --git a/src/engine/rust/src/bridge/audio.rs b/src/engine/rust/src/bridge/audio.rs index 239167b91..6e8a3d7a0 100644 --- a/src/engine/rust/src/bridge/audio.rs +++ b/src/engine/rust/src/bridge/audio.rs @@ -66,14 +66,9 @@ pub type SourceClip = oakaudio::ffi::sync::SourceClip; -/// Opaque mirror of the oakaudio recording-params POD -/// (`include/audio/manager.h`). The facade passes the engine's -/// `OakEngineEncodingParams*` through untouched — both are the same C ABI -/// mirror of `olive::EncodingParams`. -#[repr(C)] -pub struct EncodingParams { - _opaque: [u8; 0], -} +/// `oakaudio` recording-params POD — single-lib unification: aliases the +/// oakaudio crate's type (itself the oakcodec `oakcodec_encoding_params`). +pub type EncodingParams = oakaudio::bridge::codec::EncodingParams; extern "C" { pub fn oakcore_audioparams_create(sample_rate: c_int, channel_layout: u64, format: c_int) -> *mut c_void; @@ -188,17 +183,7 @@ pub fn oakaudio_manager_start_recording( error_buf: *mut c_char, error_buf_size: c_int, ) -> c_int { - unsafe { oakaudio_start_recording_extern(_self, params, error_buf, error_buf_size) } -} - -extern "C" { - #[link_name = "oakaudio_manager_start_recording"] - pub fn oakaudio_start_recording_extern( - _self: CHandle, - params: *const EncodingParams, - error_buf: *mut c_char, - error_buf_size: c_int, - ) -> c_int; + unsafe { oakaudio::ffi::manager::oakaudio_manager_start_recording(_self, params, error_buf, error_buf_size) } } /// Direct call into the `oakaudio` crate (single-lib unification; the diff --git a/src/engine/rust/src/bridge/codec.rs b/src/engine/rust/src/bridge/codec.rs index bdc6bb5bc..e0af2fe3d 100644 --- a/src/engine/rust/src/bridge/codec.rs +++ b/src/engine/rust/src/bridge/codec.rs @@ -52,97 +52,21 @@ use std::ffi::{c_char, c_int}; use crate::handle::CHandle; /// `include/codec/encoder.h` — the encoding-params POD, a complete mirror -/// of `olive::EncodingParams`. The facade's `OakEngineEncodingParams` -/// handle is a heap box over exactly this struct, so every engine getter/ -/// setter reads/writes a field and `encoder_init`/recording can consume -/// the pointer directly. -#[repr(C)] -#[derive(Clone, Copy)] -pub struct EncodingParamsPOD { - /// Output filename (NUL-terminated). - pub filename: [c_char; 1024], - /// Output container format id. - pub format: c_int, - /// Whether video is enabled (1/0). - pub video_enabled: c_int, - /// Video codec id. - pub video_codec: c_int, - /// Video width in pixels. - pub video_width: c_int, - /// Video height in pixels. - pub video_height: c_int, - /// Frame duration numerator. - pub video_time_base_num: c_int, - /// Frame duration denominator. - pub video_time_base_den: c_int, - /// Delivery pixel format (`OakPixelFormat`). - pub video_pixel_format: c_int, - /// Interlacing mode (`OAKCODEC_INTERLACE_*`). - pub video_interlacing: c_int, - /// Pixel aspect ratio numerator. - pub video_pixel_aspect_num: c_int, - /// Pixel aspect ratio denominator. - pub video_pixel_aspect_den: c_int, - /// Video bit rate in bits per second (0 = codec default). - pub video_bit_rate: i64, - /// Minimum video bit rate. - pub video_min_bit_rate: i64, - /// Maximum video bit rate. - pub video_max_bit_rate: i64, - /// Video buffer size in bytes. - pub video_buffer_size: i64, - /// Encoder threads (0 = auto). - pub video_threads: c_int, - /// Encoded pixel format name ("yuv420p", NUL-terminated). - pub video_pix_fmt: [c_char; 64], - /// Whether video is an image sequence (1/0). - pub video_is_image_sequence: c_int, - /// Scaling method (`OAKCODEC_ENCODING_SCALING_*`). - pub video_scaling_method: c_int, - /// Whether audio is enabled (1/0). - pub audio_enabled: c_int, - /// Audio codec id. - pub audio_codec: c_int, - /// Audio sample rate in Hz. - pub audio_sample_rate: c_int, - /// Audio channel layout mask. - pub audio_channel_layout: u64, - /// Audio sample format (`SampleFormat::Format`). - pub audio_sample_format: c_int, - /// Audio bit rate in bits per second. - pub audio_bit_rate: i64, - /// Whether subtitles are enabled (1/0). - pub subtitles_enabled: c_int, - /// Subtitle codec id. - pub subtitles_codec: c_int, - /// Whether subtitles are a sidecar file (1/0). - pub subtitles_are_sidecar: c_int, - /// Sidecar subtitle format (`ExportFormat::Format`). - pub subtitles_sidecar_format: c_int, - /// Output OCIO colorspace name (empty = reference space). - pub color_transform_output: [c_char; 256], - /// Export length in seconds (rational), numerator. - pub export_length_num: c_int, - /// Export length in seconds (rational), denominator. - pub export_length_den: c_int, - /// Whether a custom export range is set (1/0). - pub has_custom_range: c_int, - /// Custom range in point numerator (seconds). - pub custom_range_in_num: i64, - /// Custom range in point denominator (seconds). - pub custom_range_in_den: i64, - /// Custom range out point numerator (seconds). - pub custom_range_out_num: i64, - /// Custom range out point denominator (seconds). - pub custom_range_out_den: i64, +/// `olive::EncodingParams` — single-lib unification: aliases the oakcodec +/// crate's POD (`oakcodec_encoding_params`, identical `#[repr(C)]` layout; +/// the engine's `OakEngineEncodingParams` handle is a heap box over exactly +/// this struct, so every engine getter/setter reads/writes a field and +/// `encoder_init`/recording can consume the pointer directly). +pub type EncodingParamsPOD = oakcodec::ffi::encoder::oakcodec_encoding_params; + + +/// Zeroed encoding-params POD (all fields 0 / NUL). The codec crate's +/// struct has no zeroed constructor; this facade helper provides it. +pub fn zeroed_encoding_params() -> EncodingParamsPOD { + // All-C fields: all-zero is a valid value. + unsafe { std::mem::zeroed() } } -impl EncodingParamsPOD { - /// Zeroed POD: all tracks disabled, format unset. - pub fn zeroed() -> Self { - unsafe { std::mem::zeroed() } - } -} /// Direct call into the `oakcodec` crate (single-lib unification; the /// `#[no_mangle]` export stays for the external C ABI). @@ -298,12 +222,7 @@ pub fn oakcodec_encoding_generate_matrix( /// Kept as a link-time `extern "C"` declaration against the frozen module /// C ABI. pub fn oakcodec_encoder_init(params: *const EncodingParamsPOD) -> CHandle { - unsafe { oakcodec_encoder_init_extern(params) } -} - -extern "C" { - #[link_name = "oakcodec_encoder_init"] - pub fn oakcodec_encoder_init_extern(params: *const EncodingParamsPOD) -> CHandle; + unsafe { oakcodec::ffi::encoder::oakcodec_encoder_init(params) } } /// Direct call into the `oakcodec` crate (single-lib unification; the diff --git a/src/engine/rust/src/bridge/task.rs b/src/engine/rust/src/bridge/task.rs index dfc22aac0..84d66ef0e 100644 --- a/src/engine/rust/src/bridge/task.rs +++ b/src/engine/rust/src/bridge/task.rs @@ -256,24 +256,15 @@ pub fn oaktask_create_precache(footage: CHandle, index: c_int, sequence: CHandle /// Direct call into the `oaktask` crate (single-lib unification; the /// `#[no_mangle]` export stays for the external C ABI). -/// `oaktask_create_export` — crosses the encoding-params C ABI POD -/// (the facade keeps its own POD mirror). Kept as a link-time -/// `extern "C"` declaration against the frozen module C ABI. +/// `oaktask_create_export` — direct call into the `oaktask` crate +/// (single-lib unification; the encoding-params POD is the shared +/// oakcodec type). pub fn oaktask_create_export( viewer: CHandle, color_manager: CHandle, params: *const crate::bridge::codec::EncodingParamsPOD, ) -> CHandle { - unsafe { oaktask_create_export_extern(viewer, color_manager, params) } -} - -extern "C" { - #[link_name = "oaktask_create_export"] - pub fn oaktask_create_export_extern( - viewer: CHandle, - color_manager: CHandle, - params: *const crate::bridge::codec::EncodingParamsPOD, - ) -> CHandle; + unsafe { oaktask::ffi::project::oaktask_create_export(viewer, color_manager, params) } } /// Direct call into the `oaktask` crate (single-lib unification; the diff --git a/src/engine/rust/src/codec.rs b/src/engine/rust/src/codec.rs index 01dfe7e0e..4b7d86a2d 100644 --- a/src/engine/rust/src/codec.rs +++ b/src/engine/rust/src/codec.rs @@ -38,7 +38,7 @@ use std::collections::HashMap; use std::ffi::{c_char, c_int, c_void}; use crate::bridge::codec as k; -use crate::bridge::codec::EncodingParamsPOD; +use crate::bridge::codec::{EncodingParamsPOD, zeroed_encoding_params}; use crate::common::OakVideoParamsPod; use crate::error::{Error, Result}; use crate::handle::{guard, guard_int, string_result}; @@ -59,7 +59,7 @@ struct ParamsBox { impl ParamsBox { fn new() -> Self { - let mut pod = EncodingParamsPOD::zeroed(); + let mut pod = zeroed_encoding_params(); pod.format = -1; // unset (POD 0 is a valid format, DNxHD) ParamsBox { pod, @@ -89,23 +89,15 @@ unsafe fn params_mut(ptr: *mut OakEngineEncodingParams) -> Result<&'static mut P } /// Read a NUL-terminated fixed array field as a `String`. -fn field_str(field: &[c_char]) -> String { - let bytes: Vec = field - .iter() - .take_while(|c| **c != 0) - .map(|c| *c as u8) - .collect(); +fn field_str(field: &[u8]) -> String { + let bytes: Vec = field.iter().take_while(|c| **c != 0).copied().collect(); String::from_utf8_lossy(&bytes).into_owned() } /// Write a string into a fixed array field (truncated, NUL-terminated). -fn write_field(field: &mut [c_char], value: &str) { +fn write_field(field: &mut [u8], value: &str) { for (i, slot) in field.iter_mut().enumerate() { - *slot = if i < value.len() { - value.as_bytes()[i] as c_char - } else { - 0 - }; + *slot = if i < value.len() { value.as_bytes()[i] } else { 0 }; } } @@ -1075,7 +1067,7 @@ pub unsafe extern "C" fn oakengine_encoding_start_audio_recording( } let rc = crate::bridge::audio::oakaudio_manager_start_recording( m, - &p.pod as *const EncodingParamsPOD as *const c_void as *const crate::bridge::audio::EncodingParams, + &p.pod as *const oakaudio::bridge::codec::EncodingParams, errbuf, errbuf_size, ); diff --git a/src/engine/rust/src/task.rs b/src/engine/rust/src/task.rs index 0a09416a8..15108e8ec 100644 --- a/src/engine/rust/src/task.rs +++ b/src/engine/rust/src/task.rs @@ -43,7 +43,7 @@ use std::ffi::{c_char, c_int, c_void}; use std::sync::{Mutex, OnceLock}; use std::time::{SystemTime, UNIX_EPOCH}; -use crate::bridge::codec::EncodingParamsPOD; +use crate::bridge::codec::{EncodingParamsPOD, zeroed_encoding_params}; use crate::bridge::node as n; use crate::bridge::task as t; use crate::codec::OakEngineEncodingParams; @@ -622,7 +622,7 @@ pub unsafe extern "C" fn oakengine_task_create_export( /// them is behaviorally identical to the original for the export task; all /// other POD fields stay zeroed. fn export_params_pod(params: *const OakEngineEncodingParams) -> Result { - let mut pod = EncodingParamsPOD::zeroed(); + let mut pod = zeroed_encoding_params(); // filename (two-stage; writes NUL-terminated into `buf`) let mut buf = [0 as c_char; 1024]; @@ -633,7 +633,7 @@ fn export_params_pod(params: *const OakEngineEncodingParams) -> Result` value /// handle, so a handle can cross any module boundary by value. #[repr(C)] -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CHandle { /// Opaque box pointer. pub ctx: *mut c_void, diff --git a/src/plugin/rust/Cargo.lock b/src/plugin/rust/Cargo.lock index f37bfea15..b6acdb193 100644 --- a/src/plugin/rust/Cargo.lock +++ b/src/plugin/rust/Cargo.lock @@ -2,6 +2,137 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.13.1", + "cexpr", + "clang-sys", + "itertools", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.3", + "shlex 1.3.0", + "syn 2.0.119", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" +dependencies = [ + "serde_core", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0e56a716f1e132ff6bf4bdac1c944a3fcdc1cae65f70a4a2a1ac3b401d2d1f" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "cc" version = "1.4.2" @@ -9,7 +140,153 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e" dependencies = [ "find-msvc-tools", - "shlex", + "shlex 2.0.1", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "clang-sys" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "codespan-reporting" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "either" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fax" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" + +[[package]] +name = "ffmpeg-next" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6380599799e175191eb7ffe82c97f36a2a90a36cbc54c738a903e5287d7f516a" +dependencies = [ + "bitflags 2.13.1", + "ffmpeg-sys-next", + "libc", +] + +[[package]] +name = "ffmpeg-sys-next" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b939bf79dd5949412a4b81cfe21a07f48ea21b47fcbb5f57816c8c2de5ae30b" +dependencies = [ + "bindgen", + "cc", + "libc", + "num_cpus", + "pkg-config", + "vcpkg", ] [[package]] @@ -18,20 +295,1363 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "futures-core" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" + +[[package]] +name = "futures-task" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" + +[[package]] +name = "futures-util" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + +[[package]] +name = "glow" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-alloc" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45cf04b2726f02df5508c6de726acdc90cdf97ac771a9a0ffd8ba10a6e696bf9" +dependencies = [ + "bitflags 2.13.1", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2bbed164dd10ed526c2e4fe3e721ca4a71c61730e5aafac6844b417b3227058" +dependencies = [ + "bitflags 2.13.1", +] + +[[package]] +name = "gpu-allocator" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" +dependencies = [ + "log", + "presser", + "thiserror 1.0.69", + "windows", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags 2.13.1", + "gpu-descriptor-types", + "hashbrown 0.15.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.13.1", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "tiff", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "js-sys" +version = "0.3.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "metal" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" +dependencies = [ + "bitflags 2.13.1", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", + "paste", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "naga" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b977c445f26e49757f9aca3631c3b8b836942cb278d69a92e7b80d3b24da632" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags 2.13.1", + "cfg_aliases", + "codespan-reporting", + "half", + "hashbrown 0.15.5", + "hexf-parse", + "indexmap", + "log", + "num-traits", + "once_cell", + "rustc-hash 1.1.0", + "spirv", + "strum", + "thiserror 2.0.20", + "unicode-ident", +] + +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "oakcodec" +version = "0.1.0" +dependencies = [ + "ffmpeg-next", + "oakcore-rs", +] + +[[package]] +name = "oakcommon" +version = "0.1.0" +dependencies = [ + "image", + "log", + "oakcore-rs", + "ocio-rs", + "quick-xml", +] + [[package]] name = "oakcore-rs" version = "0.1.0" +[[package]] +name = "oaknode" +version = "0.1.0" +dependencies = [ + "oakcodec", + "oakcommon", + "oakcore-rs", + "oakundo", +] + [[package]] name = "oakplugin" version = "0.1.0" dependencies = [ "cc", "oakcore-rs", + "oaknode", + "oakrender", + "oakundo", ] +[[package]] +name = "oakrender" +version = "0.1.0" +dependencies = [ + "oakcommon", + "oakcore-rs", + "ocio-rs", + "wgpu", +] + +[[package]] +name = "oakundo" +version = "0.1.0" +dependencies = [ + "oakcore-rs", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "ocio-rs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3492534019b59e29dba06014f907dd12824537ed4d293d4108c4bfc669de7fd" +dependencies = [ + "ocio-sys", + "thiserror 1.0.69", +] + +[[package]] +name = "ocio-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63251d72d848de5eda39d59cd6490260cf031738ebd518ea37d76b5aae614ec" +dependencies = [ + "cc", + "cmake", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" + +[[package]] +name = "pxfm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "range-alloc" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.1", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "shlex" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.13.1", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.119", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +dependencies = [ + "thiserror-impl 2.0.20", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasm-bindgen" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.119", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "wgpu" +version = "25.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8fb398f119472be4d80bc3647339f56eb63b2a331f6a3d16e25d8144197dd9" +dependencies = [ + "arrayvec", + "bitflags 2.13.1", + "cfg_aliases", + "document-features", + "hashbrown 0.15.5", + "js-sys", + "log", + "naga", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "25.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7b882196f8368511d613c6aeec80655160db6646aebddf8328879a88d54e500" +dependencies = [ + "arrayvec", + "bit-set", + "bit-vec", + "bitflags 2.13.1", + "cfg_aliases", + "document-features", + "hashbrown 0.15.5", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 2.0.20", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", + "wgpu-core-deps-windows-linux-android", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core-deps-apple" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd488b3239b6b7b185c3b045c39ca6bf8af34467a4c5de4e0b1a564135d093d" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-emscripten" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09ad7aceb3818e52539acc679f049d3475775586f3f4e311c30165cf2c00445" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cba5fb5f7f9c98baa7c889d444f63ace25574833df56f5b817985f641af58e46" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-hal" +version = "25.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f968767fe4d3d33747bbd1473ccd55bf0f6451f55d733b5597e67b5deab4ad17" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 2.13.1", + "block", + "bytemuck", + "cfg-if", + "cfg_aliases", + "core-graphics-types", + "glow", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "hashbrown 0.15.5", + "js-sys", + "khronos-egl", + "libc", + "libloading", + "log", + "metal", + "naga", + "ndk-sys", + "objc", + "ordered-float", + "parking_lot", + "portable-atomic", + "profiling", + "range-alloc", + "raw-window-handle", + "renderdoc-sys", + "smallvec", + "thiserror 2.0.20", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "windows", + "windows-core", +] + +[[package]] +name = "wgpu-types" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aa49460c2a8ee8edba3fca54325540d904dd85b2e086ada762767e17d06e8bc" +dependencies = [ + "bitflags 2.13.1", + "bytemuck", + "js-sys", + "log", + "thiserror 2.0.20", + "web-sys", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "xml-rs" +version = "0.8.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7" + +[[package]] +name = "zerocopy" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zune-core" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b" + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] diff --git a/src/plugin/rust/Cargo.toml b/src/plugin/rust/Cargo.toml index ec79b750b..174c1318f 100644 --- a/src/plugin/rust/Cargo.toml +++ b/src/plugin/rust/Cargo.toml @@ -18,13 +18,18 @@ panic = "unwind" [dependencies] # 共享 ABI 类型(CHandle 等,单库化统一,见 docs/zh/plans/riir/single-lib.md)。 +# 模块桥改直接 Rust 调用(单库化,见 docs/zh/plans/riir/single-lib.md); # 只允许零依赖起步。确需引入的 crate 必须在 README 登记理由。 oakcore-rs = { path = "../../oakcore-rs" } +oakundo = { path = "../../undo/rust" } +oaknode = { path = "../../node/rust" } +oakrender = { path = "../../render/rust" } [features] -# oakrender 测试桩(库内 no_mangle 符号 + 状态访问器):cargo test -# --features test-stubs 时桥直连桩,像素路径可在无 liboakrender 的 -# 环境下跑通;真实链路仍走 dlsym 解析。 +# 测试桩(库内状态访问器):cargo test --features test-stubs 时桥直连 +# 桩,像素路径可在无真实 GL/GPU 的环境下跑通。桩的 C ABI 符号不再 +# no_mangle(真实 oaknode/oakrender/oakundo 已作为 path 依赖链接, +# 避免重复符号);真实链路走直接 Rust 调用。 test-stubs = [] [build-dependencies] diff --git a/src/plugin/rust/src/bridge/mod.rs b/src/plugin/rust/src/bridge/mod.rs index f3853ccaf..d3a0fb844 100644 --- a/src/plugin/rust/src/bridge/mod.rs +++ b/src/plugin/rust/src/bridge/mod.rs @@ -14,64 +14,21 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! bridge:oak 其余模块的 C ABI 导入。 +//! bridge:oak 其余模块的 C ABI 调用——直接 Rust 调用(单库化,见 +//! `docs/zh/plans/riir/single-lib.md`)。 //! -//! 每个子模块(node/render/undo)只声明对应公共头的子集并包一层 -//! 极薄的 safe 封装。链接在模块 dylib 装配时完成(liboaknode/ -//! liboakrender/liboakundo)。 +//! 每个子模块(node/render/undo)调用对应 crate 的 `ffi` 导出(同名 +//! `#[no_mangle]` 符号仍从 dylib 导出供外部 C ABI 使用;内部调用绕过 +//! 它们)。句柄全部是共享的 [`crate::handle::CHandle`]。 //! //! ## 双态实现(所有子模块统一) //! -//! - 默认:`dlsym(RTLD_DEFAULT)` 运行时解析(本模块被 force_load -//! 进宿主进程,符号在全局作用域;cargo test 无这些库时符号缺失 -//! → 可解释错误,测试可走通至桥边界); -//! - `--features test-stubs`:库内 no_mangle 桩 + 状态访问器 -//! (各子模块的 [`node::stub`]/[`render::stub`]/[`undo::stub`])—— -//! 桥路径在 cargo test 里全链路可跑。两种形态的调用面完全一致。 -//! -//! [`dlsym`] 是三个子模块共享的解析实现(macOS/Linux 的 RTLD_DEFAULT -//! 取值不同;函数指针按调用方签名转换)。 +//! - 默认:直接调用 oaknode/oakrender/oakundo 的 ffi; +//! - `--features test-stubs`:桥走库内状态桩(各子模块的 +//! [`node::stub`]/[`render::stub`]/[`undo::stub`],纯 Rust、无 +//! `#[no_mangle]`,与真实 crate 的导出不冲突)——像素路径可在无 +//! 真实 GL/GPU 的环境下跑通。两种形态的调用面完全一致。 pub mod node; pub mod render; pub mod undo; - -/// 共享的 dlsym 运行时解析(`#[cfg(not(feature = "test-stubs"))]`)。 -#[cfg(not(feature = "test-stubs"))] -pub(crate) mod dlsym { - use std::ffi::{c_char, c_void}; - - /// RTLD_DEFAULT(macOS: -2;Linux: 0)。 - #[cfg(target_os = "macos")] - pub(crate) const RTLD_DEFAULT: *mut c_void = -2isize as *mut c_void; - #[cfg(target_os = "linux")] - pub(crate) const RTLD_DEFAULT: *mut c_void = 0isize as *mut c_void; - - extern "C" { - fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void; - } - - /// 解析全局作用域符号;缺失返回 None。 - pub(crate) fn resolve(name: &str) -> Option<*mut c_void> { - let c = std::ffi::CString::new(name).ok()?; - let p = unsafe { dlsym(RTLD_DEFAULT, c.as_ptr()) }; - if p.is_null() { - None - } else { - Some(p) - } - } - - /// 解析并按签名调用;符号缺失返回 None。 - /// - /// # Safety - /// 调用方保证 `T` 与符号的真实函数类型一致。 - pub(crate) fn call(name: &str, f: impl FnOnce(T) -> R) -> Option - where - T: Copy, - { - let p = resolve(name)?; - let f_ptr: T = unsafe { std::mem::transmute_copy(&p) }; - Some(f(f_ptr)) - } -} diff --git a/src/plugin/rust/src/bridge/node.rs b/src/plugin/rust/src/bridge/node.rs index cc89c04c9..023e95885 100644 --- a/src/plugin/rust/src/bridge/node.rs +++ b/src/plugin/rust/src/bridge/node.rs @@ -28,10 +28,11 @@ //! //! ## 双态实现(同 [`crate::bridge::render`]) //! -//! - 默认:`dlsym(RTLD_DEFAULT)` 运行时解析(cargo test 无 liboaknode -//! 时符号缺失 → 错误码/空句柄,桥路径可解释地失败); -//! - `--features test-stubs`:库内桩([`stub`])——节点值、undo 命令 -//! 全链路可在 cargo test 跑通。两种形态的调用面完全一致。 +//! - 默认:直接 Rust 调用 oaknode 的 `ffi`(单库化,见 +//! `docs/zh/plans/riir/single-lib.md`); +//! - `--features test-stubs`:库内状态桩([`stub`],纯 Rust、无 +//! `#[no_mangle]`,与真实 oaknode 的导出不冲突)——节点值、undo +//! 命令全链路可在 cargo test 跑通。两种形态的调用面完全一致。 use std::ffi::c_char; @@ -177,9 +178,7 @@ pub(crate) unsafe fn node_from_identity(id: usize) -> NodeHandle { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(usize) -> NodeHandle; - crate::bridge::dlsym::call::("oaknode_node_from_identity", |f| unsafe { f(id) }) - .unwrap_or_else(CHandle::null) + unsafe { oaknode::ffi::node::oaknode_node_from_identity(id) } } } @@ -201,11 +200,14 @@ pub(crate) unsafe fn set_input_undoable( } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(NodeHandle, *const c_char, *const Value, *mut CommandHandle) -> i32; - crate::bridge::dlsym::call::("oaknode_node_set_input_undoable", |f| { - unsafe { f(node, input, value, out) } - }) - .unwrap_or(-30001) + unsafe { + oaknode::ffi::node::oaknode_node_set_input_undoable( + node, + input, + value as *const oaknode::value::OakNodeValue, + out, + ) + } } } @@ -226,11 +228,14 @@ pub(crate) unsafe fn set_input_string_undoable( } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(NodeHandle, *const c_char, *const c_char, *mut CommandHandle) -> i32; - crate::bridge::dlsym::call::("oaknode_node_set_input_string_undoable", |f| { - unsafe { f(node, input, value, out) } - }) - .unwrap_or(-30001) + unsafe { + oaknode::ffi::node::oaknode_node_set_input_string_undoable( + node, + input, + value, + out, + ) + } } } @@ -389,16 +394,16 @@ pub mod stub { } } -// ---- 默认模式单测(无桩;dlsym 缺失的可解释失败路径)---------------------- +// ---- 默认模式单测(无桩;空/NULL 句柄经真实 crate 的可解释失败路径)---------------------- #[cfg(all(test, not(feature = "test-stubs")))] mod tests { use super::*; - /// cargo test 无 liboaknode:符号缺失 → 空句柄/负错误码;空句柄 - /// 与空指针参数被拒。 + /// 空/NULL 句柄经真实 oaknode 容错:未登记身份 → 空句柄;空句柄 + /// 与空指针参数被拒(OAKNODE_E_INVALID)。 #[test] - fn dlsym_missing_error_paths() { + fn empty_handle_error_paths() { let mut cmd = CommandHandle::null(); unsafe { assert!(node_from_identity(0xDEAD).is_null(), "未登记身份 → 空句柄"); diff --git a/src/plugin/rust/src/bridge/render.rs b/src/plugin/rust/src/bridge/render.rs index 855ca0d36..1cdb444bf 100644 --- a/src/plugin/rust/src/bridge/render.rs +++ b/src/plugin/rust/src/bridge/render.rs @@ -26,45 +26,19 @@ //! //! ## 双态实现 //! -//! - 默认:`dlsym(RTLD_DEFAULT)` 运行时解析(本模块被 force_load -//! 进宿主进程,符号在全局作用域;cargo test 无 liboakrender 时 -//! 符号缺失 → 可解释错误,测试可走通至渲染边界); -//! - `--features test-stubs`:库内 no_mangle 桩 + 状态访问器 -//! ([`stub`])——像素路径在 cargo test 里全链路可跑。 -//! 两种形态的调用面(`texture_get_frame` 等)完全一致。 +//! - 默认:直接 Rust 调用 oakrender 的 `ffi`(单库化,见 +//! `docs/zh/plans/riir/single-lib.md`); +//! - `--features test-stubs`:库内状态桩 + 状态访问器([`stub`],纯 +//! Rust、无 `#[no_mangle]`,与真实 oakrender 的导出不冲突)——像素 +//! 路径在 cargo test 里全链路可跑。两种形态的调用面 +//! (`texture_get_frame` 等)完全一致。 use std::ffi::c_void; -/// oakrender 视频参数(include/render/renderer.h:78,字段逐字一致)。 -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -pub struct VideoParams { - /// 宽。 - pub width: i32, - /// 高。 - pub height: i32, - /// 帧时长分子(如 1001/30000 秒)。 - pub time_base_num: i32, - /// 帧时长分母。 - pub time_base_den: i32, - /// `olive::PixelFormat::Format`(invalid=-1, u8=0, u10=1, u16=2, - /// f16=3, f32=4)。 - pub format: i32, - /// 像素比分子。 - pub pixel_aspect_num: i32, - /// 像素比分母。 - pub pixel_aspect_den: i32, - /// `olive::VideoParams::Interlacing`。 - pub interlacing: i32, - /// `olive::VideoParams::ColorRange`。 - pub color_range: i32, - /// 预览分辨率除数(1 = 全分辨率)。 - pub divider: i32, - /// `olive::VideoParams::Type`(0 = video)。 - pub video_type: i32, - /// 预乘 alpha(0/1)。 - pub premultiplied_alpha: i32, -} +/// `oakrender_video_params` POD — single-lib unification: aliases the +/// oakrender crate's struct (identical layout; include/render/renderer.h:78). +pub type VideoParams = oakrender::ffi::OakRenderVideoParams; + /// olive::PixelFormat::Format 的 f32 值。 pub const PIXEL_FORMAT_F32: i32 = 4; @@ -90,11 +64,7 @@ pub(crate) unsafe fn texture_get_frame(texture: TextureHandle, out: *mut FrameHa } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(TextureHandle, *mut FrameHandle) -> i32; - crate::bridge::dlsym::call::("oakrender_display_texture_get_frame", |f| { - unsafe { f(texture, out) } - }) - .unwrap_or(-1) + unsafe { oakrender::ffi::renderer::oakrender_display_texture_get_frame(texture, out) } } } @@ -106,9 +76,7 @@ pub(crate) unsafe fn texture_is_dummy(texture: TextureHandle) -> i32 { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(TextureHandle) -> i32; - crate::bridge::dlsym::call::("oakrender_display_texture_is_dummy", |f| unsafe { f(texture) }) - .unwrap_or(1) + unsafe { oakrender::ffi::renderer::oakrender_display_texture_is_dummy(texture) } } } @@ -120,9 +88,7 @@ pub(crate) unsafe fn frame_width(frame: FrameHandle) -> i32 { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(FrameHandle) -> i32; - crate::bridge::dlsym::call::("oakrender_codec_frame_width", |f| unsafe { f(frame) }) - .unwrap_or(0) + unsafe { oakrender::ffi::renderer::oakrender_codec_frame_width(frame) } } } @@ -134,9 +100,7 @@ pub(crate) unsafe fn frame_height(frame: FrameHandle) -> i32 { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(FrameHandle) -> i32; - crate::bridge::dlsym::call::("oakrender_codec_frame_height", |f| unsafe { f(frame) }) - .unwrap_or(0) + unsafe { oakrender::ffi::renderer::oakrender_codec_frame_height(frame) } } } @@ -148,9 +112,7 @@ pub(crate) unsafe fn frame_data(frame: FrameHandle) -> *mut c_void { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(FrameHandle) -> *mut c_void; - crate::bridge::dlsym::call::("oakrender_codec_frame_data", |f| unsafe { f(frame) }) - .unwrap_or(std::ptr::null_mut()) + unsafe { oakrender::ffi::renderer::oakrender_codec_frame_data(frame) } } } @@ -162,9 +124,7 @@ pub(crate) unsafe fn frame_get_params(frame: FrameHandle, out: *mut VideoParams) } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(FrameHandle, *mut VideoParams) -> i32; - crate::bridge::dlsym::call::("oakrender_codec_frame_get_params", |f| unsafe { f(frame, out) }) - .unwrap_or(-1) + unsafe { oakrender::ffi::renderer::oakrender_codec_frame_get_params(frame, out) } } } @@ -176,9 +136,7 @@ pub(crate) unsafe fn frame_allocate(frame: FrameHandle) -> i32 { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(FrameHandle) -> i32; - crate::bridge::dlsym::call::("oakrender_codec_frame_allocate", |f| unsafe { f(frame) }) - .unwrap_or(-1) + unsafe { oakrender::ffi::renderer::oakrender_codec_frame_allocate(frame) } } } @@ -190,13 +148,7 @@ pub(crate) unsafe fn frame_free(frame: *mut FrameHandle) { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(*mut FrameHandle); - if let Some(f) = crate::bridge::dlsym::call::("oakrender_codec_frame_free", |f| { - unsafe { f(frame) } - }) { - // 调用已完成;返回值忽略。 - let _ = f; - } + unsafe { oakrender::ffi::renderer::oakrender_codec_frame_free(frame) } } } @@ -211,11 +163,7 @@ pub(crate) unsafe fn frame_linesize_bytes(frame: FrameHandle) -> i32 { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(FrameHandle) -> i32; - crate::bridge::dlsym::call::("oakrender_codec_frame_linesize_bytes", |f| { - unsafe { f(frame) } - }) - .unwrap_or(0) + unsafe { oakrender::ffi::renderer::oakrender_codec_frame_linesize_bytes(frame) } } } @@ -231,11 +179,7 @@ pub(crate) unsafe fn renderer_create_dynamic(backend: *const std::ffi::c_char) - } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(*const std::ffi::c_char) -> RendererHandle; - crate::bridge::dlsym::call::("oakrender_display_renderer_create_dynamic", |f| { - unsafe { f(backend) } - }) - .unwrap_or_else(RendererHandle::null) + unsafe { oakrender::ffi::renderer::oakrender_display_renderer_create_dynamic(backend) } } } @@ -252,11 +196,7 @@ pub(crate) unsafe fn renderer_init( } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(RendererHandle, *mut std::ffi::c_void) -> i32; - crate::bridge::dlsym::call::("oakrender_display_renderer_init", |f| { - unsafe { f(renderer, gl_context) } - }) - .unwrap_or(-1) + unsafe { oakrender::ffi::renderer::oakrender_display_renderer_init(renderer, gl_context) } } } @@ -269,11 +209,7 @@ pub(crate) unsafe fn renderer_is_open_gl(renderer: RendererHandle) -> i32 { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(RendererHandle) -> i32; - crate::bridge::dlsym::call::("oakrender_display_renderer_is_open_gl", |f| { - unsafe { f(renderer) } - }) - .unwrap_or(0) + unsafe { oakrender::ffi::renderer::oakrender_display_renderer_is_open_gl(renderer) } } } @@ -287,12 +223,7 @@ pub(crate) unsafe fn renderer_destroy(renderer: *mut RendererHandle) { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(*mut RendererHandle); - if let Some(f) = crate::bridge::dlsym::call::("oakrender_display_renderer_destroy", |f| { - unsafe { f(renderer) } - }) { - let _ = f; - } + unsafe { oakrender::ffi::renderer::oakrender_display_renderer_destroy(renderer) } } } @@ -315,11 +246,11 @@ pub(crate) unsafe fn texture_create( } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(RendererHandle, *const VideoParams, *const std::ffi::c_void, i32) -> TextureHandle; - crate::bridge::dlsym::call::("oakrender_display_texture_create", |f| { - unsafe { f(renderer, params, pixels, linesize) } - }) - .unwrap_or_else(TextureHandle::null) + unsafe { + oakrender::ffi::renderer::oakrender_display_texture_create( + renderer, params, pixels, linesize, + ) + } } } @@ -332,9 +263,7 @@ pub(crate) unsafe fn texture_id(texture: TextureHandle) -> i32 { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(TextureHandle) -> i32; - crate::bridge::dlsym::call::("oakrender_display_texture_id", |f| unsafe { f(texture) }) - .unwrap_or(0) + unsafe { oakrender::ffi::renderer::oakrender_display_texture_id(texture) } } } @@ -346,11 +275,7 @@ pub(crate) unsafe fn texture_get_params(texture: TextureHandle, out: *mut VideoP } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(TextureHandle, *mut VideoParams) -> i32; - crate::bridge::dlsym::call::("oakrender_display_texture_get_params", |f| { - unsafe { f(texture, out) } - }) - .unwrap_or(-1) + unsafe { oakrender::ffi::renderer::oakrender_display_texture_get_params(texture, out) } } } @@ -368,11 +293,7 @@ pub(crate) unsafe fn texture_download( } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(TextureHandle, *mut std::ffi::c_void, i32) -> i32; - crate::bridge::dlsym::call::("oakrender_display_texture_download", |f| { - unsafe { f(texture, pixels, linesize) } - }) - .unwrap_or(-1) + unsafe { oakrender::ffi::renderer::oakrender_display_texture_download(texture, pixels, linesize) } } } @@ -385,11 +306,7 @@ pub(crate) unsafe fn texture_retain(texture: TextureHandle) -> TextureHandle { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(TextureHandle) -> TextureHandle; - crate::bridge::dlsym::call::("oakrender_display_texture_retain", |f| { - unsafe { f(texture) } - }) - .unwrap_or_else(TextureHandle::null) + unsafe { oakrender::ffi::renderer::oakrender_display_texture_retain(texture) } } } @@ -402,12 +319,7 @@ pub(crate) unsafe fn texture_free(texture: *mut TextureHandle) { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(*mut TextureHandle); - if let Some(f) = crate::bridge::dlsym::call::("oakrender_display_texture_free", |f| { - unsafe { f(texture) } - }) { - let _ = f; - } + unsafe { oakrender::ffi::renderer::oakrender_display_texture_free(texture) } } } @@ -425,11 +337,7 @@ pub(crate) unsafe fn texture_upload( } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(TextureHandle, *const std::ffi::c_void, i32) -> i32; - crate::bridge::dlsym::call::("oakrender_display_texture_upload", |f| { - unsafe { f(texture, pixels, linesize) } - }) - .unwrap_or(-1) + unsafe { oakrender::ffi::renderer::oakrender_display_texture_upload(texture, pixels, linesize) } } } @@ -449,11 +357,11 @@ pub(crate) unsafe fn renderer_download_from_texture( } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(RendererHandle, i32, *const VideoParams, *mut std::ffi::c_void, i32) -> i32; - crate::bridge::dlsym::call::("oakrender_display_renderer_download_from_texture", |f| { - unsafe { f(renderer, texture_id, params, dst, linesize) } - }) - .unwrap_or(-1) + unsafe { + oakrender::ffi::renderer::oakrender_display_renderer_download_from_texture( + renderer, texture_id, params, dst, linesize, + ) + } } } @@ -949,26 +857,27 @@ pub mod stub { } } -// ---- 默认模式单测(无桩;dlsym 缺失的可解释失败路径)---------------------- +// ---- 默认模式单测(无桩;空/NULL 句柄经真实 crate 的可解释失败路径)---------------------- #[cfg(all(test, not(feature = "test-stubs")))] mod tests { use super::*; - /// cargo test 无 liboakrender:符号缺失 → 明确错误码/空指针/占位 - /// 判定(1),全部生命周期函数对空句柄容错不崩。 + /// 空/NULL 句柄经真实 oakrender 全部容错不崩:返回 + /// `OAKRENDER_E_INVALID`(-70001)或空值。 #[test] - fn dlsym_missing_error_paths() { + fn empty_handle_error_paths() { let mut frame = FrameHandle::null(); let tex = TextureHandle::null(); unsafe { - assert_eq!(texture_get_frame(tex, &mut frame), -1); - assert_eq!(texture_is_dummy(tex), 1, "符号缺失 → 视为占位"); + assert_eq!(texture_get_frame(tex, &mut frame), -70001); + // 空句柄不是占位纹理:真实 oakrender 返回 0。 + assert_eq!(texture_is_dummy(tex), 0); assert_eq!(frame_width(frame), 0); assert_eq!(frame_height(frame), 0); assert!(frame_data(frame).is_null()); - assert_eq!(frame_get_params(frame, &mut VideoParams::default()), -1); - assert_eq!(frame_allocate(frame), -1); + assert_eq!(frame_get_params(frame, &mut VideoParams::default()), -70001); + assert_eq!(frame_allocate(frame), -70001); frame_free(&mut frame); // 空句柄 no-op frame_free(std::ptr::null_mut()); // NULL no-op } diff --git a/src/plugin/rust/src/bridge/undo.rs b/src/plugin/rust/src/bridge/undo.rs index 504e6cc1b..e99cc91a9 100644 --- a/src/plugin/rust/src/bridge/undo.rs +++ b/src/plugin/rust/src/bridge/undo.rs @@ -27,8 +27,10 @@ //! //! ## 双态实现(同 [`crate::bridge::render`]) //! -//! - 默认:`dlsym(RTLD_DEFAULT)` 运行时解析; -//! - `--features test-stubs`:库内桩([`stub`])——命令表 + undo/redo +//! - 默认:直接 Rust 调用 oakundo 的 `ffi`(单库化,见 +//! `docs/zh/plans/riir/single-lib.md`); +//! - `--features test-stubs`:库内状态桩([`stub`],纯 Rust、无 +//! `#[no_mangle]`,与真实 oakundo 的导出不冲突)——命令表 + undo/redo //! 语义在 cargo test 里全链路可跑。两种形态的调用面完全一致。 use crate::handle::CHandle; @@ -46,9 +48,7 @@ pub(crate) unsafe fn command_init_multi() -> CommandHandle { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn() -> CommandHandle; - crate::bridge::dlsym::call::("oakundo_command_init_multi", |f| unsafe { f() }) - .unwrap_or_else(CHandle::null) + unsafe { oakundo::ffi::command::oakundo_command_init_multi() } } } @@ -60,9 +60,7 @@ pub(crate) unsafe fn command_redo_now(command: CommandHandle) -> i32 { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(CommandHandle) -> i32; - crate::bridge::dlsym::call::("oakundo_command_redo_now", |f| unsafe { f(command) }) - .unwrap_or(-40001) + unsafe { oakundo::ffi::command::oakundo_command_redo_now(command) } } } @@ -74,11 +72,7 @@ pub(crate) unsafe fn command_multi_add_child(multi: CommandHandle, child: Comman } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(CommandHandle, CommandHandle) -> i32; - crate::bridge::dlsym::call::("oakundo_command_multi_add_child", |f| unsafe { - f(multi, child) - }) - .unwrap_or(-40001) + unsafe { oakundo::ffi::command::oakundo_command_multi_add_child(multi, child) } } } @@ -90,12 +84,7 @@ pub(crate) unsafe fn command_free(command: *mut CommandHandle) { } #[cfg(not(feature = "test-stubs"))] { - type F = unsafe fn(*mut CommandHandle); - if let Some(f) = crate::bridge::dlsym::call::("oakundo_command_free", |f| { - unsafe { f(command) } - }) { - let _ = f; - } + unsafe { oakundo::ffi::command::oakundo_command_free(command) } } } @@ -364,23 +353,23 @@ pub mod stub { } } -// ---- 默认模式单测(无桩;dlsym 缺失的可解释失败路径)---------------------- +// ---- 默认模式单测(无桩;空/NULL 句柄经真实 crate 的可解释失败路径)---------------------- #[cfg(all(test, not(feature = "test-stubs")))] mod tests { use super::*; - /// cargo test 无 liboakundo:符号缺失 → 空句柄/负错误码,命令 - /// 生命周期函数对空句柄/NULL 容错不崩。 + /// 空/NULL 句柄经真实 oakundo 全部容错不崩:`init_multi` 产生 + /// 真实命令,空句柄操作返回 `OAKUNDO_E_INVALID`(-20001)。 #[test] - fn dlsym_missing_error_paths() { + fn empty_handle_error_paths() { unsafe { let mut m = command_init_multi(); - assert!(m.is_null(), "无 liboakundo 时 init_multi 应返回空句柄"); - assert_eq!(command_redo_now(CommandHandle::null()), -40001); + assert!(!m.is_null(), "init_multi 应产生真实命令句柄"); + assert_eq!(command_redo_now(CommandHandle::null()), -20001); assert_eq!( command_multi_add_child(CommandHandle::null(), CommandHandle::null()), - -40001 + -20001 ); command_free(&mut m); // 空句柄 no-op command_free(std::ptr::null_mut()); // NULL no-op diff --git a/src/plugin/rust/tests/ffi_instance_test.rs b/src/plugin/rust/tests/ffi_instance_test.rs index cbd670568..909ec4545 100644 --- a/src/plugin/rust/tests/ffi_instance_test.rs +++ b/src/plugin/rust/tests/ffi_instance_test.rs @@ -208,7 +208,9 @@ fn instance_render_one_frame() { }); } -/// render 无桩(默认构建):桥符号缺失 → 明确失败码而非崩溃。 +/// render 默认构建(真实 oakrender 链接):NULL 输出纹理在触达 +/// oakrender 之前被插件层校验拒绝(E_INVALID),不崩溃。真实纹理 +/// 路径需要 GL/GPU,属集成测试范畴。 #[cfg(not(feature = "test-stubs"))] #[test] fn instance_render_one_frame() { @@ -217,8 +219,8 @@ fn instance_render_one_frame() { if h.is_null() { return; } - let dst = fake_texture(0xA1); - assert_eq!(unsafe { oakplugin_instance_render(h, dst, CHandle::null(), 0.0) }, -90003); + let dst = CHandle::null(); + assert_eq!(unsafe { oakplugin_instance_render(h, dst, CHandle::null(), 0.0) }, -90001); unsafe { oakplugin_instance_free(&mut h) }; }); } diff --git a/src/plugin/rust/tests/render_driver_test.rs b/src/plugin/rust/tests/render_driver_test.rs index d9245a400..c99aef956 100644 --- a/src/plugin/rust/tests/render_driver_test.rs +++ b/src/plugin/rust/tests/render_driver_test.rs @@ -207,11 +207,12 @@ fn render_job_graceful_without_bridge() { if h.is_null() { return; } - let dst = fake_texture(0xA1); + // NULL 输出纹理在触达 oakrender 前被插件层校验拒绝(E_INVALID)。 + let dst = CHandle::null(); let r = unsafe { oakplugin_instance_render_job(h, dst, 0.0, 0, 0, std::ptr::null(), CHandle::null(), std::ptr::null(), 0, std::ptr::null(), 0, CHandle::null()) }; - assert_eq!(r, -90003, "无 liboakrender 时输出纹理无 CPU 帧 → E_FAILED"); + assert_eq!(r, -90001, "NULL 输出纹理 → E_INVALID(不崩溃)"); unsafe { oakplugin_instance_free(&mut h) }; }); } diff --git a/src/task/rust/Cargo.lock b/src/task/rust/Cargo.lock index c35d99fbd..30fd7ffe8 100644 --- a/src/task/rust/Cargo.lock +++ b/src/task/rust/Cargo.lock @@ -8,6 +8,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + [[package]] name = "android_system_properties" version = "0.1.6" @@ -38,6 +47,24 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.13.1", + "cexpr", + "clang-sys", + "itertools", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.3", + "shlex 1.3.0", + "syn 2.0.119", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -113,7 +140,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e" dependencies = [ "find-msvc-tools", - "shlex", + "shlex 2.0.1", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", ] [[package]] @@ -128,6 +164,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" +[[package]] +name = "clang-sys" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "cmake" version = "0.1.58" @@ -199,6 +246,12 @@ dependencies = [ "litrs", ] +[[package]] +name = "either" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" + [[package]] name = "equivalent" version = "1.0.2" @@ -211,6 +264,31 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" +[[package]] +name = "ffmpeg-next" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6380599799e175191eb7ffe82c97f36a2a90a36cbc54c738a903e5287d7f516a" +dependencies = [ + "bitflags 2.13.1", + "ffmpeg-sys-next", + "libc", +] + +[[package]] +name = "ffmpeg-sys-next" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b939bf79dd5949412a4b81cfe21a07f48ea21b47fcbb5f57816c8c2de5ae30b" +dependencies = [ + "bindgen", + "cc", + "libc", + "num_cpus", + "pkg-config", + "vcpkg", +] + [[package]] name = "find-msvc-tools" version = "0.1.10" @@ -295,6 +373,12 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + [[package]] name = "glow" version = "0.16.0" @@ -400,6 +484,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "hexf-parse" version = "0.2.1" @@ -429,6 +519,15 @@ dependencies = [ "hashbrown 0.17.1", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -564,6 +663,12 @@ dependencies = [ "paste", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -602,7 +707,7 @@ dependencies = [ "log", "num-traits", "once_cell", - "rustc-hash", + "rustc-hash 1.1.0", "spirv", "strum", "thiserror 2.0.20", @@ -618,6 +723,16 @@ dependencies = [ "jni-sys 0.3.1", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -628,6 +743,24 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "oakcodec" +version = "0.1.0" +dependencies = [ + "ffmpeg-next", + "oakcore-rs", +] + [[package]] name = "oakcommon" version = "0.1.0" @@ -643,6 +776,16 @@ dependencies = [ name = "oakcore-rs" version = "0.1.0" +[[package]] +name = "oaknode" +version = "0.1.0" +dependencies = [ + "oakcodec", + "oakcommon", + "oakcore-rs", + "oakundo", +] + [[package]] name = "oakotio" version = "0.1.0" @@ -667,9 +810,31 @@ dependencies = [ name = "oaktask" version = "0.1.0" dependencies = [ + "oakcodec", + "oakcommon", "oakcore-rs", + "oaknode", "oakotio", "oakrender", + "oaktimeline", + "oakundo", +] + +[[package]] +name = "oaktimeline" +version = "0.1.0" +dependencies = [ + "oakcommon", + "oakcore-rs", + "oaknode", + "oakundo", +] + +[[package]] +name = "oakundo" +version = "0.1.0" +dependencies = [ + "oakcore-rs", ] [[package]] @@ -835,6 +1000,35 @@ dependencies = [ "bitflags 2.13.1", ] +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + [[package]] name = "renderdoc-sys" version = "1.1.0" @@ -847,6 +1041,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + [[package]] name = "rustversion" version = "1.0.23" @@ -903,6 +1103,12 @@ dependencies = [ "zmij", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "shlex" version = "2.0.1" @@ -1070,6 +1276,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -1196,7 +1408,7 @@ dependencies = [ "portable-atomic", "profiling", "raw-window-handle", - "rustc-hash", + "rustc-hash 1.1.0", "smallvec", "thiserror 2.0.20", "wgpu-core-deps-apple", diff --git a/src/task/rust/Cargo.toml b/src/task/rust/Cargo.toml index 37c2239a3..ef0193d24 100644 --- a/src/task/rust/Cargo.toml +++ b/src/task/rust/Cargo.toml @@ -21,7 +21,7 @@ crate-type = ["staticlib", "rlib"] # `--test`) also compiles the stub-based tests, which cannot coexist with # the real oakrender exports in one binary (duplicate `#[no_mangle]` # symbols), so always pass the `--test` filter when the feature is on. -real-oakrender = ["dep:oakrender"] +real-oakrender = [] [profile.release] # FFI discipline: panics must be catchable at every exported entry. @@ -29,6 +29,11 @@ panic = "unwind" [dependencies] oakcore-rs = { path = "../../oakcore-rs" } +oakundo = { path = "../../undo/rust" } +oakcommon = { path = "../../common/rust" } +oakcodec = { path = "../../codec/rust" } +oaknode = { path = "../../node/rust" } +oaktimeline = { path = "../../timeline/rust" } # Pure-Rust OpenTimelineIO JSON binding (serde model of the OTIO JSON format # covering exactly the object graph the C++ loadotio/saveotio tasks use). @@ -36,8 +41,8 @@ oakcore-rs = { path = "../../oakcore-rs" } # LoadOTIOTask/SaveOTIOTask parse/serialize through this crate. oakotio = { path = "../../bindings/oakotio" } -# Optional: only built when the `real-oakrender` feature is on, which links -# the real oakrender crate into the integration-test binary. The library -# itself never references it (all render access goes through the C ABI in -# `bridge/render.rs`); plain `cargo test`/`cargo build` do not compile it. -oakrender = { path = "../../render/rust", optional = true } +# oakrender is a regular path dependency (single-lib: bridge/render.rs calls +# its ffi directly). The `real-oakrender` marker feature keeps the +# render-real integration test's cargo invocation working (it links the +# real oakrender — now always — and skips the ticket stubs). +oakrender = { path = "../../render/rust" } diff --git a/src/task/rust/src/bridge/codec.rs b/src/task/rust/src/bridge/codec.rs index 0c2dcc60b..ee20677ee 100644 --- a/src/task/rust/src/bridge/codec.rs +++ b/src/task/rust/src/bridge/codec.rs @@ -14,6 +14,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 . + //! oakcodec C ABI imports. The task module reaches the codec side through //! `include/codec/task.h` (the task submitter), `include/codec/decoder.h` //! (conform/proxy work) and `include/codec/encoder.h` (export). Signatures @@ -36,27 +52,8 @@ pub const OAKCODEC_E_INVALID: c_int = -50001; pub const OAKCODEC_E_FAILED: c_int = -50003; /// Mirror of `OakCodecTaskRequest` (`include/codec/task.h`). -#[repr(C)] -pub struct OakCodecTaskRequest { - /// `OakCodecTaskKind` as int. - pub kind: c_int, - /// Source media filename. - pub input_filename: *const c_char, - /// Final destination path. - pub output_filename: *const c_char, - /// Stream inside the source media. - pub stream_index: c_int, - /// conform: target sample rate. - pub sample_rate: c_int, - /// conform: target channel layout mask. - pub channel_layout: u64, - /// conform: target sample format (enum as int). - pub sample_format: c_int, - /// proxy: target width, 0 = unspecified/divider. - pub proxy_width: c_int, - /// proxy: target height, 0 = unspecified/divider. - pub proxy_height: c_int, -} +pub type OakCodecTaskRequest = oakcodec::task::OakCodecTaskRequest; + /// `oakcodec_task_submit_fn` callback typedef. pub type OakCodecTaskSubmitFn = @@ -72,147 +69,74 @@ pub type OakEncoder = CHandle; pub type OakDecoder = CHandle; /// Mirror of `OakCodecProxyParams` (`include/codec/proxy.h`). -#[repr(C)] -pub struct OakCodecProxyParams { - /// Target width. - pub width: c_int, - /// Target height. - pub height: c_int, - /// Resolution divider. - pub divider: c_int, - /// Proxy format version. - pub version: c_int, - /// CRF for the proxy encode. - pub crf: c_int, - /// Whether audio is included. - pub include_audio: c_int, - /// Output container extension. - pub extension: [c_char; 32], - /// ffmpeg preset name. - pub preset: [c_char; 32], -} +pub type OakCodecProxyParams = oakcodec::ffi::proxy::oakcodec_proxy_params; + /// Mirror of `oakcodec_encoding_params` (`include/codec/encoder.h`) — the /// export task fills this to open the encoder. Layout mirrors the header /// verbatim (a zeroed struct = all tracks disabled). -#[repr(C)] -pub struct OakCodecEncodingParams { - /// Output filename. - pub filename: [c_char; 1024], - /// Export format id (`olive::ExportFormat::Format`). - pub format: c_int, - /// Whether video is exported. - pub video_enabled: c_int, - /// Video codec id. - pub video_codec: c_int, - /// Output width. - pub video_width: c_int, - /// Output height. - pub video_height: c_int, - /// Frame duration rational numerator. - pub video_time_base_num: c_int, - /// Frame duration rational denominator. - pub video_time_base_den: c_int, - /// Delivery pixel format (`OakPixelFormat` as int). - pub video_pixel_format: c_int, - /// Interlacing (`OAKCODEC_INTERLACE_*`). - pub video_interlacing: c_int, - /// Pixel aspect ratio numerator. - pub video_pixel_aspect_num: c_int, - /// Pixel aspect ratio denominator. - pub video_pixel_aspect_den: c_int, - /// Video bit rate (bit/s, 0 = default). - pub video_bit_rate: i64, - /// Minimum video bit rate. - pub video_min_bit_rate: i64, - /// Maximum video bit rate. - pub video_max_bit_rate: i64, - /// Video buffer size (bytes). - pub video_buffer_size: i64, - /// Video threads (0 = auto). - pub video_threads: c_int, - /// Encoded pixel format name ("yuv420p"). - pub video_pix_fmt: [c_char; 64], - /// Whether the output is an image sequence. - pub video_is_image_sequence: c_int, - /// Scaling method (`OAKCODEC_ENCODING_SCALING_*`). - pub video_scaling_method: c_int, - /// Whether audio is exported. - pub audio_enabled: c_int, - /// Audio codec id. - pub audio_codec: c_int, - /// Audio sample rate. - pub audio_sample_rate: c_int, - /// Audio channel layout mask. - pub audio_channel_layout: u64, - /// Audio sample format (`olive::core::SampleFormat::Format` as int). - pub audio_sample_format: c_int, - /// Audio bit rate. - pub audio_bit_rate: i64, - /// Whether subtitles are exported. - pub subtitles_enabled: c_int, - /// Subtitle codec id. - pub subtitles_codec: c_int, - /// Whether subtitles go to a sidecar file. - pub subtitles_are_sidecar: c_int, - /// Sidecar subtitle format. - pub subtitles_sidecar_format: c_int, - /// Output OCIO colorspace name (empty = reference space). - pub color_transform_output: [c_char; 256], - /// Export length in seconds (rational). - pub export_length_num: c_int, - /// Export length denominator. - pub export_length_den: c_int, - /// Whether a custom export range is in effect. - pub has_custom_range: c_int, - /// Custom range in (seconds, rational). - pub custom_range_in_num: i64, - /// Custom range in denominator. - pub custom_range_in_den: i64, - /// Custom range out numerator. - pub custom_range_out_num: i64, - /// Custom range out denominator. - pub custom_range_out_den: i64, -} +pub type OakCodecEncodingParams = oakcodec::ffi::encoder::oakcodec_encoding_params; -extern "C" { - /// `oakcodec_set_task_submit_cb` — install the task submitter (`None` - /// unregisters, matching the header's `cb == NULL`). - pub fn oakcodec_set_task_submit_cb( + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_set_task_submit_cb( cb: Option, userdata: *mut c_void, - ); - /// `oakcodec_task_submit_is_registered`. - pub fn oakcodec_task_submit_is_registered() -> c_int; + ) { + unsafe { oakcodec::ffi::task::oakcodec_set_task_submit_cb(cb, userdata) } +} - /// `oakcodec_decoder_init`. - pub fn oakcodec_decoder_init() -> OakDecoder; - /// `oakcodec_decoder_free`. - pub fn oakcodec_decoder_free(decoder: *mut OakDecoder); - /// `oakcodec_decoder_open`. - pub fn oakcodec_decoder_open( +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_task_submit_is_registered() -> c_int { + unsafe { oakcodec::ffi::task::oakcodec_task_submit_is_registered() } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_decoder_init() -> OakDecoder { + unsafe { oakcodec::ffi::decoder::oakcodec_decoder_init() } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_decoder_free(decoder: *mut OakDecoder) { + unsafe { oakcodec::ffi::decoder::oakcodec_decoder_free(decoder) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_decoder_open( decoder: OakDecoder, filename: *const c_char, stream_index: c_int, - ) -> c_int; - /// `oakcodec_decoder_close`. - pub fn oakcodec_decoder_close(decoder: OakDecoder) -> c_int; - /// `oakcodec_decoder_is_open`. - pub fn oakcodec_decoder_is_open(decoder: OakDecoder) -> c_int; - /// `oakcodec_decoder_decode_audio`. - pub fn oakcodec_decoder_decode_audio( + ) -> c_int { + unsafe { oakcodec::ffi::decoder::oakcodec_decoder_open(decoder, filename, stream_index) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_decoder_close(decoder: OakDecoder) -> c_int { + unsafe { oakcodec::ffi::decoder::oakcodec_decoder_close(decoder) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_decoder_is_open(decoder: OakDecoder) -> c_int { + unsafe { oakcodec::ffi::decoder::oakcodec_decoder_is_open(decoder) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_decoder_decode_audio( decoder: OakDecoder, in_num: c_int, in_den: c_int, out_num: c_int, out_den: c_int, sample_rate: c_int, - channel_layout: c_uint, + channel_layout: u64, buf: *mut f32, buf_frames: c_int, - ) -> c_int; - /// `oakcodec_decoder_conform_audio` — the whole-stream conform pass. - pub fn oakcodec_decoder_conform_audio( + ) -> c_int { + unsafe { oakcodec::ffi::decoder::oakcodec_decoder_decode_audio(decoder, in_num, in_den, out_num, out_den, sample_rate, channel_layout, buf, buf_frames) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_decoder_conform_audio( decoder: OakDecoder, output_filenames: *const *const c_char, filename_count: c_int, @@ -220,65 +144,108 @@ extern "C" { channel_layout: u64, sample_format: c_int, cancelled: OakCancelAtom, - ) -> c_int; - /// `oakcodec_decoder_last_error`. - pub fn oakcodec_decoder_last_error(decoder: OakDecoder, buf: *mut c_char, buf_size: c_int) -> c_int; + ) -> c_int { + unsafe { oakcodec::ffi::decoder::oakcodec_decoder_conform_audio(decoder, output_filenames, filename_count, sample_rate, channel_layout, sample_format, cancelled) } +} - // --- decoder.h: image-sequence heuristics --- - /// `oakcodec_decoder_get_image_sequence_digit_count`. - pub fn oakcodec_decoder_get_image_sequence_digit_count(filename: *const c_char) -> c_int; - /// `oakcodec_decoder_get_image_sequence_index`. - pub fn oakcodec_decoder_get_image_sequence_index(filename: *const c_char) -> i64; - /// `oakcodec_decoder_transform_image_sequence_file_name` (two-stage). - pub fn oakcodec_decoder_transform_image_sequence_file_name( +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_decoder_last_error(decoder: OakDecoder, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oakcodec::ffi::decoder::oakcodec_decoder_last_error(decoder, buf, buf_size) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_decoder_get_image_sequence_digit_count(filename: *const c_char) -> c_int { + unsafe { oakcodec::ffi::decoder::oakcodec_decoder_get_image_sequence_digit_count(filename) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_decoder_get_image_sequence_index(filename: *const c_char) -> i64 { + unsafe { oakcodec::ffi::decoder::oakcodec_decoder_get_image_sequence_index(filename) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_decoder_transform_image_sequence_file_name( filename: *const c_char, number: i64, buf: *mut c_char, buf_size: c_int, - ) -> c_int; + ) -> c_int { + unsafe { oakcodec::ffi::decoder::oakcodec_decoder_transform_image_sequence_file_name(filename, number, buf, buf_size) } +} - // --- encoder.h extras --- - /// `oakcodec_encoder_get_desired_pixel_format`. - pub fn oakcodec_encoder_get_desired_pixel_format(encoder: OakEncoder) -> c_int; - /// `oakcodec_encoding_generate_matrix` (row-major 4x4). - pub fn oakcodec_encoding_generate_matrix( +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_encoder_get_desired_pixel_format(encoder: OakEncoder) -> c_int { + unsafe { oakcodec::ffi::encoder::oakcodec_encoder_get_desired_pixel_format(encoder) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_encoding_generate_matrix( method: c_int, src_width: c_int, src_height: c_int, dst_width: c_int, dst_height: c_int, out_matrix: *mut f64, - ) -> c_int; + ) -> c_int { + unsafe { oakcodec::ffi::encoder::oakcodec_encoding_generate_matrix(method, src_width, src_height, dst_width, dst_height, out_matrix) } +} - // --- proxy.h --- - /// `oakcodec_proxy_find_ffmpeg` (buf/size getter). - pub fn oakcodec_proxy_find_ffmpeg(configured_path: *const c_char, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oakcodec_proxy_params_default`. - pub fn oakcodec_proxy_params_default(out: *mut OakCodecProxyParams) -> c_int; +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_proxy_find_ffmpeg(configured_path: *const c_char, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oakcodec::ffi::proxy::oakcodec_proxy_find_ffmpeg(configured_path, buf, buf_size) } +} - /// `oakcodec_encoder_init`. - pub fn oakcodec_encoder_init(params: *const OakCodecEncodingParams) -> OakEncoder; - /// `oakcodec_encoder_free`. - pub fn oakcodec_encoder_free(encoder: *mut OakEncoder); - /// `oakcodec_encoder_open`. - pub fn oakcodec_encoder_open(encoder: OakEncoder) -> c_int; - /// `oakcodec_encoder_write_video`. - pub fn oakcodec_encoder_write_video(encoder: OakEncoder, frame: OakFrame) -> c_int; - /// `oakcodec_encoder_write_audio`. - pub fn oakcodec_encoder_write_audio( +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_proxy_params_default(out: *mut OakCodecProxyParams) -> c_int { + unsafe { oakcodec::ffi::proxy::oakcodec_proxy_params_default(out) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_encoder_init(params: *const OakCodecEncodingParams) -> OakEncoder { + unsafe { oakcodec::ffi::encoder::oakcodec_encoder_init(params) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_encoder_free(encoder: *mut OakEncoder) { + unsafe { oakcodec::ffi::encoder::oakcodec_encoder_free(encoder) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_encoder_open(encoder: OakEncoder) -> c_int { + unsafe { oakcodec::ffi::encoder::oakcodec_encoder_open(encoder) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_encoder_write_video(encoder: OakEncoder, frame: OakFrame) -> c_int { + unsafe { oakcodec::ffi::encoder::oakcodec_encoder_write_video(encoder, frame) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_encoder_write_audio( encoder: OakEncoder, samples: *const f32, frame_count: c_int, - ) -> c_int; - /// `oakcodec_encoder_write_subtitle`. - pub fn oakcodec_encoder_write_subtitle( + ) -> c_int { + unsafe { oakcodec::ffi::encoder::oakcodec_encoder_write_audio(encoder, samples, frame_count) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_encoder_write_subtitle( encoder: OakEncoder, text: *const c_char, in_seconds: f64, out_seconds: f64, - ) -> c_int; - /// `oakcodec_encoder_flush`. - pub fn oakcodec_encoder_flush(encoder: OakEncoder) -> c_int; - /// `oakcodec_encoder_last_error`. - pub fn oakcodec_encoder_last_error(encoder: OakEncoder, buf: *mut c_char, buf_size: c_int) -> c_int; + ) -> c_int { + unsafe { oakcodec::ffi::encoder::oakcodec_encoder_write_subtitle(encoder, text, in_seconds, out_seconds) } } + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_encoder_flush(encoder: OakEncoder) -> c_int { + unsafe { oakcodec::ffi::encoder::oakcodec_encoder_flush(encoder) } +} + +/// Direct call into the `oakcodec` crate (single-lib unification). +pub fn oakcodec_encoder_last_error(encoder: OakEncoder, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oakcodec::ffi::encoder::oakcodec_encoder_last_error(encoder, buf, buf_size) } +} + diff --git a/src/task/rust/src/bridge/common.rs b/src/task/rust/src/bridge/common.rs index 62649bf13..9aa4d5d45 100644 --- a/src/task/rust/src/bridge/common.rs +++ b/src/task/rust/src/bridge/common.rs @@ -14,6 +14,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 . + //! oakcommon / oakcore C ABI imports. The task module reaches the shared //! value types through `include/common/videoparams.h`, //! `include/common/colortransform.h` and the oakcore audio-params C ABI. @@ -45,12 +61,13 @@ pub const OAKCOMMON_VIDEO_TYPE_STILL: c_int = 1; /// `OAKCOMMON_VIDEO_TYPE_IMAGE_SEQUENCE` (`include/common/videoparams.h`). pub const OAKCOMMON_VIDEO_TYPE_IMAGE_SEQUENCE: c_int = 2; -extern "C" { - // --- videoparams.h --- - /// `oakcommon_videoparams_init`. - pub fn oakcommon_videoparams_init() -> OakVideoParams; - /// `oakcommon_videoparams_init_basic`. - pub fn oakcommon_videoparams_init_basic( +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_init() -> OakVideoParams { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_init() } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_init_basic( width: c_int, height: c_int, pixel_format: c_int, @@ -59,88 +76,146 @@ extern "C" { pixel_aspect_den: c_int, interlacing: c_int, divider: c_int, - ) -> OakVideoParams; - /// `oakcommon_videoparams_free`. - pub fn oakcommon_videoparams_free(params: *mut OakVideoParams); - /// `oakcommon_videoparams_get_width`. - pub fn oakcommon_videoparams_get_width(params: OakVideoParams, width: *mut c_int) -> c_int; - /// `oakcommon_videoparams_get_height`. - pub fn oakcommon_videoparams_get_height(params: OakVideoParams, height: *mut c_int) -> c_int; - /// `oakcommon_videoparams_get_format`. - pub fn oakcommon_videoparams_get_format(params: OakVideoParams, format: *mut c_int) -> c_int; - /// `oakcommon_videoparams_set_format`. - pub fn oakcommon_videoparams_set_format(params: OakVideoParams, format: c_int) -> c_int; - /// `oakcommon_videoparams_get_time_base`. - pub fn oakcommon_videoparams_get_time_base( + ) -> OakVideoParams { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_init_basic(width, height, pixel_format, nb_channels, pixel_aspect_num, pixel_aspect_den, interlacing, divider) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_free(params: *mut OakVideoParams) { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_free(params) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_get_width(params: OakVideoParams, width: *mut c_int) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_width(params, width) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_get_height(params: OakVideoParams, height: *mut c_int) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_height(params, height) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_get_format(params: OakVideoParams, format: *mut c_int) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_format(params, format) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_set_format(params: OakVideoParams, format: c_int) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_format(params, format) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_get_time_base( params: OakVideoParams, numerator: *mut c_int, denominator: *mut c_int, - ) -> c_int; - /// `oakcommon_videoparams_set_time_base`. - pub fn oakcommon_videoparams_set_time_base( + ) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_time_base(params, numerator, denominator) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_set_time_base( params: OakVideoParams, numerator: c_int, denominator: c_int, - ) -> c_int; - /// `oakcommon_videoparams_frame_rate_as_time_base` — frame rate flipped - /// to a frame duration rational. - pub fn oakcommon_videoparams_frame_rate_as_time_base( + ) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_time_base(params, numerator, denominator) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_frame_rate_as_time_base( params: OakVideoParams, out_num: *mut c_int, out_den: *mut c_int, - ) -> c_int; - /// `oakcommon_videoparams_set_frame_rate`. - pub fn oakcommon_videoparams_set_frame_rate( + ) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_frame_rate_as_time_base(params, out_num, out_den) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_set_frame_rate( params: OakVideoParams, numerator: c_int, denominator: c_int, - ) -> c_int; - /// `oakcommon_videoparams_get_video_type`. - pub fn oakcommon_videoparams_get_video_type(params: OakVideoParams, out_type: *mut c_int) -> c_int; - /// `oakcommon_videoparams_set_video_type`. - pub fn oakcommon_videoparams_set_video_type(params: OakVideoParams, video_type: c_int) -> c_int; - /// `oakcommon_videoparams_set_start_time`. - pub fn oakcommon_videoparams_set_start_time(params: OakVideoParams, start: i64) -> c_int; - /// `oakcommon_videoparams_set_duration`. - pub fn oakcommon_videoparams_set_duration(params: OakVideoParams, duration: i64) -> c_int; - /// `oakcommon_videoparams_get_is_valid`. - pub fn oakcommon_videoparams_get_is_valid(params: OakVideoParams, out_valid: *mut c_int) -> c_int; - /// `oakcommon_videoparams_get_frame_rate`. - pub fn oakcommon_videoparams_get_frame_rate( + ) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_frame_rate(params, numerator, denominator) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_get_video_type(params: OakVideoParams, out_type: *mut c_int) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_video_type(params, out_type) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_set_video_type(params: OakVideoParams, video_type: c_int) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_video_type(params, video_type) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_set_start_time(params: OakVideoParams, start: i64) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_start_time(params, start) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_set_duration(params: OakVideoParams, duration: i64) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_set_duration(params, duration) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_get_is_valid(params: OakVideoParams, out_valid: *mut c_int) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_is_valid(params, out_valid) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_get_frame_rate( params: OakVideoParams, numerator: *mut c_int, denominator: *mut c_int, - ) -> c_int; - /// `oakcommon_videoparams_get_duration`. - pub fn oakcommon_videoparams_get_duration(params: OakVideoParams, duration: *mut i64) -> c_int; + ) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_frame_rate(params, numerator, denominator) } +} - // --- colortransform.h --- - /// `oakcommon_colortransform_init_display`. - pub fn oakcommon_colortransform_init_display( +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_videoparams_get_duration(params: OakVideoParams, duration: *mut i64) -> c_int { + unsafe { oakcommon::ffi::videoparams::oakcommon_videoparams_get_duration(params, duration) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_colortransform_init_display( display: *const c_char, view: *const c_char, look: *const c_char, - ) -> OakColorTransform; - /// `oakcommon_colortransform_init_output`. - pub fn oakcommon_colortransform_init_output( - output: *const c_char, - display: *const c_char, - view: *const c_char, - look: *const c_char, - ) -> OakColorTransform; - /// `oakcommon_colortransform_free`. - pub fn oakcommon_colortransform_free(transform: *mut OakColorTransform); + ) -> OakColorTransform { + unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_init_display(display, view, look) } +} - // --- config.h --- - /// `oakcommon_config_get` (buf/size getter; returns needed size). - pub fn oakcommon_config_get( +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_colortransform_init_output(output: *const c_char) -> OakColorTransform { + unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_init_output(output) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_colortransform_free(transform: *mut OakColorTransform) { + unsafe { oakcommon::ffi::colortransform::oakcommon_colortransform_free(transform) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_config_get( group: *const c_char, key: *const c_char, buf: *mut c_char, buf_size: c_int, - ) -> c_int; - /// `oakcommon_config_get_int`. - pub fn oakcommon_config_get_int(group: *const c_char, key: *const c_char, default: c_int) -> c_int; - /// `oakcommon_config_get_bool`. - pub fn oakcommon_config_get_bool(group: *const c_char, key: *const c_char, default: c_int) -> c_int; + ) -> c_int { + unsafe { oakcommon::ffi::config::oakcommon_config_get(group, key, buf, buf_size) } } + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_config_get_int(group: *const c_char, key: *const c_char, default: c_int) -> c_int { + unsafe { oakcommon::ffi::config::oakcommon_config_get_int(group, key, default) } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_config_get_bool(group: *const c_char, key: *const c_char, default: c_int) -> c_int { + unsafe { oakcommon::ffi::config::oakcommon_config_get_bool(group, key, default) } +} + diff --git a/src/task/rust/src/bridge/node.rs b/src/task/rust/src/bridge/node.rs index 090b9da8e..774ba2f8e 100644 --- a/src/task/rust/src/bridge/node.rs +++ b/src/task/rust/src/bridge/node.rs @@ -14,6 +14,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 . + //! oaknode C ABI imports. The task module drives node-graph work through //! `include/node/project.h` (project shell), `include/node/folder.h`, //! `include/node/footage.h`, `include/node/sequence.h`, @@ -93,363 +109,802 @@ pub fn cstr(s: &str) -> *const c_char { bytes.leak().as_ptr() as *const c_char } -extern "C" { - // --- node.h --- - /// `oaknode_node_get_label` (two-stage string getter). - pub fn oaknode_node_get_label(node: OakNodeNode, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oaknode_node_set_label`. - pub fn oaknode_node_set_label(node: OakNodeNode, label: *const c_char) -> c_int; - /// `oaknode_node_get_id` (two-stage string getter). - pub fn oaknode_node_get_id(node: OakNodeNode, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oaknode_node_get_project` (borrowed project handle out). - pub fn oaknode_node_get_project(node: OakNodeNode, out: *mut OakNodeProject) -> c_int; - /// `oaknode_node_input_get_connected_node` (borrowed handle out). - pub fn oaknode_node_input_get_connected_node( +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_get_label(node: OakNodeNode, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::node::oaknode_node_get_label(node, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_set_label(node: OakNodeNode, label: *const c_char) -> c_int { + unsafe { oaknode::ffi::node::oaknode_node_set_label(node, label) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_get_id(node: OakNodeNode, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::node::oaknode_node_get_id(node, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_get_project(node: OakNodeNode, out: *mut OakNodeProject) -> c_int { + unsafe { oaknode::ffi::node::oaknode_node_get_project(node, out) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_input_get_connected_node( node: OakNodeNode, input_id: *const c_char, out_node: *mut OakNodeNode, - ) -> c_int; - /// `oaknode_node_set_context_position` (live, non-undoable). - pub fn oaknode_node_set_context_position( + ) -> c_int { + unsafe { oaknode::ffi::node::oaknode_node_input_get_connected_node(node, input_id, out_node) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_set_context_position( node: OakNodeNode, context: OakNodeNode, x: f64, y: f64, expanded: c_int, - ) -> c_int; - /// `oaknode_node_find_input_footage` (borrowed handle out). - pub fn oaknode_node_find_input_footage(node: OakNodeNode, out: *mut OakNodeFootage) -> c_int; - /// `oaknode_node_get_input_string` (two-stage string getter). - pub fn oaknode_node_get_input_string( + ) -> c_int { + unsafe { oaknode::ffi::node::oaknode_node_set_context_position(node, context, x, y, expanded) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_find_input_footage(node: OakNodeNode, out: *mut OakNodeFootage) -> c_int { + unsafe { oaknode::ffi::node::oaknode_node_find_input_footage(node, out) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_get_input_string( node: OakNodeNode, input_id: *const c_char, buf: *mut c_char, buf_size: c_int, - ) -> c_int; - /// `oaknode_node_create_copy` — orphaned deep copy of a node. - pub fn oaknode_node_create_copy(node: OakNodeNode) -> OakNodeNode; - /// `oaknode_node_copy_inputs`. - pub fn oaknode_node_copy_inputs(dst: OakNodeNode, src: OakNodeNode, include_connections: c_int) -> c_int; - /// `oaknode_node_connect`. - pub fn oaknode_node_connect(output_node: OakNodeNode, input_node: OakNodeNode, input_id: *const c_char) -> c_int; - /// `oaknode_node_set_value_hint_track`. - pub fn oaknode_node_set_value_hint_track( + ) -> c_int { + unsafe { oaknode::ffi::node::oaknode_node_get_input_string(node, input_id, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_create_copy(node: OakNodeNode) -> OakNodeNode { + unsafe { oaknode::ffi::node::oaknode_node_create_copy(node) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_copy_inputs(dst: OakNodeNode, src: OakNodeNode, include_connections: c_int) -> c_int { + unsafe { oaknode::ffi::node::oaknode_node_copy_inputs(dst, src, include_connections) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_connect(output_node: OakNodeNode, input_node: OakNodeNode, input_id: *const c_char) -> c_int { + unsafe { oaknode::ffi::node::oaknode_node_connect(output_node, input_node, input_id) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_set_value_hint_track( node: OakNodeNode, input_id: *const c_char, track_type: c_int, track_index: c_int, - ) -> c_int; - /// `oaknode_node_get_video_frame_cache` (borrowed cache out). - pub fn oaknode_node_get_video_frame_cache( + ) -> c_int { + unsafe { oaknode::ffi::node::oaknode_node_set_value_hint_track(node, input_id, track_type, track_index) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_get_video_frame_cache( node: OakNodeNode, out: *mut crate::bridge::render::OakRenderCache, - ) -> c_int; - /// `oaknode_node_free`. - pub fn oaknode_node_free(node: *mut OakNodeNode); - /// `oaknode_command_create_remove_node` — undoable node removal. - pub fn oaknode_command_create_remove_node(node: OakNodeNode) -> crate::bridge::undo::OakUndoCommand; + ) -> c_int { + unsafe { oaknode::ffi::node::oaknode_node_get_video_frame_cache(node, out as *mut std::ffi::c_void) } +} - // --- factory.h --- - /// `oaknode_factory_create_from_id`. - pub fn oaknode_factory_create_from_id(type_id: *const c_char) -> OakNodeNode; +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_free(node: *mut OakNodeNode) { + unsafe { oaknode::ffi::node::oaknode_node_free(node) } +} - // --- node.h (viewer) --- - /// `oaknode_viewer_set_video_params`. - pub fn oaknode_viewer_set_video_params(viewer: OakNodeNode, params: *const OakVideoParams) -> c_int; - /// `oaknode_viewer_set_audio_params`. - pub fn oaknode_viewer_set_audio_params(viewer: OakNodeNode, params: *const std::ffi::c_void) -> c_int; +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_command_create_remove_node(node: OakNodeNode) -> crate::bridge::undo::OakUndoCommand { + unsafe { oaknode::ffi::node::oaknode_command_create_remove_node(node) } +} - // --- block.h --- - /// `oaknode_block_clip_create`. - pub fn oaknode_block_clip_create() -> OakNodeBlock; - /// `oaknode_block_gap_create`. - pub fn oaknode_block_gap_create() -> OakNodeBlock; - /// `oaknode_block_transition_create` (kind: `OakNodeTransitionKind`). - pub fn oaknode_block_transition_create(kind: c_int) -> OakNodeBlock; - /// `oaknode_block_as_node` (borrowed cast). - pub fn oaknode_block_as_node(block: OakNodeBlock) -> OakNodeNode; - /// `oaknode_block_get_kind`. - pub fn oaknode_block_get_kind(block: OakNodeBlock, out_kind: *mut c_int) -> c_int; - /// `oaknode_block_get_in`. - pub fn oaknode_block_get_in(block: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_block_get_length`. - pub fn oaknode_block_get_length(block: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_block_set_length_and_media_out`. - pub fn oaknode_block_set_length_and_media_out(block: OakNodeBlock, numerator: c_int, denominator: c_int) -> c_int; - /// `oaknode_clip_set_media_in`. - pub fn oaknode_clip_set_media_in(clip: OakNodeBlock, numerator: c_int, denominator: c_int) -> c_int; - /// `oaknode_transition_get_in_offset`. - pub fn oaknode_transition_get_in_offset(transition: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_transition_get_out_offset`. - pub fn oaknode_transition_get_out_offset(transition: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_transition_set_offsets_and_length`. - pub fn oaknode_transition_set_offsets_and_length( +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_factory_create_from_id(type_id: *const c_char) -> OakNodeNode { + unsafe { oaknode::ffi::factory::oaknode_factory_create_from_id(type_id) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_viewer_set_video_params(viewer: OakNodeNode, params: *const OakVideoParams) -> c_int { + unsafe { oaknode::ffi::node::oaknode_viewer_set_video_params(viewer, params as *const std::ffi::c_void) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_viewer_set_audio_params(viewer: OakNodeNode, params: *const std::ffi::c_void) -> c_int { + unsafe { oaknode::ffi::node::oaknode_viewer_set_audio_params(viewer, params) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_clip_create() -> OakNodeBlock { + unsafe { oaknode::ffi::block::oaknode_block_clip_create() } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_gap_create() -> OakNodeBlock { + unsafe { oaknode::ffi::block::oaknode_block_gap_create() } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_transition_create(kind: c_int) -> OakNodeBlock { + unsafe { oaknode::ffi::block::oaknode_block_transition_create(kind) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_as_node(block: OakNodeBlock) -> OakNodeNode { + unsafe { oaknode::ffi::block::oaknode_block_as_node(block) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_get_kind(block: OakNodeBlock, out_kind: *mut c_int) -> c_int { + unsafe { oaknode::ffi::block::oaknode_block_get_kind(block, out_kind) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_get_in(block: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + unsafe { oaknode::ffi::block::oaknode_block_get_in(block, numerator, denominator) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_get_length(block: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + unsafe { oaknode::ffi::block::oaknode_block_get_length(block, numerator, denominator) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_set_length_and_media_out(block: OakNodeBlock, numerator: c_int, denominator: c_int) -> c_int { + unsafe { oaknode::ffi::block::oaknode_block_set_length_and_media_out(block, numerator, denominator) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_clip_set_media_in(clip: OakNodeBlock, numerator: c_int, denominator: c_int) -> c_int { + unsafe { oaknode::ffi::block::oaknode_clip_set_media_in(clip, numerator, denominator) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_transition_get_in_offset(transition: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + unsafe { oaknode::ffi::block::oaknode_transition_get_in_offset(transition, numerator, denominator) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_transition_get_out_offset(transition: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + unsafe { oaknode::ffi::block::oaknode_transition_get_out_offset(transition, numerator, denominator) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_transition_set_offsets_and_length( transition: OakNodeBlock, in_num: c_int, in_den: c_int, out_num: c_int, out_den: c_int, - ) -> c_int; - - // --- project.h --- - /// `oaknode_project_init`. - pub fn oaknode_project_init() -> OakNodeProject; - /// `oaknode_project_free`. - pub fn oaknode_project_free(project: *mut OakNodeProject); - /// `oaknode_project_initialize`. - pub fn oaknode_project_initialize(project: OakNodeProject) -> c_int; - /// `oaknode_project_clear`. - pub fn oaknode_project_clear(project: OakNodeProject) -> c_int; - /// `oaknode_project_root`. - pub fn oaknode_project_root(project: OakNodeProject) -> OakNodeFolder; - /// `oaknode_project_name`. - pub fn oaknode_project_name(project: OakNodeProject, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oaknode_project_filename`. - pub fn oaknode_project_filename(project: OakNodeProject, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oaknode_project_pretty_filename`. - pub fn oaknode_project_pretty_filename(project: OakNodeProject, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oaknode_project_set_filename`. - pub fn oaknode_project_set_filename(project: OakNodeProject, filename: *const c_char) -> c_int; - /// `oaknode_project_is_modified`. - pub fn oaknode_project_is_modified(project: OakNodeProject) -> c_int; - /// `oaknode_project_set_modified`. - pub fn oaknode_project_set_modified(project: OakNodeProject, modified: c_int) -> c_int; - /// `oaknode_project_is_new`. - pub fn oaknode_project_is_new(project: OakNodeProject) -> c_int; - /// `oaknode_project_cache_path`. - pub fn oaknode_project_cache_path(project: OakNodeProject, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oaknode_project_copy_settings`. - pub fn oaknode_project_copy_settings(dst: OakNodeProject, src: OakNodeProject) -> c_int; - /// `oaknode_project_get_cache_location_setting`. - pub fn oaknode_project_get_cache_location_setting(project: OakNodeProject) -> c_int; - /// `oaknode_project_set_cache_location_setting`. - pub fn oaknode_project_set_cache_location_setting(project: OakNodeProject, setting: c_int) -> c_int; - /// `oaknode_project_get_custom_cache_path`. - pub fn oaknode_project_get_custom_cache_path(project: OakNodeProject, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oaknode_project_set_custom_cache_path`. - pub fn oaknode_project_set_custom_cache_path(project: OakNodeProject, path: *const c_char) -> c_int; - /// `oaknode_project_get_uuid`. - pub fn oaknode_project_get_uuid(project: OakNodeProject, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oaknode_project_add_node`. - pub fn oaknode_project_add_node(project: OakNodeProject, node: OakNodeNode) -> c_int; - /// `oaknode_project_remove_node`. - pub fn oaknode_project_remove_node(project: OakNodeProject, node: OakNodeNode) -> c_int; - /// `oaknode_project_node_count`. - pub fn oaknode_project_node_count(project: OakNodeProject) -> c_int; - /// `oaknode_project_node_at`. - pub fn oaknode_project_node_at(project: OakNodeProject, index: c_int) -> OakNodeNode; - - // --- folder.h --- - /// `oaknode_folder_create`. - pub fn oaknode_folder_create(project: OakNodeProject) -> OakNodeFolder; - /// `oaknode_folder_child_count`. - pub fn oaknode_folder_child_count(folder: OakNodeFolder) -> c_int; - /// `oaknode_folder_child_at`. - pub fn oaknode_folder_child_at(folder: OakNodeFolder, index: c_int) -> OakNodeNode; - /// `oaknode_folder_add_child`. - pub fn oaknode_folder_add_child(folder: OakNodeFolder, child: OakNodeNode) -> c_int; - /// `oaknode_folder_as_node`. - pub fn oaknode_folder_as_node(folder: OakNodeFolder) -> OakNodeNode; - /// `oaknode_command_create_folder_add_child`. - pub fn oaknode_command_create_folder_add_child(folder: OakNodeFolder, child: OakNodeNode) -> crate::bridge::undo::OakUndoCommand; - /// `oaknode_folder_remove_child`. - pub fn oaknode_folder_remove_child(folder: OakNodeFolder, child: OakNodeNode) -> c_int; - /// `oaknode_folder_move_children`. - pub fn oaknode_folder_move_children(nodes: *const OakNodeNode, count: c_int, dest_folder: OakNodeFolder) -> c_int; - /// `oaknode_folder_has_child_recursive`. - pub fn oaknode_folder_has_child_recursive(folder: OakNodeFolder, child: OakNodeNode) -> c_int; - /// `oaknode_folder_index_of_child`. - pub fn oaknode_folder_index_of_child(folder: OakNodeFolder, child: OakNodeNode) -> c_int; - /// `oaknode_folder_parent_of`. - pub fn oaknode_folder_parent_of(node: OakNodeNode) -> OakNodeFolder; - - // --- footage.h --- - /// `oaknode_footage_create`. - pub fn oaknode_footage_create(project: OakNodeProject, filename: *const c_char) -> OakNodeFootage; - /// `oaknode_footage_as_node`. - pub fn oaknode_footage_as_node(footage: OakNodeFootage) -> OakNodeNode; - /// `oaknode_footage_filename`. - pub fn oaknode_footage_filename(footage: OakNodeFootage, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oaknode_footage_set_filename`. - pub fn oaknode_footage_set_filename(footage: OakNodeFootage, filename: *const c_char) -> c_int; - /// `oaknode_footage_is_valid`. - pub fn oaknode_footage_is_valid(footage: OakNodeFootage) -> c_int; - /// `oaknode_footage_timestamp`. - pub fn oaknode_footage_timestamp(footage: OakNodeFootage, out_timestamp: *mut i64) -> c_int; - /// `oaknode_footage_set_timestamp`. - pub fn oaknode_footage_set_timestamp(footage: OakNodeFootage, timestamp: i64) -> c_int; - /// `oaknode_footage_decoder`. - pub fn oaknode_footage_decoder(footage: OakNodeFootage, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oaknode_footage_total_stream_count`. - pub fn oaknode_footage_total_stream_count(footage: OakNodeFootage) -> c_int; - /// `oaknode_footage_video_stream_count`. - pub fn oaknode_footage_video_stream_count(footage: OakNodeFootage) -> c_int; - /// `oaknode_footage_audio_stream_count`. - pub fn oaknode_footage_audio_stream_count(footage: OakNodeFootage) -> c_int; - /// `oaknode_footage_subtitle_stream_count`. - pub fn oaknode_footage_subtitle_stream_count(footage: OakNodeFootage) -> c_int; - /// `oaknode_footage_duration`. - pub fn oaknode_footage_duration(footage: OakNodeFootage, out_numerator: *mut c_int, out_denominator: *mut c_int) -> c_int; - /// `oaknode_footage_proxy_enabled`. - pub fn oaknode_footage_proxy_enabled(footage: OakNodeFootage) -> c_int; - /// `oaknode_footage_set_proxy_enabled`. - pub fn oaknode_footage_set_proxy_enabled(footage: OakNodeFootage, enabled: c_int) -> c_int; - /// `oaknode_footage_proxy_path`. - pub fn oaknode_footage_proxy_path(footage: OakNodeFootage, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oaknode_footage_proxy_state`. - pub fn oaknode_footage_proxy_state(footage: OakNodeFootage) -> c_int; - /// `oaknode_footage_set_proxy`. - pub fn oaknode_footage_set_proxy(footage: OakNodeFootage, path: *const c_char, state: c_int, video_stream_index: c_int, preset_version: c_int, enabled: c_int) -> c_int; - /// `oaknode_footage_clear_proxy`. - pub fn oaknode_footage_clear_proxy(footage: OakNodeFootage) -> c_int; - /// `oaknode_footage_get_video_params`. - pub fn oaknode_footage_get_video_params(footage: OakNodeFootage, index: c_int, out: *mut OakVideoParams) -> c_int; - /// `oaknode_footage_set_video_params`. - pub fn oaknode_footage_set_video_params(footage: OakNodeFootage, index: c_int, params: *const OakVideoParams) -> c_int; - /// `oaknode_footage_get_video_length`. - pub fn oaknode_footage_get_video_length(footage: OakNodeFootage, out_num: *mut i64, out_den: *mut i64) -> c_int; - /// `oaknode_footage_set_cancel_atom`. - pub fn oaknode_footage_set_cancel_atom(footage: OakNodeFootage, atom: OakCancelAtom) -> c_int; - - // --- sequence.h --- - /// `oaknode_sequence_create`. - pub fn oaknode_sequence_create() -> OakNodeSequence; - /// `oaknode_sequence_free`. - pub fn oaknode_sequence_free(sequence: *mut OakNodeSequence); - /// `oaknode_sequence_set_default_parameters`. - pub fn oaknode_sequence_set_default_parameters(sequence: OakNodeSequence) -> c_int; - /// `oaknode_sequence_as_node`. - pub fn oaknode_sequence_as_node(sequence: OakNodeSequence) -> OakNodeNode; - /// `oaknode_sequence_from_node`. - pub fn oaknode_sequence_from_node(node: OakNodeNode) -> OakNodeSequence; - /// `oaknode_sequence_get_track_list`. - pub fn oaknode_sequence_get_track_list(sequence: OakNodeSequence, r#type: c_int, out: *mut OakNodeTrackList) -> c_int; - /// `oaknode_sequence_get_track_count`. - pub fn oaknode_sequence_get_track_count(sequence: OakNodeSequence, r#type: c_int, count: *mut c_int) -> c_int; - /// `oaknode_sequence_get_track_at`. - pub fn oaknode_sequence_get_track_at(sequence: OakNodeSequence, r#type: c_int, index: c_int, out: *mut OakNodeTrack) -> c_int; - /// `oaknode_sequence_get_all_track_count`. - pub fn oaknode_sequence_get_all_track_count(sequence: OakNodeSequence, count: *mut c_int) -> c_int; - /// `oaknode_sequence_get_all_track_at`. - pub fn oaknode_sequence_get_all_track_at(sequence: OakNodeSequence, index: c_int, out: *mut OakNodeTrack) -> c_int; - /// `oaknode_sequence_get_playhead`. - pub fn oaknode_sequence_get_playhead(sequence: OakNodeSequence, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_sequence_set_playhead`. - pub fn oaknode_sequence_set_playhead(sequence: OakNodeSequence, numerator: c_int, denominator: c_int) -> c_int; - /// `oaknode_sequence_get_length`. - pub fn oaknode_sequence_get_length(sequence: OakNodeSequence, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_sequence_get_video_length`. - pub fn oaknode_sequence_get_video_length(sequence: OakNodeSequence, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_sequence_get_audio_length`. - pub fn oaknode_sequence_get_audio_length(sequence: OakNodeSequence, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_sequence_verify_length`. - pub fn oaknode_sequence_verify_length(sequence: OakNodeSequence) -> c_int; - /// `oaknode_sequence_get_video_stream_count`. - pub fn oaknode_sequence_get_video_stream_count(sequence: OakNodeSequence, count: *mut c_int) -> c_int; - /// `oaknode_sequence_get_audio_stream_count`. - pub fn oaknode_sequence_get_audio_stream_count(sequence: OakNodeSequence, count: *mut c_int) -> c_int; - /// `oaknode_sequence_get_video_params`. - pub fn oaknode_sequence_get_video_params(sequence: OakNodeSequence, index: c_int, out: *mut OakVideoParams) -> c_int; - /// `oaknode_sequence_set_video_params`. - pub fn oaknode_sequence_set_video_params(sequence: OakNodeSequence, index: c_int, params: OakVideoParams) -> c_int; - /// `oaknode_sequence_get_audio_params`. - pub fn oaknode_sequence_get_audio_params(sequence: OakNodeSequence, index: c_int, out: *mut *const c_void) -> c_int; - /// `oaknode_sequence_set_audio_params`. - pub fn oaknode_sequence_set_audio_params(sequence: OakNodeSequence, index: c_int, params: *const c_void) -> c_int; - - // --- track.h --- - /// `oaknode_track_as_node`. - pub fn oaknode_track_as_node(track: OakNodeTrack) -> OakNodeNode; - /// `oaknode_track_create`. - pub fn oaknode_track_create(r#type: c_int) -> OakNodeTrack; - /// `oaknode_track_free`. - pub fn oaknode_track_free(track: *mut OakNodeTrack); - /// `oaknode_track_get_type`. - pub fn oaknode_track_get_type(track: OakNodeTrack, r#type: *mut c_int) -> c_int; - /// `oaknode_track_get_length`. - pub fn oaknode_track_get_length(track: OakNodeTrack, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_track_get_block_count`. - pub fn oaknode_track_get_block_count(track: OakNodeTrack, count: *mut c_int) -> c_int; - /// `oaknode_track_get_block_at` (borrowed handle out). - pub fn oaknode_track_get_block_at(track: OakNodeTrack, index: c_int, out: *mut OakNodeBlock) -> c_int; - /// `oaknode_track_append_block`. - pub fn oaknode_track_append_block(track: OakNodeTrack, block: OakNodeBlock) -> c_int; - /// `oaknode_track_set_type`. - pub fn oaknode_track_set_type(track: OakNodeTrack, r#type: c_int) -> c_int; - /// `oaknode_track_get_index`. - pub fn oaknode_track_get_index(track: OakNodeTrack, index: *mut c_int) -> c_int; - /// `oaknode_track_set_index`. - pub fn oaknode_track_set_index(track: OakNodeTrack, index: c_int) -> c_int; - /// `oaknode_track_get_sequence`. - pub fn oaknode_track_get_sequence(track: OakNodeTrack, out: *mut OakNodeSequence) -> c_int; - /// `oaknode_tracklist_get_sequence`. - pub fn oaknode_tracklist_get_sequence(list: OakNodeTrackList, out: *mut OakNodeSequence) -> c_int; - /// `oaknode_tracklist_get_track_input_id`. - pub fn oaknode_tracklist_get_track_input_id(list: OakNodeTrackList, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oaknode_tracklist_array_append`. - pub fn oaknode_tracklist_array_append(list: OakNodeTrackList) -> c_int; - /// `oaknode_tracklist_array_remove_last`. - pub fn oaknode_tracklist_array_remove_last(list: OakNodeTrackList) -> c_int; - /// `oaknode_tracklist_get_array_index_from_cache_index`. - pub fn oaknode_tracklist_get_array_index_from_cache_index(list: OakNodeTrackList, cache_index: c_int, out_index: *mut c_int) -> c_int; - /// `oaknode_tracklist_get_type`. - pub fn oaknode_tracklist_get_type(list: OakNodeTrackList, r#type: *mut c_int) -> c_int; - /// `oaknode_tracklist_get_track_count`. - pub fn oaknode_tracklist_get_track_count(list: OakNodeTrackList, count: *mut c_int) -> c_int; - /// `oaknode_tracklist_get_track_at`. - pub fn oaknode_tracklist_get_track_at(list: OakNodeTrackList, index: c_int, out: *mut OakNodeTrack) -> c_int; - /// `oaknode_tracklist_get_total_length`. - pub fn oaknode_tracklist_get_total_length(list: OakNodeTrackList, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_tracklist_get_array_size`. - pub fn oaknode_tracklist_get_array_size(list: OakNodeTrackList, size: *mut c_int) -> c_int; - /// `oaknode_tracklist_add_track`. - pub fn oaknode_tracklist_add_track(list: OakNodeTrackList, track: OakNodeTrack) -> c_int; - /// `oaknode_tracklist_remove_track`. - pub fn oaknode_tracklist_remove_track(list: OakNodeTrackList, track: OakNodeTrack) -> c_int; - - // --- colormanager.h --- - /// `oaknode_colormanager_init`. - pub fn oaknode_colormanager_init(project: OakNodeProject) -> OakNodeColorManager; - /// `oaknode_colormanager_free`. - pub fn oaknode_colormanager_free(manager: *mut OakNodeColorManager); - /// `oaknode_colormanager_wrap_borrowed`. - pub fn oaknode_colormanager_wrap_borrowed(native_manager: *mut c_void) -> OakNodeColorManager; - /// `oaknode_colormanager_initialize`. - pub fn oaknode_colormanager_initialize(manager: OakNodeColorManager) -> c_int; - /// `oaknode_colormanager_set_up_default_config`. - pub fn oaknode_colormanager_set_up_default_config() -> c_int; - - // --- serializer.h --- - /// `oaknode_serializer_initialize`. - pub fn oaknode_serializer_initialize() -> c_int; - /// `oaknode_serializer_shutdown`. - pub fn oaknode_serializer_shutdown(); - /// `oaknode_serializer_savedata_create`. - pub fn oaknode_serializer_savedata_create(load_type: c_int, project: OakNodeProject) -> OakNodeSerializerSaveData; - /// `oaknode_serializer_savedata_free`. - pub fn oaknode_serializer_savedata_free(save_data: *mut OakNodeSerializerSaveData); - /// `oaknode_serializer_savedata_set_nodes`. - pub fn oaknode_serializer_savedata_set_nodes(save_data: OakNodeSerializerSaveData, nodes: *const OakNodeNode, count: c_int) -> c_int; - /// `oaknode_serializer_savedata_set_property`. - pub fn oaknode_serializer_savedata_set_property(save_data: OakNodeSerializerSaveData, node: OakNodeNode, key: *const c_char, value: *const c_char) -> c_int; - /// `oaknode_serializer_save_to_xml`. - pub fn oaknode_serializer_save_to_xml(save_data: OakNodeSerializerSaveData, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oaknode_serializer_load_from_xml`. - pub fn oaknode_serializer_load_from_xml(project: OakNodeProject, xml: *const c_char, load_type: c_int, out_result: *mut c_int, out_load_data: *mut OakNodeSerializerLoadData, details_buf: *mut c_char, details_buf_size: c_int) -> c_int; - /// `oaknode_serializer_loaddata_free`. - pub fn oaknode_serializer_loaddata_free(load_data: *mut OakNodeSerializerLoadData); - /// `oaknode_serializer_loaddata_node_count`. - pub fn oaknode_serializer_loaddata_node_count(load_data: OakNodeSerializerLoadData) -> c_int; - /// `oaknode_serializer_loaddata_node_at`. - pub fn oaknode_serializer_loaddata_node_at(load_data: OakNodeSerializerLoadData, index: c_int) -> OakNodeNode; - /// `oaknode_serializer_loaddata_get_property`. - pub fn oaknode_serializer_loaddata_get_property(load_data: OakNodeSerializerLoadData, node: OakNodeNode, key: *const c_char, buf: *mut c_char, buf_size: c_int) -> c_int; - /// `oaknode_serializer_loaddata_connection_count`. - pub fn oaknode_serializer_loaddata_connection_count(load_data: OakNodeSerializerLoadData) -> c_int; - /// `oaknode_serializer_loaddata_connection_at`. - pub fn oaknode_serializer_loaddata_connection_at(load_data: OakNodeSerializerLoadData, index: c_int, out_output_node: *mut OakNodeNode, out_input_node: *mut OakNodeNode, input_id_buf: *mut c_char, input_id_buf_size: c_int, out_element: *mut c_int) -> c_int; - /// `oaknode_serializer_save_to_file`. - pub fn oaknode_serializer_save_to_file(project: OakNodeProject, filename: *const c_char, use_compression: c_int, out_code: *mut c_int, details: *mut c_char, details_size: c_int) -> c_int; - /// `oaknode_serializer_load_from_file`. - pub fn oaknode_serializer_load_from_file(project: OakNodeProject, filename: *const c_char, out_code: *mut c_int, details: *mut c_char, details_size: c_int) -> c_int; + ) -> c_int { + unsafe { oaknode::ffi::block::oaknode_transition_set_offsets_and_length(transition, in_num, in_den, out_num, out_den) } } + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_init() -> OakNodeProject { + unsafe { oaknode::ffi::project::oaknode_project_init() } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_free(project: *mut OakNodeProject) { + unsafe { oaknode::ffi::project::oaknode_project_free(project) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_initialize(project: OakNodeProject) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_initialize(project) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_clear(project: OakNodeProject) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_clear(project) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_root(project: OakNodeProject) -> OakNodeFolder { + unsafe { oaknode::ffi::project::oaknode_project_root(project) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_name(project: OakNodeProject, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_name(project, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_filename(project: OakNodeProject, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_filename(project, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_pretty_filename(project: OakNodeProject, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_pretty_filename(project, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_set_filename(project: OakNodeProject, filename: *const c_char) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_set_filename(project, filename) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_is_modified(project: OakNodeProject) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_is_modified(project) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_set_modified(project: OakNodeProject, modified: c_int) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_set_modified(project, modified) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_is_new(project: OakNodeProject) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_is_new(project) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_cache_path(project: OakNodeProject, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_cache_path(project, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_copy_settings(dst: OakNodeProject, src: OakNodeProject) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_copy_settings(dst, src) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_get_cache_location_setting(project: OakNodeProject) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_get_cache_location_setting(project) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_set_cache_location_setting(project: OakNodeProject, setting: c_int) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_set_cache_location_setting(project, setting) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_get_custom_cache_path(project: OakNodeProject, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_get_custom_cache_path(project, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_set_custom_cache_path(project: OakNodeProject, path: *const c_char) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_set_custom_cache_path(project, path) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_get_uuid(project: OakNodeProject, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_get_uuid(project, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_add_node(project: OakNodeProject, node: OakNodeNode) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_add_node(project, node) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_remove_node(project: OakNodeProject, node: OakNodeNode) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_remove_node(project, node) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_node_count(project: OakNodeProject) -> c_int { + unsafe { oaknode::ffi::project::oaknode_project_node_count(project) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_node_at(project: OakNodeProject, index: c_int) -> OakNodeNode { + unsafe { oaknode::ffi::project::oaknode_project_node_at(project, index) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_folder_create(project: OakNodeProject) -> OakNodeFolder { + unsafe { oaknode::ffi::folder::oaknode_folder_create(project) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_folder_child_count(folder: OakNodeFolder) -> c_int { + unsafe { oaknode::ffi::folder::oaknode_folder_child_count(folder) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_folder_child_at(folder: OakNodeFolder, index: c_int) -> OakNodeNode { + unsafe { oaknode::ffi::folder::oaknode_folder_child_at(folder, index) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_folder_add_child(folder: OakNodeFolder, child: OakNodeNode) -> c_int { + unsafe { oaknode::ffi::folder::oaknode_folder_add_child(folder, child) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_folder_as_node(folder: OakNodeFolder) -> OakNodeNode { + unsafe { oaknode::ffi::folder::oaknode_folder_as_node(folder) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_command_create_folder_add_child(folder: OakNodeFolder, child: OakNodeNode) -> crate::bridge::undo::OakUndoCommand { + unsafe { oaknode::ffi::folder::oaknode_command_create_folder_add_child(folder, child) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_folder_remove_child(folder: OakNodeFolder, child: OakNodeNode) -> c_int { + unsafe { oaknode::ffi::folder::oaknode_folder_remove_child(folder, child) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_folder_move_children(nodes: *const OakNodeNode, count: c_int, dest_folder: OakNodeFolder) -> c_int { + unsafe { oaknode::ffi::folder::oaknode_folder_move_children(nodes, count, dest_folder) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_folder_has_child_recursive(folder: OakNodeFolder, child: OakNodeNode) -> c_int { + unsafe { oaknode::ffi::folder::oaknode_folder_has_child_recursive(folder, child) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_folder_index_of_child(folder: OakNodeFolder, child: OakNodeNode) -> c_int { + unsafe { oaknode::ffi::folder::oaknode_folder_index_of_child(folder, child) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_folder_parent_of(node: OakNodeNode) -> OakNodeFolder { + unsafe { oaknode::ffi::folder::oaknode_folder_parent_of(node) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_create(project: OakNodeProject, filename: *const c_char) -> OakNodeFootage { + unsafe { oaknode::ffi::footage::oaknode_footage_create(project, filename) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_as_node(footage: OakNodeFootage) -> OakNodeNode { + unsafe { oaknode::ffi::footage::oaknode_footage_as_node(footage) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_filename(footage: OakNodeFootage, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_filename(footage, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_set_filename(footage: OakNodeFootage, filename: *const c_char) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_set_filename(footage, filename) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_is_valid(footage: OakNodeFootage) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_is_valid(footage) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_timestamp(footage: OakNodeFootage, out_timestamp: *mut i64) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_timestamp(footage, out_timestamp) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_set_timestamp(footage: OakNodeFootage, timestamp: i64) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_set_timestamp(footage, timestamp) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_decoder(footage: OakNodeFootage, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_decoder(footage, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_total_stream_count(footage: OakNodeFootage) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_total_stream_count(footage) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_video_stream_count(footage: OakNodeFootage) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_video_stream_count(footage) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_audio_stream_count(footage: OakNodeFootage) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_audio_stream_count(footage) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_subtitle_stream_count(footage: OakNodeFootage) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_subtitle_stream_count(footage) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_duration(footage: OakNodeFootage, out_numerator: *mut c_int, out_denominator: *mut c_int) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_duration(footage, out_numerator, out_denominator) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_proxy_enabled(footage: OakNodeFootage) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_proxy_enabled(footage) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_set_proxy_enabled(footage: OakNodeFootage, enabled: c_int) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_set_proxy_enabled(footage, enabled) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_proxy_path(footage: OakNodeFootage, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_proxy_path(footage, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_proxy_state(footage: OakNodeFootage) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_proxy_state(footage) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_set_proxy(footage: OakNodeFootage, path: *const c_char, state: c_int, video_stream_index: c_int, preset_version: c_int, enabled: c_int) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_set_proxy(footage, path, state, video_stream_index, preset_version, enabled) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_clear_proxy(footage: OakNodeFootage) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_clear_proxy(footage) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_get_video_params(footage: OakNodeFootage, index: c_int, out: *mut OakVideoParams) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_get_video_params(footage, index, out as *mut std::ffi::c_void) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_set_video_params(footage: OakNodeFootage, index: c_int, params: *const OakVideoParams) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_set_video_params(footage, index, params as *const std::ffi::c_void) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_get_video_length(footage: OakNodeFootage, out_num: *mut i64, out_den: *mut i64) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_get_video_length(footage, out_num, out_den) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_footage_set_cancel_atom(footage: OakNodeFootage, atom: OakCancelAtom) -> c_int { + unsafe { oaknode::ffi::footage::oaknode_footage_set_cancel_atom(footage, atom) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_create() -> OakNodeSequence { + unsafe { oaknode::ffi::sequence::oaknode_sequence_create() } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_free(sequence: *mut OakNodeSequence) { + unsafe { oaknode::ffi::sequence::oaknode_sequence_free(sequence) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_set_default_parameters(sequence: OakNodeSequence) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_set_default_parameters(sequence) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_as_node(sequence: OakNodeSequence) -> OakNodeNode { + unsafe { oaknode::ffi::sequence::oaknode_sequence_as_node(sequence) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_from_node(node: OakNodeNode) -> OakNodeSequence { + unsafe { oaknode::ffi::sequence::oaknode_sequence_from_node(node) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_track_list(sequence: OakNodeSequence, r#type: c_int, out: *mut OakNodeTrackList) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_track_list(sequence, r#type, out) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_track_count(sequence: OakNodeSequence, r#type: c_int, count: *mut c_int) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_track_count(sequence, r#type, count) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_track_at(sequence: OakNodeSequence, r#type: c_int, index: c_int, out: *mut OakNodeTrack) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_track_at(sequence, r#type, index, out) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_all_track_count(sequence: OakNodeSequence, count: *mut c_int) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_all_track_count(sequence, count) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_all_track_at(sequence: OakNodeSequence, index: c_int, out: *mut OakNodeTrack) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_all_track_at(sequence, index, out) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_playhead(sequence: OakNodeSequence, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_playhead(sequence, numerator, denominator) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_set_playhead(sequence: OakNodeSequence, numerator: c_int, denominator: c_int) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_set_playhead(sequence, numerator, denominator) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_length(sequence: OakNodeSequence, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_length(sequence, numerator, denominator) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_video_length(sequence: OakNodeSequence, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_video_length(sequence, numerator, denominator) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_audio_length(sequence: OakNodeSequence, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_audio_length(sequence, numerator, denominator) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_verify_length(sequence: OakNodeSequence) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_verify_length(sequence) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_video_stream_count(sequence: OakNodeSequence, count: *mut c_int) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_video_stream_count(sequence, count) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_audio_stream_count(sequence: OakNodeSequence, count: *mut c_int) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_audio_stream_count(sequence, count) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_video_params(sequence: OakNodeSequence, index: c_int, out: *mut OakVideoParams) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_video_params(sequence, index, out as *mut std::ffi::c_void) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_set_video_params(sequence: OakNodeSequence, index: c_int, params: OakVideoParams) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_set_video_params(sequence, index, params) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_audio_params(sequence: OakNodeSequence, index: c_int, out: *mut *mut std::ffi::c_void) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_audio_params(sequence, index, out) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_set_audio_params(sequence: OakNodeSequence, index: c_int, params: *const c_void) -> c_int { + unsafe { oaknode::ffi::sequence::oaknode_sequence_set_audio_params(sequence, index, params) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_as_node(track: OakNodeTrack) -> OakNodeNode { + unsafe { oaknode::ffi::track::oaknode_track_as_node(track) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_create(r#type: c_int) -> OakNodeTrack { + unsafe { oaknode::ffi::track::oaknode_track_create(r#type) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_free(track: *mut OakNodeTrack) { + unsafe { oaknode::ffi::track::oaknode_track_free(track) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_get_type(track: OakNodeTrack, r#type: *mut c_int) -> c_int { + unsafe { oaknode::ffi::track::oaknode_track_get_type(track, r#type) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_get_length(track: OakNodeTrack, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + unsafe { oaknode::ffi::track::oaknode_track_get_length(track, numerator, denominator) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_get_block_count(track: OakNodeTrack, count: *mut c_int) -> c_int { + unsafe { oaknode::ffi::track::oaknode_track_get_block_count(track, count) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_get_block_at(track: OakNodeTrack, index: c_int, out: *mut OakNodeBlock) -> c_int { + unsafe { oaknode::ffi::track::oaknode_track_get_block_at(track, index, out) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_append_block(track: OakNodeTrack, block: OakNodeBlock) -> c_int { + unsafe { oaknode::ffi::track::oaknode_track_append_block(track, block) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_set_type(track: OakNodeTrack, r#type: c_int) -> c_int { + unsafe { oaknode::ffi::track::oaknode_track_set_type(track, r#type) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_get_index(track: OakNodeTrack, index: *mut c_int) -> c_int { + unsafe { oaknode::ffi::track::oaknode_track_get_index(track, index) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_set_index(track: OakNodeTrack, index: c_int) -> c_int { + unsafe { oaknode::ffi::track::oaknode_track_set_index(track, index) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_get_sequence(track: OakNodeTrack, out: *mut OakNodeSequence) -> c_int { + unsafe { oaknode::ffi::track::oaknode_track_get_sequence(track, out) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_get_sequence(list: OakNodeTrackList, out: *mut OakNodeSequence) -> c_int { + unsafe { oaknode::ffi::track::oaknode_tracklist_get_sequence(list, out) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_get_track_input_id(list: OakNodeTrackList, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::track::oaknode_tracklist_get_track_input_id(list, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_array_append(list: OakNodeTrackList) -> c_int { + unsafe { oaknode::ffi::track::oaknode_tracklist_array_append(list) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_array_remove_last(list: OakNodeTrackList) -> c_int { + unsafe { oaknode::ffi::track::oaknode_tracklist_array_remove_last(list) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_get_array_index_from_cache_index(list: OakNodeTrackList, cache_index: c_int, out_index: *mut c_int) -> c_int { + unsafe { oaknode::ffi::track::oaknode_tracklist_get_array_index_from_cache_index(list, cache_index, out_index) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_get_type(list: OakNodeTrackList, r#type: *mut c_int) -> c_int { + unsafe { oaknode::ffi::track::oaknode_tracklist_get_type(list, r#type) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_get_track_count(list: OakNodeTrackList, count: *mut c_int) -> c_int { + unsafe { oaknode::ffi::track::oaknode_tracklist_get_track_count(list, count) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_get_track_at(list: OakNodeTrackList, index: c_int, out: *mut OakNodeTrack) -> c_int { + unsafe { oaknode::ffi::track::oaknode_tracklist_get_track_at(list, index, out) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_get_total_length(list: OakNodeTrackList, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + unsafe { oaknode::ffi::track::oaknode_tracklist_get_total_length(list, numerator, denominator) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_get_array_size(list: OakNodeTrackList, size: *mut c_int) -> c_int { + unsafe { oaknode::ffi::track::oaknode_tracklist_get_array_size(list, size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_add_track(list: OakNodeTrackList, track: OakNodeTrack) -> c_int { + unsafe { oaknode::ffi::track::oaknode_tracklist_add_track(list, track) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_remove_track(list: OakNodeTrackList, track: OakNodeTrack) -> c_int { + unsafe { oaknode::ffi::track::oaknode_tracklist_remove_track(list, track) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_colormanager_init(project: OakNodeProject) -> OakNodeColorManager { + unsafe { oaknode::ffi::colormanager::oaknode_colormanager_init(project) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_colormanager_free(manager: *mut OakNodeColorManager) { + unsafe { oaknode::ffi::colormanager::oaknode_colormanager_free(manager) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_colormanager_wrap_borrowed(native_manager: *mut c_void) -> OakNodeColorManager { + unsafe { oaknode::ffi::colormanager::oaknode_colormanager_wrap_borrowed(native_manager) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_colormanager_initialize(manager: OakNodeColorManager) -> c_int { + unsafe { oaknode::ffi::colormanager::oaknode_colormanager_initialize(manager) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_colormanager_set_up_default_config() -> c_int { + unsafe { oaknode::ffi::colormanager::oaknode_colormanager_set_up_default_config() } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_initialize() -> c_int { + unsafe { oaknode::ffi::serializer::oaknode_serializer_initialize() } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_shutdown() { + unsafe { oaknode::ffi::serializer::oaknode_serializer_shutdown() } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_savedata_create(load_type: c_int, project: OakNodeProject) -> OakNodeSerializerSaveData { + unsafe { oaknode::ffi::serializer::oaknode_serializer_savedata_create(load_type, project) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_savedata_free(save_data: *mut OakNodeSerializerSaveData) { + unsafe { oaknode::ffi::serializer::oaknode_serializer_savedata_free(save_data) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_savedata_set_nodes(save_data: OakNodeSerializerSaveData, nodes: *const OakNodeNode, count: c_int) -> c_int { + unsafe { oaknode::ffi::serializer::oaknode_serializer_savedata_set_nodes(save_data, nodes, count) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_savedata_set_property(save_data: OakNodeSerializerSaveData, node: OakNodeNode, key: *const c_char, value: *const c_char) -> c_int { + unsafe { oaknode::ffi::serializer::oaknode_serializer_savedata_set_property(save_data, node, key, value) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_save_to_xml(save_data: OakNodeSerializerSaveData, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::serializer::oaknode_serializer_save_to_xml(save_data, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_load_from_xml(project: OakNodeProject, xml: *const c_char, load_type: c_int, out_result: *mut c_int, out_load_data: *mut OakNodeSerializerLoadData, details_buf: *mut c_char, details_buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::serializer::oaknode_serializer_load_from_xml(project, xml, load_type, out_result, out_load_data, details_buf, details_buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_loaddata_free(load_data: *mut OakNodeSerializerLoadData) { + unsafe { oaknode::ffi::serializer::oaknode_serializer_loaddata_free(load_data) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_loaddata_node_count(load_data: OakNodeSerializerLoadData) -> c_int { + unsafe { oaknode::ffi::serializer::oaknode_serializer_loaddata_node_count(load_data) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_loaddata_node_at(load_data: OakNodeSerializerLoadData, index: c_int) -> OakNodeNode { + unsafe { oaknode::ffi::serializer::oaknode_serializer_loaddata_node_at(load_data, index) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_loaddata_get_property(load_data: OakNodeSerializerLoadData, node: OakNodeNode, key: *const c_char, buf: *mut c_char, buf_size: c_int) -> c_int { + unsafe { oaknode::ffi::serializer::oaknode_serializer_loaddata_get_property(load_data, node, key, buf, buf_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_loaddata_connection_count(load_data: OakNodeSerializerLoadData) -> c_int { + unsafe { oaknode::ffi::serializer::oaknode_serializer_loaddata_connection_count(load_data) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_loaddata_connection_at(load_data: OakNodeSerializerLoadData, index: c_int, out_output_node: *mut OakNodeNode, out_input_node: *mut OakNodeNode, input_id_buf: *mut c_char, input_id_buf_size: c_int, out_element: *mut c_int) -> c_int { + unsafe { oaknode::ffi::serializer::oaknode_serializer_loaddata_connection_at(load_data, index, out_output_node, out_input_node, input_id_buf, input_id_buf_size, out_element) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_save_to_file(project: OakNodeProject, filename: *const c_char, use_compression: c_int, out_code: *mut c_int, details: *mut c_char, details_size: c_int) -> c_int { + unsafe { oaknode::ffi::serializer::oaknode_serializer_save_to_file(project, filename, use_compression, out_code, details, details_size) } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_serializer_load_from_file(project: OakNodeProject, filename: *const c_char, out_code: *mut c_int, details: *mut c_char, details_size: c_int) -> c_int { + unsafe { oaknode::ffi::serializer::oaknode_serializer_load_from_file(project, filename, out_code, details, details_size) } +} + diff --git a/src/task/rust/src/bridge/render.rs b/src/task/rust/src/bridge/render.rs index 19200c029..478e45cb7 100644 --- a/src/task/rust/src/bridge/render.rs +++ b/src/task/rust/src/bridge/render.rs @@ -14,6 +14,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 . + //! oakrender C ABI imports. The task module reaches the render side through //! `include/render/cancelatom.h` (task cancellation), `include/render/ticket.h` //! (frame/audio render tickets), `include/render/copier.h` (export project @@ -37,166 +53,27 @@ use crate::bridge::node::{OakNodeColorManager, OakNodeNode, OakNodeProject}; use crate::handle::CHandle; /// Mirror of `OakCancelAtom` (`include/render/cancelatom.h`). -#[repr(C)] -#[derive(Clone, Copy)] -pub struct OakCancelAtom { - /// Opaque pointer to the reference-counted object. - pub ctx: *mut c_void, - /// Atomic increment. - pub addref: Option, - /// Atomic decrement; destroys at zero. - pub release: Option, - /// ABI version (`OAKRENDER_ABI_VERSION`). - pub abi_version: u32, -} +pub type OakCancelAtom = oakrender::ffi::OakCancelAtom; -impl OakCancelAtom { - /// The empty (null) atom. - pub fn null() -> Self { - OakCancelAtom { - ctx: std::ptr::null_mut(), - addref: None, - release: None, - abi_version: 0, - } - } - /// Whether the atom is empty (`ctx == NULL`). - pub fn is_null(&self) -> bool { - self.ctx.is_null() - } - - /// Adopt the fields of a generic `CHandle` (layout-identical) — the task - /// module stores the atom as a `CHandle` for its public API. - pub fn from_chandle(h: CHandle) -> Self { - OakCancelAtom { - ctx: h.ctx, - addref: h.addref, - release: h.release, - abi_version: h.abi_version, - } - } - - /// Convert back to a generic `CHandle` (layout-identical). - pub fn to_chandle(&self) -> CHandle { - CHandle { - ctx: self.ctx, - addref: self.addref, - release: self.release, - abi_version: self.abi_version, - } - } -} /// Mirror of `OakRenderTicket` (`include/render/ticket.h`). -#[repr(C)] -#[derive(Clone, Copy)] -pub struct OakRenderTicket { - /// Opaque pointer to the reference-counted object. - pub ctx: *mut c_void, - /// Atomic increment. - pub addref: Option, - /// Atomic decrement; destroys at zero. - pub release: Option, - /// ABI version (`OAKRENDER_ABI_VERSION`). - pub abi_version: u32, -} +pub type OakRenderTicket = oakrender::ffi::OakRenderTicket; + /// Mirror of `OakRenderCache` (`include/render/cache.h`). -#[repr(C)] -#[derive(Clone, Copy)] -pub struct OakRenderCache { - /// Opaque pointer to the reference-counted object. - pub ctx: *mut c_void, - /// Atomic increment. - pub addref: Option, - /// Atomic decrement; destroys at zero. - pub release: Option, - /// ABI version (`OAKRENDER_ABI_VERSION`). - pub abi_version: u32, -} +pub type OakRenderCache = oakrender::ffi::OakRenderCache; -impl OakRenderCache { - /// The empty (null) cache handle. - pub fn null() -> Self { - OakRenderCache { - ctx: std::ptr::null_mut(), - addref: None, - release: None, - abi_version: 0, - } - } - /// Convert to the layout-identical generic `CHandle`. - pub fn to_chandle(&self) -> CHandle { - CHandle { - ctx: self.ctx, - addref: self.addref, - release: self.release, - abi_version: self.abi_version, - } - } - - /// Adopt the fields of a generic `CHandle` (layout-identical). - pub fn from_chandle(h: CHandle) -> Self { - OakRenderCache { - ctx: h.ctx, - addref: h.addref, - release: h.release, - abi_version: h.abi_version, - } - } -} /// Mirror of `OakRenderProjectCopier` (`include/render/copier.h`). -#[repr(C)] -#[derive(Clone, Copy)] -pub struct OakRenderProjectCopier { - /// Opaque pointer to the reference-counted object. - pub ctx: *mut c_void, - /// Atomic increment. - pub addref: Option, - /// Atomic decrement; destroys at zero. - pub release: Option, - /// ABI version (`OAKRENDER_ABI_VERSION`). - pub abi_version: u32, -} +pub type OakRenderProjectCopier = oakrender::ffi::OakRenderProjectCopier; + /// Mirror of `OakColorProcessor` (`include/render/color.h`). -#[repr(C)] -#[derive(Clone, Copy)] -pub struct OakColorProcessor { - /// Opaque pointer to the reference-counted object. - pub ctx: *mut c_void, - /// Atomic increment. - pub addref: Option, - /// Atomic decrement; destroys at zero. - pub release: Option, - /// ABI version (`OAKRENDER_ABI_VERSION`). - pub abi_version: u32, -} +pub type OakColorProcessor = oakrender::ffi::OakColorProcessor; -impl OakColorProcessor { - /// Convert to the layout-identical generic `CHandle`. - pub fn to_chandle(&self) -> CHandle { - CHandle { - ctx: self.ctx, - addref: self.addref, - release: self.release, - abi_version: self.abi_version, - } - } - /// Adopt the fields of a generic `CHandle` (layout-identical). - pub fn from_chandle(h: CHandle) -> Self { - OakColorProcessor { - ctx: h.ctx, - addref: h.addref, - release: h.release, - abi_version: h.abi_version, - } - } -} /// Mirror of `oakrender_ticket_finished_fn` (`include/render/ticket.h`). /// @@ -208,64 +85,45 @@ impl OakColorProcessor { pub type OakRenderTicketFinishedFn = unsafe extern "C" fn(ticket: OakRenderTicket, userdata: *mut c_void); /// Mirror of `oakrender_video_ticket_params` (`include/render/ticket.h`). -#[repr(C)] -pub struct OakRenderVideoTicketParams { - /// Connected texture output node (borrowed). - pub output_node: OakNodeNode, - /// Video params by value (oakcommon handle). - pub video_params: OakVideoParams, - /// Borrowed oakcore audio params, may be null ctx. - pub audio_params: *const OakAudioParams, - /// Frame timestamp numerator (seconds rational). - pub time_num: i64, - /// Frame timestamp denominator. - pub time_den: i64, - /// Borrowed color manager; empty ctx = NULL. - pub color_manager: OakNodeColorManager, - /// `olive::RenderMode::Mode` as int. - pub mode: c_int, - /// Forced output width (0 = off). - pub force_width: c_int, - /// Forced output height (0 = off). - pub force_height: c_int, - /// Forced color matrix (used when `has_force_matrix` != 0). - pub force_matrix: [f64; 16], - /// Whether `force_matrix` is in effect. - pub has_force_matrix: c_int, - /// Forced pixel format (`PixelFormat` as int, -1 = off). - pub force_format: c_int, - /// Forced channel count (0 = off). - pub force_channel_count: c_int, - /// Forced color output (borrowed; empty ctx = none). - pub force_color_output: OakColorProcessor, - /// Forced color transform by value (empty ctx = default). - pub force_color_transform: OakColorTransform, - /// Borrowed frame cache; empty ctx = none. - pub cache: OakRenderCache, +pub type OakRenderVideoTicketParams = oakrender::ffi::OakVideoTicketParams; + + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_cancelatom_init() -> OakCancelAtom { + unsafe { oakrender::ffi::cancelatom::oakrender_cancelatom_init() } } -extern "C" { - // --- cancelatom.h --- - /// `oakrender_cancelatom_init`. - pub fn oakrender_cancelatom_init() -> OakCancelAtom; - /// `oakrender_cancelatom_free`. - pub fn oakrender_cancelatom_free(atom: *mut OakCancelAtom); - /// `oakrender_cancelatom_cancel`. - pub fn oakrender_cancelatom_cancel(atom: OakCancelAtom) -> c_int; - /// `oakrender_cancelatom_is_cancelled`. - pub fn oakrender_cancelatom_is_cancelled(atom: OakCancelAtom, cancelled: *mut c_int) -> c_int; - /// `oakrender_cancelatom_heard_cancel`. - pub fn oakrender_cancelatom_heard_cancel(atom: OakCancelAtom, heard: *mut c_int) -> c_int; +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_cancelatom_free(atom: *mut OakCancelAtom) { + unsafe { oakrender::ffi::cancelatom::oakrender_cancelatom_free(atom) } +} - // --- ticket.h --- - /// `oakrender_ticket_render_frame`. - pub fn oakrender_ticket_render_frame( +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_cancelatom_cancel(atom: OakCancelAtom) -> c_int { + unsafe { oakrender::ffi::cancelatom::oakrender_cancelatom_cancel(atom) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_cancelatom_is_cancelled(atom: OakCancelAtom, cancelled: *mut c_int) -> c_int { + unsafe { oakrender::ffi::cancelatom::oakrender_cancelatom_is_cancelled(atom, cancelled) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_cancelatom_heard_cancel(atom: OakCancelAtom, heard: *mut c_int) -> c_int { + unsafe { oakrender::ffi::cancelatom::oakrender_cancelatom_heard_cancel(atom, heard) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_ticket_render_frame( params: *const OakRenderVideoTicketParams, cb: Option, userdata: *mut c_void, - ) -> OakRenderTicket; - /// `oakrender_ticket_render_audio`. - pub fn oakrender_ticket_render_audio( + ) -> OakRenderTicket { + unsafe { oakrender::ffi::ticket::oakrender_ticket_render_frame(params, cb, userdata) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_ticket_render_audio( output_node: OakNodeNode, in_num: i64, in_den: i64, @@ -275,80 +133,125 @@ extern "C" { mode: c_int, cb: Option, userdata: *mut c_void, - ) -> OakRenderTicket; - /// `oakrender_ticket_is_finished`. - pub fn oakrender_ticket_is_finished(ticket: OakRenderTicket) -> c_int; - /// `oakrender_ticket_wait`. - pub fn oakrender_ticket_wait(ticket: OakRenderTicket) -> c_int; - /// `oakrender_ticket_cancel`. - pub fn oakrender_ticket_cancel(ticket: OakRenderTicket) -> c_int; - /// `oakrender_ticket_get_type`. - pub fn oakrender_ticket_get_type(ticket: OakRenderTicket) -> c_int; - /// `oakrender_ticket_get_frame`. - pub fn oakrender_ticket_get_frame(ticket: OakRenderTicket, out: *mut OakFrame) -> c_int; - /// `oakrender_ticket_get_time`. - pub fn oakrender_ticket_get_time(ticket: OakRenderTicket, out_num: *mut i64, out_den: *mut i64) -> c_int; - /// `oakrender_ticket_get_range`. - pub fn oakrender_ticket_get_range( + ) -> OakRenderTicket { + unsafe { oakrender::ffi::ticket::oakrender_ticket_render_audio(output_node, in_num, in_den, out_num, out_den, params, mode, cb, userdata) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_ticket_is_finished(ticket: OakRenderTicket) -> c_int { + unsafe { oakrender::ffi::ticket::oakrender_ticket_is_finished(ticket) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_ticket_wait(ticket: OakRenderTicket) -> c_int { + unsafe { oakrender::ffi::ticket::oakrender_ticket_wait(ticket) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_ticket_cancel(ticket: OakRenderTicket) -> c_int { + unsafe { oakrender::ffi::ticket::oakrender_ticket_cancel(ticket) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_ticket_get_type(ticket: OakRenderTicket) -> c_int { + unsafe { oakrender::ffi::ticket::oakrender_ticket_get_type(ticket) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_ticket_get_frame(ticket: OakRenderTicket, out: *mut OakFrame) -> c_int { + unsafe { oakrender::ffi::ticket::oakrender_ticket_get_frame(ticket, out) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_ticket_get_time(ticket: OakRenderTicket, out_num: *mut i64, out_den: *mut i64) -> c_int { + unsafe { oakrender::ffi::ticket::oakrender_ticket_get_time(ticket, out_num, out_den) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_ticket_get_range( ticket: OakRenderTicket, in_num: *mut i64, in_den: *mut i64, out_num: *mut i64, out_den: *mut i64, - ) -> c_int; - /// `oakrender_ticket_get_samples`. - pub fn oakrender_ticket_get_samples(ticket: OakRenderTicket, out: *mut *mut std::ffi::c_void) -> c_int; - /// `oakrender_ticket_free`. - pub fn oakrender_ticket_free(ticket: *mut OakRenderTicket); + ) -> c_int { + unsafe { oakrender::ffi::ticket::oakrender_ticket_get_range(ticket, in_num, in_den, out_num, out_den) } +} - // --- copier.h --- - /// `oakrender_project_copier_create`. - pub fn oakrender_project_copier_create() -> OakRenderProjectCopier; - /// `oakrender_project_copier_free`. - pub fn oakrender_project_copier_free(copier: *mut OakRenderProjectCopier); - /// `oakrender_project_copier_set_project`. - pub fn oakrender_project_copier_set_project( +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_ticket_get_samples(ticket: OakRenderTicket, out: *mut *mut std::ffi::c_void) -> c_int { + unsafe { oakrender::ffi::ticket::oakrender_ticket_get_samples(ticket, out) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_ticket_free(ticket: *mut OakRenderTicket) { + unsafe { oakrender::ffi::ticket::oakrender_ticket_free(ticket) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_project_copier_create() -> OakRenderProjectCopier { + unsafe { oakrender::ffi::copier::oakrender_project_copier_create() } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_project_copier_free(copier: *mut OakRenderProjectCopier) { + unsafe { oakrender::ffi::copier::oakrender_project_copier_free(copier) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_project_copier_set_project( copier: OakRenderProjectCopier, project: OakNodeProject, - ) -> c_int; - /// `oakrender_project_copier_get_copy`. - pub fn oakrender_project_copier_get_copy( + ) -> c_int { + unsafe { oakrender::ffi::copier::oakrender_project_copier_set_project(copier, project) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_project_copier_get_copy( copier: OakRenderProjectCopier, original: OakNodeNode, - ) -> OakNodeNode; - /// `oakrender_project_copier_get_copied_project`. - pub fn oakrender_project_copier_get_copied_project( - copier: OakRenderProjectCopier, - ) -> OakNodeProject; + ) -> OakNodeNode { + unsafe { oakrender::ffi::copier::oakrender_project_copier_get_copy(copier, original) } +} - // --- color.h --- - /// `oakrender_color_processor_create` — builds a processor between two - /// color spaces; `direction` is `OAKRENDER_COLOR_DIRECTION_*` - /// (include/render/color.h). - pub fn oakrender_color_processor_create( +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_project_copier_get_copied_project( + copier: OakRenderProjectCopier, + ) -> OakNodeProject { + unsafe { oakrender::ffi::copier::oakrender_project_copier_get_copied_project(copier) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_color_processor_create( src_space: *const c_char, dst_transform: *const c_char, direction: c_int, - ) -> OakColorProcessor; - /// `oakrender_color_processor_free`. - pub fn oakrender_color_processor_free(processor: *mut OakColorProcessor); - /// `oakrender_color_processor_is_valid`. - pub fn oakrender_color_processor_is_valid(processor: OakColorProcessor) -> c_int; + ) -> OakColorProcessor { + unsafe { oakrender::ffi::color::oakrender_color_processor_create(src_space, dst_transform, direction) } +} - // --- renderer.h --- - /// `oakrender_codec_frame_free` — release a frame handed out by a - /// render ticket. - pub fn oakrender_codec_frame_free(frame: *mut crate::bridge::codec::OakFrame); +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_color_processor_free(processor: *mut OakColorProcessor) { + unsafe { oakrender::ffi::color::oakrender_color_processor_free(processor) } +} - // --- manager.h --- - /// `oakrender_manager_set_aggressive_gc`. - pub fn oakrender_manager_set_aggressive_gc(enabled: c_int) -> c_int; +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_color_processor_is_valid(processor: OakColorProcessor) -> c_int { + unsafe { oakrender::ffi::color::oakrender_color_processor_is_valid(processor) } +} - // --- cache.h --- - /// `oakrender_cache_get_invalidated_ranges` — two-stage range query on - /// a frame cache; returns the number of ranges (or the required flat - /// array size). - pub fn oakrender_cache_get_invalidated_ranges( +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_codec_frame_free(frame: *mut crate::bridge::codec::OakFrame) { + unsafe { oakrender::ffi::renderer::oakrender_codec_frame_free(frame) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_manager_set_aggressive_gc(enabled: c_int) -> c_int { + unsafe { oakrender::ffi::ticket::oakrender_manager_set_aggressive_gc(enabled) } +} + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_cache_get_invalidated_ranges( cache: OakRenderCache, in_num: i64, in_den: i64, @@ -356,7 +259,12 @@ extern "C" { out_den: i64, flat: *mut i64, flat_size: c_int, - ) -> c_int; - /// `oakrender_cache_free`. - pub fn oakrender_cache_free(cache: *mut OakRenderCache); + ) -> c_int { + unsafe { oakrender::ffi::cache::oakrender_cache_get_invalidated_ranges(cache, in_num, in_den, out_num, out_den, flat, flat_size) } } + +/// Direct call into the `oakrender` crate (single-lib unification). +pub fn oakrender_cache_free(cache: *mut OakRenderCache) { + unsafe { oakrender::ffi::cache::oakrender_cache_free(cache) } +} + diff --git a/src/task/rust/src/bridge/timeline.rs b/src/task/rust/src/bridge/timeline.rs index f474b839b..58537c860 100644 --- a/src/task/rust/src/bridge/timeline.rs +++ b/src/task/rust/src/bridge/timeline.rs @@ -14,6 +14,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 . + //! oaktimeline C ABI imports. The OTIO load task builds tracks through the //! timeline edit factories (`include/timeline/edit.h`) instead of the raw //! track-list primitives, matching `src/task/src/project/loadotio/loadotio.cpp`. @@ -22,8 +38,8 @@ use crate::bridge::node::OakNodeTrackList; use crate::bridge::undo::OakUndoCommand; -extern "C" { - /// `oaktimeline_add_track_command` (`include/timeline/edit.h`) — - /// olive::TimelineAddTrackCommand. - pub fn oaktimeline_add_track_command(list: OakNodeTrackList) -> OakUndoCommand; +/// Direct call into the `oaktimeline` crate (single-lib unification). +pub fn oaktimeline_add_track_command(list: OakNodeTrackList) -> OakUndoCommand { + unsafe { oaktimeline::ffi::edit::oaktimeline_add_track_command(list) } } + diff --git a/src/task/rust/src/bridge/undo.rs b/src/task/rust/src/bridge/undo.rs index c794995de..c35bd7721 100644 --- a/src/task/rust/src/bridge/undo.rs +++ b/src/task/rust/src/bridge/undo.rs @@ -14,6 +14,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 . + //! oakundo C ABI imports. Import/load tasks build undo commands through the //! C ABI vtable (`oakundo_command_init` with Rust closures as userdata) — //! no C++ UndoCommand subclassing exists on this side. Mirrors @@ -27,31 +43,46 @@ use crate::handle::CHandle; pub type OakUndoCommand = CHandle; /// Mirror of `OakUndoCommandVtable` (`include/undo/undocommand.h`). -#[repr(C)] -pub struct OakUndoCommandVtable { - /// Redo callback; may be NULL. - pub redo: Option, - /// Undo callback; may be NULL. - pub undo: Option, - /// Free callback; may be NULL. - pub free_fn: Option, +pub type OakUndoCommandVtable = oakundo::undocommand::OakUndoCommandVtable; + + +/// Direct call into the `oakundo` crate (single-lib unification). +pub fn oakundo_command_init(vtable: *const OakUndoCommandVtable, userdata: *mut c_void) -> OakUndoCommand { + unsafe { oakundo::ffi::command::oakundo_command_init(vtable, userdata) } } -extern "C" { - /// `oakundo_command_init` (vtable command). - pub fn oakundo_command_init(vtable: *const OakUndoCommandVtable, userdata: *mut c_void) -> OakUndoCommand; - /// `oakundo_command_init_multi`. - pub fn oakundo_command_init_multi() -> OakUndoCommand; - /// `oakundo_command_multi_add_child`. - pub fn oakundo_command_multi_add_child(multi: OakUndoCommand, child: OakUndoCommand) -> c_int; - /// `oakundo_command_multi_child_count`. - pub fn oakundo_command_multi_child_count(multi: OakUndoCommand, out_count: *mut c_int) -> c_int; - /// `oakundo_command_multi_child`. - pub fn oakundo_command_multi_child(multi: OakUndoCommand, index: c_int, out_child: *mut OakUndoCommand) -> c_int; - /// `oakundo_command_redo_now`. - pub fn oakundo_command_redo_now(command: OakUndoCommand) -> c_int; - /// `oakundo_command_undo_now`. - pub fn oakundo_command_undo_now(command: OakUndoCommand) -> c_int; - /// `oakundo_command_free`. - pub fn oakundo_command_free(command: *mut OakUndoCommand); +/// Direct call into the `oakundo` crate (single-lib unification). +pub fn oakundo_command_init_multi() -> OakUndoCommand { + unsafe { oakundo::ffi::command::oakundo_command_init_multi() } } + +/// Direct call into the `oakundo` crate (single-lib unification). +pub fn oakundo_command_multi_add_child(multi: OakUndoCommand, child: OakUndoCommand) -> c_int { + unsafe { oakundo::ffi::command::oakundo_command_multi_add_child(multi, child) } +} + +/// Direct call into the `oakundo` crate (single-lib unification). +pub fn oakundo_command_multi_child_count(multi: OakUndoCommand, out_count: *mut c_int) -> c_int { + unsafe { oakundo::ffi::command::oakundo_command_multi_child_count(multi, out_count) } +} + +/// Direct call into the `oakundo` crate (single-lib unification). +pub fn oakundo_command_multi_child(multi: OakUndoCommand, index: c_int, out_child: *mut OakUndoCommand) -> c_int { + unsafe { oakundo::ffi::command::oakundo_command_multi_child(multi, index, out_child) } +} + +/// Direct call into the `oakundo` crate (single-lib unification). +pub fn oakundo_command_redo_now(command: OakUndoCommand) -> c_int { + unsafe { oakundo::ffi::command::oakundo_command_redo_now(command) } +} + +/// Direct call into the `oakundo` crate (single-lib unification). +pub fn oakundo_command_undo_now(command: OakUndoCommand) -> c_int { + unsafe { oakundo::ffi::command::oakundo_command_undo_now(command) } +} + +/// Direct call into the `oakundo` crate (single-lib unification). +pub fn oakundo_command_free(command: *mut OakUndoCommand) { + unsafe { oakundo::ffi::command::oakundo_command_free(command) } +} + diff --git a/src/task/rust/src/codecbridge.rs b/src/task/rust/src/codecbridge.rs index a2d8acfd9..6e000f05a 100644 --- a/src/task/rust/src/codecbridge.rs +++ b/src/task/rust/src/codecbridge.rs @@ -47,7 +47,7 @@ fn proxy_params_from_codec(params: &bridge::codec::OakCodecProxyParams) -> Proxy } /// Read a NUL-terminated char array into a String (lossy). -unsafe fn cstr_buf_to_string(buf: &[c_char]) -> String { +unsafe fn cstr_buf_to_string(buf: &[u8]) -> String { let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len()); let bytes = unsafe { std::slice::from_raw_parts(buf.as_ptr() as *const u8, len) }; String::from_utf8_lossy(bytes).into_owned() diff --git a/src/task/rust/src/conform.rs b/src/task/rust/src/conform.rs index 82c23b627..cc42eb11c 100644 --- a/src/task/rust/src/conform.rs +++ b/src/task/rust/src/conform.rs @@ -142,7 +142,7 @@ impl TaskBehavior for ConformTask { self.sample_rate, self.channel_layout, self.sample_format, - bridge::render::OakCancelAtom::from_chandle(task.get_cancel_atom()), + task.get_cancel_atom(), ) }; unsafe { diff --git a/src/task/rust/src/customcache.rs b/src/task/rust/src/customcache.rs index 5d8ed7e52..fe18cc014 100644 --- a/src/task/rust/src/customcache.rs +++ b/src/task/rust/src/customcache.rs @@ -49,7 +49,7 @@ impl CustomCacheState { pub fn finish(&self) { *self.cancelled_through_finish.lock().unwrap() = true; if !self.atom.is_null() { - let atom = crate::bridge::render::OakCancelAtom::from_chandle(self.atom); + let atom = self.atom; unsafe { crate::bridge::render::oakrender_cancelatom_cancel(atom); } @@ -72,7 +72,7 @@ impl CustomCacheState { /// hook. Used by the owning cache to abort a fill. pub fn cancel(&self) { if !self.atom.is_null() { - let atom = crate::bridge::render::OakCancelAtom::from_chandle(self.atom); + let atom = self.atom; unsafe { crate::bridge::render::oakrender_cancelatom_cancel(atom); } diff --git a/src/task/rust/src/export.rs b/src/task/rust/src/export.rs index d5ab338bc..e3760213a 100644 --- a/src/task/rust/src/export.rs +++ b/src/task/rust/src/export.rs @@ -165,7 +165,7 @@ impl ExportTask { }; let bytes = self.encoding_params.filename.as_bytes(); let n = bytes.len().min(1023); - params.filename[..n].copy_from_slice(unsafe { std::slice::from_raw_parts(bytes.as_ptr() as *const i8, n) }); + params.filename[..n].copy_from_slice(&bytes[..n]); params.format = self.encoding_params.format; params.video_enabled = self.encoding_params.video_enabled as i32; params.video_codec = self.encoding_params.video_codec; diff --git a/src/task/rust/src/ffi/project.rs b/src/task/rust/src/ffi/project.rs index 5981fcb64..26accad7b 100644 --- a/src/task/rust/src/ffi/project.rs +++ b/src/task/rust/src/ffi/project.rs @@ -386,8 +386,7 @@ unsafe fn convert_encoding_params(p: &OakCodecEncodingParams) -> EncodingParams } /// Read a NUL-terminated char array into a String (lossy). -unsafe fn c_char_array_to_string(buf: &[c_char]) -> String { +unsafe fn c_char_array_to_string(buf: &[u8]) -> String { let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len()); - let bytes = unsafe { std::slice::from_raw_parts(buf.as_ptr() as *const u8, len) }; - String::from_utf8_lossy(bytes).into_owned() + String::from_utf8_lossy(&buf[..len]).into_owned() } diff --git a/src/task/rust/src/precache.rs b/src/task/rust/src/precache.rs index 3bdd8dc83..b1a254362 100644 --- a/src/task/rust/src/precache.rs +++ b/src/task/rust/src/precache.rs @@ -97,7 +97,7 @@ impl TaskBehavior for PreCacheTask { bridge::node::oaknode_node_get_video_frame_cache(self.sequence, &mut cache); } - self.render.set_render_inputs(color_manager, cache.to_chandle(), 0 /* k_online */, false, range); + self.render.set_render_inputs(color_manager, cache, 0 /* k_online */, false, range); // Drive the render with `self` as the subclass behavior (the C++ // virtual dispatch receiver); the render is temporarily moved out to diff --git a/src/task/rust/src/project/import.rs b/src/task/rust/src/project/import.rs index aa4bd223c..5896c662c 100644 --- a/src/task/rust/src/project/import.rs +++ b/src/task/rust/src/project/import.rs @@ -197,7 +197,7 @@ impl ProjectImportTask { } unsafe { - bridge::node::oaknode_footage_set_cancel_atom(footage, bridge::render::OakCancelAtom::from_chandle(task.get_cancel_atom())); + bridge::node::oaknode_footage_set_cancel_atom(footage, task.get_cancel_atom()); } let ok = unsafe { bridge::node::oaknode_footage_set_filename(footage, cstr(file_path)) } == 0; unsafe { diff --git a/src/task/rust/src/render.rs b/src/task/rust/src/render.rs index 0c8845bc9..54f252d37 100644 --- a/src/task/rust/src/render.rs +++ b/src/task/rust/src/render.rs @@ -259,14 +259,14 @@ impl RenderTask { has_force_matrix: if force.has_force_matrix { 1 } else { 0 }, force_format: force.force_format, force_channel_count: force.force_channel_count, - force_color_output: bridge::render::OakColorProcessor::from_chandle(force.force_color_output), + force_color_output: force.force_color_output, force_color_transform: OakColorTransform { ctx: std::ptr::null_mut(), addref: None, release: None, abi_version: 0, }, - cache: bridge::render::OakRenderCache::from_chandle(self.cache), + cache: self.cache, }; let ticket = unsafe { diff --git a/src/task/rust/src/task.rs b/src/task/rust/src/task.rs index d8dfd21cf..f0f7d2257 100644 --- a/src/task/rust/src/task.rs +++ b/src/task/rust/src/task.rs @@ -122,7 +122,7 @@ impl Drop for Task { // Free the cancellation atom only when we created it ourselves; // borrowed atoms are released by their owner. if self.owns_atom && !self.cancel_atom.is_null() { - let mut atom = bridge::render::OakCancelAtom::from_chandle(self.cancel_atom); + let mut atom = self.cancel_atom; unsafe { bridge::render::oakrender_cancelatom_free(&mut atom); } @@ -137,7 +137,7 @@ impl Task { pub fn new(title: &str, cancel_atom: CHandle) -> Task { let (cancel_atom, owns_atom) = if cancel_atom.is_null() { let atom = unsafe { bridge::render::oakrender_cancelatom_init() }; - (atom.to_chandle(), true) + (atom, true) } else { (cancel_atom, false) }; @@ -210,7 +210,7 @@ impl Task { /// invoke the cancel event callback if one is registered. pub fn cancel(&mut self) { if !self.cancel_atom.is_null() { - let atom = bridge::render::OakCancelAtom::from_chandle(self.cancel_atom); + let atom = self.cancel_atom; unsafe { bridge::render::oakrender_cancelatom_cancel(atom); } @@ -225,7 +225,7 @@ impl Task { if self.cancel_atom.is_null() { return false; } - let atom = bridge::render::OakCancelAtom::from_chandle(self.cancel_atom); + let atom = self.cancel_atom; let mut cancelled = 0; unsafe { bridge::render::oakrender_cancelatom_is_cancelled(atom, &mut cancelled); @@ -243,7 +243,7 @@ impl Task { /// between a task and its behavior's inner base task. pub fn set_cancel_atom(&mut self, atom: CHandle) { if self.owns_atom && !self.cancel_atom.is_null() { - let mut old = bridge::render::OakCancelAtom::from_chandle(self.cancel_atom); + let mut old = self.cancel_atom; unsafe { bridge::render::oakrender_cancelatom_free(&mut old); } diff --git a/src/task/rust/tests/common/mod.rs b/src/task/rust/tests/common/mod.rs index 3322f6b2c..4f94368ba 100644 --- a/src/task/rust/tests/common/mod.rs +++ b/src/task/rust/tests/common/mod.rs @@ -62,12 +62,12 @@ pub fn fake_atom() -> oaktask::bridge::render::OakCancelAtom { } } -pub fn write_cstr(s: &str, buf: *mut c_char, size: i32) -> i32 { +pub fn write_cstr(s: &str, buf: *mut u8, size: i32) -> i32 { let bytes = s.as_bytes(); let needed = bytes.len() as i32 + 1; if !buf.is_null() && size >= needed { unsafe { - std::ptr::copy_nonoverlapping(bytes.as_ptr() as *const c_char, buf, bytes.len()); + std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len()); *buf.add(bytes.len()) = 0; } } @@ -346,7 +346,7 @@ pub unsafe extern "C" fn oakcodec_decoder_last_error( buf: *mut c_char, size: c_int, ) -> c_int { - write_cstr(&DECODER_LAST_ERROR.lock().unwrap().clone(), buf, size) + write_cstr(&DECODER_LAST_ERROR.lock().unwrap().clone(), buf as *mut u8, size) } #[no_mangle] @@ -390,7 +390,7 @@ pub unsafe extern "C" fn oakcodec_decoder_transform_image_sequence_file_name( let count = digits.len().max(1); let prefix = &stem[..stem.len() - digits.len()]; let out = format!("{prefix}{:0count$}{ext}", number); - write_cstr(&out, buf, size) + write_cstr(&out, buf as *mut u8, size) } #[no_mangle] @@ -460,7 +460,7 @@ pub unsafe extern "C" fn oakcodec_encoder_last_error( size: c_int, ) -> c_int { if ENCODER_FLUSH_ERROR.load(Ordering::SeqCst) != 0 { - write_cstr(&ENCODER_LAST_ERROR.lock().unwrap().clone(), buf, size) + write_cstr(&ENCODER_LAST_ERROR.lock().unwrap().clone(), buf as *mut u8, size) } else { 0 } @@ -485,7 +485,7 @@ pub unsafe extern "C" fn oakcodec_encoding_generate_matrix( #[no_mangle] pub unsafe extern "C" fn oakcodec_export_format_get_extension(_format: c_int, buf: *mut c_char, size: c_int) -> c_int { - write_cstr("srt", buf, size) + write_cstr("srt", buf as *mut u8, size) } #[no_mangle] @@ -498,7 +498,7 @@ pub unsafe extern "C" fn oakcodec_proxy_find_ffmpeg( if path.is_empty() { return 0; } - write_cstr(&path, buf, size) + write_cstr(&path, buf as *mut u8, size) } #[no_mangle] @@ -729,7 +729,7 @@ pub unsafe extern "C" fn oakcommon_config_get( } let key = std::ffi::CStr::from_ptr(key).to_string_lossy().into_owned(); if key == "DefaultSequenceFrameRate" && CONFIG_DEFAULT_SEQ_FRAME_RATE.load(Ordering::SeqCst) != 0 { - write_cstr("25/1", buf, size) + write_cstr("25/1", buf as *mut u8, size) } else { 0 } @@ -774,17 +774,17 @@ pub unsafe extern "C" fn oaknode_project_root(_project: oaktask::bridge::node::O #[no_mangle] pub unsafe extern "C" fn oaknode_project_name(_project: oaktask::bridge::node::OakNodeProject, buf: *mut c_char, size: c_int) -> c_int { - write_cstr("proj", buf, size) + write_cstr("proj", buf as *mut u8, size) } #[no_mangle] pub unsafe extern "C" fn oaknode_project_filename(_project: oaktask::bridge::node::OakNodeProject, buf: *mut c_char, size: c_int) -> c_int { - write_cstr(&PROJECT_FILENAME.lock().unwrap().clone(), buf, size) + write_cstr(&PROJECT_FILENAME.lock().unwrap().clone(), buf as *mut u8, size) } #[no_mangle] pub unsafe extern "C" fn oaknode_project_pretty_filename(_project: oaktask::bridge::node::OakNodeProject, buf: *mut c_char, size: c_int) -> c_int { - write_cstr("pretty", buf, size) + write_cstr("pretty", buf as *mut u8, size) } #[no_mangle] @@ -809,7 +809,7 @@ pub unsafe extern "C" fn oaknode_project_is_new(_project: oaktask::bridge::node: #[no_mangle] pub unsafe extern "C" fn oaknode_project_cache_path(_project: oaktask::bridge::node::OakNodeProject, buf: *mut c_char, size: c_int) -> c_int { - write_cstr("/tmp/oak-cache", buf, size) + write_cstr("/tmp/oak-cache", buf as *mut u8, size) } #[no_mangle] @@ -829,7 +829,7 @@ pub unsafe extern "C" fn oaknode_project_set_cache_location_setting(_project: oa #[no_mangle] pub unsafe extern "C" fn oaknode_project_get_custom_cache_path(_project: oaktask::bridge::node::OakNodeProject, buf: *mut c_char, size: c_int) -> c_int { - write_cstr("", buf, size) + write_cstr("", buf as *mut u8, size) } #[no_mangle] @@ -839,7 +839,7 @@ pub unsafe extern "C" fn oaknode_project_set_custom_cache_path(_project: oaktask #[no_mangle] pub unsafe extern "C" fn oaknode_project_get_uuid(_project: oaktask::bridge::node::OakNodeProject, buf: *mut c_char, size: c_int) -> c_int { - write_cstr("uuid", buf, size) + write_cstr("uuid", buf as *mut u8, size) } #[no_mangle] @@ -941,7 +941,7 @@ pub unsafe extern "C" fn oaknode_footage_as_node(footage: oaktask::bridge::node: #[no_mangle] pub unsafe extern "C" fn oaknode_footage_filename(_footage: oaktask::bridge::node::OakNodeFootage, buf: *mut c_char, size: c_int) -> c_int { - write_cstr(&FOOTAGE_FILENAME.lock().unwrap().clone(), buf, size) + write_cstr(&FOOTAGE_FILENAME.lock().unwrap().clone(), buf as *mut u8, size) } #[no_mangle] @@ -973,7 +973,7 @@ pub unsafe extern "C" fn oaknode_footage_set_timestamp(_footage: oaktask::bridge #[no_mangle] pub unsafe extern "C" fn oaknode_footage_decoder(_footage: oaktask::bridge::node::OakNodeFootage, buf: *mut c_char, size: c_int) -> c_int { - write_cstr("ffmpeg", buf, size) + write_cstr("ffmpeg", buf as *mut u8, size) } #[no_mangle] @@ -1023,7 +1023,7 @@ pub unsafe extern "C" fn oaknode_footage_set_proxy_enabled(_footage: oaktask::br #[no_mangle] pub unsafe extern "C" fn oaknode_footage_proxy_path(_footage: oaktask::bridge::node::OakNodeFootage, buf: *mut c_char, size: c_int) -> c_int { - write_cstr("", buf, size) + write_cstr("", buf as *mut u8, size) } #[no_mangle] @@ -1335,7 +1335,7 @@ pub unsafe extern "C" fn oaknode_tracklist_get_sequence(_list: oaktask::bridge:: #[no_mangle] pub unsafe extern "C" fn oaknode_tracklist_get_track_input_id(_list: oaktask::bridge::node::OakNodeTrackList, buf: *mut c_char, size: c_int) -> c_int { - write_cstr("sub_in", buf, size) + write_cstr("sub_in", buf as *mut u8, size) } #[no_mangle] @@ -1648,7 +1648,7 @@ pub unsafe extern "C" fn oaknode_serializer_savedata_set_property( #[no_mangle] pub unsafe extern "C" fn oaknode_serializer_save_to_xml(_save_data: oaktask::bridge::node::OakNodeSerializerSaveData, buf: *mut c_char, size: c_int) -> c_int { - write_cstr("", buf, size) + write_cstr("", buf as *mut u8, size) } #[no_mangle] @@ -1667,7 +1667,7 @@ pub unsafe extern "C" fn oaknode_serializer_load_from_xml( if !_out_load_data.is_null() { *_out_load_data = fake_handle(); } - write_cstr(&SERIALIZER_DETAILS.lock().unwrap().clone(), _details, _details_size); + write_cstr(&SERIALIZER_DETAILS.lock().unwrap().clone(), _details as *mut u8, _details_size); SERIALIZER_LOAD_RET.load(Ordering::SeqCst) } @@ -1692,7 +1692,7 @@ pub unsafe extern "C" fn oaknode_serializer_loaddata_get_property( buf: *mut c_char, size: c_int, ) -> c_int { - write_cstr("", buf, size) + write_cstr("", buf as *mut u8, size) } #[no_mangle] @@ -1726,7 +1726,7 @@ pub unsafe extern "C" fn oaknode_serializer_save_to_file( if !out_code.is_null() { *out_code = code; } - write_cstr(&SERIALIZER_DETAILS.lock().unwrap().clone(), _details, _details_size); + write_cstr(&SERIALIZER_DETAILS.lock().unwrap().clone(), _details as *mut u8, _details_size); if code == 0 && !filename.is_null() { // Success path writes a real file so tests can assert existence. let name = std::ffi::CStr::from_ptr(filename).to_string_lossy().into_owned(); @@ -1755,13 +1755,13 @@ pub unsafe extern "C" fn oaknode_serializer_load_from_file( if !out_code.is_null() { *out_code = code; } - write_cstr(&SERIALIZER_DETAILS.lock().unwrap().clone(), _details, _details_size); + write_cstr(&SERIALIZER_DETAILS.lock().unwrap().clone(), _details as *mut u8, _details_size); SERIALIZER_LOAD_RET.load(Ordering::SeqCst) } #[no_mangle] pub unsafe extern "C" fn oaknode_node_get_label(_node: oaktask::bridge::node::OakNodeNode, buf: *mut c_char, size: c_int) -> c_int { - write_cstr(&NODE_LABEL.lock().unwrap().clone(), buf, size) + write_cstr(&NODE_LABEL.lock().unwrap().clone(), buf as *mut u8, size) } #[no_mangle] @@ -1771,7 +1771,7 @@ pub unsafe extern "C" fn oaknode_node_set_label(_node: oaktask::bridge::node::Oa #[no_mangle] pub unsafe extern "C" fn oaknode_node_get_id(_node: oaktask::bridge::node::OakNodeNode, buf: *mut c_char, size: c_int) -> c_int { - write_cstr(&NODE_ID.lock().unwrap().clone(), buf, size) + write_cstr(&NODE_ID.lock().unwrap().clone(), buf as *mut u8, size) } #[no_mangle] @@ -1831,7 +1831,7 @@ pub unsafe extern "C" fn oaknode_node_get_input_string( buf: *mut c_char, size: c_int, ) -> c_int { - write_cstr("subtitle text", buf, size) + write_cstr("subtitle text", buf as *mut u8, size) } #[no_mangle] diff --git a/src/task/rust/tests/render_loop_test.rs b/src/task/rust/tests/render_loop_test.rs index 6416b5734..fed759c82 100644 --- a/src/task/rust/tests/render_loop_test.rs +++ b/src/task/rust/tests/render_loop_test.rs @@ -54,7 +54,7 @@ fn render_task(frames: i64, audio: bool) -> RenderTask { VIDEO_TIME_BASE_NUM.store(1, Ordering::SeqCst); VIDEO_TIME_BASE_DEN.store(1, Ordering::SeqCst); let range = TimeRange::new(Rational::new(0, 1), Rational::new(frames, 1)); - let base = Task::new("render-loop-test", fake_atom().to_chandle()); + let base = Task::new("render-loop-test", fake_atom()); let mut rt = RenderTask::new( base, CHandle::null(), @@ -190,7 +190,7 @@ fn render_cancel_drains_inflight_and_fires_their_completions() { // the loop; the loop then sees the cancellation and drains. unsafe { oaktask::bridge::render::oakrender_cancelatom_cancel( - oaktask::bridge::render::OakCancelAtom::from_chandle(atom), + atom, ); } diff --git a/src/task/rust/tests/task_run_test.rs b/src/task/rust/tests/task_run_test.rs index adb1d8314..fd3c9bd33 100644 --- a/src/task/rust/tests/task_run_test.rs +++ b/src/task/rust/tests/task_run_test.rs @@ -476,7 +476,7 @@ fn task_base_api_and_events() { let _g = lock(); reset_stubs(); - let mut task = Task::new("Base API", common::fake_atom().to_chandle()); + let mut task = Task::new("Base API", common::fake_atom()); task.set_title("Renamed"); assert_eq!(task.title(), "Renamed"); assert!(task.error().is_none()); @@ -514,7 +514,7 @@ fn task_base_api_and_events() { task.cancel(); assert!(task.is_cancelled()); unsafe { - oaktask::bridge::render::oakrender_cancelatom_cancel(oaktask::bridge::render::OakCancelAtom::from_chandle(atom)); + oaktask::bridge::render::oakrender_cancelatom_cancel(atom); } assert!(task.is_cancelled()); diff --git a/src/timeline/rust/Cargo.lock b/src/timeline/rust/Cargo.lock index efe526240..5949956de 100644 --- a/src/timeline/rust/Cargo.lock +++ b/src/timeline/rust/Cargo.lock @@ -2,13 +2,572 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex 1.3.0", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" + +[[package]] +name = "bytemuck" +version = "1.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "cc" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e" +dependencies = [ + "find-msvc-tools", + "shlex 2.0.1", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "either" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" + +[[package]] +name = "fax" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" + +[[package]] +name = "ffmpeg-next" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6380599799e175191eb7ffe82c97f36a2a90a36cbc54c738a903e5287d7f516a" +dependencies = [ + "bitflags", + "ffmpeg-sys-next", + "libc", +] + +[[package]] +name = "ffmpeg-sys-next" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b939bf79dd5949412a4b81cfe21a07f48ea21b47fcbb5f57816c8c2de5ae30b" +dependencies = [ + "bindgen", + "cc", + "libc", + "num_cpus", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "tiff", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "oakcodec" +version = "0.1.0" +dependencies = [ + "ffmpeg-next", + "oakcore-rs", +] + +[[package]] +name = "oakcommon" +version = "0.1.0" +dependencies = [ + "image", + "log", + "oakcore-rs", + "ocio-rs", + "quick-xml", +] + [[package]] name = "oakcore-rs" version = "0.1.0" +[[package]] +name = "oaknode" +version = "0.1.0" +dependencies = [ + "oakcodec", + "oakcommon", + "oakcore-rs", + "oakundo", +] + [[package]] name = "oaktimeline" version = "0.1.0" +dependencies = [ + "oakcommon", + "oakcore-rs", + "oaknode", + "oakundo", +] + +[[package]] +name = "oakundo" +version = "0.1.0" dependencies = [ "oakcore-rs", ] + +[[package]] +name = "ocio-rs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3492534019b59e29dba06014f907dd12824537ed4d293d4108c4bfc669de7fd" +dependencies = [ + "ocio-sys", + "thiserror", +] + +[[package]] +name = "ocio-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63251d72d848de5eda39d59cd6490260cf031738ebd518ea37d76b5aae614ec" +dependencies = [ + "cc", + "cmake", +] + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pxfm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "zerocopy" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zune-core" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b" + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] diff --git a/src/timeline/rust/Cargo.toml b/src/timeline/rust/Cargo.toml index 2c51f22c0..b18531fd3 100644 --- a/src/timeline/rust/Cargo.toml +++ b/src/timeline/rust/Cargo.toml @@ -13,10 +13,15 @@ crate-type = ["staticlib", "rlib"] panic = "unwind" [features] -# Compile the in-crate C ABI mocks (src/bridge/teststubs.rs) so the unit and -# integration tests can link without the oaknode/oakundo/oakcommon DLLs. -# Every test run must pass this flag (see README.md "testing"). +# Compile the in-crate mock implementations (src/bridge/teststubs.rs) so the +# integration tests can resolve `bridge::teststubs`; the bridge fns route to +# them under this feature (see single-lib.md). Plain Rust mocks, no +# `#[no_mangle]` — they coexist with the real oaknode/oakundo/oakcommon +# path dependencies. Every test run must pass this flag (README "testing"). test-stubs = [] [dependencies] oakcore-rs = { path = "../../oakcore-rs" } +oakundo = { path = "../../undo/rust" } +oaknode = { path = "../../node/rust" } +oakcommon = { path = "../../common/rust" } diff --git a/src/timeline/rust/src/bridge/common.rs b/src/timeline/rust/src/bridge/common.rs index e48f3df71..d2983b6d5 100644 --- a/src/timeline/rust/src/bridge/common.rs +++ b/src/timeline/rust/src/bridge/common.rs @@ -14,6 +14,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 . + //! oakcommon C ABI imports (XML reader/writer + config), mirroring //! `include/common/xmlutils.h` and `include/common/config.h`. @@ -21,49 +37,233 @@ use std::ffi::{c_char, c_int}; use crate::handle::CHandle; -extern "C" { - /// `oakcommon_xml_reader_init` — create a reader over a NUL-terminated - /// document; the document must outlive the reader. - pub fn oakcommon_xml_reader_init(data: *const c_char) -> CHandle; - /// `oakcommon_xml_reader_free` — release the reader; NULL is a no-op. - pub fn oakcommon_xml_reader_free(reader: *mut CHandle); - /// `oakcommon_xml_reader_skip_current_element` — skip to the end of the - /// current element subtree. - pub fn oakcommon_xml_reader_skip_current_element(reader: CHandle) -> c_int; - /// `oakcommon_xml_reader_read_next_start_element` — advance to the next - /// start element, writing its (possibly empty) name into `name`. - pub fn oakcommon_xml_reader_read_next_start_element(reader: CHandle, name: *mut c_char, buf_size: c_int) -> c_int; - /// `oakcommon_xml_reader_name` — the current element's name. - pub fn oakcommon_xml_reader_name(reader: CHandle, name: *mut c_char, buf_size: c_int) -> c_int; - /// `oakcommon_xml_reader_read_element_text` — read the current element's - /// text content. - pub fn oakcommon_xml_reader_read_element_text(reader: CHandle, text: *mut c_char, buf_size: c_int) -> c_int; - /// `oakcommon_xml_reader_attribute_count` — number of attributes on the - /// current element. - pub fn oakcommon_xml_reader_attribute_count(reader: CHandle, count: *mut c_int) -> c_int; - /// `oakcommon_xml_reader_attribute_name` — name of attribute `i`. - pub fn oakcommon_xml_reader_attribute_name(reader: CHandle, index: c_int, name: *mut c_char, buf_size: c_int) -> c_int; - /// `oakcommon_xml_reader_attribute_value` — value of attribute `i`. - pub fn oakcommon_xml_reader_attribute_value(reader: CHandle, index: c_int, value: *mut c_char, buf_size: c_int) -> c_int; - /// `oakcommon_xml_reader_has_error` — whether the reader hit an error. - pub fn oakcommon_xml_reader_has_error(reader: CHandle, has_error: *mut c_int) -> c_int; - /// `oakcommon_xml_writer_init` — create a writer. - pub fn oakcommon_xml_writer_init() -> CHandle; - /// `oakcommon_xml_writer_free` — release the writer; NULL is a no-op. - pub fn oakcommon_xml_writer_free(writer: *mut CHandle); - /// `oakcommon_xml_writer_write_start_element` — open ``. - pub fn oakcommon_xml_writer_write_start_element(writer: CHandle, name: *const c_char) -> c_int; - /// `oakcommon_xml_writer_write_end_element` — close the current element. - pub fn oakcommon_xml_writer_write_end_element(writer: CHandle) -> c_int; - /// `oakcommon_xml_writer_write_end_document` — finish the document. - pub fn oakcommon_xml_writer_write_end_document(writer: CHandle) -> c_int; - /// `oakcommon_xml_writer_write_attribute` — write `key="value"`. - pub fn oakcommon_xml_writer_write_attribute(writer: CHandle, key: *const c_char, value: *const c_char) -> c_int; - /// `oakcommon_xml_writer_write_characters` — write raw text content. - pub fn oakcommon_xml_writer_write_characters(writer: CHandle, text: *const c_char) -> c_int; - /// `oakcommon_xml_writer_write_text_element` — write `text`. - pub fn oakcommon_xml_writer_write_text_element(writer: CHandle, name: *const c_char, text: *const c_char) -> c_int; - /// `oakcommon_config_get_int` — read an integer config value (marker default - /// colour). - pub fn oakcommon_config_get_int(group: *const c_char, key: *const c_char, default: c_int) -> c_int; +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_reader_init(data: *const c_char) -> CHandle { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_reader_init(data) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_init(data) } + } } + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_reader_free(reader: *mut CHandle) { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_reader_free(reader) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_free(reader) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_reader_skip_current_element(reader: CHandle) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_reader_skip_current_element(reader) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_skip_current_element(reader) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). The +/// real ABI advances the reader and writes a 1/0 `found` flag (the name is +/// read with [`oakcommon_xml_reader_name`]). +pub fn oakcommon_xml_reader_read_next_start_element(reader: CHandle, found: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_reader_read_next_start_element(reader, found) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_read_next_start_element(reader, found) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_reader_name(reader: CHandle, name: *mut c_char, buf_size: c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_reader_name(reader, name, buf_size) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_name(reader, name, buf_size) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_reader_read_element_text(reader: CHandle, text: *mut c_char, buf_size: c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_reader_read_element_text(reader, text, buf_size) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_read_element_text(reader, text, buf_size) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_reader_attribute_count(reader: CHandle, count: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_reader_attribute_count(reader, count) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_attribute_count(reader, count) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_reader_attribute_name(reader: CHandle, index: c_int, name: *mut c_char, buf_size: c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_reader_attribute_name(reader, index, name, buf_size) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_attribute_name(reader, index, name, buf_size) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_reader_attribute_value(reader: CHandle, index: c_int, value: *mut c_char, buf_size: c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_reader_attribute_value(reader, index, value, buf_size) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_attribute_value(reader, index, value, buf_size) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_reader_has_error(reader: CHandle, has_error: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_reader_has_error(reader, has_error) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_reader_has_error(reader, has_error) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_writer_init() -> CHandle { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_writer_init() + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_init() } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_writer_free(writer: *mut CHandle) { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_writer_free(writer) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_free(writer) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_writer_write_start_element(writer: CHandle, name: *const c_char) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_writer_write_start_element(writer, name) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_start_element(writer, name) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_writer_write_end_element(writer: CHandle) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_writer_write_end_element(writer) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_end_element(writer) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_writer_write_end_document(writer: CHandle) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_writer_write_end_document(writer) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_end_document(writer) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_writer_write_attribute(writer: CHandle, key: *const c_char, value: *const c_char) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_writer_write_attribute(writer, key, value) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_attribute(writer, key, value) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_writer_write_characters(writer: CHandle, text: *const c_char) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_writer_write_characters(writer, text) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_characters(writer, text) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_xml_writer_write_text_element(writer: CHandle, name: *const c_char, text: *const c_char) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_xml_writer_write_text_element(writer, name, text) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::xmlutils::oakcommon_xml_writer_write_text_element(writer, name, text) } + } +} + +/// Direct call into the `oakcommon` crate (single-lib unification). +pub fn oakcommon_config_get_int(group: *const c_char, key: *const c_char, default: c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakcommon_config_get_int(group, key, default) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakcommon::ffi::config::oakcommon_config_get_int(group, key, default) } + } +} + diff --git a/src/timeline/rust/src/bridge/mod.rs b/src/timeline/rust/src/bridge/mod.rs index e7534281b..61f40e4dd 100644 --- a/src/timeline/rust/src/bridge/mod.rs +++ b/src/timeline/rust/src/bridge/mod.rs @@ -25,9 +25,10 @@ pub mod common; pub mod node; pub mod undo; -/// In-crate C ABI mocks, compiled only for tests (feature `test-stubs`). -/// Each `#[no_mangle]` function here provides a definition for the `extern -/// "C"` symbol declared in the submodules above, so `cargo test -/// --features test-stubs` links without the real oak DLLs. -#[cfg(feature = "test-stubs")] +/// In-crate mock implementations of the bridge surface, compiled for tests +/// (`cfg(test)` or the `test-stubs` feature); the bridge fns route to them +/// via their `#[cfg(any(test, feature = "test-stubs"))]` variants. Plain +/// Rust (no `#[no_mangle]`), so they coexist with the real +/// oaknode/oakundo/oakcommon rlibs linked in the same test binary. +#[cfg(any(test, feature = "test-stubs"))] pub mod teststubs; diff --git a/src/timeline/rust/src/bridge/node.rs b/src/timeline/rust/src/bridge/node.rs index 4a521c6cf..c5197adc4 100644 --- a/src/timeline/rust/src/bridge/node.rs +++ b/src/timeline/rust/src/bridge/node.rs @@ -14,6 +14,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 . + //! oaknode C ABI imports (`include/node/{block,node,sequence,track,project}.h`). //! Timeline reads and mutates the block/track/sequence graph exclusively //! through these frozen functions — it never reimplements node internals. @@ -39,129 +55,663 @@ pub type OakNodeProject = CHandle; /// A track list (`OakNodeTrackList`). Opaque handle. pub type OakNodeTrackList = CHandle; -extern "C" { - /// `oaknode_block_clip_create` — a new clip block (count 1). - pub fn oaknode_block_clip_create() -> OakNodeBlock; - /// `oaknode_block_gap_create` — a new gap block (count 1). - pub fn oaknode_block_gap_create() -> OakNodeBlock; - /// `oaknode_block_as_node` — the block viewed as a generic node. - pub fn oaknode_block_as_node(block: OakNodeBlock) -> OakNodeNode; - /// `oaknode_block_get_in` — the block's in point as an int pair. - pub fn oaknode_block_get_in(block: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_block_get_out` — the block's out point as an int pair. - pub fn oaknode_block_get_out(block: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_block_get_length` — the block's length as an int pair. - pub fn oaknode_block_get_length(block: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_block_set_length_and_media_out` — resize keeping media-in fixed. - pub fn oaknode_block_set_length_and_media_out(block: OakNodeBlock, numerator: c_int, denominator: c_int) -> c_int; - /// `oaknode_block_set_length_and_media_in` — resize keeping the out point fixed. - pub fn oaknode_block_set_length_and_media_in(block: OakNodeBlock, numerator: c_int, denominator: c_int) -> c_int; - /// `oaknode_block_get_enabled` — the block's enabled flag. - pub fn oaknode_block_get_enabled(block: OakNodeBlock, enabled: *mut c_int) -> c_int; - /// `oaknode_block_set_enabled` — set the enabled flag. - pub fn oaknode_block_set_enabled(block: OakNodeBlock, enabled: c_int) -> c_int; - /// `oaknode_block_get_previous` — borrowed previous block (empty when none). - pub fn oaknode_block_get_previous(block: OakNodeBlock, out: *mut OakNodeBlock) -> c_int; - /// `oaknode_block_get_next` — borrowed next block (empty when none). - pub fn oaknode_block_get_next(block: OakNodeBlock, out: *mut OakNodeBlock) -> c_int; - /// `oaknode_block_get_track` — the owning track (empty when detached). - pub fn oaknode_block_get_track(block: OakNodeBlock, out: *mut OakNodeTrack) -> c_int; - /// `oaknode_block_link` — link two blocks so they move together. - pub fn oaknode_block_link(a: OakNodeBlock, b: OakNodeBlock) -> c_int; - /// `oaknode_block_unlink` — unlink two blocks. - pub fn oaknode_block_unlink(a: OakNodeBlock, b: OakNodeBlock) -> c_int; - /// `oaknode_track_get_length` — the track's length as an int pair. - pub fn oaknode_track_get_length(track: OakNodeTrack, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_track_get_sequence` — the owning sequence (empty when detached). - pub fn oaknode_track_get_sequence(track: OakNodeTrack, out: *mut OakNodeSequence) -> c_int; - /// `oaknode_track_prepend_block` — prepend a block. - pub fn oaknode_track_prepend_block(track: OakNodeTrack, block: OakNodeBlock) -> c_int; - /// `oaknode_track_insert_block_after` — insert after `before`. - pub fn oaknode_track_insert_block_after(track: OakNodeTrack, block: OakNodeBlock, before: OakNodeBlock) -> c_int; - /// `oaknode_track_ripple_remove_block` — remove a block, shifting later ones - /// earlier; ownership returns to the caller. - pub fn oaknode_track_ripple_remove_block(track: OakNodeTrack, block: OakNodeBlock) -> c_int; - /// `oaknode_track_replace_block` — replace `old_block` with `new_block` - /// (equal lengths required). - pub fn oaknode_track_replace_block(track: OakNodeTrack, old_block: OakNodeBlock, new_block: OakNodeBlock) -> c_int; - /// `oaknode_sequence_as_node` — the sequence viewed as a generic node. - pub fn oaknode_sequence_as_node(sequence: OakNodeSequence) -> OakNodeNode; - /// `oaknode_sequence_from_node` — a generic node viewed as a sequence. - pub fn oaknode_sequence_from_node(node: OakNodeNode) -> OakNodeSequence; - /// `oaknode_node_get_project` — the owning project (empty when detached). - pub fn oaknode_node_get_project(node: OakNodeNode, out: *mut OakNodeProject) -> c_int; - /// `oaknode_node_output_connection_count` — number of output connections. - pub fn oaknode_node_output_connection_count(node: OakNodeNode, out_count: *mut c_int) -> c_int; - /// `oaknode_node_get_markers` — borrow the node's marker list. - pub fn oaknode_node_get_markers(node: OakNodeNode, out: *mut crate::handle::CHandle) -> c_int; - /// `oaknode_node_get_work_area` — borrow the node's work area. - pub fn oaknode_node_get_work_area(node: OakNodeNode, out: *mut crate::handle::CHandle) -> c_int; - /// `oaknode_project_add_node` — adopt a node into a project. - pub fn oaknode_project_add_node(project: OakNodeProject, node: OakNodeNode) -> c_int; - /// `oaknode_project_remove_node` — remove a node from a project. - pub fn oaknode_project_remove_node(project: OakNodeProject, node: OakNodeNode) -> c_int; - /// `oaknode_command_create_remove_node` — a command that removes a node. - pub fn oaknode_command_create_remove_node(node: OakNodeNode) -> crate::handle::CHandle; - - // --- block.h: querying / building blocks ------------------------------- - - /// `oaknode_block_get_kind` — the block's kind (`OAKNODE_BLOCK_*`). - pub fn oaknode_block_get_kind(block: OakNodeBlock, out_kind: *mut c_int) -> c_int; - /// `oaknode_block_from_node` — a generic node viewed as a block (empty when not a block). - pub fn oaknode_block_from_node(node: OakNodeNode) -> OakNodeBlock; - /// `oaknode_block_are_linked` — whether two blocks are linked (`linked` receives 1/0). - pub fn oaknode_block_are_linked(a: OakNodeBlock, b: OakNodeBlock, linked: *mut c_int) -> c_int; - /// `oaknode_clip_add_cache_passthrough_from` — copy render-cache passthroughs from `other`. - pub fn oaknode_clip_add_cache_passthrough_from(clip: OakNodeBlock, other: OakNodeBlock) -> c_int; - /// `oaknode_clip_get_media_in` — the clip's media-in as an int pair (clip blocks only). - pub fn oaknode_clip_get_media_in(clip: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int; - /// `oaknode_clip_set_media_in` — set the clip's media-in as an int pair (clip blocks only). - pub fn oaknode_clip_set_media_in(clip: OakNodeBlock, numerator: c_int, denominator: c_int) -> c_int; - - // --- track.h: tracks and track lists ----------------------------------- - - /// `oaknode_track_create` — a new detached track of the given type. - pub fn oaknode_track_create(kind: c_int) -> OakNodeTrack; - /// `oaknode_track_get_locked` — whether the track is locked (`locked` receives 1/0). - pub fn oaknode_track_get_locked(track: OakNodeTrack, locked: *mut c_int) -> c_int; - /// `oaknode_track_set_locked` — set the locked flag. - pub fn oaknode_track_set_locked(track: OakNodeTrack, locked: c_int) -> c_int; - /// `oaknode_track_get_block_count` — number of blocks on the track. - pub fn oaknode_track_get_block_count(track: OakNodeTrack, count: *mut c_int) -> c_int; - /// `oaknode_track_get_block_at` — borrowed block at `index`. - pub fn oaknode_track_get_block_at(track: OakNodeTrack, index: c_int, out: *mut OakNodeBlock) -> c_int; - /// `oaknode_track_get_block_containing_time` — block strictly containing `time` (in < t < out). - pub fn oaknode_track_get_block_containing_time(track: OakNodeTrack, numerator: c_int, denominator: c_int, out: *mut OakNodeBlock) -> c_int; - /// `oaknode_track_get_nearest_block_before_or_at` — block at or before `time`. - pub fn oaknode_track_get_nearest_block_before_or_at(track: OakNodeTrack, numerator: c_int, denominator: c_int, out: *mut OakNodeBlock) -> c_int; - /// `oaknode_track_get_nearest_block_after_or_at` — block at or after `time`. - pub fn oaknode_track_get_nearest_block_after_or_at(track: OakNodeTrack, numerator: c_int, denominator: c_int, out: *mut OakNodeBlock) -> c_int; - /// `oaknode_tracklist_get_type` — the track list's track type. - pub fn oaknode_tracklist_get_type(list: OakNodeTrackList, kind: *mut c_int) -> c_int; - /// `oaknode_tracklist_get_track_count` — number of tracks in the list. - pub fn oaknode_tracklist_get_track_count(list: OakNodeTrackList, count: *mut c_int) -> c_int; - /// `oaknode_tracklist_get_track_at` — borrowed track at `index`. - pub fn oaknode_tracklist_get_track_at(list: OakNodeTrackList, index: c_int, out: *mut OakNodeTrack) -> c_int; - /// `oaknode_tracklist_array_append` — append a track-array element on the parent sequence. - pub fn oaknode_tracklist_array_append(list: OakNodeTrackList) -> c_int; - /// `oaknode_tracklist_array_remove_last` — remove the last track-array element. - pub fn oaknode_tracklist_array_remove_last(list: OakNodeTrackList) -> c_int; - - // --- sequence.h --------------------------------------------------------- - - /// `oaknode_sequence_get_track_list` — borrowed per-type track list. - pub fn oaknode_sequence_get_track_list(sequence: OakNodeSequence, kind: c_int, out: *mut OakNodeTrackList) -> c_int; - /// `oaknode_sequence_get_all_track_count` — total connected tracks across all types. - pub fn oaknode_sequence_get_all_track_count(sequence: OakNodeSequence, count: *mut c_int) -> c_int; - /// `oaknode_sequence_get_all_track_at` — borrowed track at `index` (flat cache). - pub fn oaknode_sequence_get_all_track_at(sequence: OakNodeSequence, index: c_int, out: *mut OakNodeTrack) -> c_int; - - // --- node.h: edges and cloning ------------------------------------------ - - /// `oaknode_node_connect` — connect `output_node` to `input_node`'s `input_id` (live). - pub fn oaknode_node_connect(output_node: OakNodeNode, input_node: OakNodeNode, input_id: *const c_char) -> c_int; - /// `oaknode_node_disconnect` — remove the edge feeding `input_node`'s `input_id` (live). - pub fn oaknode_node_disconnect(input_node: OakNodeNode, input_id: *const c_char) -> c_int; - /// `oaknode_node_copy_in_graph` — clone `node` in its graph; `*out_command` receives an owned undo handle. - pub fn oaknode_node_copy_in_graph(node: OakNodeNode, out_command: *mut crate::handle::CHandle) -> OakNodeNode; +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_clip_create() -> OakNodeBlock { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_clip_create() + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_clip_create() } + } } + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_gap_create() -> OakNodeBlock { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_gap_create() + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_gap_create() } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_as_node(block: OakNodeBlock) -> OakNodeNode { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_as_node(block) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_as_node(block) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_get_in(block: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_get_in(block, numerator, denominator) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_get_in(block, numerator, denominator) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_get_out(block: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_get_out(block, numerator, denominator) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_get_out(block, numerator, denominator) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_get_length(block: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_get_length(block, numerator, denominator) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_get_length(block, numerator, denominator) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_set_length_and_media_out(block: OakNodeBlock, numerator: c_int, denominator: c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_set_length_and_media_out(block, numerator, denominator) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_set_length_and_media_out(block, numerator, denominator) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_set_length_and_media_in(block: OakNodeBlock, numerator: c_int, denominator: c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_set_length_and_media_in(block, numerator, denominator) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_set_length_and_media_in(block, numerator, denominator) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_get_enabled(block: OakNodeBlock, enabled: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_get_enabled(block, enabled) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_get_enabled(block, enabled) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_set_enabled(block: OakNodeBlock, enabled: c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_set_enabled(block, enabled) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_set_enabled(block, enabled) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_get_previous(block: OakNodeBlock, out: *mut OakNodeBlock) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_get_previous(block, out) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_get_previous(block, out) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_get_next(block: OakNodeBlock, out: *mut OakNodeBlock) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_get_next(block, out) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_get_next(block, out) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_get_track(block: OakNodeBlock, out: *mut OakNodeTrack) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_get_track(block, out) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_get_track(block, out) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_link(a: OakNodeBlock, b: OakNodeBlock) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_link(a, b) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_link(a, b) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_unlink(a: OakNodeBlock, b: OakNodeBlock) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_unlink(a, b) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_unlink(a, b) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_get_length(track: OakNodeTrack, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_track_get_length(track, numerator, denominator) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_track_get_length(track, numerator, denominator) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_get_sequence(track: OakNodeTrack, out: *mut OakNodeSequence) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_track_get_sequence(track, out) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_track_get_sequence(track, out) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_prepend_block(track: OakNodeTrack, block: OakNodeBlock) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_track_prepend_block(track, block) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_track_prepend_block(track, block) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_insert_block_after(track: OakNodeTrack, block: OakNodeBlock, before: OakNodeBlock) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_track_insert_block_after(track, block, before) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_track_insert_block_after(track, block, before) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_ripple_remove_block(track: OakNodeTrack, block: OakNodeBlock) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_track_ripple_remove_block(track, block) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_track_ripple_remove_block(track, block) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_replace_block(track: OakNodeTrack, old_block: OakNodeBlock, new_block: OakNodeBlock) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_track_replace_block(track, old_block, new_block) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_track_replace_block(track, old_block, new_block) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_as_node(sequence: OakNodeSequence) -> OakNodeNode { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_sequence_as_node(sequence) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::sequence::oaknode_sequence_as_node(sequence) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_from_node(node: OakNodeNode) -> OakNodeSequence { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_sequence_from_node(node) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::sequence::oaknode_sequence_from_node(node) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_get_project(node: OakNodeNode, out: *mut OakNodeProject) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_node_get_project(node, out) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::node::oaknode_node_get_project(node, out) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_output_connection_count(node: OakNodeNode, out_count: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_node_output_connection_count(node, out_count) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::node::oaknode_node_output_connection_count(node, out_count) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_get_markers(node: OakNodeNode, out: *mut crate::handle::CHandle) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_node_get_markers(node, out) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::node::oaknode_node_get_markers(node, out as *mut std::ffi::c_void) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_get_work_area(node: OakNodeNode, out: *mut crate::handle::CHandle) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_node_get_work_area(node, out) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::node::oaknode_node_get_work_area(node, out as *mut std::ffi::c_void) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_add_node(project: OakNodeProject, node: OakNodeNode) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_project_add_node(project, node) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::project::oaknode_project_add_node(project, node) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_project_remove_node(project: OakNodeProject, node: OakNodeNode) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_project_remove_node(project, node) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::project::oaknode_project_remove_node(project, node) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_command_create_remove_node(node: OakNodeNode) -> crate::handle::CHandle { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_command_create_remove_node(node) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::node::oaknode_command_create_remove_node(node) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_get_kind(block: OakNodeBlock, out_kind: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_get_kind(block, out_kind) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_get_kind(block, out_kind) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_from_node(node: OakNodeNode) -> OakNodeBlock { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_from_node(node) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_from_node(node) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_block_are_linked(a: OakNodeBlock, b: OakNodeBlock, linked: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_block_are_linked(a, b, linked) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_block_are_linked(a, b, linked) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_clip_add_cache_passthrough_from(clip: OakNodeBlock, other: OakNodeBlock) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_clip_add_cache_passthrough_from(clip, other) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_clip_add_cache_passthrough_from(clip, other) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_clip_get_media_in(clip: OakNodeBlock, numerator: *mut c_int, denominator: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_clip_get_media_in(clip, numerator, denominator) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_clip_get_media_in(clip, numerator, denominator) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_clip_set_media_in(clip: OakNodeBlock, numerator: c_int, denominator: c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_clip_set_media_in(clip, numerator, denominator) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::block::oaknode_clip_set_media_in(clip, numerator, denominator) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_create(kind: c_int) -> OakNodeTrack { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_track_create(kind) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_track_create(kind) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_get_locked(track: OakNodeTrack, locked: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_track_get_locked(track, locked) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_track_get_locked(track, locked) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_set_locked(track: OakNodeTrack, locked: c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_track_set_locked(track, locked) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_track_set_locked(track, locked) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_get_block_count(track: OakNodeTrack, count: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_track_get_block_count(track, count) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_track_get_block_count(track, count) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_get_block_at(track: OakNodeTrack, index: c_int, out: *mut OakNodeBlock) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_track_get_block_at(track, index, out) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_track_get_block_at(track, index, out) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_get_block_containing_time(track: OakNodeTrack, numerator: c_int, denominator: c_int, out: *mut OakNodeBlock) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_track_get_block_containing_time(track, numerator, denominator, out) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_track_get_block_containing_time(track, numerator, denominator, out) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_get_nearest_block_before_or_at(track: OakNodeTrack, numerator: c_int, denominator: c_int, out: *mut OakNodeBlock) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_track_get_nearest_block_before_or_at(track, numerator, denominator, out) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_track_get_nearest_block_before_or_at(track, numerator, denominator, out) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_track_get_nearest_block_after_or_at(track: OakNodeTrack, numerator: c_int, denominator: c_int, out: *mut OakNodeBlock) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_track_get_nearest_block_after_or_at(track, numerator, denominator, out) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_track_get_nearest_block_after_or_at(track, numerator, denominator, out) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_get_type(list: OakNodeTrackList, kind: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_tracklist_get_type(list, kind) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_tracklist_get_type(list, kind) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_get_track_count(list: OakNodeTrackList, count: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_tracklist_get_track_count(list, count) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_tracklist_get_track_count(list, count) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_get_track_at(list: OakNodeTrackList, index: c_int, out: *mut OakNodeTrack) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_tracklist_get_track_at(list, index, out) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_tracklist_get_track_at(list, index, out) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_array_append(list: OakNodeTrackList) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_tracklist_array_append(list) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_tracklist_array_append(list) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_tracklist_array_remove_last(list: OakNodeTrackList) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_tracklist_array_remove_last(list) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::track::oaknode_tracklist_array_remove_last(list) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_track_list(sequence: OakNodeSequence, kind: c_int, out: *mut OakNodeTrackList) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_sequence_get_track_list(sequence, kind, out) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_track_list(sequence, kind, out) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_all_track_count(sequence: OakNodeSequence, count: *mut c_int) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_sequence_get_all_track_count(sequence, count) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_all_track_count(sequence, count) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_sequence_get_all_track_at(sequence: OakNodeSequence, index: c_int, out: *mut OakNodeTrack) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_sequence_get_all_track_at(sequence, index, out) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::sequence::oaknode_sequence_get_all_track_at(sequence, index, out) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_connect(output_node: OakNodeNode, input_node: OakNodeNode, input_id: *const c_char) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_node_connect(output_node, input_node, input_id) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::node::oaknode_node_connect(output_node, input_node, input_id) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_disconnect(input_node: OakNodeNode, input_id: *const c_char) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_node_disconnect(input_node, input_id) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::node::oaknode_node_disconnect(input_node, input_id) } + } +} + +/// Direct call into the `oaknode` crate (single-lib unification). +pub fn oaknode_node_copy_in_graph(node: OakNodeNode, out_command: *mut crate::handle::CHandle) -> OakNodeNode { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oaknode_node_copy_in_graph(node, out_command) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oaknode::ffi::node::oaknode_node_copy_in_graph(node, out_command) } + } +} + diff --git a/src/timeline/rust/src/bridge/teststubs.rs b/src/timeline/rust/src/bridge/teststubs.rs index ff3a71d21..783382c48 100644 --- a/src/timeline/rust/src/bridge/teststubs.rs +++ b/src/timeline/rust/src/bridge/teststubs.rs @@ -16,10 +16,12 @@ //! In-crate C ABI mocks, compiled only with `--features test-stubs`. //! -//! Each `#[no_mangle]` function here supplies a definition for the `extern -//! "C"` symbol declared in [`super::undo`] / [`super::common`] / -//! [`super::node`], so unit and integration tests link without the real -//! oaknode / oakundo / oakcommon DLLs. +//! In-crate mock implementations of the oaknode/oakundo/oakcommon bridge +//! surface, called directly by the bridge fns' `#[cfg(test)]` variants. +//! The mocks are plain Rust (no `#[no_mangle]`/`extern "C"`), so they +//! coexist with the real oaknode/oakundo/oakcommon rlibs linked in the +//! same test binary. Timeline's tests build fixtures with these mocks; the +//! production (non-test) bridge fns call the real crates. //! //! The mocks are intentionally simple and single-threaded. Handles box a //! [`MockNode`] (for every node-graph kind), a [`MockMarkerList`], a @@ -59,8 +61,7 @@ unsafe impl Send for MockUndoCommand {} /// `oakundo_command_init`: box the vtable + userdata and hand back an owning /// handle. -#[no_mangle] -pub extern "C" fn oakundo_command_init( +pub fn oakundo_command_init( vtable: *const super::undo::OakUndoCommandVtable, userdata: *mut c_void, ) -> CHandle { @@ -78,8 +79,7 @@ pub extern "C" fn oakundo_command_init( } /// `oakundo_command_init_multi`: an empty multi command (no-op here). -#[no_mangle] -pub extern "C" fn oakundo_command_init_multi() -> CHandle { +pub fn oakundo_command_init_multi() -> CHandle { make_owned(MockUndoCommand { vtable: super::undo::OakUndoCommandVtable { redo: None, @@ -91,14 +91,12 @@ pub extern "C" fn oakundo_command_init_multi() -> CHandle { } /// `oakundo_command_multi_add_child`: no-op in the mock (returns 0). -#[no_mangle] -pub extern "C" fn oakundo_command_multi_add_child(_multi: CHandle, _child: CHandle) -> c_int { +pub fn oakundo_command_multi_add_child(_multi: CHandle, _child: CHandle) -> c_int { 0 } /// `oakundo_command_redo_now`: invoke the redo callback. -#[no_mangle] -pub extern "C" fn oakundo_command_redo_now(command: CHandle) -> c_int { +pub fn oakundo_command_redo_now(command: CHandle) -> c_int { // SAFETY: the handle boxes a MockUndoCommand (created by init above). if let Some(m) = unsafe { get_mut::(&command) } { if let Some(f) = m.vtable.redo { @@ -111,8 +109,7 @@ pub extern "C" fn oakundo_command_redo_now(command: CHandle) -> c_int { } /// `oakundo_command_undo_now`: invoke the undo callback. -#[no_mangle] -pub extern "C" fn oakundo_command_undo_now(command: CHandle) -> c_int { +pub fn oakundo_command_undo_now(command: CHandle) -> c_int { // SAFETY: the handle boxes a MockUndoCommand. if let Some(m) = unsafe { get_mut::(&command) } { if let Some(f) = m.vtable.undo { @@ -125,8 +122,7 @@ pub extern "C" fn oakundo_command_undo_now(command: CHandle) -> c_int { /// `oakundo_command_free`: run `free_fn`, drop the box and clear the handle. /// NULL / empty-handle is a no-op. -#[no_mangle] -pub extern "C" fn oakundo_command_free(command: *mut CHandle) { +pub fn oakundo_command_free(command: *mut CHandle) { if command.is_null() { return; } @@ -153,8 +149,7 @@ pub extern "C" fn oakundo_command_free(command: *mut CHandle) { } /// `oakundo_stack_push`: record the push into the stack's mock (returns 0). -#[no_mangle] -pub extern "C" fn oakundo_stack_push( +pub fn oakundo_stack_push( stack: CHandle, _command: CHandle, _text: *const c_char, @@ -216,47 +211,41 @@ pub fn xml_reader_handle(nodes: Vec) -> CHandle { /// `oakcommon_xml_reader_init`: build an empty reader over a NUL-terminated /// document. The mock only supports the `reader_handle` builder, so a /// document string yields an empty reader. -#[no_mangle] -pub extern "C" fn oakcommon_xml_reader_init(_data: *const c_char) -> CHandle { +pub fn oakcommon_xml_reader_init(_data: *const c_char) -> CHandle { make_owned(MockXmlReader::new(Vec::new())) } /// `oakcommon_xml_reader_free`: release and clear; NULL / empty no-op. -#[no_mangle] -pub extern "C" fn oakcommon_xml_reader_free(reader: *mut CHandle) { +pub fn oakcommon_xml_reader_free(reader: *mut CHandle) { free_box::(reader); } /// `oakcommon_xml_reader_skip_current_element`: no-op (flat model). -#[no_mangle] -pub extern "C" fn oakcommon_xml_reader_skip_current_element(_reader: CHandle) -> c_int { +pub fn oakcommon_xml_reader_skip_current_element(_reader: CHandle) -> c_int { 0 } /// `oakcommon_xml_reader_read_next_start_element`: advance and write the /// element name. Returns 1 while elements remain, else 0. -#[no_mangle] -pub extern "C" fn oakcommon_xml_reader_read_next_start_element( - reader: CHandle, - name: *mut c_char, - buf_size: c_int, -) -> c_int { +pub fn oakcommon_xml_reader_read_next_start_element(reader: CHandle, found: *mut c_int) -> c_int { // SAFETY: handle boxes a MockXmlReader. let Some(r) = (unsafe { get_mut::(&reader) }) else { return 0; }; + if found.is_null() { + return 0; + } if r.cur >= r.nodes.len() { + // End of stream: no element yielded (0), found stays 0. return 0; } - let n = r.nodes[r.cur].name.clone(); r.cur += 1; - write_cstr(name, buf_size, &n); + unsafe { *found = 1 }; 1 } /// `oakcommon_xml_reader_name`: the current element's name. -#[no_mangle] -pub extern "C" fn oakcommon_xml_reader_name(reader: CHandle, name: *mut c_char, buf_size: c_int) -> c_int { +pub fn oakcommon_xml_reader_name(reader: CHandle, name: *mut c_char, buf_size: c_int) -> c_int { // SAFETY: handle boxes a MockXmlReader. let Some(r) = (unsafe { get::(&reader) }) else { return 0; @@ -269,8 +258,7 @@ pub extern "C" fn oakcommon_xml_reader_name(reader: CHandle, name: *mut c_char, } /// `oakcommon_xml_reader_read_element_text`: the current element's text. -#[no_mangle] -pub extern "C" fn oakcommon_xml_reader_read_element_text( +pub fn oakcommon_xml_reader_read_element_text( reader: CHandle, text: *mut c_char, buf_size: c_int, @@ -288,8 +276,7 @@ pub extern "C" fn oakcommon_xml_reader_read_element_text( /// `oakcommon_xml_reader_attribute_count`: attribute count on the current /// element. -#[no_mangle] -pub extern "C" fn oakcommon_xml_reader_attribute_count(reader: CHandle, count: *mut c_int) -> c_int { +pub fn oakcommon_xml_reader_attribute_count(reader: CHandle, count: *mut c_int) -> c_int { // SAFETY: caller passes a valid count pointer. let c = match unsafe { get::(&reader) } { Some(r) => r @@ -305,8 +292,7 @@ pub extern "C" fn oakcommon_xml_reader_attribute_count(reader: CHandle, count: * } /// `oakcommon_xml_reader_attribute_name`: name of attribute `i`. -#[no_mangle] -pub extern "C" fn oakcommon_xml_reader_attribute_name( +pub fn oakcommon_xml_reader_attribute_name( reader: CHandle, index: c_int, name: *mut c_char, @@ -327,8 +313,7 @@ pub extern "C" fn oakcommon_xml_reader_attribute_name( } /// `oakcommon_xml_reader_attribute_value`: value of attribute `i`. -#[no_mangle] -pub extern "C" fn oakcommon_xml_reader_attribute_value( +pub fn oakcommon_xml_reader_attribute_value( reader: CHandle, index: c_int, value: *mut c_char, @@ -349,8 +334,7 @@ pub extern "C" fn oakcommon_xml_reader_attribute_value( } /// `oakcommon_xml_reader_has_error`: whether the reader errored. -#[no_mangle] -pub extern "C" fn oakcommon_xml_reader_has_error(reader: CHandle, has_error: *mut c_int) -> c_int { +pub fn oakcommon_xml_reader_has_error(reader: CHandle, has_error: *mut c_int) -> c_int { let e = match unsafe { get::(&reader) } { Some(r) => r.error as c_int, None => 0, @@ -367,20 +351,17 @@ pub struct MockXmlWriter { } /// `oakcommon_xml_writer_init`: a new writer. -#[no_mangle] -pub extern "C" fn oakcommon_xml_writer_init() -> CHandle { +pub fn oakcommon_xml_writer_init() -> CHandle { make_owned(MockXmlWriter { buf: String::new() }) } /// `oakcommon_xml_writer_free`: release and clear; NULL / empty no-op. -#[no_mangle] -pub extern "C" fn oakcommon_xml_writer_free(writer: *mut CHandle) { +pub fn oakcommon_xml_writer_free(writer: *mut CHandle) { free_box::(writer); } /// `oakcommon_xml_writer_write_start_element`: append ``. -#[no_mangle] -pub extern "C" fn oakcommon_xml_writer_write_start_element(writer: CHandle, name: *const c_char) -> c_int { +pub fn oakcommon_xml_writer_write_start_element(writer: CHandle, name: *const c_char) -> c_int { // SAFETY: caller passes a NUL-terminated string. let n = unsafe { cstr(name) }; // SAFETY: handle boxes a MockXmlWriter. @@ -391,8 +372,7 @@ pub extern "C" fn oakcommon_xml_writer_write_start_element(writer: CHandle, name } /// `oakcommon_xml_writer_write_end_element`: append ``. -#[no_mangle] -pub extern "C" fn oakcommon_xml_writer_write_end_element(writer: CHandle) -> c_int { +pub fn oakcommon_xml_writer_write_end_element(writer: CHandle) -> c_int { // SAFETY: handle boxes a MockXmlWriter. if let Some(w) = unsafe { get_mut::(&writer) } { w.buf.push_str(""); @@ -401,14 +381,12 @@ pub extern "C" fn oakcommon_xml_writer_write_end_element(writer: CHandle) -> c_i } /// `oakcommon_xml_writer_write_end_document`: no-op. -#[no_mangle] -pub extern "C" fn oakcommon_xml_writer_write_end_document(_writer: CHandle) -> c_int { +pub fn oakcommon_xml_writer_write_end_document(_writer: CHandle) -> c_int { 0 } /// `oakcommon_xml_writer_write_attribute`: append ` key="value"`. -#[no_mangle] -pub extern "C" fn oakcommon_xml_writer_write_attribute( +pub fn oakcommon_xml_writer_write_attribute( writer: CHandle, key: *const c_char, value: *const c_char, @@ -423,8 +401,7 @@ pub extern "C" fn oakcommon_xml_writer_write_attribute( } /// `oakcommon_xml_writer_write_characters`: append raw text. -#[no_mangle] -pub extern "C" fn oakcommon_xml_writer_write_characters(writer: CHandle, text: *const c_char) -> c_int { +pub fn oakcommon_xml_writer_write_characters(writer: CHandle, text: *const c_char) -> c_int { // SAFETY: caller passes a NUL-terminated string. let t = unsafe { cstr(text) }; // SAFETY: handle boxes a MockXmlWriter. @@ -435,8 +412,7 @@ pub extern "C" fn oakcommon_xml_writer_write_characters(writer: CHandle, text: * } /// `oakcommon_xml_writer_write_text_element`: append `text`. -#[no_mangle] -pub extern "C" fn oakcommon_xml_writer_write_text_element( +pub fn oakcommon_xml_writer_write_text_element( writer: CHandle, name: *const c_char, text: *const c_char, @@ -452,8 +428,7 @@ pub extern "C" fn oakcommon_xml_writer_write_text_element( /// `oakcommon_config_get_int`: return the supplied default (the mock keeps no /// config store; the marker default colour path uses the default). -#[no_mangle] -pub extern "C" fn oakcommon_config_get_int( +pub fn oakcommon_config_get_int( _group: *const c_char, _key: *const c_char, default: c_int, @@ -554,8 +529,7 @@ impl Default for MockNode { } /// `oaknode_block_clip_create`: a new clip block. -#[no_mangle] -pub extern "C" fn oaknode_block_clip_create() -> CHandle { +pub fn oaknode_block_clip_create() -> CHandle { make_owned(MockNode { kind: MockKind::Clip, ..Default::default() @@ -563,8 +537,7 @@ pub extern "C" fn oaknode_block_clip_create() -> CHandle { } /// `oaknode_block_gap_create`: a new gap block. -#[no_mangle] -pub extern "C" fn oaknode_block_gap_create() -> CHandle { +pub fn oaknode_block_gap_create() -> CHandle { make_owned(MockNode { kind: MockKind::Gap, ..Default::default() @@ -572,14 +545,12 @@ pub extern "C" fn oaknode_block_gap_create() -> CHandle { } /// `oaknode_block_as_node`: a borrowed generic-node view of a block. -#[no_mangle] -pub extern "C" fn oaknode_block_as_node(block: CHandle) -> CHandle { +pub fn oaknode_block_as_node(block: CHandle) -> CHandle { ref_clone(&block) } /// `oaknode_block_get_in`: read the in point as an int pair. -#[no_mangle] -pub extern "C" fn oaknode_block_get_in( +pub fn oaknode_block_get_in( block: CHandle, numerator: *mut c_int, denominator: *mut c_int, @@ -593,8 +564,7 @@ pub extern "C" fn oaknode_block_get_in( } /// `oaknode_block_get_out`: read the out point as an int pair. -#[no_mangle] -pub extern "C" fn oaknode_block_get_out( +pub fn oaknode_block_get_out( block: CHandle, numerator: *mut c_int, denominator: *mut c_int, @@ -608,8 +578,7 @@ pub extern "C" fn oaknode_block_get_out( } /// `oaknode_block_get_length`: read the length as an int pair. -#[no_mangle] -pub extern "C" fn oaknode_block_get_length( +pub fn oaknode_block_get_length( block: CHandle, numerator: *mut c_int, denominator: *mut c_int, @@ -624,8 +593,7 @@ pub extern "C" fn oaknode_block_get_length( /// `oaknode_block_set_length_and_media_out`: set length, keeping the media-in /// fixed (the out point follows). -#[no_mangle] -pub extern "C" fn oaknode_block_set_length_and_media_out( +pub fn oaknode_block_set_length_and_media_out( block: CHandle, numerator: c_int, denominator: c_int, @@ -641,8 +609,7 @@ pub extern "C" fn oaknode_block_set_length_and_media_out( /// `oaknode_block_set_length_and_media_in`: set length, keeping the out point /// fixed (the media-in follows). -#[no_mangle] -pub extern "C" fn oaknode_block_set_length_and_media_in( +pub fn oaknode_block_set_length_and_media_in( block: CHandle, numerator: c_int, denominator: c_int, @@ -657,8 +624,7 @@ pub extern "C" fn oaknode_block_set_length_and_media_in( } /// `oaknode_block_get_enabled`: read the enabled flag. -#[no_mangle] -pub extern "C" fn oaknode_block_get_enabled(block: CHandle, enabled: *mut c_int) -> c_int { +pub fn oaknode_block_get_enabled(block: CHandle, enabled: *mut c_int) -> c_int { // SAFETY: handle boxes a MockNode. let Some(b) = (unsafe { get::(&block) }) else { return -1; @@ -669,8 +635,7 @@ pub extern "C" fn oaknode_block_get_enabled(block: CHandle, enabled: *mut c_int) } /// `oaknode_block_set_enabled`: set the enabled flag. -#[no_mangle] -pub extern "C" fn oaknode_block_set_enabled(block: CHandle, enabled: c_int) -> c_int { +pub fn oaknode_block_set_enabled(block: CHandle, enabled: c_int) -> c_int { // SAFETY: handle boxes a MockNode. let Some(b) = (unsafe { get_mut::(&block) }) else { return -1; @@ -680,8 +645,7 @@ pub extern "C" fn oaknode_block_set_enabled(block: CHandle, enabled: c_int) -> c } /// `oaknode_block_get_previous`: write a borrowed handle to the previous block. -#[no_mangle] -pub extern "C" fn oaknode_block_get_previous(block: CHandle, out: *mut CHandle) -> c_int { +pub fn oaknode_block_get_previous(block: CHandle, out: *mut CHandle) -> c_int { // SAFETY: handle boxes a MockNode. let Some(b) = (unsafe { get::(&block) }) else { return -1; @@ -691,8 +655,7 @@ pub extern "C" fn oaknode_block_get_previous(block: CHandle, out: *mut CHandle) } /// `oaknode_block_get_next`: write a borrowed handle to the next block. -#[no_mangle] -pub extern "C" fn oaknode_block_get_next(block: CHandle, out: *mut CHandle) -> c_int { +pub fn oaknode_block_get_next(block: CHandle, out: *mut CHandle) -> c_int { // SAFETY: handle boxes a MockNode. let Some(b) = (unsafe { get::(&block) }) else { return -1; @@ -702,8 +665,7 @@ pub extern "C" fn oaknode_block_get_next(block: CHandle, out: *mut CHandle) -> c } /// `oaknode_block_get_track`: write a borrowed handle to the owning track. -#[no_mangle] -pub extern "C" fn oaknode_block_get_track(block: CHandle, out: *mut CHandle) -> c_int { +pub fn oaknode_block_get_track(block: CHandle, out: *mut CHandle) -> c_int { // SAFETY: handle boxes a MockNode. let Some(b) = (unsafe { get::(&block) }) else { return -1; @@ -713,8 +675,7 @@ pub extern "C" fn oaknode_block_get_track(block: CHandle, out: *mut CHandle) -> } /// `oaknode_block_link`: link two blocks so they move together. -#[no_mangle] -pub extern "C" fn oaknode_block_link(a: CHandle, b: CHandle) -> c_int { +pub fn oaknode_block_link(a: CHandle, b: CHandle) -> c_int { // SAFETY: both handles box MockNodes. let (pa, pb) = unsafe { match (get_mut::(&a), get_mut::(&b)) { @@ -734,8 +695,7 @@ pub extern "C" fn oaknode_block_link(a: CHandle, b: CHandle) -> c_int { } /// `oaknode_block_unlink`: unlink two blocks. -#[no_mangle] -pub extern "C" fn oaknode_block_unlink(a: CHandle, b: CHandle) -> c_int { +pub fn oaknode_block_unlink(a: CHandle, b: CHandle) -> c_int { // SAFETY: both handles box MockNodes. let (pa, pb) = unsafe { match (get_mut::(&a), get_mut::(&b)) { @@ -753,8 +713,7 @@ pub extern "C" fn oaknode_block_unlink(a: CHandle, b: CHandle) -> c_int { } /// `oaknode_track_get_length`: read the track length as an int pair. -#[no_mangle] -pub extern "C" fn oaknode_track_get_length(track: CHandle, numerator: *mut c_int, denominator: *mut c_int) -> c_int { +pub fn oaknode_track_get_length(track: CHandle, numerator: *mut c_int, denominator: *mut c_int) -> c_int { // SAFETY: handle boxes a MockNode. let Some(t) = (unsafe { get::(&track) }) else { return -1; @@ -770,8 +729,7 @@ pub extern "C" fn oaknode_track_get_length(track: CHandle, numerator: *mut c_int } /// `oaknode_track_get_sequence`: write a borrowed handle to the owning sequence. -#[no_mangle] -pub extern "C" fn oaknode_track_get_sequence(track: CHandle, out: *mut CHandle) -> c_int { +pub fn oaknode_track_get_sequence(track: CHandle, out: *mut CHandle) -> c_int { // SAFETY: handle boxes a MockNode. let Some(t) = (unsafe { get::(&track) }) else { return -1; @@ -781,8 +739,7 @@ pub extern "C" fn oaknode_track_get_sequence(track: CHandle, out: *mut CHandle) } /// `oaknode_track_prepend_block`: insert a block at the front of the track. -#[no_mangle] -pub extern "C" fn oaknode_track_prepend_block(track: CHandle, block: CHandle) -> c_int { +pub fn oaknode_track_prepend_block(track: CHandle, block: CHandle) -> c_int { // SAFETY: both handles box MockNodes. let (pt, pb) = unsafe { match (get_mut::(&track), get_mut::(&block)) { @@ -808,8 +765,7 @@ pub extern "C" fn oaknode_track_prepend_block(track: CHandle, block: CHandle) -> } /// `oaknode_track_insert_block_after`: insert `block` after `before` on `track`. -#[no_mangle] -pub extern "C" fn oaknode_track_insert_block_after( +pub fn oaknode_track_insert_block_after( track: CHandle, block: CHandle, before: CHandle, @@ -866,8 +822,7 @@ pub extern "C" fn oaknode_track_insert_block_after( /// `oaknode_track_ripple_remove_block`: remove a block, shifting later ones /// earlier; ownership returns to the caller (the caller keeps its handle). -#[no_mangle] -pub extern "C" fn oaknode_track_ripple_remove_block(track: CHandle, block: CHandle) -> c_int { +pub fn oaknode_track_ripple_remove_block(track: CHandle, block: CHandle) -> c_int { // SAFETY: handles box MockNodes. let (pt, pb) = unsafe { match (get_mut::(&track), get_mut::(&block)) { @@ -900,8 +855,7 @@ pub extern "C" fn oaknode_track_ripple_remove_block(track: CHandle, block: CHand } /// `oaknode_track_replace_block`: replace `old_block` with `new_block`. -#[no_mangle] -pub extern "C" fn oaknode_track_replace_block( +pub fn oaknode_track_replace_block( track: CHandle, old_block: CHandle, new_block: CHandle, @@ -948,20 +902,17 @@ pub extern "C" fn oaknode_track_replace_block( } /// `oaknode_sequence_as_node`: a borrowed generic-node view of a sequence. -#[no_mangle] -pub extern "C" fn oaknode_sequence_as_node(sequence: CHandle) -> CHandle { +pub fn oaknode_sequence_as_node(sequence: CHandle) -> CHandle { ref_clone(&sequence) } /// `oaknode_sequence_from_node`: a borrowed sequence view of a generic node. -#[no_mangle] -pub extern "C" fn oaknode_sequence_from_node(node: CHandle) -> CHandle { +pub fn oaknode_sequence_from_node(node: CHandle) -> CHandle { ref_clone(&node) } /// `oaknode_node_get_project`: write a borrowed handle to the owning project. -#[no_mangle] -pub extern "C" fn oaknode_node_get_project(node: CHandle, out: *mut CHandle) -> c_int { +pub fn oaknode_node_get_project(node: CHandle, out: *mut CHandle) -> c_int { // SAFETY: handle boxes a MockNode. let Some(n) = (unsafe { get::(&node) }) else { return -1; @@ -971,8 +922,7 @@ pub extern "C" fn oaknode_node_get_project(node: CHandle, out: *mut CHandle) -> } /// `oaknode_node_output_connection_count`: read the output connection count. -#[no_mangle] -pub extern "C" fn oaknode_node_output_connection_count(node: CHandle, out_count: *mut c_int) -> c_int { +pub fn oaknode_node_output_connection_count(node: CHandle, out_count: *mut c_int) -> c_int { // SAFETY: handle boxes a MockNode. let Some(n) = (unsafe { get::(&node) }) else { return -1; @@ -983,8 +933,7 @@ pub extern "C" fn oaknode_node_output_connection_count(node: CHandle, out_count: } /// `oaknode_node_get_markers`: write a borrowed handle to the node's markers. -#[no_mangle] -pub extern "C" fn oaknode_node_get_markers(node: CHandle, out: *mut CHandle) -> c_int { +pub fn oaknode_node_get_markers(node: CHandle, out: *mut CHandle) -> c_int { // SAFETY: handle boxes a MockNode. let Some(n) = (unsafe { get::(&node) }) else { return -1; @@ -995,8 +944,7 @@ pub extern "C" fn oaknode_node_get_markers(node: CHandle, out: *mut CHandle) -> } /// `oaknode_node_get_work_area`: write a borrowed handle to the node's work area. -#[no_mangle] -pub extern "C" fn oaknode_node_get_work_area(node: CHandle, out: *mut CHandle) -> c_int { +pub fn oaknode_node_get_work_area(node: CHandle, out: *mut CHandle) -> c_int { // SAFETY: handle boxes a MockNode. let Some(n) = (unsafe { get::(&node) }) else { return -1; @@ -1007,8 +955,7 @@ pub extern "C" fn oaknode_node_get_work_area(node: CHandle, out: *mut CHandle) - } /// `oaknode_project_add_node`: adopt a node into a project. -#[no_mangle] -pub extern "C" fn oaknode_project_add_node(project: CHandle, node: CHandle) -> c_int { +pub fn oaknode_project_add_node(project: CHandle, node: CHandle) -> c_int { // SAFETY: handles box MockNodes. let (pp, pn) = unsafe { match (get_mut::(&project), get_mut::(&node)) { @@ -1022,8 +969,7 @@ pub extern "C" fn oaknode_project_add_node(project: CHandle, node: CHandle) -> c } /// `oaknode_project_remove_node`: remove a node from a project. -#[no_mangle] -pub extern "C" fn oaknode_project_remove_node(project: CHandle, node: CHandle) -> c_int { +pub fn oaknode_project_remove_node(project: CHandle, node: CHandle) -> c_int { // SAFETY: handles box MockNodes. let (pp, pn) = unsafe { match (get_mut::(&project), get_mut::(&node)) { @@ -1043,8 +989,7 @@ pub extern "C" fn oaknode_project_remove_node(project: CHandle, node: CHandle) - /// `oaknode_command_create_remove_node`: a command that removes a node. The /// mock returns a command handle whose redo/undo no-op, but whose free drops /// the wrapped node handle it borrows. -#[no_mangle] -pub extern "C" fn oaknode_command_create_remove_node(node: CHandle) -> CHandle { +pub fn oaknode_command_create_remove_node(node: CHandle) -> CHandle { let node_owned = ref_clone(&node); make_owned(MockUndoCommand { vtable: super::undo::OakUndoCommandVtable { @@ -1064,8 +1009,7 @@ extern "C" fn free_owned_handle(userdata: *mut c_void) { } /// `oaknode_block_get_kind`: map a block's kind to an `OAKNODE_BLOCK_*` int. -#[no_mangle] -pub extern "C" fn oaknode_block_get_kind(block: CHandle, out_kind: *mut c_int) -> c_int { +pub fn oaknode_block_get_kind(block: CHandle, out_kind: *mut c_int) -> c_int { // SAFETY: handle boxes a MockNode. let Some(b) = (unsafe { get::(&block) }) else { return -1; @@ -1082,8 +1026,7 @@ pub extern "C" fn oaknode_block_get_kind(block: CHandle, out_kind: *mut c_int) - /// `oaknode_block_from_node`: a borrowed block view of a generic node (empty /// when the node is not a block). -#[no_mangle] -pub extern "C" fn oaknode_block_from_node(node: CHandle) -> CHandle { +pub fn oaknode_block_from_node(node: CHandle) -> CHandle { // SAFETY: handle boxes a MockNode. let Some(n) = (unsafe { get::(&node) }) else { return CHandle::null(); @@ -1096,8 +1039,7 @@ pub extern "C" fn oaknode_block_from_node(node: CHandle) -> CHandle { /// `oaknode_block_are_linked`: whether `a` and `b` are linked (`linked` gets /// 1/0). -#[no_mangle] -pub extern "C" fn oaknode_block_are_linked(a: CHandle, b: CHandle, linked: *mut c_int) -> c_int { +pub fn oaknode_block_are_linked(a: CHandle, b: CHandle, linked: *mut c_int) -> c_int { // SAFETY: both handles box MockNodes. let (pa, pb) = unsafe { match (get::(&a), get::(&b)) { @@ -1119,14 +1061,12 @@ pub extern "C" fn oaknode_block_are_linked(a: CHandle, b: CHandle, linked: *mut /// `oaknode_clip_add_cache_passthrough_from`: copy render-cache passthroughs /// from `other`. The mock has no cache model, so this is a no-op. -#[no_mangle] -pub extern "C" fn oaknode_clip_add_cache_passthrough_from(_clip: CHandle, _other: CHandle) -> c_int { +pub fn oaknode_clip_add_cache_passthrough_from(_clip: CHandle, _other: CHandle) -> c_int { 0 } /// `oaknode_clip_get_media_in`: read a clip's media-in as an int pair. -#[no_mangle] -pub extern "C" fn oaknode_clip_get_media_in( +pub fn oaknode_clip_get_media_in( clip: CHandle, numerator: *mut c_int, denominator: *mut c_int, @@ -1143,8 +1083,7 @@ pub extern "C" fn oaknode_clip_get_media_in( } /// `oaknode_clip_set_media_in`: set a clip's media-in as an int pair. -#[no_mangle] -pub extern "C" fn oaknode_clip_set_media_in( +pub fn oaknode_clip_set_media_in( clip: CHandle, numerator: c_int, denominator: c_int, @@ -1161,8 +1100,7 @@ pub extern "C" fn oaknode_clip_set_media_in( } /// `oaknode_track_create`: a new detached track of the given type. -#[no_mangle] -pub extern "C" fn oaknode_track_create(kind: c_int) -> CHandle { +pub fn oaknode_track_create(kind: c_int) -> CHandle { make_owned(MockNode { kind: MockKind::Track, track_type: kind, @@ -1171,8 +1109,7 @@ pub extern "C" fn oaknode_track_create(kind: c_int) -> CHandle { } /// `oaknode_track_get_locked`: read a track's locked flag (`locked` gets 1/0). -#[no_mangle] -pub extern "C" fn oaknode_track_get_locked(track: CHandle, locked: *mut c_int) -> c_int { +pub fn oaknode_track_get_locked(track: CHandle, locked: *mut c_int) -> c_int { // SAFETY: handle boxes a MockNode. let Some(t) = (unsafe { get::(&track) }) else { return -1; @@ -1186,8 +1123,7 @@ pub extern "C" fn oaknode_track_get_locked(track: CHandle, locked: *mut c_int) - } /// `oaknode_track_set_locked`: set a track's locked flag. -#[no_mangle] -pub extern "C" fn oaknode_track_set_locked(track: CHandle, locked: c_int) -> c_int { +pub fn oaknode_track_set_locked(track: CHandle, locked: c_int) -> c_int { // SAFETY: handle boxes a MockNode. let Some(t) = (unsafe { get_mut::(&track) }) else { return -1; @@ -1200,8 +1136,7 @@ pub extern "C" fn oaknode_track_set_locked(track: CHandle, locked: c_int) -> c_i } /// `oaknode_track_get_block_count`: number of blocks on a track. -#[no_mangle] -pub extern "C" fn oaknode_track_get_block_count(track: CHandle, count: *mut c_int) -> c_int { +pub fn oaknode_track_get_block_count(track: CHandle, count: *mut c_int) -> c_int { // SAFETY: handle boxes a MockNode. let Some(t) = (unsafe { get::(&track) }) else { return -1; @@ -1215,8 +1150,7 @@ pub extern "C" fn oaknode_track_get_block_count(track: CHandle, count: *mut c_in } /// `oaknode_track_get_block_at`: borrowed block at `index` on a track. -#[no_mangle] -pub extern "C" fn oaknode_track_get_block_at(track: CHandle, index: c_int, out: *mut CHandle) -> c_int { +pub fn oaknode_track_get_block_at(track: CHandle, index: c_int, out: *mut CHandle) -> c_int { // SAFETY: handle boxes a MockNode. let Some(t) = (unsafe { get::(&track) }) else { return -1; @@ -1233,8 +1167,7 @@ pub extern "C" fn oaknode_track_get_block_at(track: CHandle, index: c_int, out: /// `oaknode_track_get_block_containing_time`: the block strictly containing /// `time` (in < t < out); writes null and returns -1 when none. -#[no_mangle] -pub extern "C" fn oaknode_track_get_block_containing_time( +pub fn oaknode_track_get_block_containing_time( track: CHandle, numerator: c_int, denominator: c_int, @@ -1264,8 +1197,7 @@ pub extern "C" fn oaknode_track_get_block_containing_time( /// `oaknode_track_get_nearest_block_before_or_at`: the block whose in point is /// at or before `time` (the latest such); null when none. -#[no_mangle] -pub extern "C" fn oaknode_track_get_nearest_block_before_or_at( +pub fn oaknode_track_get_nearest_block_before_or_at( track: CHandle, numerator: c_int, denominator: c_int, @@ -1295,8 +1227,7 @@ pub extern "C" fn oaknode_track_get_nearest_block_before_or_at( /// `oaknode_track_get_nearest_block_after_or_at`: the block whose in point is /// at or after `time` (the earliest such); null when none. -#[no_mangle] -pub extern "C" fn oaknode_track_get_nearest_block_after_or_at( +pub fn oaknode_track_get_nearest_block_after_or_at( track: CHandle, numerator: c_int, denominator: c_int, @@ -1325,8 +1256,7 @@ pub extern "C" fn oaknode_track_get_nearest_block_after_or_at( } /// `oaknode_tracklist_get_type`: the track list's track type. -#[no_mangle] -pub extern "C" fn oaknode_tracklist_get_type(list: CHandle, kind: *mut c_int) -> c_int { +pub fn oaknode_tracklist_get_type(list: CHandle, kind: *mut c_int) -> c_int { // SAFETY: handle boxes a MockNode. let Some(l) = (unsafe { get::(&list) }) else { return -1; @@ -1340,8 +1270,7 @@ pub extern "C" fn oaknode_tracklist_get_type(list: CHandle, kind: *mut c_int) -> } /// `oaknode_tracklist_get_track_count`: number of tracks in a track list. -#[no_mangle] -pub extern "C" fn oaknode_tracklist_get_track_count(list: CHandle, count: *mut c_int) -> c_int { +pub fn oaknode_tracklist_get_track_count(list: CHandle, count: *mut c_int) -> c_int { // SAFETY: handle boxes a MockNode. let Some(l) = (unsafe { get::(&list) }) else { return -1; @@ -1355,8 +1284,7 @@ pub extern "C" fn oaknode_tracklist_get_track_count(list: CHandle, count: *mut c } /// `oaknode_tracklist_get_track_at`: borrowed track at `index` in a track list. -#[no_mangle] -pub extern "C" fn oaknode_tracklist_get_track_at(list: CHandle, index: c_int, out: *mut CHandle) -> c_int { +pub fn oaknode_tracklist_get_track_at(list: CHandle, index: c_int, out: *mut CHandle) -> c_int { // SAFETY: handle boxes a MockNode. let Some(l) = (unsafe { get::(&list) }) else { return -1; @@ -1374,22 +1302,19 @@ pub extern "C" fn oaknode_tracklist_get_track_at(list: CHandle, index: c_int, ou /// `oaknode_tracklist_array_append`: append a track-array element on the /// parent sequence. The mock keeps track lists as plain vectors, so this is a /// no-op. -#[no_mangle] -pub extern "C" fn oaknode_tracklist_array_append(_list: CHandle) -> c_int { +pub fn oaknode_tracklist_array_append(_list: CHandle) -> c_int { 0 } /// `oaknode_tracklist_array_remove_last`: remove the last track-array element. /// No-op in the mock (see `oaknode_tracklist_array_append`). -#[no_mangle] -pub extern "C" fn oaknode_tracklist_array_remove_last(_list: CHandle) -> c_int { +pub fn oaknode_tracklist_array_remove_last(_list: CHandle) -> c_int { 0 } /// `oaknode_sequence_get_track_list`: the borrowed per-type track list, or /// null when the sequence has no list of that type. -#[no_mangle] -pub extern "C" fn oaknode_sequence_get_track_list( +pub fn oaknode_sequence_get_track_list( sequence: CHandle, kind: c_int, out: *mut CHandle, @@ -1416,8 +1341,7 @@ pub extern "C" fn oaknode_sequence_get_track_list( /// `oaknode_sequence_get_all_track_count`: total connected tracks across all /// types. -#[no_mangle] -pub extern "C" fn oaknode_sequence_get_all_track_count(sequence: CHandle, count: *mut c_int) -> c_int { +pub fn oaknode_sequence_get_all_track_count(sequence: CHandle, count: *mut c_int) -> c_int { // SAFETY: handle boxes a MockNode. let Some(s) = (unsafe { get::(&sequence) }) else { return -1; @@ -1440,8 +1364,7 @@ pub extern "C" fn oaknode_sequence_get_all_track_count(sequence: CHandle, count: /// `oaknode_sequence_get_all_track_at`: borrowed track at `index` across the /// flat, all-types track list. -#[no_mangle] -pub extern "C" fn oaknode_sequence_get_all_track_at( +pub fn oaknode_sequence_get_all_track_at( sequence: CHandle, index: c_int, out: *mut CHandle, @@ -1471,8 +1394,7 @@ pub extern "C" fn oaknode_sequence_get_all_track_at( /// `oaknode_node_connect`: connect `output_node` to `input_node`'s `input_id`. /// The mock tracks only the output connection count (input edge table is not /// modelled), so this bumps `output_conns`. -#[no_mangle] -pub extern "C" fn oaknode_node_connect( +pub fn oaknode_node_connect( output_node: CHandle, _input_node: CHandle, _input_id: *const c_char, @@ -1487,16 +1409,14 @@ pub extern "C" fn oaknode_node_connect( /// `oaknode_node_disconnect`: remove the edge feeding `input_node`'s /// `input_id`. The mock has no input-edge table, so this is a no-op. -#[no_mangle] -pub extern "C" fn oaknode_node_disconnect(_input_node: CHandle, _input_id: *const c_char) -> c_int { +pub fn oaknode_node_disconnect(_input_node: CHandle, _input_id: *const c_char) -> c_int { 0 } /// `oaknode_node_copy_in_graph`: clone `node` in its graph; `*out_command` /// receives an owned undo handle. The mock clones the node box and hands back /// a no-op command. -#[no_mangle] -pub extern "C" fn oaknode_node_copy_in_graph(node: CHandle, out_command: *mut CHandle) -> CHandle { +pub fn oaknode_node_copy_in_graph(node: CHandle, out_command: *mut CHandle) -> CHandle { // SAFETY: handle boxes a MockNode. let Some(n) = (unsafe { get::(&node) }) else { return CHandle::null(); diff --git a/src/timeline/rust/src/bridge/undo.rs b/src/timeline/rust/src/bridge/undo.rs index 3bd3673bd..c7fc6da37 100644 --- a/src/timeline/rust/src/bridge/undo.rs +++ b/src/timeline/rust/src/bridge/undo.rs @@ -14,6 +14,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// 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 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 . + //! oakundo C ABI imports (`include/undo/undocommand.h`). Undo commands are //! created through the C ABI vtable (`oakundo_command_init` with Rust //! closures as `userdata`) — timeline does not subclass the C++ @@ -27,30 +43,92 @@ use crate::handle::CHandle; /// `OakUndoCommandVtable` — callback table backing a caller-defined undo /// command (undocommand.h). Any callback may be NULL (a NULL redo/undo makes /// that direction a no-op); `free_fn` runs when the command is destroyed. -#[repr(C)] -pub struct OakUndoCommandVtable { - /// `redo` callback (NULL = no-op). - pub redo: Option, - /// `undo` callback (NULL = no-op). - pub undo: Option, - /// `free_fn` — releases `userdata` on destruction. - pub free_fn: Option, +/// Single-lib unification: aliases the oakundo crate's vtable POD. +pub type OakUndoCommandVtable = oakundo::undocommand::OakUndoCommandVtable; + +/// Direct call into the `oakundo` crate (single-lib unification). +pub fn oakundo_command_init(vtable: *const OakUndoCommandVtable, userdata: *mut c_void) -> CHandle { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakundo_command_init(vtable, userdata) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakundo::ffi::command::oakundo_command_init(vtable, userdata) } + } } -extern "C" { - /// `oakundo_command_init` — create a command backed by C callbacks; takes - /// ownership of `userdata`, copies `vtable`. - pub fn oakundo_command_init(vtable: *const OakUndoCommandVtable, userdata: *mut c_void) -> CHandle; - /// `oakundo_command_init_multi` — an empty multi command. - pub fn oakundo_command_init_multi() -> CHandle; - /// `oakundo_command_multi_add_child` — add a child to a multi command. - pub fn oakundo_command_multi_add_child(multi: CHandle, child: CHandle) -> c_int; - /// `oakundo_command_redo_now` — run the command's redo outside a stack. - pub fn oakundo_command_redo_now(command: CHandle) -> c_int; - /// `oakundo_command_undo_now` — run the command's undo outside a stack. - pub fn oakundo_command_undo_now(command: CHandle) -> c_int; - /// `oakundo_command_free` — release one reference; NULL is a no-op. - pub fn oakundo_command_free(command: *mut CHandle); - /// `oakundo_stack_push` — push a command onto a facade-owned stack. - pub fn oakundo_stack_push(stack: CHandle, command: CHandle, text: *const c_char) -> c_int; +/// Direct call into the `oakundo` crate (single-lib unification). +pub fn oakundo_command_init_multi() -> CHandle { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakundo_command_init_multi() + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakundo::ffi::command::oakundo_command_init_multi() } + } } + +/// Direct call into the `oakundo` crate (single-lib unification). +pub fn oakundo_command_multi_add_child(multi: CHandle, child: CHandle) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakundo_command_multi_add_child(multi, child) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakundo::ffi::command::oakundo_command_multi_add_child(multi, child) } + } +} + +/// Direct call into the `oakundo` crate (single-lib unification). +pub fn oakundo_command_redo_now(command: CHandle) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakundo_command_redo_now(command) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakundo::ffi::command::oakundo_command_redo_now(command) } + } +} + +/// Direct call into the `oakundo` crate (single-lib unification). +pub fn oakundo_command_undo_now(command: CHandle) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakundo_command_undo_now(command) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakundo::ffi::command::oakundo_command_undo_now(command) } + } +} + +/// Direct call into the `oakundo` crate (single-lib unification). +pub fn oakundo_command_free(command: *mut CHandle) { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakundo_command_free(command) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakundo::ffi::command::oakundo_command_free(command) } + } +} + +/// Direct call into the `oakundo` crate (single-lib unification; the C ABI +/// name is `oakundo_undostack_push` — kept as `oakundo_stack_push` here for +/// source compatibility). +pub fn oakundo_stack_push(stack: CHandle, command: CHandle, text: *const c_char) -> c_int { + #[cfg(any(test, feature = "test-stubs"))] + { + super::teststubs::oakundo_stack_push(stack, command, text) + } + #[cfg(not(any(test, feature = "test-stubs")))] + { + unsafe { oakundo::ffi::undostack::oakundo_undostack_push(stack, command, text) } + } +} + diff --git a/src/timeline/rust/src/ffi.rs b/src/timeline/rust/src/ffi.rs index ccf27fb9e..257e69611 100644 --- a/src/timeline/rust/src/ffi.rs +++ b/src/timeline/rust/src/ffi.rs @@ -976,12 +976,18 @@ fn write_cstr(dst: *mut c_char, size: c_int, s: &str) { /// Read the next start element's name from `reader`; empty at EOF/error. fn xml_read_next(reader: &H) -> String { + // Advance to the next start element (the real ABI writes a 1/0 flag). + let mut found = 0; + // SAFETY: `found` is a valid out pointer; `reader` is a valid handle. + let r = unsafe { xml::oakcommon_xml_reader_read_next_start_element(reader.clone(), &mut found) }; + if r <= 0 || found == 0 { + return String::new(); + } + // Read the current element's name (two-stage). let mut buf = [0 as c_char; 4096]; // SAFETY: `buf` is a valid writable buffer; `reader` is a valid handle. - let r = unsafe { - xml::oakcommon_xml_reader_read_next_start_element(reader.clone(), buf.as_mut_ptr(), buf.len() as c_int) - }; - if r <= 0 { + let n = unsafe { xml::oakcommon_xml_reader_name(reader.clone(), buf.as_mut_ptr(), buf.len() as c_int) }; + if n <= 0 { return String::new(); } // SAFETY: the reader wrote a NUL-terminated string into `buf`. diff --git a/src/timeline/rust/src/util.rs b/src/timeline/rust/src/util.rs index 0c9663039..f8b25abba 100644 --- a/src/timeline/rust/src/util.rs +++ b/src/timeline/rust/src/util.rs @@ -105,7 +105,7 @@ pub fn free_detached_handle(h: *mut CHandle) { } /// Read a block time field as a `Rational`. -fn block_pair(b: CHandle, f: unsafe extern "C" fn(CHandle, *mut i32, *mut i32) -> i32) -> Rational { +fn block_pair(b: CHandle, f: fn(CHandle, *mut i32, *mut i32) -> i32) -> Rational { let mut n = 0; let mut d = 0; // SAFETY: `n`/`d` are valid out pointers. diff --git a/src/timeline/rust/tests/bridge_test.rs b/src/timeline/rust/tests/bridge_test.rs index 1487bb424..1a2ad43aa 100644 --- a/src/timeline/rust/tests/bridge_test.rs +++ b/src/timeline/rust/tests/bridge_test.rs @@ -208,8 +208,9 @@ fn xml_bridge_validate_handles() { // A null reader is an invalid argument: read fails cleanly (0). let mut buf = [0i8; 64]; + let mut found = 0; assert_eq!( - unsafe { oakcommon_xml_reader_read_next_start_element(CHandle::null(), buf.as_mut_ptr(), 64) }, + unsafe { oakcommon_xml_reader_read_next_start_element(CHandle::null(), &mut found) }, 0 ); @@ -217,8 +218,9 @@ fn xml_bridge_validate_handles() { // elements it reports end-of-stream for every accessor. let mut reader = unsafe { oakcommon_xml_reader_init(b"\0".as_ptr() as *const std::ffi::c_char) }; assert!(!reader.is_null()); + let mut found = 0; assert_eq!( - unsafe { oakcommon_xml_reader_read_next_start_element(reader.clone(), buf.as_mut_ptr(), 64) }, + unsafe { oakcommon_xml_reader_read_next_start_element(reader.clone(), &mut found) }, 0 ); assert_eq!(unsafe { oakcommon_xml_reader_name(reader.clone(), buf.as_mut_ptr(), 64) }, 0); @@ -242,8 +244,14 @@ fn xml_bridge_validate_handles() { text: "hello".to_string(), attrs: vec![("color".to_string(), "red".to_string())], }]); + let mut found = 0; assert_eq!( - unsafe { oakcommon_xml_reader_read_next_start_element(r.clone(), buf.as_mut_ptr(), 64) }, + unsafe { oakcommon_xml_reader_read_next_start_element(r.clone(), &mut found) }, + 1 + ); + assert_eq!(found, 1); + assert_eq!( + unsafe { oakcommon_xml_reader_name(r.clone(), buf.as_mut_ptr(), 64) }, 1 ); assert_eq!( diff --git a/src/timeline/rust/tests/cov_bridge.rs b/src/timeline/rust/tests/cov_bridge.rs index 24a99068f..c17e56976 100644 --- a/src/timeline/rust/tests/cov_bridge.rs +++ b/src/timeline/rust/tests/cov_bridge.rs @@ -107,8 +107,9 @@ fn xml_reader_accessors_missing() { let mut buf = [0 as c_char; 16]; // No current element: every accessor returns 0. let mut r = xml_reader_handle(Vec::new()); + let mut found = 0; unsafe { oaktimeline::bridge::common::oakcommon_xml_reader_read_next_start_element( - r.clone(), buf.as_mut_ptr(), 16, + r.clone(), &mut found, ) }; assert_eq!(unsafe { oakcommon_xml_reader_name(r.clone(), buf.as_mut_ptr(), 16) }, 0); assert_eq!(unsafe { oakcommon_xml_reader_read_element_text(r.clone(), buf.as_mut_ptr(), 16) }, 0); @@ -126,8 +127,9 @@ fn xml_reader_accessors_missing() { text: String::new(), attrs: Vec::new(), }]); + let mut found = 0; unsafe { oaktimeline::bridge::common::oakcommon_xml_reader_read_next_start_element( - r2.clone(), buf.as_mut_ptr(), 16, + r2.clone(), &mut found, ) }; unsafe { oakcommon_xml_reader_attribute_count(r2.clone(), &mut count) }; assert_eq!(count, 0);