use error::UIError; use ffi_tools; use libc::{c_int, c_void}; use ui_sys; use std::ffi::CStr; use std::marker::PhantomData; use std::mem; use std::rc::Rc; use std::thread::sleep; use std::time::Duration; use controls::Window; /// RAII guard for the UI; when dropped, it uninits libUI. struct UIToken { // This PhantomData prevents UIToken from being Send and Sync _pd: PhantomData<*mut ()>, } 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!" ); unsafe { Window::destroy_all_windows(); ui_sys::uiUninit(); ffi_tools::unset_initialized(); } } } /// A handle to user interface functionality. #[derive(Clone)] pub struct UI { token: Rc, } impl UI { /// Initializes the underlying UI bindings, producing a [`UI`](struct.UI.html) struct which can be used /// to actually build your user interface. This is a reference counted type; clone it /// to get an additional reference that can be passed to, e.g., callbacks. /// /// Only one libUI binding can be active at once; if multiple instances are detected, /// this function will return a [`MultipleInitError`](enum.UIError.html#variant.MultipleInitError). /// Be aware the Cocoa (GUI toolkit on Mac OS) requires that the _first thread spawned_ controls /// the UI, so do _not_ spin off your UI interactions into an alternative thread. You're likely to /// have problems on Mac OS. /// /// ``` /// # use iui::UI; /// { /// let ui1 = UI::init().unwrap(); /// /// // This will fail because there is already an instance of UI. /// let ui2 = UI::init(); /// assert!(ui2.is_err()); /// /// // ui1 dropped here. /// } /// let ui3 = UI::init().unwrap(); /// ``` /// /// If libUI cannot initialize its hooks into the platform bindings, this function will /// return a [`FailedInitError`](enum.UIError.html#variant.FailedInitError) with the description of the problem. pub fn init() -> Result { if ffi_tools::is_initialized() { return Err(UIError::MultipleInitError {}); }; unsafe { // Create the magic value needed to init libUI let mut init_options = ui_sys::uiInitOptions { Size: mem::size_of::(), }; // Actually start up the library's functionality let err = ui_sys::uiInit(&mut init_options); if err.is_null() { // Success! We can safely give the user a token allowing them to do UI things. ffi_tools::set_initialized(); Ok(UI { token: Rc::new(UIToken { _pd: PhantomData }), }) } else { // Error occurred; copy the string describing it, then free that memory. let error_string = CStr::from_ptr(err).to_string_lossy().into_owned(); ui_sys::uiFreeInitError(err); Err(UIError::FailedInitError { error: error_string, }) } } } /// Hands control of this thread to the UI toolkit, allowing it to display the UI and respond to events. /// Does not return until the UI [quit](struct.UI.html#method.quit)s. /// /// For more control, use the `EventLoop` struct. pub fn main(&self) { self.event_loop().run(self); } /// Returns an `EventLoop`, a struct that allows you to step over iterations or events in the UI. pub fn event_loop(&self) -> EventLoop { unsafe { ui_sys::uiMainSteps() }; return EventLoop { _pd: PhantomData, callback: None, }; } /// Running this function causes the UI to quit, exiting from [main](struct.UI.html#method.main) and no longer showing any widgets. /// /// Run in every window's default `on_closing` callback. pub fn quit(&self) { unsafe { ui_sys::uiQuit() } } /// Queues a function to be executed on the GUI threa when next possible. Returns /// immediately, not waiting for the function to be executed. /// /// # Example /// /// ``` /// use iui::prelude::*; /// /// let ui = UI::init().unwrap(); /// /// ui.queue_main(|| { println!("Runs first") } ); /// ui.queue_main(|| { println!("Runs second") } ); /// ui.quit(); /// ``` pub fn queue_main(&self, callback: F) { unsafe { let mut data: Box> = Box::new(Box::new(callback)); ui_sys::uiQueueMain( ffi_tools::void_void_callback, &mut *data as *mut Box as *mut c_void, ); mem::forget(data); } } /// Set a callback to be run when the application quits. pub fn on_should_quit(&self, callback: F) { unsafe { let mut data: Box> = Box::new(Box::new(callback)); ui_sys::uiOnShouldQuit( ffi_tools::void_void_callback, &mut *data as *mut Box as *mut c_void, ); mem::forget(data); } } } /// Provides fine-grained control over the user interface event loop, exposing the `on_tick` event /// which allows integration with other event loops, custom logic on event ticks, etc. /// Be aware the Cocoa (GUI toolkit on Mac OS) requires that the _first thread spawned_ controls /// the UI, so do _not_ spin off your UI interactions into an alternative thread. You're likely to /// have problems on Mac OS. pub struct EventLoop { // This PhantomData prevents UIToken from being Send and Sync _pd: PhantomData<*mut ()>, // This callback gets run during "run_delay" loops. callback: Option>, } impl EventLoop { /// Set the given callback to run when the event loop is executed. /// Note that if integrating other event loops you should consider /// the potential benefits and drawbacks of the various run modes. pub fn on_tick(&mut self, _ctx: &UI, callback: F) { self.callback = Some(Box::new(callback)); } /// Executes a tick in the event loop, returning immediately. /// The `on_tick` callback is executed after the UI step. /// /// Returns `true` if the application should continue running, and `false` /// if it should quit. pub fn next_tick(&mut self, _ctx: &UI) -> bool { let result = unsafe { ui_sys::uiMainStep(false as c_int) == 1 }; if let Some(ref mut c) = self.callback { c(); } result } /// Hands control to the event loop until the next UI event occurs. /// The `on_tick` callback is executed after the UI step. /// /// Returns `true` if the application should continue running, and `false` /// if it should quit. pub fn next_event_tick(&mut self, _ctx: &UI) -> bool { let result = unsafe { ui_sys::uiMainStep(true as c_int) == 1 }; if let Some(ref mut c) = self.callback { c(); } result } /// Hands control to the event loop until [`UI::quit()`](struct.UI.html#method.quit) is called, /// running the callback given with `on_tick` after each UI event. pub fn run(&mut self, ctx: &UI) { loop { if !self.next_event_tick(ctx) { break; } } } /// Hands control to the event loop until [`UI::quit()`](struct.UI.html#method.quit) is called, /// running the callback given with `on_tick` approximately every /// `delay` milliseconds. pub fn run_delay(&mut self, ctx: &UI, delay_ms: u32) { loop { if !self.next_tick(ctx) { break; } } sleep(Duration::new(0, delay_ms * 1000000)) } }