1 // Take a look at the license at the top of the repository in the LICENSE file. 2 3 use glib::translate::*; 4 use std::cell::Cell; 5 use std::sync::atomic::{AtomicBool, Ordering}; 6 7 #[cfg(target_os = "macos")] 8 extern "C" { pthread_main_np() -> i329 fn pthread_main_np() -> i32; 10 } 11 12 thread_local! { 13 static IS_MAIN_THREAD: Cell<bool> = Cell::new(false) 14 } 15 16 static INITIALIZED: AtomicBool = AtomicBool::new(false); 17 18 /// Asserts that this is the main thread and `gtk::init` has been called. 19 macro_rules! assert_initialized_main_thread { 20 () => { 21 if !crate::rt::is_initialized_main_thread() { 22 if crate::rt::is_initialized() { 23 panic!("GTK may only be used from the main thread."); 24 } else { 25 panic!("GTK has not been initialized. Call `gtk::init` first."); 26 } 27 } 28 }; 29 } 30 31 /// No-op. 32 macro_rules! skip_assert_initialized { 33 () => {}; 34 } 35 36 /// Asserts that `gtk::init` has not been called. 37 #[allow(unused_macros)] 38 macro_rules! assert_not_initialized { 39 () => { 40 if crate::rt::is_initialized() { 41 panic!("This function has to be called before `gtk::init`."); 42 } 43 }; 44 } 45 46 /// Returns `true` if GTK has been initialized. 47 #[inline] is_initialized() -> bool48pub fn is_initialized() -> bool { 49 skip_assert_initialized!(); 50 INITIALIZED.load(Ordering::Acquire) 51 } 52 53 /// Returns `true` if GTK has been initialized and this is the main thread. 54 #[inline] is_initialized_main_thread() -> bool55pub fn is_initialized_main_thread() -> bool { 56 skip_assert_initialized!(); 57 IS_MAIN_THREAD.with(|c| c.get()) 58 } 59 60 /// Informs this crate that GTK has been initialized and the current thread is the main one. 61 /// 62 /// # Panics 63 /// 64 /// This function will panic if you attempt to initialise GTK from more than 65 /// one thread. 66 /// 67 /// # Safety 68 /// 69 /// You must only call this if: 70 /// 71 /// 1. You have initialised the underlying GTK library yourself. 72 /// 2. You did 1 on the thread with which you are calling this function 73 /// 3. You ensure that this thread is the main thread for the process. set_initialized()74pub unsafe fn set_initialized() { 75 skip_assert_initialized!(); 76 if is_initialized_main_thread() { 77 return; 78 } else if is_initialized() { 79 panic!("Attempted to initialize GTK from two different threads."); 80 } 81 82 // OS X has its own notion of the main thread and init must be called on that thread. 83 #[cfg(target_os = "macos")] 84 { 85 if pthread_main_np() == 0 { 86 panic!("Attempted to initialize GTK on OSX from non-main thread"); 87 } 88 } 89 90 gdk::set_initialized(); 91 INITIALIZED.store(true, Ordering::Release); 92 IS_MAIN_THREAD.with(|c| c.set(true)); 93 } 94 95 /// Tries to initialize GTK+. 96 /// 97 /// Call either this function or [`Application::new`][new] before using any 98 /// other GTK+ functions. 99 /// 100 /// [new]: struct.Application.html#method.new 101 /// 102 /// Note that this function calls `gtk_init_check()` rather than `gtk_init()`, 103 /// so will not cause the program to terminate if GTK could not be initialized. 104 /// Instead, an Ok is returned if the windowing system was successfully 105 /// initialized otherwise an Err is returned. 106 #[doc(alias = "gtk_init")] init() -> Result<(), glib::BoolError>107pub fn init() -> Result<(), glib::BoolError> { 108 skip_assert_initialized!(); 109 if is_initialized_main_thread() { 110 return Ok(()); 111 } else if is_initialized() { 112 panic!("Attempted to initialize GTK from two different threads."); 113 } 114 unsafe { 115 // We just want to keep the program's name since more arguments could lead to unwanted 116 // behaviors... 117 let argv = ::std::env::args().take(1).collect::<Vec<_>>(); 118 119 if from_glib(ffi::gtk_init_check(&mut 1, &mut argv.to_glib_none().0)) { 120 // See https://github.com/gtk-rs/gtk-rs-core/issues/186 for reasoning behind 121 // acquiring and leaking the main context here. 122 let result: bool = from_glib(glib::ffi::g_main_context_acquire( 123 glib::ffi::g_main_context_default(), 124 )); 125 if !result { 126 return Err(glib::bool_error!("Failed to acquire default main context")); 127 } 128 set_initialized(); 129 Ok(()) 130 } else { 131 Err(glib::bool_error!("Failed to initialize GTK")) 132 } 133 } 134 } 135 136 #[doc(alias = "gtk_main_quit")] main_quit()137pub fn main_quit() { 138 assert_initialized_main_thread!(); 139 unsafe { 140 if ffi::gtk_main_level() > 0 { 141 ffi::gtk_main_quit(); 142 } else if cfg!(debug_assertions) { 143 panic!("Attempted to quit a GTK main loop when none is running."); 144 } 145 } 146 } 147