irondash_run_loop/platform/linux/
mod.rs1mod sys;
2
3use std::{
4 cell::{Cell, RefCell},
5 collections::HashMap,
6 os::raw::c_uint,
7 rc::Rc,
8 time::Duration,
9};
10
11use sys::glib::*;
12
13use crate::RunLoop;
14
15use self::sys::libc;
16
17type SourceId = c_uint;
18
19pub type HandleType = usize;
20pub const INVALID_HANDLE: HandleType = 0;
21
22pub struct PlatformRunLoop {
23 context: ContextHolder,
24 main_loop: *mut GMainLoop,
25 next_handle: Cell<HandleType>,
26 timers: Rc<RefCell<HashMap<HandleType, SourceId>>>,
27}
28
29fn context_add_source<F>(context: *mut GMainContext, interval: Duration, func: F) -> SourceId
30where
31 F: FnMut() -> gboolean + 'static,
32{
33 unsafe extern "C" fn trampoline<F: FnMut() -> gboolean + 'static>(func: gpointer) -> gboolean {
34 let func: &RefCell<F> = &*(func as *const RefCell<F>);
35 (*func.borrow_mut())()
36 }
37
38 fn into_raw<F: FnMut() -> gboolean + 'static>(func: F) -> gpointer {
39 let func: Box<RefCell<F>> = Box::new(RefCell::new(func));
40 Box::into_raw(func) as gpointer
41 }
42
43 unsafe extern "C" fn destroy_closure<F: FnMut() -> gboolean + 'static>(ptr: gpointer) {
44 let _ = Box::<RefCell<F>>::from_raw(ptr as *mut _);
45 }
46
47 unsafe {
48 let source = g_timeout_source_new(interval.as_millis() as _);
49 g_source_set_callback(
50 source,
51 Some(trampoline::<F>),
52 into_raw(func),
53 Some(destroy_closure::<F>),
54 );
55 let id = g_source_attach(source, context);
56
57 g_source_unref(source);
58 id
59 }
60}
61
62fn context_invoke<F>(context: *mut GMainContext, func: F)
63where
64 F: FnOnce() + 'static,
65{
66 unsafe extern "C" fn trampoline<F: FnOnce() + 'static>(func: gpointer) -> gboolean {
67 let func: &mut Option<F> = &mut *(func as *mut Option<F>);
68 let func = func
69 .take()
70 .expect("MainContext::invoke() closure called multiple times");
71 func();
72 G_SOURCE_REMOVE
73 }
74 unsafe extern "C" fn destroy_closure<F: FnOnce() + 'static>(ptr: gpointer) {
75 let _ = Box::<Option<F>>::from_raw(ptr as *mut _);
76 }
77 let callback = Box::into_raw(Box::new(Some(func)));
78 unsafe {
79 g_main_context_invoke_full(
80 context,
81 0,
82 Some(trampoline::<F>),
83 callback as gpointer,
84 Some(destroy_closure::<F>),
85 )
86 }
87}
88
89fn context_remove_source(context: *mut GMainContext, source_id: SourceId) {
90 unsafe {
91 let source = g_main_context_find_source_by_id(context, source_id);
92 if !source.is_null() {
93 g_source_destroy(source);
94 }
95 }
96}
97
98static mut FIRST_THREAD: PlatformThreadId = 0;
99
100fn is_main_thread() -> bool {
101 unsafe { FIRST_THREAD == get_system_thread_id() }
102}
103
104#[used]
105#[cfg_attr(
106 any(target_os = "linux", target_os = "android"),
107 link_section = ".init_array"
108)]
109static ON_LOAD: extern "C" fn() = {
110 #[cfg_attr(
111 any(target_os = "linux", target_os = "android"),
112 link_section = ".text.startup"
113 )]
114 extern "C" fn on_load() {
115 unsafe { FIRST_THREAD = get_system_thread_id() };
116 }
117 on_load
118};
119
120#[allow(unused_variables)]
121impl PlatformRunLoop {
122 pub fn new() -> Self {
123 let context = unsafe {
124 let default_context = g_main_context_default();
125 if g_main_context_is_owner(default_context) == GTRUE {
126 ContextHolder::retain(default_context)
127 } else {
128 let thread_context = g_main_context_get_thread_default();
129 if !thread_context.is_null() {
130 ContextHolder::retain(thread_context)
131 } else if is_main_thread() {
132 ContextHolder::retain(default_context)
133 } else {
134 ContextHolder::adopt(g_main_context_new())
135 }
136 }
137 };
138 unsafe { g_main_context_push_thread_default(context.0) };
139 let main_loop = unsafe { g_main_loop_new(context.0, GFALSE) };
140 Self {
141 context,
142 next_handle: Cell::new(INVALID_HANDLE + 1),
143 timers: Rc::new(RefCell::new(HashMap::new())),
144 main_loop,
145 }
146 }
147
148 pub fn unschedule(&self, handle: HandleType) {
149 let source = self.timers.borrow_mut().remove(&handle);
150 if let Some(source) = source {
151 context_remove_source(self.context.0, source);
152 }
153 }
154
155 fn next_handle(&self) -> HandleType {
156 let r = self.next_handle.get();
157 self.next_handle.replace(r + 1);
158 r
159 }
160
161 #[must_use]
162 pub fn schedule<F>(&self, in_time: Duration, callback: F) -> HandleType
163 where
164 F: FnOnce() + 'static,
165 {
166 let callback = Rc::new(RefCell::new(Some(callback)));
167 let handle = self.next_handle();
168
169 let timers = self.timers.clone();
170
171 let source_id = context_add_source(self.context.0, in_time, move || {
172 timers.borrow_mut().remove(&handle);
173 let f = callback
174 .borrow_mut()
175 .take()
176 .expect("Timer callback was called multiple times");
177 f();
178 G_SOURCE_REMOVE
179 });
180 self.timers.borrow_mut().insert(handle, source_id);
181 handle
182 }
183
184 pub fn run(&self) {
185 unsafe { g_main_loop_run(self.main_loop) };
186 }
187
188 pub fn stop(&self) {
189 unsafe { g_main_loop_quit(self.main_loop) };
190 }
191
192 pub fn run_app(&self) {
193 unsafe { gtk_main() };
194 }
195
196 pub fn stop_app(&self) {
197 unsafe { gtk_main_quit() };
198 }
199
200 pub fn poll_once(&self) {
201 unsafe { gtk_main_iteration() };
202 }
203
204 pub fn is_main_thread() -> bool {
205 unsafe { g_main_context_is_owner(g_main_context_default()) == GTRUE }
206 }
207
208 pub fn new_sender(self: &Rc<Self>) -> PlatformRunLoopSender {
209 PlatformRunLoopSender::new(self.context.clone())
210 }
211}
212
213impl Drop for PlatformRunLoop {
214 fn drop(&mut self) {
215 unsafe {
216 g_main_context_pop_thread_default(self.context.0);
217 g_main_loop_unref(self.main_loop);
218 }
219 }
220}
221
222struct ContextHolder(*mut GMainContext);
223
224unsafe impl Send for ContextHolder {}
225unsafe impl Sync for ContextHolder {}
226
227impl ContextHolder {
228 unsafe fn retain(context: *mut GMainContext) -> Self {
229 Self(g_main_context_ref(context))
230 }
231 unsafe fn adopt(context: *mut GMainContext) -> Self {
232 Self(context)
233 }
234}
235
236impl Clone for ContextHolder {
237 fn clone(&self) -> Self {
238 Self(unsafe { g_main_context_ref(self.0) })
239 }
240}
241
242impl Drop for ContextHolder {
243 fn drop(&mut self) {
244 unsafe { g_main_context_unref(self.0) };
245 }
246}
247
248#[derive(Clone)]
249pub struct PlatformRunLoopSender {
250 context: ContextHolder,
251 thread_id: PlatformThreadId,
252}
253
254#[allow(unused_variables)]
255impl PlatformRunLoopSender {
256 fn new(context: ContextHolder) -> Self {
257 Self {
258 context,
259 thread_id: get_system_thread_id(),
260 }
261 }
262
263 pub fn send<F>(&self, callback: F) -> bool
264 where
265 F: FnOnce() + 'static + Send,
266 {
267 if get_system_thread_id() == self.thread_id {
271 assert!(unsafe { g_main_context_is_owner(self.context.0) == GTRUE });
272 let run_loop = RunLoop::current();
273 run_loop.schedule(Duration::from_secs(0), callback).detach();
274 return true;
275 }
276
277 context_invoke(self.context.0, callback);
278
279 true
280 }
281}
282
283pub(crate) type PlatformThreadId = usize;
284
285pub(crate) fn get_system_thread_id() -> PlatformThreadId {
286 unsafe { libc::pthread_self() }
287}