Skip to main content

fxplug_sys/
lib.rs

1//! Raw C-compatible ABI shared by the Objective-C FxPlug shim and Rust crates.
2
3#![allow(non_snake_case)]
4
5use core::ffi::{c_char, c_void};
6
7pub const FXPLUG_RUST_OK: i32 = 0;
8pub const FXPLUG_RUST_ERROR: i32 = -1;
9pub const FXPLUG_RUST_NOT_IMPLEMENTED: i32 = 1;
10
11pub const FXPLUG_RUST_STRING_CAPACITY: usize = 128;
12pub const FXPLUG_RUST_INFO_CAPACITY: usize = 256;
13pub const FXPLUG_RUST_UUID_CAPACITY: usize = 40;
14
15pub const FXPLUG_RUST_PARAMETER_KIND_FLOAT_SLIDER: u32 = 1;
16pub const FXPLUG_RUST_PARAMETER_KIND_INT_SLIDER: u32 = 2;
17pub const FXPLUG_RUST_PARAMETER_KIND_TOGGLE_BUTTON: u32 = 3;
18
19pub const FXPLUG_RUST_RENDER_BACKEND_CPU: u32 = 0;
20pub const FXPLUG_RUST_RENDER_BACKEND_METAL: u32 = 1;
21pub const FXPLUG_RUST_RENDER_BACKEND_WGPU: u32 = 2;
22
23pub const FXPLUG_RUST_PIXEL_FORMAT_RGBA_F32: u32 = 1;
24pub const FXPLUG_RUST_PIXEL_FORMAT_BGRA_U8: u32 = 2;
25
26pub const FXPLUG_RUST_MTL_PIXEL_FORMAT_RGBA8_UNORM: u64 = 70;
27pub const FXPLUG_RUST_MTL_PIXEL_FORMAT_RGBA8_UNORM_SRGB: u64 = 71;
28pub const FXPLUG_RUST_MTL_PIXEL_FORMAT_BGRA8_UNORM: u64 = 80;
29pub const FXPLUG_RUST_MTL_PIXEL_FORMAT_BGRA8_UNORM_SRGB: u64 = 81;
30pub const FXPLUG_RUST_MTL_PIXEL_FORMAT_RGBA16_FLOAT: u64 = 115;
31pub const FXPLUG_RUST_MTL_PIXEL_FORMAT_RGBA32_FLOAT: u64 = 125;
32
33pub const FXPLUG_RUST_QUALITY_DRAFT: u32 = 0;
34pub const FXPLUG_RUST_QUALITY_NORMAL: u32 = 1;
35pub const FXPLUG_RUST_QUALITY_HIGH: u32 = 2;
36pub const FXPLUG_RUST_QUALITY_BEST: u32 = 3;
37
38pub const FXPLUG_RUST_PIXEL_TRANSFORM_SCALE: u32 = 1;
39pub const FXPLUG_RUST_PIXEL_TRANSFORM_SCALE_TRANSLATE: u32 = 3;
40pub const FXPLUG_RUST_PIXEL_TRANSFORM_FULL: u32 = 6;
41
42#[repr(C)]
43#[derive(Clone, Copy, Debug, Default, PartialEq)]
44pub struct FxPlugRustRect {
45    pub x: f64,
46    pub y: f64,
47    pub width: f64,
48    pub height: f64,
49}
50
51#[repr(C)]
52#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
53pub struct FxPlugRustTime {
54    pub value: i64,
55    pub timescale: i32,
56    pub flags: u32,
57    pub epoch: i64,
58}
59
60#[repr(C)]
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
62pub struct FxPlugRustPluginDescriptor {
63    pub identifier: [c_char; FXPLUG_RUST_STRING_CAPACITY],
64    pub display_name: [c_char; FXPLUG_RUST_STRING_CAPACITY],
65    pub group_name: [c_char; FXPLUG_RUST_STRING_CAPACITY],
66    pub info: [c_char; FXPLUG_RUST_INFO_CAPACITY],
67    pub uuid: [c_char; FXPLUG_RUST_UUID_CAPACITY],
68    pub source_count: u32,
69}
70
71impl Default for FxPlugRustPluginDescriptor {
72    fn default() -> Self {
73        Self {
74            identifier: [0; FXPLUG_RUST_STRING_CAPACITY],
75            display_name: [0; FXPLUG_RUST_STRING_CAPACITY],
76            group_name: [0; FXPLUG_RUST_STRING_CAPACITY],
77            info: [0; FXPLUG_RUST_INFO_CAPACITY],
78            uuid: [0; FXPLUG_RUST_UUID_CAPACITY],
79            source_count: 1,
80        }
81    }
82}
83
84#[repr(C)]
85#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
86pub struct FxPlugRustProperties {
87    pub may_remap_time: u8,
88    pub varies_when_params_are_static: u8,
89    pub pixel_transform_support: u32,
90}
91
92#[repr(C)]
93#[derive(Clone, Copy, Debug, PartialEq)]
94pub struct FxPlugRustParameterSpec {
95    pub parameter_id: u32,
96    pub kind: u32,
97    pub flags: u32,
98    pub name: [c_char; FXPLUG_RUST_STRING_CAPACITY],
99    pub default_value: f64,
100    pub parameter_min: f64,
101    pub parameter_max: f64,
102    pub slider_min: f64,
103    pub slider_max: f64,
104    pub delta: f64,
105}
106
107impl Default for FxPlugRustParameterSpec {
108    fn default() -> Self {
109        Self {
110            parameter_id: 0,
111            kind: 0,
112            flags: 0,
113            name: [0; FXPLUG_RUST_STRING_CAPACITY],
114            default_value: 0.0,
115            parameter_min: 0.0,
116            parameter_max: 0.0,
117            slider_min: 0.0,
118            slider_max: 0.0,
119            delta: 0.0,
120        }
121    }
122}
123
124#[repr(C)]
125#[derive(Clone, Copy, Debug, Default, PartialEq)]
126pub struct FxPlugRustParameterValue {
127    pub parameter_id: u32,
128    pub kind: u32,
129    pub value: f64,
130}
131
132#[repr(C)]
133#[derive(Clone, Copy, Debug, Default, PartialEq)]
134pub struct FxPlugRustImageTile {
135    pub data: *const c_void,
136    pub data_mut: *mut c_void,
137    pub width: u32,
138    pub height: u32,
139    pub row_bytes: usize,
140    pub pixel_format: u32,
141    pub bounds: FxPlugRustRect,
142    pub io_surface: *mut c_void,
143    pub metal_texture: *mut c_void,
144    pub metal_texture_ptr: usize,
145    pub metal_pixel_format: u64,
146    pub metal_device_registry_id: u64,
147}
148
149#[repr(C)]
150#[derive(Clone, Copy, Debug, Default, PartialEq)]
151pub struct FxPlugRustVertex2D {
152    pub position: [f32; 2],
153    pub texture_coordinate: [f32; 2],
154}
155
156#[repr(C)]
157#[derive(Clone, Copy, Debug, PartialEq)]
158pub struct FxPlugRustMetalRenderPlan {
159    pub vertex_count: u32,
160    pub vertices: [FxPlugRustVertex2D; 4],
161    pub brightness: f32,
162}
163
164impl Default for FxPlugRustMetalRenderPlan {
165    fn default() -> Self {
166        Self {
167            vertex_count: 0,
168            vertices: [FxPlugRustVertex2D::default(); 4],
169            brightness: 1.0,
170        }
171    }
172}
173
174#[repr(C)]
175#[derive(Clone, Copy, Debug, Default, PartialEq)]
176pub struct FxPlugRustRenderContext {
177    pub time: FxPlugRustTime,
178    pub quality: u32,
179    pub backend: u32,
180    pub tile_rect: FxPlugRustRect,
181    pub source: FxPlugRustImageTile,
182    pub destination: FxPlugRustImageTile,
183}
184
185pub type FxPlugRustResult = i32;
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190    use core::mem::{align_of, size_of};
191    use std::fs;
192    use std::process::Command;
193
194    #[test]
195    fn abi_defaults_are_stable() {
196        let descriptor = FxPlugRustPluginDescriptor::default();
197        assert_eq!(descriptor.source_count, 1);
198
199        let spec = FxPlugRustParameterSpec::default();
200        assert_eq!(spec.kind, 0);
201        assert_eq!(spec.default_value, 0.0);
202
203        let plan = FxPlugRustMetalRenderPlan::default();
204        assert_eq!(plan.vertex_count, 0);
205        assert_eq!(plan.brightness, 1.0);
206    }
207
208    #[test]
209    fn abi_layout_matches_expected_c_shapes() {
210        assert_eq!(size_of::<FxPlugRustRect>(), 32);
211        assert_eq!(align_of::<FxPlugRustRect>(), align_of::<f64>());
212        assert_eq!(size_of::<FxPlugRustTime>(), 24);
213        assert_eq!(size_of::<FxPlugRustPluginDescriptor>(), 684);
214        assert_eq!(size_of::<FxPlugRustParameterSpec>(), 192);
215        assert_eq!(size_of::<FxPlugRustParameterValue>(), 16);
216        assert_eq!(size_of::<FxPlugRustVertex2D>(), 16);
217        assert_eq!(size_of::<FxPlugRustMetalRenderPlan>(), 72);
218    }
219
220    #[test]
221    fn c_header_compiles_standalone() {
222        let dir = std::env::temp_dir().join(format!("fxplug-sys-header-{}", std::process::id()));
223        fs::create_dir_all(&dir).unwrap();
224        let source = dir.join("smoke.c");
225        let object = dir.join("smoke.o");
226        fs::write(
227            &source,
228            r#"
229#include "fxplug_rust_abi.h"
230int main(void) {
231    FxPlugRustParameterValue value = {0};
232    value.kind = FXPLUG_RUST_PARAMETER_KIND_TOGGLE_BUTTON;
233    return value.kind == FXPLUG_RUST_PARAMETER_KIND_TOGGLE_BUTTON ? 0 : 1;
234}
235"#,
236        )
237        .unwrap();
238
239        let compiler = std::env::var("CC").unwrap_or_else(|_| "cc".to_string());
240        let status = Command::new(compiler)
241            .arg("-I")
242            .arg(concat!(env!("CARGO_MANIFEST_DIR"), "/include"))
243            .arg("-c")
244            .arg(&source)
245            .arg("-o")
246            .arg(&object)
247            .status()
248            .unwrap();
249        assert!(status.success());
250
251        let _ = fs::remove_file(source);
252        let _ = fs::remove_file(object);
253        let _ = fs::remove_dir(dir);
254    }
255}