Skip to main content

maplibre_native_ffi_sys/
lib.rs

1//! Raw declarations for the MapLibre Native C ABI.
2//!
3//! This crate mirrors the C boundary: constants, layouts, opaque handle types,
4//! and unsafe extern functions. It also declares the delivery queue protocol,
5//! and the exports of `binding_support.h`, which carry no compatibility
6//! promise. Prefer the safe `maplibre-native-ffi` crate.
7//!
8//! # Linking
9//!
10//! The build script links the native library statically. It takes the library
11//! from the install prefix that `MAPLIBRE_NATIVE_FFI_LOCAL_BUILD` names as an
12//! absolute path, which holds `share/maplibre-native-c/artifact.json`. Without
13//! one, a published crate downloads the release that it was published against,
14//! from the base URL in `MAPLIBRE_NATIVE_FFI_RELEASE_URL` when that is set.
15//!
16//! The `metal`, `opengl`, `vulkan`, and `webgpu` features select the render
17//! backend, and at most one can be enabled. Without one, a downloaded release
18//! takes the default backend its artifact descriptor records: Metal on Apple
19//! platforms, OpenGL on Android and OpenHarmony, WebGPU in the browser, and
20//! Vulkan elsewhere. A local install prefix serves its own backend unless a
21//! feature selects another, and must have been built for the Cargo target; a
22//! mismatch is an error. Dependents read the backend from
23//! `DEP_MAPLIBRE_NATIVE_C_RENDER_BACKEND` and the install prefix from
24//! `DEP_MAPLIBRE_NATIVE_C_PREFIX`.
25//!
26//! On `x86_64-pc-windows-gnu`, link with MSYS2's MINGW64 Clang and LLD
27//! (`-C link-arg=-fuse-ld=lld`). Its MSVCRT and libstdc++ must match the native
28//! artifact. Keep MINGW64's `bin` directory on `PATH` for the runtime DLLs.
29//!
30//! In the browser, the library uses shared memory, so the final module needs a
31//! standard library built with atomics: `-C target-feature=+atomics,+bulk-memory`
32//! and `-Zbuild-std=std,panic_abort`. It also needs the link options listed in
33//! `share/maplibre-native-c/emscripten-link-flags.txt` under the install prefix,
34//! passed through `-C link-arg`.
35
36#![allow(non_camel_case_types)]
37
38// The build script sets this cfg where Cargo builds the platform layer. Nothing
39// here names that layer, which only the C library calls, so this keeps it on the
40// link line.
41#[cfg(mln_platform_crate)]
42extern crate mln_ffi_platform as _;
43
44mod generated;
45
46pub use generated::*;
47
48/// A C handle type: a transparent newtype over the 64-bit id the C API issues.
49pub trait NativeHandle: Copy + 'static {
50    fn to_raw(self) -> u64;
51    fn from_raw(raw: u64) -> Self;
52}
53
54/// The receiver of a call that has none, which admits no handle.
55impl NativeHandle for () {
56    fn to_raw(self) -> u64 {
57        0
58    }
59
60    fn from_raw(_: u64) -> Self {}
61}
62
63macro_rules! native_handles {
64    ($($handle:ident),* $(,)?) => {$(
65        impl $crate::NativeHandle for $handle {
66            fn to_raw(self) -> u64 {
67                self.0
68            }
69
70            fn from_raw(raw: u64) -> Self {
71                Self(raw)
72            }
73        }
74    )*};
75}
76use native_handles;
77
78/// Upstream's `mln/plugin/plugin_api.h` declares the plugin ABI. This crate
79/// declares only the registration entry point, which this library exports;
80/// a plugin integration binds the descriptor types itself.
81#[repr(C)]
82pub struct mln_plugin_descriptor_v1 {
83    _opaque: [u8; 0],
84}
85
86pub type mln_plugin_status = std::ffi::c_uint;
87pub const MLN_PLUGIN_STATUS_OK: mln_plugin_status = 0;
88pub const MLN_PLUGIN_STATUS_ALREADY_REGISTERED: mln_plugin_status = 1;
89pub const MLN_PLUGIN_STATUS_INVALID_ARGUMENT: mln_plugin_status = 2;
90pub const MLN_PLUGIN_STATUS_UNSUPPORTED_ABI: mln_plugin_status = 3;
91pub const MLN_PLUGIN_STATUS_CONFLICT: mln_plugin_status = 4;
92pub const MLN_PLUGIN_STATUS_NOT_FOUND: mln_plugin_status = 5;
93pub const MLN_PLUGIN_STATUS_CALLBACK_ERROR: mln_plugin_status = 6;
94
95pub type mln_plugin_register_function_v1 = Option<
96    unsafe extern "C" fn(
97        descriptor: *const mln_plugin_descriptor_v1,
98        error_message: *mut std::ffi::c_char,
99        error_message_capacity: usize,
100    ) -> mln_plugin_status,
101>;
102
103unsafe extern "C" {
104    pub fn mln_plugin_register_v1(
105        descriptor: *const mln_plugin_descriptor_v1,
106        error_message: *mut std::ffi::c_char,
107        error_message_capacity: usize,
108    ) -> mln_plugin_status;
109}
110
111#[cfg(test)]
112mod tests {
113    /// Calls into the native library so the test binary links and loads it.
114    /// Nothing else in this crate references a symbol, so without this the
115    /// linker drops the dependency and an unusable install prefix still builds
116    /// clean.
117    #[test]
118    fn loads_the_native_library() {
119        // SAFETY: mln_supported_render_backend_mask takes no arguments and
120        // returns a process-global constant.
121        assert_ne!(unsafe { super::mln_supported_render_backend_mask() }, 0);
122    }
123
124    /// The plugin registration entry point links, which nothing above this
125    /// crate references.
126    #[test]
127    fn links_plugin_registration() {
128        // SAFETY: a null descriptor is rejected before it is read, and a null
129        // message buffer with zero capacity asks for no diagnostic.
130        let status =
131            unsafe { super::mln_plugin_register_v1(std::ptr::null(), std::ptr::null_mut(), 0) };
132        assert_eq!(status, super::MLN_PLUGIN_STATUS_INVALID_ARGUMENT);
133    }
134
135    /// The delivery protocol links, which nothing above this crate calls yet.
136    #[test]
137    fn links_delivery_queue() {
138        let mut queue = super::mln_delivery_queue(0);
139        // SAFETY: null options ask for the defaults, `queue` holds the null
140        // handle, and a null diagnostic asks for none.
141        let status = unsafe {
142            super::mln_delivery_queue_create(std::ptr::null(), &mut queue, std::ptr::null_mut())
143        };
144        assert_eq!(status, super::MLN_STATUS_OK);
145        // SAFETY: the queue is live and released once.
146        unsafe { super::mln_delivery_queue_release(queue) };
147    }
148}