fltk/utils/
mod.rs

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