Skip to main content

mbgl_sys/
lib.rs

1//! Low-level FFI bindings to MapLibre GL Native
2//!
3//! This crate provides unsafe Rust bindings to the MapLibre GL Native C API.
4//! It is intended to be used by higher-level safe wrappers.
5//!
6//! # Safety
7//!
8//! All functions in this crate are unsafe and require careful handling of:
9//! - Pointer validity
10//! - Memory ownership
11//! - Thread safety
12//!
13//! Users should prefer the safe `maplibre-native` wrapper crate instead.
14
15#![allow(non_camel_case_types)]
16#![allow(non_snake_case)]
17#![allow(dead_code)]
18
19use libc::{c_char, c_double, c_float, c_uchar, c_uint, c_void, size_t};
20
21/// Opaque type for MLNMap
22#[repr(C)]
23pub struct MLNMap {
24    _private: [u8; 0],
25}
26
27/// Opaque type for MLNHeadlessFrontend
28#[repr(C)]
29pub struct MLNHeadlessFrontend {
30    _private: [u8; 0],
31}
32
33/// Error codes returned by the API
34#[repr(C)]
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum MLNErrorCode {
37    MLN_OK = 0,
38    MLN_ERROR_INVALID_ARGUMENT = 1,
39    MLN_ERROR_STYLE_PARSE = 2,
40    MLN_ERROR_RENDER_FAILED = 3,
41    MLN_ERROR_NOT_LOADED = 4,
42    MLN_ERROR_TIMEOUT = 5,
43    MLN_ERROR_UNKNOWN = 99,
44}
45
46/// Map rendering mode
47#[repr(C)]
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum MLNMapMode {
50    MLN_MAP_MODE_STATIC = 0,
51    MLN_MAP_MODE_TILE = 1,
52}
53
54/// Debug options (bitflags)
55#[repr(C)]
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum MLNDebugOptions {
58    MLN_DEBUG_NONE = 0,
59    MLN_DEBUG_TILE_BORDERS = 1,
60    MLN_DEBUG_PARSE_STATUS = 2,
61    MLN_DEBUG_TIMESTAMPS = 4,
62    MLN_DEBUG_COLLISION = 8,
63    MLN_DEBUG_OVERDRAW = 16,
64}
65
66/// Size structure
67#[repr(C)]
68#[derive(Debug, Clone, Copy, Default)]
69pub struct MLNSize {
70    pub width: c_uint,
71    pub height: c_uint,
72}
73
74impl MLNSize {
75    pub fn new(width: u32, height: u32) -> Self {
76        Self { width, height }
77    }
78}
79
80/// Camera options for rendering
81#[repr(C)]
82#[derive(Debug, Clone, Copy, Default)]
83pub struct MLNCameraOptions {
84    pub latitude: c_double,
85    pub longitude: c_double,
86    pub zoom: c_double,
87    pub bearing: c_double,
88    pub pitch: c_double,
89}
90
91impl MLNCameraOptions {
92    pub fn new(latitude: f64, longitude: f64, zoom: f64) -> Self {
93        Self {
94            latitude,
95            longitude,
96            zoom,
97            bearing: 0.0,
98            pitch: 0.0,
99        }
100    }
101
102    pub fn with_bearing(mut self, bearing: f64) -> Self {
103        self.bearing = bearing;
104        self
105    }
106
107    pub fn with_pitch(mut self, pitch: f64) -> Self {
108        self.pitch = pitch;
109        self
110    }
111}
112
113/// Render options
114#[repr(C)]
115#[derive(Debug, Clone, Copy)]
116pub struct MLNRenderOptions {
117    pub size: MLNSize,
118    pub pixel_ratio: c_float,
119    pub camera: MLNCameraOptions,
120    pub mode: MLNMapMode,
121    pub debug: MLNDebugOptions,
122}
123
124impl Default for MLNRenderOptions {
125    fn default() -> Self {
126        Self {
127            size: MLNSize::new(512, 512),
128            pixel_ratio: 1.0,
129            camera: MLNCameraOptions::default(),
130            mode: MLNMapMode::MLN_MAP_MODE_TILE,
131            debug: MLNDebugOptions::MLN_DEBUG_NONE,
132        }
133    }
134}
135
136/// Rendered image data
137#[repr(C)]
138#[derive(Debug)]
139pub struct MLNImageData {
140    /// RGBA pixel data (premultiplied alpha)
141    pub data: *mut c_uchar,
142    /// Length in bytes (width * height * 4)
143    pub data_len: size_t,
144    /// Image width in pixels
145    pub width: c_uint,
146    /// Image height in pixels
147    pub height: c_uint,
148}
149
150impl Default for MLNImageData {
151    fn default() -> Self {
152        Self {
153            data: std::ptr::null_mut(),
154            data_len: 0,
155            width: 0,
156            height: 0,
157        }
158    }
159}
160
161/// Resource request (for custom file source)
162#[repr(C)]
163#[derive(Debug)]
164pub struct MLNResourceRequest {
165    pub url: *const c_char,
166    /// 0=Unknown, 1=Style, 2=Source, 3=Tile, 4=Glyphs, 5=SpriteImage, 6=SpriteJSON
167    pub kind: c_uchar,
168}
169
170/// Resource response
171#[repr(C)]
172#[derive(Debug)]
173pub struct MLNResourceResponse {
174    pub data: *const c_uchar,
175    pub data_len: size_t,
176    /// NULL if no error
177    pub error: *const c_char,
178    /// true if 404
179    pub not_found: bool,
180}
181
182impl Default for MLNResourceResponse {
183    fn default() -> Self {
184        Self {
185            data: std::ptr::null(),
186            data_len: 0,
187            error: std::ptr::null(),
188            not_found: false,
189        }
190    }
191}
192
193/// Callback type for async rendering
194pub type MLNRenderCallback = Option<
195    unsafe extern "C" fn(error: MLNErrorCode, image: *mut MLNImageData, user_data: *mut c_void),
196>;
197
198/// Callback type for resource requests
199pub type MLNResourceCallback = Option<
200    unsafe extern "C" fn(
201        request: *const MLNResourceRequest,
202        response: *mut MLNResourceResponse,
203        user_data: *mut c_void,
204    ),
205>;
206
207// External C functions - these are stubbed for now
208// In a real implementation, these would link to libmaplibre-native
209
210unsafe extern "C" {
211    /// Initialize the MapLibre Native library.
212    pub fn mln_init() -> MLNErrorCode;
213
214    /// Cleanup the MapLibre Native library.
215    pub fn mln_cleanup();
216
217    /// Create a new headless frontend for rendering.
218    pub fn mln_headless_frontend_create(
219        size: MLNSize,
220        pixel_ratio: c_float,
221    ) -> *mut MLNHeadlessFrontend;
222
223    /// Destroy a headless frontend.
224    pub fn mln_headless_frontend_destroy(frontend: *mut MLNHeadlessFrontend);
225
226    /// Set the size of the headless frontend.
227    pub fn mln_headless_frontend_set_size(frontend: *mut MLNHeadlessFrontend, size: MLNSize);
228
229    /// Get the size of the headless frontend.
230    pub fn mln_headless_frontend_get_size(frontend: *mut MLNHeadlessFrontend) -> MLNSize;
231
232    /// Create a new map instance.
233    pub fn mln_map_create(
234        frontend: *mut MLNHeadlessFrontend,
235        pixel_ratio: c_float,
236        mode: MLNMapMode,
237    ) -> *mut MLNMap;
238
239    /// Create a new map instance with custom resource loader.
240    pub fn mln_map_create_with_loader(
241        frontend: *mut MLNHeadlessFrontend,
242        pixel_ratio: c_float,
243        mode: MLNMapMode,
244        request_callback: MLNResourceCallback,
245        user_data: *mut c_void,
246    ) -> *mut MLNMap;
247
248    /// Destroy a map instance.
249    pub fn mln_map_destroy(map: *mut MLNMap);
250
251    /// Load a style JSON into the map.
252    pub fn mln_map_load_style(map: *mut MLNMap, style_json: *const c_char) -> MLNErrorCode;
253
254    /// Load a style from a URL.
255    pub fn mln_map_load_style_url(map: *mut MLNMap, url: *const c_char) -> MLNErrorCode;
256
257    /// Check if the style is fully loaded.
258    pub fn mln_map_is_fully_loaded(map: *mut MLNMap) -> bool;
259
260    /// Set camera options.
261    pub fn mln_map_set_camera(map: *mut MLNMap, camera: *const MLNCameraOptions);
262
263    /// Get current camera options.
264    pub fn mln_map_get_camera(map: *mut MLNMap) -> MLNCameraOptions;
265
266    /// Set map size.
267    pub fn mln_map_set_size(map: *mut MLNMap, size: MLNSize);
268
269    /// Set debug options.
270    pub fn mln_map_set_debug(map: *mut MLNMap, options: MLNDebugOptions);
271
272    /// Render a still image synchronously.
273    pub fn mln_map_render_still(
274        map: *mut MLNMap,
275        options: *const MLNRenderOptions,
276        image: *mut MLNImageData,
277    ) -> MLNErrorCode;
278
279    /// Render a still image asynchronously.
280    pub fn mln_map_render_still_async(
281        map: *mut MLNMap,
282        options: *const MLNRenderOptions,
283        callback: MLNRenderCallback,
284        user_data: *mut c_void,
285    );
286
287    /// Free image data returned by mln_map_render_still.
288    pub fn mln_image_free(image: *mut MLNImageData);
289
290    /// Get the last error message.
291    pub fn mln_get_last_error() -> *const c_char;
292
293    /// Add a custom image to the map's style.
294    pub fn mln_map_add_image(
295        map: *mut MLNMap,
296        id: *const c_char,
297        data: *const c_uchar,
298        width: c_uint,
299        height: c_uint,
300        pixel_ratio: c_float,
301        sdf: bool,
302    ) -> MLNErrorCode;
303
304    /// Remove an image from the map's style.
305    pub fn mln_map_remove_image(map: *mut MLNMap, id: *const c_char) -> MLNErrorCode;
306
307    /// Set the base path for local file resources.
308    pub fn mln_set_base_path(path: *const c_char);
309
310    /// Set the API key for MapTiler/Mapbox style URLs.
311    pub fn mln_set_api_key(key: *const c_char);
312}
313
314/// Resource kind constants
315pub mod resource_kind {
316    pub const UNKNOWN: u8 = 0;
317    pub const STYLE: u8 = 1;
318    pub const SOURCE: u8 = 2;
319    pub const TILE: u8 = 3;
320    pub const GLYPHS: u8 = 4;
321    pub const SPRITE_IMAGE: u8 = 5;
322    pub const SPRITE_JSON: u8 = 6;
323}
324
325#[cfg(test)]
326mod tests {
327    use super::*;
328
329    #[test]
330    fn test_size_layout() {
331        assert_eq!(std::mem::size_of::<MLNSize>(), 8);
332    }
333
334    #[test]
335    fn test_camera_options() {
336        let camera = MLNCameraOptions::new(37.8, -122.4, 12.0)
337            .with_bearing(45.0)
338            .with_pitch(30.0);
339
340        assert_eq!(camera.latitude, 37.8);
341        assert_eq!(camera.longitude, -122.4);
342        assert_eq!(camera.zoom, 12.0);
343        assert_eq!(camera.bearing, 45.0);
344        assert_eq!(camera.pitch, 30.0);
345    }
346}