android_ndk/
android_app.rs

1//! Bindings for the `android_app` struct found in `android_native_app_glue.c`
2//!
3//! If you are not using `native_app_glue`, you can disable these bindings by disabling the
4//! `native_app_glue` Cargo feature.
5
6use crate::configuration::Configuration;
7use crate::input_queue::InputQueue;
8use crate::looper::ForeignLooper;
9use crate::native_activity::NativeActivity;
10
11use num_enum::{IntoPrimitive, TryFromPrimitive};
12use std::convert::TryInto;
13use std::fmt;
14use std::marker::PhantomData;
15use std::mem::ManuallyDrop;
16use std::ops::Deref;
17use std::ptr::NonNull;
18
19/// A `struct android_app *`.
20#[derive(Debug)]
21pub struct AndroidApp {
22    ptr: NonNull<ffi::native_app_glue::android_app>,
23}
24
25// It is used between threads in android_native_app_glue
26unsafe impl Send for AndroidApp {}
27unsafe impl Sync for AndroidApp {}
28
29// TODO: docs
30impl AndroidApp {
31    pub unsafe fn from_ptr(ptr: NonNull<ffi::native_app_glue::android_app>) -> Self {
32        Self { ptr }
33    }
34
35    pub fn ptr(&self) -> NonNull<ffi::native_app_glue::android_app> {
36        self.ptr
37    }
38
39    // It's OK to not give a Ref<'_, _> because the ANativeActivity * will never change
40    pub fn activity(&self) -> NativeActivity {
41        unsafe { NativeActivity::from_ptr(NonNull::new(self.ptr.as_ref().activity).unwrap()) }
42    }
43
44    pub fn input_queue(&self) -> Ref<'_, InputQueue> {
45        unsafe {
46            Ref::new(InputQueue::from_ptr(
47                NonNull::new(self.ptr.as_ref().inputQueue).unwrap(),
48            ))
49        }
50    }
51
52    pub fn config(&self) -> Ref<'_, Configuration> {
53        unsafe {
54            Ref::new(Configuration::from_ptr(
55                NonNull::new(self.ptr.as_ref().config).unwrap(),
56            ))
57        }
58    }
59
60    /* pub */
61    fn read_cmd(&mut self) -> Cmd {
62        unsafe {
63            ffi::native_app_glue::android_app_read_cmd(self.ptr.as_ptr())
64                .try_into()
65                .unwrap()
66        }
67    }
68
69    /* pub */
70    fn pre_exec(&mut self, cmd: Cmd) {
71        unsafe {
72            ffi::native_app_glue::android_app_pre_exec_cmd(self.ptr.as_ptr(), cmd.into());
73        }
74    }
75
76    /* pub */
77    fn post_exec(&mut self, cmd: Cmd) {
78        unsafe {
79            ffi::native_app_glue::android_app_post_exec_cmd(self.ptr.as_ptr(), cmd.into());
80        }
81    }
82
83    pub fn handle_cmd<T>(&mut self, f: impl FnOnce(&mut Self, Cmd) -> T) -> T {
84        let cmd = self.read_cmd();
85        self.pre_exec(cmd);
86        let res = f(self, cmd);
87        self.post_exec(cmd);
88        res
89    }
90
91    pub fn saved_state(&self) -> Option<&[u8]> {
92        unsafe {
93            let ptr = self.ptr.as_ref().savedState;
94            if ptr.is_null() {
95                None
96            } else {
97                Some(std::slice::from_raw_parts(
98                    ptr as *mut u8,
99                    self.ptr.as_ref().savedStateSize,
100                ))
101            }
102        }
103    }
104
105    pub fn content_rect(&self) -> ffi::ARect {
106        unsafe { self.ptr.as_ref().contentRect }
107    }
108
109    // The looper will also never change
110    pub fn looper(&self) -> ForeignLooper {
111        unsafe { ForeignLooper::from_ptr(NonNull::new(self.ptr.as_ref().looper).unwrap()) }
112    }
113
114    // TODO: all the other things
115}
116
117// TODO docs
118// Best thing would be to word-for-word copy the docs from android_native_app_glue.h to here,
119// because there's really no good online source for it
120#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
121#[repr(i8)]
122pub enum Cmd {
123    InputChanged = ffi::native_app_glue::APP_CMD_INPUT_CHANGED,
124    InitWindow = ffi::native_app_glue::APP_CMD_INIT_WINDOW,
125    TermWindow = ffi::native_app_glue::APP_CMD_TERM_WINDOW,
126    WindowResized = ffi::native_app_glue::APP_CMD_WINDOW_RESIZED,
127    WindowRedrawNeeded = ffi::native_app_glue::APP_CMD_WINDOW_REDRAW_NEEDED,
128    ContentRectChanged = ffi::native_app_glue::APP_CMD_CONTENT_RECT_CHANGED,
129    GainedFocus = ffi::native_app_glue::APP_CMD_GAINED_FOCUS,
130    LostFocus = ffi::native_app_glue::APP_CMD_LOST_FOCUS,
131    ConfigChanged = ffi::native_app_glue::APP_CMD_CONFIG_CHANGED,
132    LowMemory = ffi::native_app_glue::APP_CMD_LOW_MEMORY,
133    Start = ffi::native_app_glue::APP_CMD_START,
134    Resume = ffi::native_app_glue::APP_CMD_RESUME,
135    SaveState = ffi::native_app_glue::APP_CMD_SAVE_STATE,
136    Pause = ffi::native_app_glue::APP_CMD_PAUSE,
137    Stop = ffi::native_app_glue::APP_CMD_STOP,
138    Destroy = ffi::native_app_glue::APP_CMD_DESTROY,
139}
140
141/// A wrapper that associates a lifetime with data.
142///
143/// This is used to ensure that data associated with an `AndroidApp` doesn't outlive its container.
144pub struct Ref<'a, T> {
145    _marker: PhantomData<&'a T>,
146    data: ManuallyDrop<T>,
147}
148
149impl<'a, T: fmt::Debug> fmt::Debug for Ref<'a, T> {
150    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151        write!(f, "Ref {{ {:?} }}", &*self)
152    }
153}
154
155impl<'a, T> Deref for Ref<'a, T> {
156    type Target = T;
157
158    fn deref(&self) -> &T {
159        &*self.data
160    }
161}
162
163impl<'a, T> Ref<'a, T> {
164    fn new(data: T) -> Self {
165        Self {
166            _marker: PhantomData,
167            data: ManuallyDrop::new(data),
168        }
169    }
170}