use callback_helpers::{from_void_ptr, to_heap_ptr};
use error::UIError;
use ffi_tools;
use libui_ffi;
use std::os::raw::{c_int, c_void};
use std::ffi::CStr;
use std::marker::PhantomData;
use std::mem;
use std::mem::ManuallyDrop;
use std::rc::Rc;
use std::sync::{Arc, RwLock};
use std::thread;
use std::time::{Duration, SystemTime};
use controls::Window;
struct UIToken {
_pd: PhantomData<*mut ()>,
initialized: Arc<RwLock<bool>>,
}
impl UIToken {
fn new() -> Self {
Self {
_pd: PhantomData,
initialized: Arc::new(RwLock::new(true)),
}
}
}
impl Drop for UIToken {
fn drop(&mut self) {
assert!(
ffi_tools::is_initialized(),
"Attempted to uninit libUI in UIToken destructor when libUI was not initialized!"
);
*self.initialized.write().unwrap() = false;
unsafe {
Window::destroy_all_windows();
libui_ffi::uiUninit();
ffi_tools::unset_initialized();
}
}
}
#[derive(Clone)]
pub struct UI {
_token: Rc<UIToken>,
}
impl UI {
pub fn init() -> Result<UI, UIError> {
if ffi_tools::is_initialized() {
return Err(UIError::MultipleInitError {});
};
unsafe {
let mut init_options = libui_ffi::uiInitOptions {
Size: mem::size_of::<libui_ffi::uiInitOptions>(),
};
let err = libui_ffi::uiInit(&mut init_options);
if err.is_null() {
ffi_tools::set_initialized();
Ok(UI {
_token: Rc::new(UIToken::new()),
})
} else {
let error_string = CStr::from_ptr(err).to_string_lossy().into_owned();
libui_ffi::uiFreeInitError(err);
Err(UIError::FailedInitError {
error: error_string,
})
}
}
}
pub fn main(&self) {
self.event_loop().run();
}
pub fn event_loop(&self) -> EventLoop {
unsafe { libui_ffi::uiMainSteps() };
return EventLoop {
_pd: PhantomData,
callback: None,
};
}
pub fn event_queue(&self) -> EventQueue {
EventQueue {
initialized: self._token.initialized.clone(),
}
}
pub fn quit(&self) {
unsafe { libui_ffi::uiQuit() }
}
pub fn queue_main<F: FnMut() + 'static>(&self, callback: F) {
extern "C" fn c_callback<G: FnMut()>(data: *mut c_void) {
unsafe {
from_void_ptr::<G>(data)();
}
}
unsafe {
libui_ffi::uiQueueMain(Some(c_callback::<F>), to_heap_ptr(callback));
}
}
pub fn on_should_quit<F: FnMut() + 'static>(&self, callback: F) {
extern "C" fn c_callback<G: FnMut()>(data: *mut c_void) -> i32 {
unsafe {
from_void_ptr::<G>(data)();
0
}
}
unsafe {
libui_ffi::uiOnShouldQuit(Some(c_callback::<F>), to_heap_ptr(callback));
}
}
}
pub struct EventLoop<'s> {
_pd: PhantomData<*mut ()>,
callback: Option<Box<dyn FnMut() + 's>>,
}
impl<'s> EventLoop<'s> {
pub fn on_tick<'ctx, F: FnMut() + 's + 'ctx>(&'ctx mut self, callback: F) {
self.callback = Some(Box::new(callback));
}
pub fn next_tick(&mut self) -> bool {
let result = unsafe { libui_ffi::uiMainStep(false as c_int) == 1 };
if let Some(ref mut c) = self.callback {
c();
}
result
}
pub fn next_event_tick(&mut self) -> bool {
let result = unsafe { libui_ffi::uiMainStep(true as c_int) == 1 };
if let Some(ref mut c) = self.callback {
c();
}
result
}
pub fn run(&mut self) {
loop {
if !self.next_event_tick() {
break;
}
}
}
pub fn run_delay(&mut self, delay_ms: u32) {
if let Some(ref mut c) = self.callback {
let delay_ms = delay_ms as u128;
let mut t0 = SystemTime::now();
'event_loop: loop {
for _ in 0..5 {
if !unsafe { libui_ffi::uiMainStep(false as c_int) == 1 } {
break 'event_loop;
}
}
if let Ok(duration) = t0.elapsed() {
if duration.as_millis() >= delay_ms {
c();
t0 = SystemTime::now();
}
} else {
t0 = SystemTime::now();
}
thread::sleep(Duration::from_millis(5));
}
} else {
self.run()
}
}
}
pub struct EventQueue {
initialized: Arc<RwLock<bool>>,
}
impl EventQueue {
pub fn queue_main<F: FnOnce() + 'static + Send>(&self, callback: F) -> bool {
let initialized = self.initialized.read().unwrap();
if !*initialized {
return false;
}
extern "C" fn c_callback<G: FnOnce()>(data: *mut c_void) {
unsafe {
from_void_ptr::<Option<G>>(data).take().map(|f| f());
}
}
unsafe {
libui_ffi::uiQueueMain(Some(c_callback::<F>), to_heap_ptr(Some(callback)));
}
true
}
}
pub struct EventQueueWithData<T: 'static> {
data: Arc<DropOnQueueCell<T>>,
}
impl<T> EventQueueWithData<T> {
pub fn new(ui: &UI, data: T) -> Self {
Self {
data: Arc::new(DropOnQueueCell::new(ui, data)),
}
}
pub fn queue_main<F: FnOnce(&T) + 'static + Send>(&self, callback: F) {
let arc = self.data.clone();
let queue = &self.data.queue;
queue.queue_main(move || {
callback(unsafe { arc.inner() });
});
}
}
struct DropOnQueueCell<T: 'static> {
data: SendCell<T>,
queue: EventQueue,
}
impl<T> Drop for DropOnQueueCell<T> {
fn drop(&mut self) {
let mut data = unsafe { self.data.clone() };
self.queue.queue_main(move || {
unsafe {
data.drop();
}
});
}
}
impl<T> DropOnQueueCell<T> {
pub fn new(ui: &UI, data: T) -> Self {
Self {
queue: ui.event_queue(),
data: SendCell::new(data),
}
}
pub unsafe fn inner(&self) -> &T {
self.data.inner()
}
}
struct SendCell<T: 'static> {
data: ManuallyDrop<T>,
}
impl<T> SendCell<T> {
fn new(data: T) -> Self {
Self {
data: ManuallyDrop::new(data),
}
}
unsafe fn clone(&mut self) -> SendCell<T> {
SendCell {
data: unsafe { ManuallyDrop::new(ManuallyDrop::take(&mut self.data)) },
}
}
unsafe fn drop(&mut self) {
ManuallyDrop::drop(&mut self.data);
}
unsafe fn inner(&self) -> &T {
&*self.data
}
}
unsafe impl<T> Send for SendCell<T> {}
unsafe impl<T> Sync for SendCell<T> {}