fltk/app/
rt.rs

1use crate::app::{init::init_all, init::is_initialized, widget::windows};
2use crate::prelude::*;
3use fltk_sys::fl;
4use std::{mem, os::raw, panic, thread, time};
5
6/// Runs the event loop
7/// # Errors
8/// Returns `FailedToRun`, this is fatal to the app
9pub fn run() -> Result<(), FltkError> {
10    unsafe {
11        if !is_initialized() {
12            init_all();
13        }
14        if !crate::app::is_ui_thread() {
15            return Err(FltkError::Internal(FltkErrorKind::FailedToRun));
16        }
17        match fl::Fl_run() {
18            0 => Ok(()),
19            _ => Err(FltkError::Internal(FltkErrorKind::FailedToRun)),
20        }
21    }
22}
23
24/// Enable locks. This is called automatically in the beginning of the app initialization
25pub fn enable_locks() -> Result<(), FltkError> {
26    lock()?;
27    Ok(())
28}
29
30/// Locks the main UI thread
31/// # Errors
32/// Returns `FailedToLock` if locking is unsupported. This is fatal to the app
33pub fn lock() -> Result<(), FltkError> {
34    unsafe {
35        match fl::Fl_lock() {
36            0 => Ok(()),
37            _ => Err(FltkError::Internal(FltkErrorKind::FailedToLock)),
38        }
39    }
40}
41
42/// Unlocks the main UI thread
43pub fn unlock() {
44    unsafe {
45        fl::Fl_unlock();
46    }
47}
48
49/// Trigger event loop handling in the main thread
50pub fn awake() {
51    unsafe { fl::Fl_awake() }
52}
53
54/// Registers a function that will be called by the main thread during the next message handling cycle
55pub fn awake_callback<F: FnMut() + 'static>(cb: F) {
56    unsafe {
57        unsafe extern "C" fn shim(data: *mut raw::c_void) {
58            unsafe {
59                let mut a: Box<Box<dyn FnMut()>> = Box::from_raw(data as *mut Box<dyn FnMut()>);
60                let f: &mut (dyn FnMut()) = &mut **a;
61                let _ = panic::catch_unwind(panic::AssertUnwindSafe(f));
62            }
63        }
64        let a: *mut Box<dyn FnMut()> = Box::into_raw(Box::new(Box::new(cb)));
65        let data: *mut raw::c_void = a as *mut raw::c_void;
66        let callback: fl::Fl_Awake_Handler = Some(shim);
67        fl::Fl_awake_callback(callback, data);
68    }
69}
70
71/// Starts waiting for events.
72/// Calls to redraw within wait require an explicit sleep
73pub fn wait() -> bool {
74    unsafe {
75        if !is_initialized() {
76            init_all();
77        }
78        assert!(crate::app::is_ui_thread());
79        fl::Fl_wait() != 0
80    }
81}
82
83/// Put the thread to sleep for `dur` seconds
84pub fn sleep(dur: f64) {
85    let dur = dur * 1000.;
86    thread::sleep(time::Duration::from_millis(dur as u64));
87}
88
89/// Waits a maximum of `dur` seconds or until "something happens".
90/// Returns true if an event happened (always true on windows).
91/// Returns false if nothing happened.
92/// # Errors
93/// Can error out on X11 system if interrupted by a signal
94pub fn wait_for(dur: f64) -> Result<bool, FltkError> {
95    unsafe {
96        if !is_initialized() {
97            init_all();
98        }
99        if !crate::app::is_ui_thread() {
100            return Err(FltkError::Internal(FltkErrorKind::FailedToRun));
101        }
102        match fl::Fl_wait_for(dur) as i32 {
103            0 => Ok(false),
104            1 => Ok(true),
105            _ => Err(FltkError::Unknown(String::from(
106                "The event loop was probably interrupted by an OS signal!",
107            ))),
108        }
109    }
110}
111
112/// Returns whether a quit signal was sent
113pub fn should_program_quit() -> bool {
114    unsafe { fl::Fl_should_program_quit() != 0 }
115}
116
117/// Determines whether a program should quit
118pub fn program_should_quit(flag: bool) {
119    unsafe { fl::Fl_program_should_quit(i32::from(flag)) }
120}
121
122/// Calling this during a big calculation will keep the screen up to date and the interface responsive.
123pub fn check() -> bool {
124    unsafe {
125        if !is_initialized() {
126            init_all();
127        }
128        assert!(crate::app::is_ui_thread());
129        fl::Fl_check() != 0
130    }
131}
132
133/// This is similar to `app::check()` except this does not call `app::flush()` or any callbacks,
134/// which is useful if your program is in a state where such callbacks are illegal.
135pub fn ready() -> bool {
136    unsafe {
137        if !is_initialized() {
138            init_all();
139        }
140        assert!(crate::app::is_ui_thread());
141        fl::Fl_ready() != 0
142    }
143}
144
145/// Quit the app
146pub fn quit() {
147    if let Some(wins) = windows() {
148        for mut i in wins {
149            if i.shown() {
150                i.hide();
151            }
152        }
153    }
154}
155
156/// Handle object for interacting with idle callbacks
157pub type IdleHandle = *mut ();
158
159unsafe extern "C" fn idle_shim(data: *mut raw::c_void) {
160    unsafe {
161        let a: *mut Box<dyn FnMut(IdleHandle)> = data as *mut Box<dyn FnMut(IdleHandle)>;
162        let f: &mut (dyn FnMut(IdleHandle)) = &mut **a;
163        let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| (*f)(data as _)));
164    }
165}
166
167/// Add an idle callback to run within the event loop.
168/// This function returns a handle that can be used for future interaction with the callback.
169/// Calls to [`WidgetExt::redraw`](`crate::prelude::WidgetExt::redraw`) within the callback require an explicit sleep
170pub fn add_idle<F: FnMut(IdleHandle) + 'static>(cb: F) -> IdleHandle {
171    unsafe {
172        let a: *mut Box<dyn FnMut(IdleHandle)> = Box::into_raw(Box::new(Box::new(cb)));
173        let data: *mut raw::c_void = a as *mut raw::c_void;
174        let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> = Some(idle_shim);
175        fl::Fl_add_idle(callback, data);
176
177        data as _
178    }
179}
180
181/// Remove the idle function associated with the handle
182pub fn remove_idle(handle: IdleHandle) {
183    unsafe {
184        let data: *mut raw::c_void = handle as *mut raw::c_void;
185        let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> = Some(idle_shim);
186        fl::Fl_remove_idle(callback, data);
187    }
188}
189
190/// Checks whether the idle function, associated with the handle, is installed
191pub fn has_idle(handle: IdleHandle) -> bool {
192    unsafe {
193        let data: *mut raw::c_void = handle as *mut raw::c_void;
194        let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> = Some(idle_shim);
195        fl::Fl_has_idle(callback, data) != 0
196    }
197}
198
199/// Handle object for interacting with check callbacks
200pub type CheckHandle = *mut ();
201
202/// Add a check callback to run within the event loop.
203/// This function returns a handle that can be used for future interaction with the callback.
204pub fn add_check<F: FnMut(CheckHandle) + 'static>(cb: F) -> CheckHandle {
205    unsafe {
206        let a: *mut Box<dyn FnMut(CheckHandle)> = Box::into_raw(Box::new(Box::new(cb)));
207        let data: *mut raw::c_void = a as *mut raw::c_void;
208        let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> = Some(idle_shim);
209        fl::Fl_add_check(callback, data);
210
211        data as _
212    }
213}
214
215/// Remove the check function associated with the handle
216pub fn remove_check(handle: CheckHandle) {
217    unsafe {
218        let data: *mut raw::c_void = handle as *mut raw::c_void;
219        let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> = Some(idle_shim);
220        fl::Fl_remove_check(callback, data);
221    }
222}
223
224/// Checks whether the check function, associated with the handle, is installed
225pub fn has_check(handle: CheckHandle) -> bool {
226    unsafe {
227        let data: *mut raw::c_void = handle as *mut raw::c_void;
228        let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> = Some(idle_shim);
229        fl::Fl_has_check(callback, data) != 0
230    }
231}
232
233unsafe extern "C" fn clipboard_notify_shim(source: i32, data: *mut raw::c_void) {
234    unsafe {
235        let a: *mut Box<dyn FnMut(i32)> = data as *mut Box<dyn FnMut(i32)>;
236        let f: &mut (dyn FnMut(i32)) = &mut **a;
237        let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| (*f)(source)));
238    }
239}
240
241/// Register a callback whenever there is a change to the selection buffer or the clipboard.
242/// The clipboard is source 1 and the selection buffer is source 0.
243/// A callback via closure cannot be removed!
244pub fn add_clipboard_notify<F: FnMut(i32) + 'static>(cb: F) {
245    unsafe {
246        let a: *mut Box<dyn FnMut(i32)> = Box::into_raw(Box::new(Box::new(cb)));
247        let data: *mut raw::c_void = a as *mut raw::c_void;
248        let callback: Option<unsafe extern "C" fn(source: i32, arg1: *mut raw::c_void)> =
249            Some(clipboard_notify_shim);
250        fl::Fl_add_clipboard_notify(callback, data);
251    }
252}
253
254/// Stop calling the specified callback when there are changes to the selection
255/// buffer or the clipboard.
256/// The clipboard is source 1 and the selection buffer is source 0
257pub fn remove_clipboard_notify() {
258    unsafe {
259        let callback: Option<unsafe extern "C" fn(source: i32, arg1: *mut raw::c_void)> =
260            Some(clipboard_notify_shim);
261        fl::Fl_remove_clipboard_notify(callback);
262    }
263}
264
265/// Handle object for interacting with timeouts
266pub type TimeoutHandle = *mut ();
267
268unsafe extern "C" fn timeout_shim(data: *mut raw::c_void) {
269    unsafe {
270        let a: *mut Box<dyn FnMut(TimeoutHandle)> = data as *mut Box<dyn FnMut(TimeoutHandle)>;
271        let f: &mut (dyn FnMut(TimeoutHandle)) = &mut **a;
272        let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| (*f)(data as _)));
273    }
274}
275
276/**
277    Adds a one-shot timeout callback. The timeout duration `tm` is indicated in seconds
278    This function returns a handle that can be use for future interaction with the timeout
279    Example:
280    ```rust,no_run
281    use fltk::{prelude::*, *};
282    fn main() {
283        let callback = |_handle| {
284            println!("FIRED");
285        };
286
287        let app = app::App::default();
288        let mut wind = window::Window::new(100, 100, 400, 300, "");
289        wind.show();
290        let _handle = app::add_timeout(1.0, callback);
291        app.run().unwrap();
292    }
293    ```
294*/
295pub fn add_timeout<F: FnMut(TimeoutHandle) + 'static>(tm: f64, cb: F) -> TimeoutHandle {
296    assert!(crate::app::is_ui_thread());
297    unsafe {
298        let a: *mut Box<dyn FnMut(TimeoutHandle)> = Box::into_raw(Box::new(Box::new(cb)));
299        let data: *mut raw::c_void = a as *mut raw::c_void;
300        let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> = Some(timeout_shim);
301        fl::Fl_add_timeout(tm, callback, data);
302
303        data as _
304    }
305}
306
307/**
308    Repeats the timeout callback, associated with the hadle, from the expiration of the previous timeout.
309    You may only call this method inside a timeout callback.
310    The timeout duration `tm` is indicated in seconds
311    Example:
312    ```rust,no_run
313    use fltk::{prelude::*, *};
314    fn main() {
315        let callback = |handle| {
316            println!("TICK");
317            app::repeat_timeout(1.0, handle);
318        };
319
320        let app = app::App::default();
321        let mut wind = window::Window::new(100, 100, 400, 300, "");
322        wind.show();
323        app::add_timeout(1.0, callback);
324        app.run().unwrap();
325    }
326    ```
327*/
328pub fn repeat_timeout(tm: f64, handle: TimeoutHandle) {
329    assert!(crate::app::is_ui_thread());
330    unsafe {
331        let data: *mut raw::c_void = handle as *mut raw::c_void;
332        let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> = Some(timeout_shim);
333        fl::Fl_repeat_timeout(tm, callback, data);
334    }
335}
336
337/**
338    Removes the timeout callback associated with the handle
339    ```rust,no_run
340    use fltk::{prelude::*, *};
341    fn main() {
342        let callback = |handle| {
343            println!("FIRED");
344        };
345
346        let app = app::App::default();
347        let mut wind = window::Window::new(100, 100, 400, 300, "");
348        wind.show();
349        let handle = app::add_timeout(1.0, callback);
350        app::remove_timeout(handle);
351        app.run().unwrap();
352    }
353    ```
354*/
355pub fn remove_timeout(handle: TimeoutHandle) {
356    assert!(crate::app::is_ui_thread());
357    unsafe {
358        let data: *mut raw::c_void = handle as *mut raw::c_void;
359        let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> = Some(timeout_shim);
360        fl::Fl_remove_timeout(callback, data);
361    }
362}
363
364/// Check whether the timeout, associated with the handle, is installed
365pub fn has_timeout(handle: TimeoutHandle) -> bool {
366    assert!(crate::app::is_ui_thread());
367    unsafe {
368        let data: *mut raw::c_void = handle as *mut raw::c_void;
369        let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> = Some(timeout_shim);
370        fl::Fl_has_timeout(callback, data) != 0
371    }
372}
373
374#[doc(hidden)]
375pub fn add_raw_timeout<T>(tm: f64, cb: fn(*mut T), data: *mut T) {
376    unsafe {
377        let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> =
378            Some(mem::transmute(cb));
379        let data: *mut raw::c_void = data as *mut raw::c_void;
380        fl::Fl_add_timeout(tm, callback, data);
381    }
382}
383
384#[doc(hidden)]
385pub fn repeat_raw_timeout<T>(tm: f64, cb: fn(*mut T), data: *mut T) {
386    unsafe {
387        let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> =
388            Some(mem::transmute(cb));
389        let data: *mut raw::c_void = data as *mut raw::c_void;
390        fl::Fl_repeat_timeout(tm, callback, data);
391    }
392}
393
394#[doc(hidden)]
395pub fn remove_raw_timeout<T>(cb: fn(*mut T), data: *mut T) {
396    unsafe {
397        let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> =
398            Some(mem::transmute(cb));
399        let data: *mut raw::c_void = data as *mut raw::c_void;
400        fl::Fl_remove_timeout(callback, data);
401    }
402}
403
404#[doc(hidden)]
405pub fn has_raw_timeout<T>(cb: fn(*mut T), data: *mut T) -> bool {
406    unsafe {
407        let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> =
408            Some(mem::transmute(cb));
409        let data: *mut raw::c_void = data as *mut raw::c_void;
410        fl::Fl_has_timeout(callback, data) != 0
411    }
412}
413
414/// Add a system handler
415/// # Safety
416/// FLTK makes no assurances regarding handling by the system handler
417pub unsafe fn add_system_handler(
418    cb: Option<unsafe extern "C" fn(*mut raw::c_void, *mut raw::c_void) -> i32>,
419    data: *mut raw::c_void,
420) {
421    unsafe {
422        assert!(crate::app::is_ui_thread());
423        fl::Fl_add_system_handler(cb, data);
424    }
425}
426
427/// Add a system handler
428/// # Safety
429/// FLTK makes no assurances regarding handling by the system handler
430pub unsafe fn remove_system_handler(
431    cb: Option<unsafe extern "C" fn(*mut raw::c_void, *mut raw::c_void) -> i32>,
432) {
433    unsafe {
434        assert!(crate::app::is_ui_thread());
435        fl::Fl_remove_system_handler(cb);
436    }
437}