fltk/utils/
mod.rs

1use fltk_sys::utils::*;
2use std::ffi::{CStr, CString};
3use std::os::raw;
4use std::sync::atomic::{AtomicBool, Ordering};
5
6use crate::enums::ColorDepth;
7use crate::prelude::FltkError;
8use crate::prelude::FltkErrorKind;
9use crate::prelude::ImageExt;
10
11#[doc(hidden)]
12/// A helper trait to get CStrings from Strings without panicking
13pub trait FlString {
14    /// Get `CStrings` from Strings without panicking
15    fn safe_new(s: &str) -> CString;
16}
17
18impl FlString for CString {
19    fn safe_new(s: &str) -> CString {
20        match CString::new(s) {
21            Ok(v) => v,
22            Err(r) => {
23                let i = r.nul_position();
24                CString::new(&r.into_vec()[0..i]).unwrap()
25            }
26        }
27    }
28}
29
30/**
31    Convenience function to convert rgb to hex.
32    Example:
33    ```rust,no_run
34    use fltk::utils::rgb2hex;
35    let ret = rgb2hex(0, 255, 0); println!("0x{:06x}", ret);
36    ```
37*/
38pub const fn rgb2hex(r: u8, g: u8, b: u8) -> u32 {
39    let r = r as u32;
40    let g = g as u32;
41    let b = b as u32;
42    ((r & 0xff) << 16) + ((g & 0xff) << 8) + (b & 0xff)
43}
44
45/**
46    Convenience function to convert rgba to hex.
47    Example:
48    ```rust,no_run
49    use fltk::utils::rgba2hex;
50    let ret = rgba2hex(0, 255, 0, 255); println!("0x{:08x}", ret);
51    ```
52*/
53pub const fn rgba2hex(r: u8, g: u8, b: u8, a: u8) -> u32 {
54    let r = r as u32;
55    let g = g as u32;
56    let b = b as u32;
57    let a = a as u32;
58    ((r & 0xff) << 24) + ((g & 0xff) << 16) + ((b & 0xff) << 8) + (a & 0xff)
59}
60
61/**
62    Convenience function to convert hex to rgb.
63    Example:
64    ```rust,no_run
65    use fltk::utils::hex2rgb;
66    let (r, g, b) = hex2rgb(0x000000);
67    ```
68*/
69pub const fn hex2rgb(val: u32) -> (u8, u8, u8) {
70    let r = ((val >> 16) & 0xff) as u8;
71    let g = ((val >> 8) & 0xff) as u8;
72    let b = (val & 0xff) as u8;
73    (r, g, b)
74}
75
76/**
77    Convenience function to convert hex to rgba.
78    Example:
79    ```rust,no_run
80    use fltk::utils::hex2rgba;
81    let (r, g, b, a) = hex2rgba(0xff0000ff);
82    ```
83*/
84pub const fn hex2rgba(val: u32) -> (u8, u8, u8, u8) {
85    let r = ((val >> 24) & 0xff) as u8;
86    let g = ((val >> 16) & 0xff) as u8;
87    let b = ((val >> 8) & 0xff) as u8;
88    let a = (val & 0xff) as u8;
89    (r, g, b, a)
90}
91
92/// Expand a filename
93pub fn filename_expand(path: &str) -> Result<String, FltkError> {
94    assert!(path.len() <= 2048);
95    let mut out: Vec<u8> = vec![0u8; 2048];
96    let path = CString::safe_new(path);
97    unsafe {
98        let ret = Fl_filename_expand(
99            out.as_mut_ptr() as *mut raw::c_char,
100            2048,
101            path.as_ptr() as _,
102        );
103        if ret == 0 {
104            Err(FltkError::Internal(FltkErrorKind::FailedOperation))
105        } else {
106            let val = out.iter().position(|&x| x == 0).unwrap();
107            let out = out.split_at(val);
108            match String::from_utf8(out.0.to_vec()) {
109                Ok(s) => Ok(s),
110                Err(err) => Err(FltkError::Utf8Error(err)),
111            }
112        }
113    }
114}
115
116/// Open a uri using the system's browser
117pub fn open_uri(s: &str) -> Result<String, FltkError> {
118    let s = CString::safe_new(s);
119    let mut v: Vec<u8> = vec![0u8; 255];
120    unsafe {
121        let ret = Fl_open_uri(s.as_ptr(), v.as_mut_ptr() as _, 255);
122        let v: Vec<u8> = v.into_iter().partition(|x| *x == 0).1;
123        let s = String::from_utf8(v)?;
124        if ret == 1 {
125            Ok(s)
126        } else {
127            Err(FltkError::Unknown(s))
128        }
129    }
130}
131
132/// Decode a uri
133pub fn decode_uri(s: &str) -> String {
134    let mut s = s.as_bytes().to_vec();
135    s.push(0);
136    unsafe {
137        Fl_decode_uri(s.as_mut_ptr() as _);
138        CStr::from_ptr(s.as_ptr() as _)
139            .to_string_lossy()
140            .to_string()
141    }
142}
143
144/// Get the length of a char in terms of C strings
145pub fn char_len(c: char) -> usize {
146    unsafe extern "C" {
147        pub fn strlen(s: *const std::os::raw::c_char) -> usize;
148    }
149    let s = CString::new(c.to_string()).unwrap();
150    unsafe { strlen(s.as_ptr() as _) }
151}
152
153#[cfg(target_os = "macos")]
154/// Get a window's content view
155pub fn content_view<W: crate::prelude::WindowExt>(w: &W) -> *const raw::c_void {
156    unsafe extern "C" {
157        pub fn cfltk_getContentView(xid: *mut raw::c_void) -> *mut raw::c_void;
158    }
159    unsafe { cfltk_getContentView(w.raw_handle() as _) as _ }
160}
161
162/// Check whether a widget is of a certain type
163pub fn is<W: crate::prelude::WidgetBase>(w: &W) -> bool {
164    W::from_dyn_widget(w).is_some()
165}
166
167/// Check whether a widget is of a certain type
168pub fn is_ptr_of<W: crate::prelude::WidgetBase>(w: *mut fltk_sys::widget::Fl_Widget) -> bool {
169    W::from_dyn_widget_ptr(w).is_some()
170}
171
172/// Get the dynamic typeinfo of a widget, useful for debugging.
173/// This prints the mangled name, which can be unmangled using a crate like `cpp_demangle`
174pub fn type_name<W: crate::prelude::WidgetExt>(w: &W) -> String {
175    unsafe {
176        let p = Fl_type_name(w.as_widget_ptr() as _);
177        CStr::from_ptr(p as *mut raw::c_char)
178            .to_string_lossy()
179            .to_string()
180    }
181}
182
183#[cfg(target_os = "emscripten")]
184unsafe extern "C" {
185    fn fl_read_to_string(empath: *const raw::c_char) -> *mut raw::c_char;
186    fn fl_read_to_binary(empath: *const raw::c_char, len: *mut i32) -> *mut u8;
187    fn fl_write_to_file(empath: *const raw::c_char, data: *const u8, len: i32) -> i32;
188    fn free(data: *mut raw::c_void);
189}
190
191/// Read a web text file (chosen from the browser's file picker) to a string
192#[cfg(target_os = "emscripten")]
193pub fn em_file_read_to_string<S: AsRef<str>>(path: S) -> Result<String, FltkError> {
194    unsafe {
195        let path = CString::safe_new(path.as_ref());
196        let ptr = fl_read_to_string(path.as_ptr());
197        if ptr.is_null() {
198            Err(FltkError::Unknown(String::from("Failed to read from file")))
199        } else {
200            let s = CStr::from_ptr(ptr).to_string_lossy().to_string();
201            free(ptr as _);
202            Ok(s)
203        }
204    }
205}
206
207/// Read a web binary file (chosen from the browser's file picker) to a Vec
208#[cfg(target_os = "emscripten")]
209pub fn em_file_read_to_binary(path: &str) -> Result<Vec<u8>, FltkError> {
210    unsafe {
211        let path = CString::safe_new(path);
212        let mut len = 0;
213        let ptr = fl_read_to_binary(path.as_ptr(), &mut len as _);
214        if ptr.is_null() {
215            Err(FltkError::Unknown(String::from("Failed to read from file")))
216        } else {
217            Ok(std::slice::from_raw_parts(ptr, len as _).to_vec())
218        }
219    }
220}
221
222/// Write to a file chosen by the browser's file picker
223#[cfg(target_os = "emscripten")]
224pub fn em_write_to_file(path: &str, data: &[u8]) -> Result<(), FltkError> {
225    unsafe {
226        let path = CString::safe_new(path);
227        let ret = fl_write_to_file(path.as_ptr(), data.as_ptr(), data.len() as _);
228        if ret == -1 {
229            Err(FltkError::Unknown(String::from("Failed to write to file")))
230        } else {
231            Ok(())
232        }
233    }
234}
235
236/// Draw a framebuffer (rgba) into a widget
237/// # Errors
238/// Errors on invalid or unsupported image formats
239pub fn blit_rgba<'a, T: crate::prelude::WidgetBase>(
240    wid: &'a mut T,
241    fb: &'a [u8],
242) -> Result<(), FltkError> {
243    let width = wid.w();
244    let height = wid.h();
245    let mut img = crate::image::RgbImage::new(fb, width, height, ColorDepth::Rgba8)?;
246    wid.draw(move |s| {
247        let x = s.x();
248        let y = s.y();
249        let w = s.w();
250        let h = s.h();
251        img.scale(w, h, false, true);
252        img.draw(x, y, w, h);
253    });
254    Ok(())
255}
256
257/// Draw a framebuffer (rgba) into a widget
258/// # Safety
259/// The data passed should be valid and outlive the widget
260pub unsafe fn blit_rgba_nocopy<T: crate::prelude::WidgetBase>(wid: &mut T, fb: &[u8]) {
261    unsafe {
262        let ptr = fb.as_ptr();
263        let len = fb.len();
264        let width = wid.w();
265        let height = wid.h();
266        wid.draw(move |s| {
267            let x = s.x();
268            let y = s.y();
269            let w = s.w();
270            let h = s.h();
271            if let Ok(mut img) = crate::image::RgbImage::from_data(
272                std::slice::from_raw_parts(ptr, len),
273                width,
274                height,
275                ColorDepth::Rgba8,
276            ) {
277                img.scale(w, h, false, true);
278                img.draw(x, y, w, h);
279            }
280        });
281    }
282}
283
284/// Draw a framebuffer (rgba) into a widget
285/// # Errors
286/// Errors on invalid or unsupported image formats
287pub fn blit_rgb<'a, T: crate::prelude::WidgetBase>(
288    wid: &'a mut T,
289    fb: &'a [u8],
290) -> Result<(), FltkError> {
291    let width = wid.w();
292    let height = wid.h();
293    let mut img = crate::image::RgbImage::new(fb, width, height, ColorDepth::Rgb8)?;
294    wid.draw(move |s| {
295        let x = s.x();
296        let y = s.y();
297        let w = s.w();
298        let h = s.h();
299        img.scale(w, h, false, true);
300        img.draw(x, y, w, h);
301    });
302    Ok(())
303}
304
305/// Draw a framebuffer (rgba) into a widget
306/// # Safety
307/// The data passed should be valid and outlive the widget
308pub unsafe fn blit_rgb_nocopy<T: crate::prelude::WidgetBase>(wid: &mut T, fb: &[u8]) {
309    unsafe {
310        let ptr = fb.as_ptr();
311        let len = fb.len();
312        let width = wid.w();
313        let height = wid.h();
314        wid.draw(move |s| {
315            let x = s.x();
316            let y = s.y();
317            let w = s.w();
318            let h = s.h();
319            if let Ok(mut img) = crate::image::RgbImage::from_data(
320                std::slice::from_raw_parts(ptr, len),
321                width,
322                height,
323                ColorDepth::Rgb8,
324            ) {
325                img.scale(w, h, false, true);
326                img.draw(x, y, w, h);
327            }
328        });
329    }
330}
331
332/// Basically a check for image support
333#[allow(dead_code)]
334pub(crate) static IMAGES_REGISTERED: AtomicBool = AtomicBool::new(false);
335
336/// Check if fltk-rs was initialized
337#[allow(dead_code)]
338pub(crate) fn images_registered() -> bool {
339    IMAGES_REGISTERED.load(Ordering::Relaxed)
340}
341
342/// Registers all images supported by `SharedImage`
343#[allow(dead_code)]
344pub(crate) fn register_images() {
345    #[cfg(feature = "use-images")]
346    unsafe {
347        fltk_sys::image::Fl_register_images();
348        fltk_sys::fl::Fl_load_system_icons();
349    }
350}