webp_screenshot_rust/ffi/
mod.rs1#![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
16pub struct webp_screenshot_handle {
18 inner: Box<WebPScreenshot>,
19}
20
21#[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#[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#[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#[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#[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
89const 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#[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#[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#[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#[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 *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#[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#[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#[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#[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#[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#[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#[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
363fn 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}