1#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
2
3use log::Level;
4use ndk::input_queue::InputQueue;
5use ndk::looper::{FdEvent, ForeignLooper, ThreadLooper};
6use ndk::native_activity::NativeActivity;
7use ndk::native_window::NativeWindow;
8use ndk_sys::{AInputQueue, ANativeActivity, ANativeWindow, ARect};
9use once_cell::sync::Lazy;
10use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
11use std::ffi::{CStr, CString};
12use std::fmt;
13use std::fs::File;
14use std::io::{BufRead, BufReader};
15use std::ops::Deref;
16use std::os::raw;
17use std::os::unix::prelude::*;
18use std::ptr::NonNull;
19use std::sync::{Arc, Condvar, Mutex};
20use std::thread;
21
22#[cfg(feature = "logger")]
23pub use android_logger;
24#[cfg(feature = "logger")]
25pub use log;
26
27pub use ndk_macro::main;
28
29pub const NDK_GLUE_LOOPER_EVENT_PIPE_IDENT: i32 = 0;
34
35pub const NDK_GLUE_LOOPER_INPUT_QUEUE_IDENT: i32 = 1;
40
41pub fn android_log(level: Level, tag: &CStr, msg: &CStr) {
42 let prio = match level {
43 Level::Error => ndk_sys::android_LogPriority::ANDROID_LOG_ERROR,
44 Level::Warn => ndk_sys::android_LogPriority::ANDROID_LOG_WARN,
45 Level::Info => ndk_sys::android_LogPriority::ANDROID_LOG_INFO,
46 Level::Debug => ndk_sys::android_LogPriority::ANDROID_LOG_DEBUG,
47 Level::Trace => ndk_sys::android_LogPriority::ANDROID_LOG_VERBOSE,
48 };
49 unsafe {
50 ndk_sys::__android_log_write(prio.0 as raw::c_int, tag.as_ptr(), msg.as_ptr());
51 }
52}
53
54static NATIVE_WINDOW: Lazy<RwLock<Option<NativeWindow>>> = Lazy::new(Default::default);
55static INPUT_QUEUE: Lazy<RwLock<Option<InputQueue>>> = Lazy::new(Default::default);
56static CONTENT_RECT: Lazy<RwLock<Rect>> = Lazy::new(Default::default);
57static LOOPER: Lazy<Mutex<Option<ForeignLooper>>> = Lazy::new(Default::default);
58
59static mut NATIVE_ACTIVITY: Option<NativeActivity> = None;
60
61pub fn native_activity() -> &'static NativeActivity {
68 unsafe { NATIVE_ACTIVITY.as_ref().unwrap() }
69}
70
71pub struct LockReadGuard<T: ?Sized + 'static>(MappedRwLockReadGuard<'static, T>);
72
73impl<T> LockReadGuard<T> {
74 fn from_wrapped_option(wrapped: RwLockReadGuard<'static, Option<T>>) -> Option<Self> {
80 RwLockReadGuard::try_map(wrapped, Option::as_ref)
81 .ok()
82 .map(Self)
83 }
84}
85
86impl<T: ?Sized> Deref for LockReadGuard<T> {
87 type Target = T;
88
89 fn deref(&self) -> &Self::Target {
90 &self.0
91 }
92}
93
94impl<T: ?Sized + fmt::Debug> fmt::Debug for LockReadGuard<T> {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 self.0.fmt(f)
97 }
98}
99
100impl<T: ?Sized + fmt::Display> fmt::Display for LockReadGuard<T> {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 self.0.fmt(f)
103 }
104}
105
106pub fn native_window() -> Option<LockReadGuard<NativeWindow>> {
120 LockReadGuard::from_wrapped_option(NATIVE_WINDOW.read())
121}
122
123pub fn input_queue() -> Option<LockReadGuard<InputQueue>> {
135 LockReadGuard::from_wrapped_option(INPUT_QUEUE.read())
136}
137
138pub fn content_rect() -> Rect {
141 CONTENT_RECT.read().clone()
142}
143
144static PIPE: Lazy<[RawFd; 2]> = Lazy::new(|| {
145 let mut pipe: [RawFd; 2] = Default::default();
146 unsafe { libc::pipe(pipe.as_mut_ptr()) };
147 pipe
148});
149
150pub fn poll_events() -> Option<Event> {
151 unsafe {
152 let size = std::mem::size_of::<Event>();
153 let mut event = Event::Start;
154 if libc::read(PIPE[0], &mut event as *mut _ as *mut _, size) == size as _ {
155 Some(event)
156 } else {
157 None
158 }
159 }
160}
161
162unsafe fn wake(_activity: *mut ANativeActivity, event: Event) {
163 log::trace!("{:?}", event);
164 let size = std::mem::size_of::<Event>();
165 let res = libc::write(PIPE[1], &event as *const _ as *const _, size);
166 assert_eq!(res, size as _);
167}
168
169#[derive(Clone, Debug, Default, Eq, PartialEq)]
170pub struct Rect {
171 pub left: u32,
172 pub top: u32,
173 pub right: u32,
174 pub bottom: u32,
175}
176
177#[derive(Clone, Debug, Eq, PartialEq)]
178#[repr(u8)]
179pub enum Event {
180 Start,
181 Resume,
182 SaveInstanceState,
183 Pause,
184 Stop,
185 Destroy,
186 ConfigChanged,
187 LowMemory,
188 WindowLostFocus,
189 WindowHasFocus,
190 WindowCreated,
196 WindowResized,
197 WindowRedrawNeeded,
198 WindowDestroyed,
208 InputQueueCreated,
212 InputQueueDestroyed,
219 ContentRectChanged,
220}
221
222pub unsafe fn init(
226 activity: *mut ANativeActivity,
227 _saved_state: *mut u8,
228 _saved_state_size: usize,
229 main: fn(),
230) {
231 let mut activity = NonNull::new(activity).unwrap();
232 let mut callbacks = activity.as_mut().callbacks.as_mut().unwrap();
233 callbacks.onStart = Some(on_start);
234 callbacks.onResume = Some(on_resume);
235 callbacks.onSaveInstanceState = Some(on_save_instance_state);
236 callbacks.onPause = Some(on_pause);
237 callbacks.onStop = Some(on_stop);
238 callbacks.onDestroy = Some(on_destroy);
239 callbacks.onWindowFocusChanged = Some(on_window_focus_changed);
240 callbacks.onNativeWindowCreated = Some(on_window_created);
241 callbacks.onNativeWindowResized = Some(on_window_resized);
242 callbacks.onNativeWindowRedrawNeeded = Some(on_window_redraw_needed);
243 callbacks.onNativeWindowDestroyed = Some(on_window_destroyed);
244 callbacks.onInputQueueCreated = Some(on_input_queue_created);
245 callbacks.onInputQueueDestroyed = Some(on_input_queue_destroyed);
246 callbacks.onContentRectChanged = Some(on_content_rect_changed);
247 callbacks.onConfigurationChanged = Some(on_configuration_changed);
248 callbacks.onLowMemory = Some(on_low_memory);
249
250 let activity = NativeActivity::from_ptr(activity);
251 ndk_context::initialize_android_context(activity.vm().cast(), activity.activity().cast());
252 NATIVE_ACTIVITY.replace(activity);
253
254 let mut logpipe: [RawFd; 2] = Default::default();
255 libc::pipe(logpipe.as_mut_ptr());
256 libc::dup2(logpipe[1], libc::STDOUT_FILENO);
257 libc::dup2(logpipe[1], libc::STDERR_FILENO);
258 thread::spawn(move || {
259 let tag = CStr::from_bytes_with_nul(b"RustStdoutStderr\0").unwrap();
260 let file = File::from_raw_fd(logpipe[0]);
261 let mut reader = BufReader::new(file);
262 let mut buffer = String::new();
263 loop {
264 buffer.clear();
265 if let Ok(len) = reader.read_line(&mut buffer) {
266 if len == 0 {
267 break;
268 } else if let Ok(msg) = CString::new(buffer.clone()) {
269 android_log(Level::Info, tag, &msg);
270 }
271 }
272 }
273 });
274
275 let looper_ready = Arc::new(Condvar::new());
276 let signal_looper_ready = looper_ready.clone();
277
278 thread::spawn(move || {
279 let looper = ThreadLooper::prepare();
280 let foreign = looper.into_foreign();
281 foreign
282 .add_fd(
283 PIPE[0],
284 NDK_GLUE_LOOPER_EVENT_PIPE_IDENT,
285 FdEvent::INPUT,
286 std::ptr::null_mut(),
287 )
288 .unwrap();
289
290 {
291 let mut locked_looper = LOOPER.lock().unwrap();
292 locked_looper.replace(foreign);
293 signal_looper_ready.notify_one();
294 }
295
296 main()
297 });
298
299 let locked_looper = LOOPER.lock().unwrap();
304 let _mutex_guard = looper_ready
305 .wait_while(locked_looper, |looper| looper.is_none())
306 .unwrap();
307}
308
309unsafe extern "C" fn on_start(activity: *mut ANativeActivity) {
310 wake(activity, Event::Start);
311}
312
313unsafe extern "C" fn on_resume(activity: *mut ANativeActivity) {
314 wake(activity, Event::Resume);
315}
316
317unsafe extern "C" fn on_save_instance_state(
318 activity: *mut ANativeActivity,
319 _out_size: *mut ndk_sys::size_t,
320) -> *mut raw::c_void {
321 wake(activity, Event::SaveInstanceState);
323 std::ptr::null_mut()
324}
325
326unsafe extern "C" fn on_pause(activity: *mut ANativeActivity) {
327 wake(activity, Event::Pause);
328}
329
330unsafe extern "C" fn on_stop(activity: *mut ANativeActivity) {
331 wake(activity, Event::Stop);
332}
333
334unsafe extern "C" fn on_destroy(activity: *mut ANativeActivity) {
335 wake(activity, Event::Destroy);
336 ndk_context::release_android_context();
337}
338
339unsafe extern "C" fn on_configuration_changed(activity: *mut ANativeActivity) {
340 wake(activity, Event::ConfigChanged);
341}
342
343unsafe extern "C" fn on_low_memory(activity: *mut ANativeActivity) {
344 wake(activity, Event::LowMemory);
345}
346
347unsafe extern "C" fn on_window_focus_changed(
348 activity: *mut ANativeActivity,
349 has_focus: raw::c_int,
350) {
351 let event = if has_focus == 0 {
352 Event::WindowLostFocus
353 } else {
354 Event::WindowHasFocus
355 };
356 wake(activity, event);
357}
358
359unsafe extern "C" fn on_window_created(activity: *mut ANativeActivity, window: *mut ANativeWindow) {
360 NATIVE_WINDOW
361 .write()
362 .replace(NativeWindow::clone_from_ptr(NonNull::new(window).unwrap()));
363 wake(activity, Event::WindowCreated);
364}
365
366unsafe extern "C" fn on_window_resized(
367 activity: *mut ANativeActivity,
368 _window: *mut ANativeWindow,
369) {
370 wake(activity, Event::WindowResized);
371}
372
373unsafe extern "C" fn on_window_redraw_needed(
374 activity: *mut ANativeActivity,
375 _window: *mut ANativeWindow,
376) {
377 wake(activity, Event::WindowRedrawNeeded);
378}
379
380unsafe extern "C" fn on_window_destroyed(
381 activity: *mut ANativeActivity,
382 window: *mut ANativeWindow,
383) {
384 wake(activity, Event::WindowDestroyed);
385 let mut native_window_guard = NATIVE_WINDOW.write();
386 assert_eq!(native_window_guard.as_ref().unwrap().ptr().as_ptr(), window);
387 native_window_guard.take();
388}
389
390unsafe extern "C" fn on_input_queue_created(
391 activity: *mut ANativeActivity,
392 queue: *mut AInputQueue,
393) {
394 let input_queue = InputQueue::from_ptr(NonNull::new(queue).unwrap());
395 let locked_looper = LOOPER.lock().unwrap();
396 let looper = locked_looper.as_ref().expect("Looper does not exist");
399 input_queue.attach_looper(looper, NDK_GLUE_LOOPER_INPUT_QUEUE_IDENT);
400 INPUT_QUEUE.write().replace(input_queue);
401 wake(activity, Event::InputQueueCreated);
402}
403
404unsafe extern "C" fn on_input_queue_destroyed(
405 activity: *mut ANativeActivity,
406 queue: *mut AInputQueue,
407) {
408 wake(activity, Event::InputQueueDestroyed);
409 let mut input_queue_guard = INPUT_QUEUE.write();
410 assert_eq!(input_queue_guard.as_ref().unwrap().ptr().as_ptr(), queue);
411 let input_queue = InputQueue::from_ptr(NonNull::new(queue).unwrap());
412 input_queue.detach_looper();
413 input_queue_guard.take();
414}
415
416unsafe extern "C" fn on_content_rect_changed(activity: *mut ANativeActivity, rect: *const ARect) {
417 let rect = Rect {
418 left: (*rect).left as _,
419 top: (*rect).top as _,
420 right: (*rect).right as _,
421 bottom: (*rect).bottom as _,
422 };
423 *CONTENT_RECT.write() = rect;
424 wake(activity, Event::ContentRectChanged);
425}