From f1772bcd833ca677d54baba43cfa34e28eb651ce Mon Sep 17 00:00:00 2001 From: David Craven Date: Mon, 10 Feb 2020 17:22:56 +0100 Subject: [PATCH] Android glue. --- Cargo.toml | 2 +- android-glue/Cargo.toml | 13 ++ android-glue/src/lib.rs | 278 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 android-glue/Cargo.toml create mode 100644 android-glue/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index aa152d79..53b09b55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] - members = [ + "android-glue", "android-ndk-sys", "android-ndk", ] diff --git a/android-glue/Cargo.toml b/android-glue/Cargo.toml new file mode 100644 index 00000000..ea093023 --- /dev/null +++ b/android-glue/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "android-glue" +version = "0.1.0" +authors = ["David Craven "] +edition = "2018" + +[dependencies] +android_log-sys = "0.1.2" +android-ndk-sys = { path = "../android-ndk-sys" } +android-ndk = { path = "../android-ndk" } +lazy_static = "1.4.0" +libc = "0.2.66" +log = "0.4.8" diff --git a/android-glue/src/lib.rs b/android-glue/src/lib.rs new file mode 100644 index 00000000..91275af5 --- /dev/null +++ b/android-glue/src/lib.rs @@ -0,0 +1,278 @@ +use android_ndk::input_queue::InputQueue; +use android_ndk::looper::ThreadLooper; +use android_ndk::native_activity::NativeActivity; +use android_ndk::native_window::NativeWindow; +use android_ndk_sys::{AInputQueue, ANativeActivity, ANativeWindow, ARect, ALOOPER_EVENT_INPUT}; +use lazy_static::lazy_static; +use log::Level; +use std::ffi::{CStr, CString}; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::os::raw; +use std::os::unix::prelude::*; +use std::ptr::NonNull; +use std::sync::RwLock; +use std::thread; + +pub fn android_log(level: Level, tag: &CStr, msg: &CStr) { + use android_log_sys::LogPriority; + let prio = match level { + Level::Error => LogPriority::ERROR, + Level::Warn => LogPriority::WARN, + Level::Info => LogPriority::INFO, + Level::Debug => LogPriority::DEBUG, + Level::Trace => LogPriority::VERBOSE, + }; + unsafe { + android_log_sys::__android_log_write( + prio as _, + tag.as_ptr() as *const _, + msg.as_ptr() as *const _, + ); + } +} + +lazy_static! { + static ref NATIVE_WINDOW: RwLock> = Default::default(); + static ref INPUT_QUEUE: RwLock> = Default::default(); + static ref CONTENT_RECT: RwLock = Default::default(); +} + +static mut NATIVE_ACTIVITY: Option = None; + +pub fn native_activity() -> &'static NativeActivity { + unsafe { NATIVE_ACTIVITY.as_ref().unwrap() } +} + +pub fn native_window() -> &'static RwLock> { + &NATIVE_WINDOW +} + +pub fn input_queue() -> &'static RwLock> { + &INPUT_QUEUE +} + +pub fn content_rect() -> Rect { + CONTENT_RECT.read().unwrap().clone() +} + +lazy_static! { + static ref PIPE: [RawFd; 2] = { + let mut pipe: [RawFd; 2] = Default::default(); + unsafe { libc::pipe(pipe.as_mut_ptr()) }; + pipe + }; +} + +pub fn poll_events() -> Option { + unsafe { + let size = std::mem::size_of::(); + let mut event = Event::Start; + if libc::read(PIPE[0], &mut event as *mut _ as *mut _, size) == size as _ { + Some(event) + } else { + None + } + } +} + +unsafe fn wake(_activity: *mut ANativeActivity, event: Event) { + log::trace!("{:?}", event); + let size = std::mem::size_of::(); + let res = libc::write(PIPE[1], &event as *const _ as *const _, size); + assert_eq!(res, size as _); +} + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct Rect { + pub left: u32, + pub top: u32, + pub right: u32, + pub bottom: u32, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum Event { + Start, + Resume, + SaveInstanceState, + Pause, + Stop, + Destroy, + ConfigChanged, + LowMemory, + WindowLostFocus, + WindowHasFocus, + WindowCreated, + WindowResized, + WindowRedrawNeeded, + WindowDestroyed, + InputQueueCreated, + InputQueueDestroyed, + ContentRectChanged, +} + +pub unsafe fn init( + activity: *mut ANativeActivity, + _saved_state: *mut u8, + _saved_state_size: usize, + main: fn(), +) { + let mut activity = NonNull::new(activity).unwrap(); + let mut callbacks = activity.as_mut().callbacks.as_mut().unwrap(); + callbacks.onStart = Some(on_start); + callbacks.onResume = Some(on_resume); + callbacks.onSaveInstanceState = Some(on_save_instance_state); + callbacks.onPause = Some(on_pause); + callbacks.onStop = Some(on_stop); + callbacks.onDestroy = Some(on_destroy); + callbacks.onWindowFocusChanged = Some(on_window_focus_changed); + callbacks.onNativeWindowCreated = Some(on_window_created); + callbacks.onNativeWindowResized = Some(on_window_resized); + callbacks.onNativeWindowRedrawNeeded = Some(on_window_redraw_needed); + callbacks.onNativeWindowDestroyed = Some(on_window_destroyed); + callbacks.onInputQueueCreated = Some(on_input_queue_created); + callbacks.onInputQueueDestroyed = Some(on_input_queue_destroyed); + callbacks.onContentRectChanged = Some(on_content_rect_changed); + callbacks.onConfigurationChanged = Some(on_configuration_changed); + callbacks.onLowMemory = Some(on_low_memory); + + let activity = NativeActivity::from_ptr(activity); + NATIVE_ACTIVITY = Some(activity); + + let mut logpipe: [RawFd; 2] = Default::default(); + libc::pipe(logpipe.as_mut_ptr()); + libc::dup2(logpipe[1], libc::STDOUT_FILENO); + libc::dup2(logpipe[1], libc::STDERR_FILENO); + thread::spawn(move || { + let tag = CStr::from_bytes_with_nul(b"RustStdoutStderr\0").unwrap(); + let file = File::from_raw_fd(logpipe[0]); + let mut reader = BufReader::new(file); + let mut buffer = String::new(); + loop { + buffer.clear(); + if let Ok(len) = reader.read_line(&mut buffer) { + if len == 0 { + break; + } else { + if let Ok(msg) = CString::new(buffer.clone()) { + android_log(Level::Info, tag, &msg); + } + } + } + } + }); + + thread::spawn(move || { + let looper = ThreadLooper::prepare(); + looper + .as_foreign() + .add_fd(PIPE[0], 0, ALOOPER_EVENT_INPUT as _, 0 as _) + .unwrap(); + main() + }); +} + +unsafe extern "C" fn on_start(activity: *mut ANativeActivity) { + wake(activity, Event::Start); +} + +unsafe extern "C" fn on_resume(activity: *mut ANativeActivity) { + wake(activity, Event::Resume); +} + +unsafe extern "C" fn on_save_instance_state( + activity: *mut ANativeActivity, + _out_size: *mut usize, +) -> *mut raw::c_void { + // TODO + wake(activity, Event::SaveInstanceState); + std::ptr::null_mut() +} + +unsafe extern "C" fn on_pause(activity: *mut ANativeActivity) { + wake(activity, Event::Pause); +} + +unsafe extern "C" fn on_stop(activity: *mut ANativeActivity) { + wake(activity, Event::Stop); +} + +unsafe extern "C" fn on_destroy(activity: *mut ANativeActivity) { + wake(activity, Event::Destroy); +} + +unsafe extern "C" fn on_configuration_changed(activity: *mut ANativeActivity) { + wake(activity, Event::ConfigChanged); +} + +unsafe extern "C" fn on_low_memory(activity: *mut ANativeActivity) { + wake(activity, Event::LowMemory); +} + +unsafe extern "C" fn on_window_focus_changed( + activity: *mut ANativeActivity, + has_focus: raw::c_int, +) { + let event = if has_focus == 0 { + Event::WindowLostFocus + } else { + Event::WindowHasFocus + }; + wake(activity, event); +} + +unsafe extern "C" fn on_window_created(activity: *mut ANativeActivity, window: *mut ANativeWindow) { + *NATIVE_WINDOW.write().unwrap() = Some(NativeWindow::from_ptr(NonNull::new(window).unwrap())); + wake(activity, Event::WindowCreated); +} + +unsafe extern "C" fn on_window_resized( + activity: *mut ANativeActivity, + _window: *mut ANativeWindow, +) { + wake(activity, Event::WindowResized); +} + +unsafe extern "C" fn on_window_redraw_needed( + activity: *mut ANativeActivity, + _window: *mut ANativeWindow, +) { + wake(activity, Event::WindowRedrawNeeded); +} + +unsafe extern "C" fn on_window_destroyed( + activity: *mut ANativeActivity, + _window: *mut ANativeWindow, +) { + *NATIVE_WINDOW.write().unwrap() = None; + wake(activity, Event::WindowDestroyed); +} + +unsafe extern "C" fn on_input_queue_created( + activity: *mut ANativeActivity, + queue: *mut AInputQueue, +) { + *INPUT_QUEUE.write().unwrap() = Some(InputQueue::from_ptr(NonNull::new(queue).unwrap())); + wake(activity, Event::InputQueueCreated); +} + +unsafe extern "C" fn on_input_queue_destroyed( + activity: *mut ANativeActivity, + _queue: *mut AInputQueue, +) { + *INPUT_QUEUE.write().unwrap() = None; + wake(activity, Event::InputQueueDestroyed); +} + +unsafe extern "C" fn on_content_rect_changed(activity: *mut ANativeActivity, rect: *const ARect) { + let rect = Rect { + left: (*rect).left as _, + top: (*rect).top as _, + right: (*rect).right as _, + bottom: (*rect).bottom as _, + }; + *CONTENT_RECT.write().unwrap() = rect; + wake(activity, Event::ContentRectChanged); +}