fltk/utils/
mod.rs

1/// A utility module containing a OnceCell and Lazy types
2pub mod oncelock;
3
4use fltk_sys::utils::*;
5use std::ffi::{CStr, CString};
6use std::os::raw;
7use std::sync::atomic::{AtomicBool, Ordering};
8
9use crate::prelude::FltkError;
10use crate::prelude::FltkErrorKind;
11
12#[doc(hidden)]
13/// A helper trait to get CStrings from Strings without panicking
14pub trait FlString {
15    /// Get CStrings from Strings without panicking
16    fn safe_new(s: &str) -> CString;
17}
18
19impl FlString for CString {
20    fn safe_new(s: &str) -> CString {
21        match CString::new(s) {
22            Ok(v) => v,
23            Err(r) => {
24                let i = r.nul_position();
25                CString::new(&r.into_vec()[0..i]).unwrap()
26            }
27        }
28    }
29}
30
31/**
32    Convenience function to convert rgb to hex.
33    Example:
34    ```rust,no_run
35    use fltk::utils::rgb2hex;
36    let ret = rgb2hex(0, 255, 0); println!("0x{:06x}", ret);
37    ```
38*/
39pub const fn rgb2hex(r: u8, g: u8, b: u8) -> u32 {
40    let r = r as u32;
41    let g = g as u32;
42    let b = b as u32;
43    ((r & 0xff) << 16) + ((g & 0xff) << 8) + (b & 0xff)
44}
45
46/**
47    Convenience function to convert rgba to hex.
48    Example:
49    ```rust,no_run
50    use fltk::utils::rgba2hex;
51    let ret = rgba2hex(0, 255, 0, 255); println!("0x{:08x}", ret);
52    ```
53*/
54pub const fn rgba2hex(r: u8, g: u8, b: u8, a: u8) -> u32 {
55    let r = r as u32;
56    let g = g as u32;
57    let b = b as u32;
58    let a = a as u32;
59    ((r & 0xff) << 24) + ((g & 0xff) << 16) + ((b & 0xff) << 8) + (a & 0xff)
60}
61
62/**
63    Convenience function to convert hex to rgb.
64    Example:
65    ```rust,no_run
66    use fltk::utils::hex2rgb;
67    let (r, g, b) = hex2rgb(0x000000);
68    ```
69*/
70pub const fn hex2rgb(val: u32) -> (u8, u8, u8) {
71    let r = ((val >> 16) & 0xff) as u8;
72    let g = ((val >> 8) & 0xff) as u8;
73    let b = (val & 0xff) as u8;
74    (r, g, b)
75}
76
77/**
78    Convenience function to convert hex to rgba.
79    Example:
80    ```rust,no_run
81    use fltk::utils::hex2rgba;
82    let (r, g, b, a) = hex2rgba(0xff0000ff);
83    ```
84*/
85pub const fn hex2rgba(val: u32) -> (u8, u8, u8, u8) {
86    let r = ((val >> 24) & 0xff) as u8;
87    let g = ((val >> 16) & 0xff) as u8;
88    let b = ((val >> 8) & 0xff) as u8;
89    let a = (val & 0xff) as u8;
90    (r, g, b, a)
91}
92
93/// Expand a filename
94pub fn filename_expand(path: &str) -> Result<String, FltkError> {
95    assert!(path.len() <= 2048);
96    let mut out: Vec<u8> = vec![0u8; 2048];
97    let path = CString::safe_new(path);
98    unsafe {
99        let ret = Fl_filename_expand(
100            out.as_mut_ptr() as *mut raw::c_char,
101            2048,
102            path.as_ptr() as _,
103        );
104        if ret == 0 {
105            Err(FltkError::Internal(FltkErrorKind::FailedOperation))
106        } else {
107            let val = out.iter().position(|&x| x == 0).unwrap();
108            let out = out.split_at(val);
109            match String::from_utf8(out.0.to_vec()) {
110                Ok(s) => Ok(s),
111                Err(err) => Err(FltkError::Utf8Error(err)),
112            }
113        }
114    }
115}
116
117/// Open a uri using the system's browser
118pub fn open_uri(s: &str) -> Result<String, FltkError> {
119    let s = CString::safe_new(s);
120    let mut v: Vec<u8> = vec![0u8; 255];
121    unsafe {
122        let ret = Fl_open_uri(s.as_ptr(), v.as_mut_ptr() as _, 255);
123        let v: Vec<u8> = v.into_iter().partition(|x| *x == 0).1;
124        let s = String::from_utf8(v)?;
125        if ret == 1 {
126            Ok(s)
127        } else {
128            Err(FltkError::Unknown(s))
129        }
130    }
131}
132
133/// Decode a uri
134pub fn decode_uri(s: &str) -> String {
135    let mut s = s.as_bytes().to_vec();
136    s.push(0);
137    unsafe {
138        Fl_decode_uri(s.as_mut_ptr() as _);
139        CStr::from_ptr(s.as_ptr() as _)
140            .to_string_lossy()
141            .to_string()
142    }
143}
144
145/// Get the length of a char in terms of C strings
146pub fn char_len(c: char) -> usize {
147    extern "C" {
148        pub fn strlen(s: *const std::os::raw::c_char) -> usize;
149    }
150    let s = CString::new(c.to_string()).unwrap();
151    unsafe { strlen(s.as_ptr() as _) }
152}
153
154#[cfg(target_os = "macos")]
155/// Get a window's content view
156pub fn content_view<W: crate::prelude::WindowExt>(w: &W) -> *const raw::c_void {
157    extern "C" {
158        pub fn cfltk_getContentView(xid: *mut raw::c_void) -> *mut raw::c_void;
159    }
160    unsafe { cfltk_getContentView(w.raw_handle() as _) as _ }
161}
162
163/// Check whether a widget is of a certain type
164pub fn is<W: crate::prelude::WidgetBase>(w: &W) -> bool {
165    W::from_dyn_widget(w).is_some()
166}
167
168/// Check whether a widget is of a certain type
169pub fn is_ptr_of<W: crate::prelude::WidgetBase>(w: *mut fltk_sys::widget::Fl_Widget) -> bool {
170    W::from_dyn_widget_ptr(w).is_some()
171}
172
173/// Get the dynamic typeinfo of a widget, useful for debugging.
174/// This prints the mangled name, which can be unmangled using a crate like cpp_demangle
175pub fn type_name<W: crate::prelude::WidgetExt>(w: &W) -> String {
176    unsafe {
177        let p = Fl_type_name(w.as_widget_ptr() as _);
178        CStr::from_ptr(p as *mut raw::c_char)
179            .to_string_lossy()
180            .to_string()
181    }
182}
183
184#[cfg(target_os = "emscripten")]
185extern "C" {
186    fn fl_read_to_string(empath: *const raw::c_char) -> *mut raw::c_char;
187    fn fl_read_to_binary(empath: *const raw::c_char, len: *mut i32) -> *mut u8;
188    fn fl_write_to_file(empath: *const raw::c_char, data: *const u8, len: i32) -> i32;
189    fn free(data: *mut raw::c_void);
190}
191
192/// Read a web text file (chosen from the browser's file picker) to a string
193#[cfg(target_os = "emscripten")]
194pub fn em_file_read_to_string<S: AsRef<str>>(path: S) -> Result<String, FltkError> {
195    unsafe {
196        let path = CString::safe_new(path.as_ref());
197        let ptr = fl_read_to_string(path.as_ptr());
198        if ptr.is_null() {
199            Err(FltkError::Unknown(String::from("Failed to read from file")))
200        } else {
201            let s = CStr::from_ptr(ptr).to_string_lossy().to_string();
202            free(ptr as _);
203            Ok(s)
204        }
205    }
206}
207
208/// Read a web binary file (chosen from the browser's file picker) to a Vec
209#[cfg(target_os = "emscripten")]
210pub fn em_file_read_to_binary(path: &str) -> Result<Vec<u8>, FltkError> {
211    unsafe {
212        let path = CString::safe_new(path);
213        let mut len = 0;
214        let ptr = fl_read_to_binary(path.as_ptr(), &mut len as _);
215        if ptr.is_null() {
216            Err(FltkError::Unknown(String::from("Failed to read from file")))
217        } else {
218            Ok(std::slice::from_raw_parts(ptr, len as _).to_vec())
219        }
220    }
221}
222
223/// Write to a file chosen by the browser's file picker
224#[cfg(target_os = "emscripten")]
225pub fn em_write_to_file(path: &str, data: &[u8]) -> Result<(), FltkError> {
226    unsafe {
227        let path = CString::safe_new(path);
228        let ret = fl_write_to_file(path.as_ptr(), data.as_ptr(), data.len() as _);
229        if ret == -1 {
230            Err(FltkError::Unknown(String::from("Failed to write to file")))
231        } else {
232            Ok(())
233        }
234    }
235}
236
237/// Basically a check for image support
238#[allow(dead_code)]
239pub(crate) static IMAGES_REGISTERED: AtomicBool = AtomicBool::new(false);
240
241/// Check if fltk-rs was initialized
242#[allow(dead_code)]
243pub(crate) fn images_registered() -> bool {
244    IMAGES_REGISTERED.load(Ordering::Relaxed)
245}
246
247/// Registers all images supported by `SharedImage`
248#[allow(dead_code)]
249pub(crate) fn register_images() {
250    #[cfg(not(feature = "no-images"))]
251    unsafe {
252        fltk_sys::image::Fl_register_images();
253        fltk_sys::fl::Fl_load_system_icons();
254    }
255}