fxplug-sys 0.1.1

Raw C ABI types for Rust-authored FxPlug effects
Documentation
//! Raw C-compatible ABI shared by the Objective-C FxPlug shim and Rust crates.

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