1#![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}