#![allow(non_snake_case)]
use core::ffi::{c_char, c_void};
pub const FXPLUG_RUST_OK: i32 = 0;
pub const FXPLUG_RUST_ERROR: i32 = -1;
pub const FXPLUG_RUST_NOT_IMPLEMENTED: i32 = 1;
pub const FXPLUG_RUST_STRING_CAPACITY: usize = 128;
pub const FXPLUG_RUST_INFO_CAPACITY: usize = 256;
pub const FXPLUG_RUST_UUID_CAPACITY: usize = 40;
pub const FXPLUG_RUST_PARAMETER_KIND_FLOAT_SLIDER: u32 = 1;
pub const FXPLUG_RUST_PARAMETER_KIND_INT_SLIDER: u32 = 2;
pub const FXPLUG_RUST_PARAMETER_KIND_TOGGLE_BUTTON: u32 = 3;
pub const FXPLUG_RUST_RENDER_BACKEND_CPU: u32 = 0;
pub const FXPLUG_RUST_RENDER_BACKEND_METAL: u32 = 1;
pub const FXPLUG_RUST_RENDER_BACKEND_WGPU: u32 = 2;
pub const FXPLUG_RUST_PIXEL_FORMAT_RGBA_F32: u32 = 1;
pub const FXPLUG_RUST_PIXEL_FORMAT_BGRA_U8: u32 = 2;
pub const FXPLUG_RUST_MTL_PIXEL_FORMAT_RGBA8_UNORM: u64 = 70;
pub const FXPLUG_RUST_MTL_PIXEL_FORMAT_RGBA8_UNORM_SRGB: u64 = 71;
pub const FXPLUG_RUST_MTL_PIXEL_FORMAT_BGRA8_UNORM: u64 = 80;
pub const FXPLUG_RUST_MTL_PIXEL_FORMAT_BGRA8_UNORM_SRGB: u64 = 81;
pub const FXPLUG_RUST_MTL_PIXEL_FORMAT_RGBA16_FLOAT: u64 = 115;
pub const FXPLUG_RUST_MTL_PIXEL_FORMAT_RGBA32_FLOAT: u64 = 125;
pub const FXPLUG_RUST_QUALITY_DRAFT: u32 = 0;
pub const FXPLUG_RUST_QUALITY_NORMAL: u32 = 1;
pub const FXPLUG_RUST_QUALITY_HIGH: u32 = 2;
pub const FXPLUG_RUST_QUALITY_BEST: u32 = 3;
pub const FXPLUG_RUST_PIXEL_TRANSFORM_SCALE: u32 = 1;
pub const FXPLUG_RUST_PIXEL_TRANSFORM_SCALE_TRANSLATE: u32 = 3;
pub const FXPLUG_RUST_PIXEL_TRANSFORM_FULL: u32 = 6;
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct FxPlugRustRect {
pub x: f64,
pub y: f64,
pub width: f64,
pub height: f64,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct FxPlugRustTime {
pub value: i64,
pub timescale: i32,
pub flags: u32,
pub epoch: i64,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FxPlugRustPluginDescriptor {
pub identifier: [c_char; FXPLUG_RUST_STRING_CAPACITY],
pub display_name: [c_char; FXPLUG_RUST_STRING_CAPACITY],
pub group_name: [c_char; FXPLUG_RUST_STRING_CAPACITY],
pub info: [c_char; FXPLUG_RUST_INFO_CAPACITY],
pub uuid: [c_char; FXPLUG_RUST_UUID_CAPACITY],
pub source_count: u32,
}
impl Default for FxPlugRustPluginDescriptor {
fn default() -> Self {
Self {
identifier: [0; FXPLUG_RUST_STRING_CAPACITY],
display_name: [0; FXPLUG_RUST_STRING_CAPACITY],
group_name: [0; FXPLUG_RUST_STRING_CAPACITY],
info: [0; FXPLUG_RUST_INFO_CAPACITY],
uuid: [0; FXPLUG_RUST_UUID_CAPACITY],
source_count: 1,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct FxPlugRustProperties {
pub may_remap_time: u8,
pub varies_when_params_are_static: u8,
pub pixel_transform_support: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FxPlugRustParameterSpec {
pub parameter_id: u32,
pub kind: u32,
pub flags: u32,
pub name: [c_char; FXPLUG_RUST_STRING_CAPACITY],
pub default_value: f64,
pub parameter_min: f64,
pub parameter_max: f64,
pub slider_min: f64,
pub slider_max: f64,
pub delta: f64,
}
impl Default for FxPlugRustParameterSpec {
fn default() -> Self {
Self {
parameter_id: 0,
kind: 0,
flags: 0,
name: [0; FXPLUG_RUST_STRING_CAPACITY],
default_value: 0.0,
parameter_min: 0.0,
parameter_max: 0.0,
slider_min: 0.0,
slider_max: 0.0,
delta: 0.0,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct FxPlugRustParameterValue {
pub parameter_id: u32,
pub kind: u32,
pub value: f64,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct FxPlugRustImageTile {
pub data: *const c_void,
pub data_mut: *mut c_void,
pub width: u32,
pub height: u32,
pub row_bytes: usize,
pub pixel_format: u32,
pub bounds: FxPlugRustRect,
pub io_surface: *mut c_void,
pub metal_texture: *mut c_void,
pub metal_texture_ptr: usize,
pub metal_pixel_format: u64,
pub metal_device_registry_id: u64,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct FxPlugRustVertex2D {
pub position: [f32; 2],
pub texture_coordinate: [f32; 2],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FxPlugRustMetalRenderPlan {
pub vertex_count: u32,
pub vertices: [FxPlugRustVertex2D; 4],
pub brightness: f32,
}
impl Default for FxPlugRustMetalRenderPlan {
fn default() -> Self {
Self {
vertex_count: 0,
vertices: [FxPlugRustVertex2D::default(); 4],
brightness: 1.0,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct FxPlugRustRenderContext {
pub time: FxPlugRustTime,
pub quality: u32,
pub backend: u32,
pub tile_rect: FxPlugRustRect,
pub source: FxPlugRustImageTile,
pub destination: FxPlugRustImageTile,
}
pub type FxPlugRustResult = i32;
#[cfg(test)]
mod tests {
use super::*;
use core::mem::{align_of, size_of};
use std::fs;
use std::process::Command;
#[test]
fn abi_defaults_are_stable() {
let descriptor = FxPlugRustPluginDescriptor::default();
assert_eq!(descriptor.source_count, 1);
let spec = FxPlugRustParameterSpec::default();
assert_eq!(spec.kind, 0);
assert_eq!(spec.default_value, 0.0);
let plan = FxPlugRustMetalRenderPlan::default();
assert_eq!(plan.vertex_count, 0);
assert_eq!(plan.brightness, 1.0);
}
#[test]
fn abi_layout_matches_expected_c_shapes() {
assert_eq!(size_of::<FxPlugRustRect>(), 32);
assert_eq!(align_of::<FxPlugRustRect>(), align_of::<f64>());
assert_eq!(size_of::<FxPlugRustTime>(), 24);
assert_eq!(size_of::<FxPlugRustPluginDescriptor>(), 684);
assert_eq!(size_of::<FxPlugRustParameterSpec>(), 192);
assert_eq!(size_of::<FxPlugRustParameterValue>(), 16);
assert_eq!(size_of::<FxPlugRustVertex2D>(), 16);
assert_eq!(size_of::<FxPlugRustMetalRenderPlan>(), 72);
}
#[test]
fn c_header_compiles_standalone() {
let dir = std::env::temp_dir().join(format!("fxplug-sys-header-{}", std::process::id()));
fs::create_dir_all(&dir).unwrap();
let source = dir.join("smoke.c");
let object = dir.join("smoke.o");
fs::write(
&source,
r#"
#include "fxplug_rust_abi.h"
int main(void) {
FxPlugRustParameterValue value = {0};
value.kind = FXPLUG_RUST_PARAMETER_KIND_TOGGLE_BUTTON;
return value.kind == FXPLUG_RUST_PARAMETER_KIND_TOGGLE_BUTTON ? 0 : 1;
}
"#,
)
.unwrap();
let compiler = std::env::var("CC").unwrap_or_else(|_| "cc".to_string());
let status = Command::new(compiler)
.arg("-I")
.arg(concat!(env!("CARGO_MANIFEST_DIR"), "/include"))
.arg("-c")
.arg(&source)
.arg("-o")
.arg(&object)
.status()
.unwrap();
assert!(status.success());
let _ = fs::remove_file(source);
let _ = fs::remove_file(object);
let _ = fs::remove_dir(dir);
}
}