pub use crate::enums::*;
use crate::prelude::*;
use crate::window::*;
use fltk_sys::fl::*;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::{
any,
ffi::{CStr, CString},
marker, mem,
os::raw,
panic, path, ptr,
sync::{
atomic::{AtomicBool, Ordering},
Mutex,
},
thread, time,
};
pub type WidgetPtr = *mut fltk_sys::widget::Fl_Widget;
lazy_static! {
static ref CURRENT_FONT: Mutex<i32> = Mutex::new(0);
static ref CURRENT_FRAME: Mutex<i32> = Mutex::new(2);
static ref LOADED_FONT: Option<&'static str> = None;
static ref IS_INIT: AtomicBool = AtomicBool::new(false);
pub(crate) static ref FONTS: Mutex<Vec<String>> = Mutex::new(Vec::new());
}
pub fn run() -> Result<(), FltkError> {
unsafe {
if !IS_INIT.load(Ordering::Relaxed) {
init_all();
}
match Fl_run() {
0 => Ok(()),
_ => Err(FltkError::Internal(FltkErrorKind::FailedToRun)),
}
}
}
pub fn lock() -> Result<(), FltkError> {
unsafe {
match Fl_lock() {
0 => Ok(()),
_ => Err(FltkError::Internal(FltkErrorKind::FailedToLock)),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Scheme {
Base,
Plastic,
Gtk,
Gleam,
}
pub fn set_scheme(scheme: Scheme) {
let name_str = match scheme {
Scheme::Base => "base",
Scheme::Gtk => "gtk+",
Scheme::Gleam => "gleam",
Scheme::Plastic => "plastic",
};
let name_str = CString::safe_new(name_str);
unsafe { Fl_set_scheme(name_str.as_ptr()) }
}
pub fn scheme() -> Scheme {
unsafe {
use Scheme::*;
match Fl_scheme() {
0 => Base,
1 => Gtk,
2 => Gleam,
3 => Plastic,
_ => unreachable!(),
}
}
}
pub type AppScheme = Scheme;
pub fn unlock() {
unsafe {
Fl_unlock();
}
}
pub fn awake_callback<F: FnMut() + 'static>(cb: F) {
unsafe {
unsafe extern "C" fn shim(data: *mut raw::c_void) {
let a: *mut Box<dyn FnMut()> = data as *mut Box<dyn FnMut()>;
let f: &mut (dyn FnMut()) = &mut **a;
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| f()));
}
let a: *mut Box<dyn FnMut()> = Box::into_raw(Box::new(Box::new(cb)));
let data: *mut raw::c_void = a as *mut raw::c_void;
let callback: Fl_Awake_Handler = Some(shim);
Fl_awake_callback(callback, data);
}
}
pub fn awake() {
unsafe { Fl_awake() }
}
#[derive(Debug, Copy, Clone)]
pub struct App {}
impl App {
pub fn default() -> App {
init_all();
App {}
}
pub fn set_scheme(&mut self, scheme: Scheme) {
set_scheme(scheme);
}
pub fn with_scheme(self, scheme: Scheme) -> App {
set_scheme(scheme);
self
}
pub fn scheme(self) -> Scheme {
scheme()
}
pub fn run(self) -> Result<(), FltkError> {
run()
}
pub fn wait(self) -> bool {
wait()
}
pub fn load_system_fonts(self) -> Self {
*FONTS.lock().unwrap() = get_font_names();
self
}
pub fn load_font<P: AsRef<path::Path>>(self, path: P) -> Result<String, FltkError> {
self.load_font_(path.as_ref())
}
fn load_font_(self, path: &path::Path) -> Result<String, FltkError> {
if !path.exists() {
return Err::<String, FltkError>(FltkError::Internal(FltkErrorKind::ResourceNotFound));
}
if let Some(p) = path.to_str() {
let name = load_font(p)?;
Ok(name)
} else {
Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
}
}
pub fn set_visual(self, mode: Mode) -> Result<(), FltkError> {
set_visual(mode)
}
pub fn redraw(self) {
redraw()
}
pub fn quit(self) {
quit()
}
}
pub fn set_scrollbar_size(sz: u32) {
debug_assert!(sz < std::u32::MAX);
unsafe { Fl_set_scrollbar_size(sz as i32) }
}
pub fn scrollbar_size() -> u32 {
unsafe { Fl_scrollbar_size() as u32 }
}
pub fn grab() -> Option<impl WindowExt> {
unsafe {
let ptr = Fl_grab();
if ptr.is_null() {
None
} else {
Some(crate::window::Window::from_widget_ptr(ptr as *mut _))
}
}
}
pub fn set_grab<W: WindowExt>(win: Option<W>) {
unsafe {
if let Some(w) = win {
Fl_set_grab(w.as_widget_ptr() as *mut _)
} else {
Fl_set_grab(ptr::null_mut())
}
}
}
pub fn event() -> Event {
unsafe {
let x = Fl_event();
let x: Event = mem::transmute(x);
x
}
}
pub fn event_key() -> Key {
unsafe {
let x = Fl_event_key();
mem::transmute(x)
}
}
pub fn event_key_down(key: Key) -> bool {
unsafe { Fl_event_key_down(mem::transmute(key)) != 0 }
}
pub fn event_text() -> String {
unsafe {
let text = Fl_event_text();
if text.is_null() {
String::from("")
} else {
CStr::from_ptr(text as *mut raw::c_char)
.to_string_lossy()
.to_string()
}
}
}
pub fn event_button() -> i32 {
unsafe { Fl_event_button() }
}
pub fn event_mouse_button() -> Mouse {
unsafe { mem::transmute(Fl_event_button()) }
}
pub fn event_clicks() -> bool {
unsafe { Fl_event_clicks() != 0 }
}
pub fn event_x() -> i32 {
unsafe { Fl_event_x() }
}
pub fn event_y() -> i32 {
unsafe { Fl_event_y() }
}
pub fn event_x_root() -> i32 {
unsafe { Fl_event_x_root() }
}
pub fn event_y_root() -> i32 {
unsafe { Fl_event_y_root() }
}
pub fn event_dx() -> i32 {
unsafe { Fl_event_dx() }
}
pub fn event_dy() -> i32 {
unsafe { Fl_event_dy() }
}
pub fn get_mouse() -> (i32, i32) {
unsafe {
let mut x: i32 = 0;
let mut y: i32 = 0;
Fl_get_mouse(&mut x, &mut y);
(x, y)
}
}
pub fn event_coords() -> (i32, i32) {
unsafe { (Fl_event_x(), Fl_event_y()) }
}
pub fn event_is_click() -> bool {
unsafe { Fl_event_is_click() != 0 }
}
pub fn event_length() -> u32 {
unsafe { Fl_event_length() as u32 }
}
pub fn event_state() -> Shortcut {
unsafe { mem::transmute(Fl_event_state()) }
}
pub fn screen_size() -> (f64, f64) {
unsafe { ((Fl_screen_w() as f64 / 0.96), (Fl_screen_h() as f64 / 0.96)) }
}
pub fn paste<T>(widget: &T)
where
T: WidgetBase + InputExt,
{
assert!(!widget.was_deleted());
unsafe {
Fl_paste(widget.as_widget_ptr() as *mut fltk_sys::fl::Fl_Widget, 1);
}
}
pub fn set_callback<F, W>(widget: &mut W, cb: F)
where
F: FnMut(),
W: WidgetExt,
{
assert!(!widget.was_deleted());
unsafe {
unsafe extern "C" fn shim(_wid: *mut fltk_sys::widget::Fl_Widget, data: *mut raw::c_void) {
let a: *mut Box<dyn FnMut()> = data as *mut Box<dyn FnMut()>;
let f: &mut (dyn FnMut()) = &mut **a;
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| f()));
}
let _old_data = widget.user_data();
let a: *mut Box<dyn FnMut()> = Box::into_raw(Box::new(Box::new(cb)));
let data: *mut raw::c_void = a as *mut raw::c_void;
let callback: fltk_sys::widget::Fl_Callback = Some(shim);
fltk_sys::widget::Fl_Widget_set_callback(widget.as_widget_ptr(), callback, data);
}
}
pub unsafe fn set_raw_callback<W>(
widget: &mut W,
data: *mut raw::c_void,
cb: Option<fn(WidgetPtr, *mut raw::c_void)>,
) where
W: WidgetExt,
{
assert!(!widget.was_deleted());
let cb: Option<unsafe extern "C" fn(WidgetPtr, *mut raw::c_void)> = mem::transmute(cb);
fltk_sys::widget::Fl_Widget_set_callback(widget.as_widget_ptr(), cb, data);
}
pub fn visible_focus() -> bool {
unsafe { Fl_visible_focus() != 0 }
}
pub fn set_visible_focus(flag: bool) {
unsafe { Fl_set_visible_focus(flag as i32) }
}
pub fn set_frame_type(new_frame: FrameType) {
unsafe {
let new_frame = new_frame as i32;
let mut curr = CURRENT_FRAME.lock().unwrap();
Fl_set_box_type(56, *curr);
Fl_set_box_type(*curr, new_frame);
Fl_set_box_type(new_frame, 56);
*curr = new_frame;
}
}
pub fn set_font(new_font: Font) {
unsafe {
let new_font = new_font.bits() as i32;
let mut f = CURRENT_FONT.lock().unwrap();
Fl_set_font(15, *f);
Fl_set_font(0, new_font);
Fl_set_font(new_font, *f);
*f = new_font;
}
}
pub fn get_font(font: Font) -> String {
unsafe {
CStr::from_ptr(Fl_get_font(font.bits() as i32))
.to_string_lossy()
.to_string()
}
}
pub fn set_fonts(name: &str) -> u8 {
let name = CString::safe_new(name);
unsafe { Fl_set_fonts(name.as_ptr() as *mut raw::c_char) as u8 }
}
pub fn font_name(idx: usize) -> Option<String> {
let f = FONTS.lock().unwrap();
Some(f[idx].clone())
}
pub fn get_font_names() -> Vec<String> {
let mut vec: Vec<String> = vec![];
let cnt = set_fonts("*") as usize;
for i in 0..cnt {
let temp = unsafe {
CStr::from_ptr(Fl_get_font(i as i32))
.to_string_lossy()
.to_string()
};
vec.push(temp);
}
vec
}
pub fn font_index(name: &str) -> Option<usize> {
let f = FONTS.lock().unwrap();
f.iter().position(|i| i == name)
}
pub fn font_count() -> usize {
(*FONTS.lock().unwrap()).len()
}
pub fn fonts() -> Vec<String> {
(*FONTS.lock().unwrap()).clone()
}
pub fn add_handler(cb: fn(Event) -> bool) {
unsafe {
let callback: Option<unsafe extern "C" fn(ev: raw::c_int) -> raw::c_int> =
Some(mem::transmute(move |ev| {
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| cb(ev) as i32));
}));
Fl_add_handler(callback);
}
}
pub fn wait() -> bool {
unsafe {
if !IS_INIT.load(Ordering::Relaxed) {
init_all();
}
Fl_wait() != 0
}
}
pub fn sleep(dur: f64) {
let dur = dur * 1000.;
thread::sleep(time::Duration::from_millis(dur as u64));
}
pub fn add_idle<F: FnMut() + 'static>(cb: F) {
unsafe {
unsafe extern "C" fn shim(data: *mut raw::c_void) {
let a: *mut Box<dyn FnMut()> = data as *mut Box<dyn FnMut()>;
let f: &mut (dyn FnMut()) = &mut **a;
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| f()));
}
let a: *mut Box<dyn FnMut()> = Box::into_raw(Box::new(Box::new(cb)));
let data: *mut raw::c_void = a as *mut raw::c_void;
let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> = Some(shim);
Fl_add_idle(callback, data);
}
}
pub fn wait_for(dur: f64) -> Result<(), FltkError> {
unsafe {
if !IS_INIT.load(Ordering::Relaxed) {
init_all();
}
if Fl_wait_for(dur) >= 0.0 {
Ok(())
} else {
Err(FltkError::Unknown(String::from(
"An unknown error occured!",
)))
}
}
}
pub unsafe fn awake_msg<T>(msg: T) {
Fl_awake_msg(Box::into_raw(Box::from(msg)) as *mut raw::c_void);
}
pub unsafe fn thread_msg<T>() -> Option<T> {
let msg = Fl_thread_msg();
if msg.is_null() {
None
} else {
let msg = Box::from_raw(msg as *const _ as *mut T);
Some(*msg)
}
}
#[repr(C)]
struct Message<T: Send + Sync> {
hash: u64,
sz: usize,
msg: T,
}
#[derive(Debug, Clone, Copy)]
pub struct Sender<T: Send + Sync> {
data: marker::PhantomData<T>,
hash: u64,
sz: usize,
}
impl<T: Send + Sync> Sender<T> {
pub fn send(&self, val: T) {
let msg = Message {
hash: self.hash,
sz: self.sz,
msg: val,
};
unsafe { awake_msg(msg) }
}
}
#[derive(Debug, Clone, Copy)]
pub struct Receiver<T: Send + Sync> {
data: marker::PhantomData<T>,
hash: u64,
sz: usize,
}
impl<T: Send + Sync> Receiver<T> {
pub fn recv(&self) -> Option<T> {
let data: Option<Message<T>> = unsafe { thread_msg() };
if let Some(data) = data {
if data.sz == self.sz && data.hash == self.hash {
Some(data.msg)
} else {
None
}
} else {
None
}
}
}
pub fn channel<T: Send + Sync>() -> (Sender<T>, Receiver<T>) {
let msg_sz = mem::size_of::<T>();
let type_name = any::type_name::<T>();
let mut hasher = DefaultHasher::new();
type_name.hash(&mut hasher);
let type_hash = hasher.finish();
let s = Sender {
data: marker::PhantomData,
hash: type_hash,
sz: msg_sz,
};
let r = Receiver {
data: marker::PhantomData,
hash: type_hash,
sz: msg_sz,
};
(s, r)
}
pub fn first_window() -> Option<impl WindowExt> {
unsafe {
let x = Fl_first_window();
if x.is_null() {
None
} else {
let x = Window::from_widget_ptr(x as *mut fltk_sys::widget::Fl_Widget);
Some(x)
}
}
}
pub fn next_window<W: WindowExt>(w: &W) -> Option<impl WindowExt> {
unsafe {
let x = Fl_next_window(w.as_widget_ptr() as *const raw::c_void);
if x.is_null() {
None
} else {
let x = Window::from_widget_ptr(x as *mut fltk_sys::widget::Fl_Widget);
Some(x)
}
}
}
pub fn quit() {
if let Some(loaded_font) = *LOADED_FONT {
unload_font(loaded_font).unwrap_or(());
}
if let Some(wins) = windows() {
for mut i in wins {
if i.shown() {
i.hide();
}
}
}
}
pub fn add_timeout<F: FnMut() + 'static>(tm: f64, cb: F) {
unsafe {
unsafe extern "C" fn shim(data: *mut raw::c_void) {
let a: *mut Box<dyn FnMut()> = data as *mut Box<dyn FnMut()>;
let f: &mut (dyn FnMut()) = &mut **a;
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| f()));
}
let a: *mut Box<dyn FnMut()> = Box::into_raw(Box::new(Box::new(cb)));
let data: *mut raw::c_void = a as *mut raw::c_void;
let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> = Some(shim);
fltk_sys::fl::Fl_add_timeout(tm, callback, data);
}
}
pub fn repeat_timeout<F: FnMut() + 'static>(tm: f64, cb: F) {
unsafe {
unsafe extern "C" fn shim(data: *mut raw::c_void) {
let a: *mut Box<dyn FnMut()> = data as *mut Box<dyn FnMut()>;
let f: &mut (dyn FnMut()) = &mut **a;
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| f()));
}
let a: *mut Box<dyn FnMut()> = Box::into_raw(Box::new(Box::new(cb)));
let data: *mut raw::c_void = a as *mut raw::c_void;
let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> = Some(shim);
fltk_sys::fl::Fl_repeat_timeout(tm, callback, data);
}
}
pub fn remove_timeout<F: FnMut() + 'static>(cb: F) {
unsafe {
unsafe extern "C" fn shim(data: *mut raw::c_void) {
let a: *mut Box<dyn FnMut()> = data as *mut Box<dyn FnMut()>;
let f: &mut (dyn FnMut()) = &mut **a;
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| f()));
}
let a: *mut Box<dyn FnMut()> = Box::into_raw(Box::new(Box::new(cb)));
let data: *mut raw::c_void = a as *mut raw::c_void;
let callback: Option<unsafe extern "C" fn(arg1: *mut raw::c_void)> = Some(shim);
fltk_sys::fl::Fl_remove_timeout(callback, data);
}
}
pub fn should_program_quit() -> bool {
unsafe { Fl_should_program_quit() != 0 }
}
pub fn program_should_quit(flag: bool) {
unsafe { Fl_program_should_quit(flag as i32) }
}
pub fn event_inside_widget<Wid: WidgetExt>(wid: &Wid) -> bool {
assert!(!wid.was_deleted());
let x = wid.x();
let y = wid.y();
let w = wid.width();
let h = wid.height();
unsafe { Fl_event_inside(x, y, w, h) != 0 }
}
pub fn event_inside(x: i32, y: i32, w: i32, h: i32) -> bool {
unsafe { Fl_event_inside(x, y, w, h) != 0 }
}
pub fn belowmouse<Wid: WidgetExt>() -> Option<impl WidgetExt> {
unsafe {
let x = Fl_belowmouse() as *mut fltk_sys::fl::Fl_Widget;
if x.is_null() {
None
} else {
Some(crate::widget::Widget::from_widget_ptr(
x as *mut fltk_sys::widget::Fl_Widget,
))
}
}
}
pub fn delete_widget<Wid: WidgetBase>(wid: Wid) {
assert!(!wid.was_deleted());
WidgetBase::delete(wid)
}
pub fn register_images() {
unsafe { fltk_sys::image::Fl_register_images() }
}
pub fn init_all() {
unsafe {
fltk_sys::fl::Fl_init_all();
lock().expect("fltk-rs requires threading support!");
register_images();
*FONTS.lock().unwrap() = vec![
"Helvetica".to_owned(),
"HelveticaBold".to_owned(),
"HelveticaItalic".to_owned(),
"HelveticaBoldItalic".to_owned(),
"Courier".to_owned(),
"CourierBold".to_owned(),
"CourierItalic".to_owned(),
"CourierBoldItalic".to_owned(),
"Times".to_owned(),
"TimesBold".to_owned(),
"TimesItalic".to_owned(),
"TimesBoldItalic".to_owned(),
"Symbol".to_owned(),
"Screen".to_owned(),
"ScreenBold".to_owned(),
"Zapfdingbats".to_owned(),
];
#[cfg(feature = "enable-glwindow")]
{
gl_loader::init_gl();
}
if !IS_INIT.load(Ordering::Relaxed) {
IS_INIT.store(true, Ordering::Relaxed);
}
}
}
pub fn redraw() {
unsafe { Fl_redraw() }
}
pub fn is_event_shift() -> bool {
unsafe { Fl_event_shift() != 0 }
}
pub fn is_event_ctrl() -> bool {
unsafe { Fl_event_ctrl() != 0 }
}
pub fn is_event_command() -> bool {
unsafe { Fl_event_command() != 0 }
}
pub fn is_event_alt() -> bool {
unsafe { Fl_event_alt() != 0 }
}
pub fn set_damage(flag: bool) {
unsafe { Fl_set_damage(flag as i32) }
}
pub fn damage() -> bool {
unsafe { Fl_damage() != 0 }
}
pub fn set_visual(mode: Mode) -> Result<(), FltkError> {
unsafe {
match Fl_visual(mode.bits() as i32) {
0 => Err(FltkError::Internal(FltkErrorKind::FailedOperation)),
_ => Ok(()),
}
}
}
pub fn own_colormap() {
unsafe { Fl_own_colormap() }
}
pub fn pushed() -> Option<impl WidgetExt> {
unsafe {
let ptr = Fl_pushed();
if ptr.is_null() {
None
} else {
Some(crate::widget::Widget::from_widget_ptr(ptr as *mut _))
}
}
}
pub fn focus() -> Option<impl WidgetExt> {
unsafe {
let ptr = Fl_focus();
if ptr.is_null() {
None
} else {
Some(crate::widget::Widget::from_widget_ptr(
ptr as *mut fltk_sys::widget::Fl_Widget,
))
}
}
}
pub fn set_focus<W: WidgetExt>(wid: &W) {
unsafe { Fl_set_focus(wid.as_widget_ptr() as *mut raw::c_void) }
}
pub fn delay(millis: u128) {
let now = time::Instant::now();
loop {
let after = time::Instant::now();
if after.duration_since(now).as_millis() > millis {
break;
}
}
}
pub fn version() -> f64 {
unsafe { Fl_version() }
}
pub fn api_version() -> i32 {
unsafe { Fl_api_version() }
}
pub fn abi_version() -> i32 {
unsafe { Fl_abi_version() }
}
pub fn crate_version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
pub type GraphicsContext = *mut raw::c_void;
pub fn graphics_context() -> GraphicsContext {
unsafe {
let ctx = fltk_sys::window::Fl_gc();
assert!(!ctx.is_null());
ctx
}
}
pub type Display = *mut raw::c_void;
pub fn display() -> Display {
unsafe {
let disp = fltk_sys::window::Fl_display();
assert!(!disp.is_null());
disp
}
}
pub fn dnd() {
unsafe {
Fl_dnd();
}
}
fn load_font(path: &str) -> Result<String, FltkError> {
unsafe {
let path = CString::new(path)?;
if let Some(load_font) = *LOADED_FONT {
unload_font(load_font)?;
}
let ptr = Fl_load_font(path.as_ptr());
if ptr.is_null() {
Err::<String, FltkError>(FltkError::Internal(FltkErrorKind::FailedOperation))
} else {
let name = CStr::from_ptr(ptr as *mut _).to_string_lossy().to_string();
let mut f = FONTS.lock().unwrap();
if f.len() < 17 {
f.push(name.clone());
} else {
f[16] = name.clone();
}
Ok(name)
}
}
}
fn unload_font(path: &str) -> Result<(), FltkError> {
unsafe {
let check = path::Path::new(path);
if !check.exists() {
return Err::<(), FltkError>(FltkError::Internal(FltkErrorKind::ResourceNotFound));
}
let path = CString::new(path)?;
Fl_unload_font(path.as_ptr());
Ok(())
}
}
pub fn windows() -> Option<Vec<impl WindowExt>> {
let mut v: Vec<Window> = vec![];
if let Some(first) = first_window() {
let first: Window = unsafe { first.into_widget() };
v.push(first.clone());
let mut win = first;
while let Some(wind) = next_window(&win) {
let w = unsafe { wind.into_widget::<Window>() };
v.push(w.clone());
win = w;
}
Some(v)
} else {
None
}
}
pub fn foreground(r: u8, g: u8, b: u8) {
unsafe { Fl_foreground(r, g, b) }
}
pub fn background(r: u8, g: u8, b: u8) {
unsafe { Fl_background(r, g, b) }
}
pub fn background2(r: u8, g: u8, b: u8) {
unsafe { Fl_background2(r, g, b) }
}
pub fn get_system_colors() {
unsafe { Fl_get_system_colors() }
}
pub unsafe fn handle<I: Into<i32>, W: WindowExt>(msg: I, w: &W) -> bool {
Fl_handle(msg.into(), w.as_widget_ptr() as _) != 0
}
pub unsafe fn handle_main<I: Into<i32>>(msg: I) -> bool {
if let Some(win) = first_window() {
Fl_handle(msg.into(), win.as_widget_ptr() as _) != 0
} else {
false
}
}