Skip to main content

webp_screenshot_rust/ffi/
mod.rs

1//! FFI/C API for WebP Screenshot library
2//!
3//! Provides C-compatible interface for use from other languages
4
5#![allow(non_camel_case_types)]
6
7use crate::{
8    CaptureConfig, WebPConfig, WebPScreenshot,
9};
10use libc::{c_char, c_int, c_uint, c_void, size_t};
11use std::{
12    ffi::CString,
13    ptr,
14};
15
16/// Opaque handle for WebPScreenshot instance
17pub struct webp_screenshot_handle {
18    inner: Box<WebPScreenshot>,
19}
20
21/// Display information structure for C API
22#[repr(C)]
23pub struct webp_display_info {
24    pub index: c_uint,
25    pub width: c_uint,
26    pub height: c_uint,
27    pub x: c_int,
28    pub y: c_int,
29    pub scale_factor: f32,
30    pub is_primary: c_int,
31    pub refresh_rate: c_uint,
32    pub name: *const c_char,
33}
34
35/// WebP configuration for C API
36#[repr(C)]
37pub struct webp_config {
38    pub quality: c_uint,
39    pub method: c_uint,
40    pub lossless: c_int,
41    pub near_lossless: c_uint,
42    pub segments: c_uint,
43    pub sns_strength: c_uint,
44    pub filter_strength: c_uint,
45    pub filter_sharpness: c_uint,
46    pub auto_filter: c_int,
47    pub alpha_compression: c_int,
48    pub alpha_filtering: c_uint,
49    pub alpha_quality: c_uint,
50    pub pass: c_uint,
51    pub thread_count: c_uint,
52    pub low_memory: c_int,
53    pub exact: c_int,
54}
55
56/// Capture options for C API
57#[repr(C)]
58pub struct capture_options {
59    pub webp_config: webp_config,
60    pub include_cursor: c_int,
61    pub use_hardware_acceleration: c_int,
62    pub max_retries: c_uint,
63    pub retry_delay_ms: c_uint,
64}
65
66/// Screenshot result for C API
67#[repr(C)]
68pub struct screenshot_result {
69    pub data: *mut c_void,
70    pub size: size_t,
71    pub width: c_uint,
72    pub height: c_uint,
73    pub success: c_int,
74    pub error_message: *const c_char,
75}
76
77/// Statistics for C API
78#[repr(C)]
79pub struct performance_stats {
80    pub total_captures: u64,
81    pub successful_captures: u64,
82    pub failed_captures: u64,
83    pub total_bytes_captured: u64,
84    pub total_bytes_encoded: u64,
85    pub average_capture_time_ms: f64,
86    pub average_compression_ratio: f64,
87}
88
89// Error codes
90const SUCCESS: c_int = 0;
91const ERROR_NULL_POINTER: c_int = -1;
92#[allow(dead_code)]
93const ERROR_INVALID_PARAMETER: c_int = -2;
94const ERROR_OUT_OF_MEMORY: c_int = -3;
95const ERROR_CAPTURE_FAILED: c_int = -4;
96#[allow(dead_code)]
97const ERROR_ENCODING_FAILED: c_int = -5;
98#[allow(dead_code)]
99const ERROR_PERMISSION_DENIED: c_int = -6;
100#[allow(dead_code)]
101const ERROR_DISPLAY_NOT_FOUND: c_int = -7;
102#[allow(dead_code)]
103const ERROR_NOT_SUPPORTED: c_int = -8;
104
105/// Create a new WebPScreenshot instance
106#[no_mangle]
107pub extern "C" fn webp_screenshot_create() -> *mut webp_screenshot_handle {
108    match WebPScreenshot::new() {
109        Ok(screenshot) => {
110            let handle = Box::new(webp_screenshot_handle {
111                inner: Box::new(screenshot),
112            });
113            Box::into_raw(handle)
114        }
115        Err(_) => ptr::null_mut(),
116    }
117}
118
119/// Create with custom options
120#[no_mangle]
121pub extern "C" fn webp_screenshot_create_with_options(
122    options: *const capture_options,
123) -> *mut webp_screenshot_handle {
124    if options.is_null() {
125        return ptr::null_mut();
126    }
127
128    unsafe {
129        let opts = &*options;
130        let config = convert_capture_options(opts);
131
132        match WebPScreenshot::with_config(config) {
133            Ok(screenshot) => {
134                let handle = Box::new(webp_screenshot_handle {
135                    inner: Box::new(screenshot),
136                });
137                Box::into_raw(handle)
138            }
139            Err(_) => ptr::null_mut(),
140        }
141    }
142}
143
144/// Destroy a WebPScreenshot instance
145#[no_mangle]
146pub extern "C" fn webp_screenshot_destroy(handle: *mut webp_screenshot_handle) {
147    if !handle.is_null() {
148        unsafe {
149            let _ = Box::from_raw(handle);
150        }
151    }
152}
153
154/// Get available displays
155#[no_mangle]
156pub extern "C" fn webp_screenshot_get_displays(
157    handle: *mut webp_screenshot_handle,
158    displays: *mut webp_display_info,
159    count: *mut c_uint,
160) -> c_int {
161    if handle.is_null() || count.is_null() {
162        return ERROR_NULL_POINTER;
163    }
164
165    unsafe {
166        let screenshot = &(*handle).inner;
167
168        match screenshot.get_displays() {
169            Ok(display_list) => {
170                let display_count = display_list.len() as c_uint;
171
172                if displays.is_null() {
173                    // Just return count
174                    *count = display_count;
175                    return SUCCESS;
176                }
177
178                let max_count = (*count).min(display_count);
179                *count = max_count;
180
181                for i in 0..max_count as usize {
182                    let info = &display_list[i];
183                    let c_info = webp_display_info {
184                        index: i as c_uint,
185                        width: info.width,
186                        height: info.height,
187                        x: info.x,
188                        y: info.y,
189                        scale_factor: info.scale_factor,
190                        is_primary: if info.is_primary { 1 } else { 0 },
191                        refresh_rate: info.refresh_rate,
192                        name: CString::new(info.name.clone())
193                            .unwrap_or_default()
194                            .into_raw(),
195                    };
196                    ptr::write(displays.add(i), c_info);
197                }
198
199                SUCCESS
200            }
201            Err(_) => ERROR_CAPTURE_FAILED,
202        }
203    }
204}
205
206/// Capture a display
207#[no_mangle]
208pub extern "C" fn webp_screenshot_capture_display(
209    handle: *mut webp_screenshot_handle,
210    display_index: c_uint,
211    result: *mut screenshot_result,
212) -> c_int {
213    if handle.is_null() || result.is_null() {
214        return ERROR_NULL_POINTER;
215    }
216
217    unsafe {
218        let screenshot = &mut (*handle).inner;
219
220        match screenshot.capture_display(display_index as usize) {
221            Ok(capture) => {
222                let data_size = capture.data.len();
223                let data_ptr = libc::malloc(data_size) as *mut u8;
224
225                if data_ptr.is_null() {
226                    return ERROR_OUT_OF_MEMORY;
227                }
228
229                ptr::copy_nonoverlapping(capture.data.as_ptr(), data_ptr, data_size);
230
231                *result = screenshot_result {
232                    data: data_ptr as *mut c_void,
233                    size: data_size,
234                    width: capture.width,
235                    height: capture.height,
236                    success: 1,
237                    error_message: ptr::null(),
238                };
239
240                SUCCESS
241            }
242            Err(e) => {
243                let error_msg = CString::new(e.to_string()).unwrap_or_default();
244
245                *result = screenshot_result {
246                    data: ptr::null_mut(),
247                    size: 0,
248                    width: 0,
249                    height: 0,
250                    success: 0,
251                    error_message: error_msg.into_raw(),
252                };
253
254                ERROR_CAPTURE_FAILED
255            }
256        }
257    }
258}
259
260/// Free screenshot result
261#[no_mangle]
262pub extern "C" fn webp_screenshot_free_result(result: *mut screenshot_result) {
263    if result.is_null() {
264        return;
265    }
266
267    unsafe {
268        let res = &mut *result;
269
270        if !res.data.is_null() {
271            libc::free(res.data);
272            res.data = ptr::null_mut();
273        }
274
275        if !res.error_message.is_null() {
276            let _ = CString::from_raw(res.error_message as *mut c_char);
277            res.error_message = ptr::null();
278        }
279    }
280}
281
282/// Get performance statistics
283#[no_mangle]
284pub extern "C" fn webp_screenshot_get_stats(
285    handle: *mut webp_screenshot_handle,
286    stats: *mut performance_stats,
287) -> c_int {
288    if handle.is_null() || stats.is_null() {
289        return ERROR_NULL_POINTER;
290    }
291
292    unsafe {
293        let screenshot = &(*handle).inner;
294        let perf_stats = screenshot.stats();
295
296        *stats = performance_stats {
297            total_captures: perf_stats.total_captures,
298            successful_captures: perf_stats.successful_captures,
299            failed_captures: perf_stats.failed_captures,
300            total_bytes_captured: perf_stats.total_bytes_captured,
301            total_bytes_encoded: perf_stats.total_bytes_encoded,
302            average_capture_time_ms: perf_stats.average_capture_time().as_millis() as f64,
303            average_compression_ratio: perf_stats.average_compression_ratio(),
304        };
305
306        SUCCESS
307    }
308}
309
310/// Get library version
311#[no_mangle]
312pub extern "C" fn webp_screenshot_version() -> *const c_char {
313    static VERSION: once_cell::sync::Lazy<CString> =
314        once_cell::sync::Lazy::new(|| CString::new(crate::version()).unwrap_or_default());
315    VERSION.as_ptr()
316}
317
318/// Check if hardware acceleration is available
319#[no_mangle]
320pub extern "C" fn webp_screenshot_is_hardware_accelerated(
321    handle: *mut webp_screenshot_handle,
322) -> c_int {
323    if handle.is_null() {
324        return 0;
325    }
326
327    unsafe {
328        let screenshot = &(*handle).inner;
329        if screenshot.is_hardware_accelerated() {
330            1
331        } else {
332            0
333        }
334    }
335}
336
337/// Get implementation name
338#[no_mangle]
339pub extern "C" fn webp_screenshot_implementation_name(
340    handle: *mut webp_screenshot_handle,
341) -> *const c_char {
342    if handle.is_null() {
343        return ptr::null();
344    }
345
346    unsafe {
347        let screenshot = &(*handle).inner;
348        let name = CString::new(screenshot.implementation_name()).unwrap_or_default();
349        name.into_raw()
350    }
351}
352
353/// Free string returned by the library
354#[no_mangle]
355pub extern "C" fn webp_screenshot_free_string(str: *mut c_char) {
356    if !str.is_null() {
357        unsafe {
358            let _ = CString::from_raw(str);
359        }
360    }
361}
362
363// Helper functions
364
365fn convert_capture_options(opts: &capture_options) -> CaptureConfig {
366    CaptureConfig {
367        webp_config: WebPConfig {
368            quality: opts.webp_config.quality as u8,
369            method: opts.webp_config.method as u8,
370            lossless: opts.webp_config.lossless != 0,
371            near_lossless: opts.webp_config.near_lossless as u8,
372            segments: opts.webp_config.segments as u8,
373            sns_strength: opts.webp_config.sns_strength as u8,
374            filter_strength: opts.webp_config.filter_strength as u8,
375            filter_sharpness: opts.webp_config.filter_sharpness as u8,
376            auto_filter: opts.webp_config.auto_filter != 0,
377            alpha_compression: opts.webp_config.alpha_compression != 0,
378            alpha_filtering: opts.webp_config.alpha_filtering as u8,
379            alpha_quality: opts.webp_config.alpha_quality as u8,
380            pass: opts.webp_config.pass as u8,
381            thread_count: opts.webp_config.thread_count as usize,
382            low_memory: opts.webp_config.low_memory != 0,
383            exact: opts.webp_config.exact != 0,
384        },
385        include_cursor: opts.include_cursor != 0,
386        use_hardware_acceleration: opts.use_hardware_acceleration != 0,
387        max_retries: opts.max_retries,
388        retry_delay: std::time::Duration::from_millis(opts.retry_delay_ms as u64),
389        ..Default::default()
390    }
391}
392
393#[cfg(test)]
394mod tests {
395    use super::*;
396    use std::ffi::CStr;
397
398    #[test]
399    fn test_c_api_create_destroy() {
400        let handle = webp_screenshot_create();
401        assert!(!handle.is_null());
402        webp_screenshot_destroy(handle);
403    }
404
405    #[test]
406    fn test_c_api_version() {
407        let version = webp_screenshot_version();
408        assert!(!version.is_null());
409
410        unsafe {
411            let version_str = CStr::from_ptr(version);
412            assert!(!version_str.to_bytes().is_empty());
413        }
414    }
415}