use std::convert::TryInto;
use std::os::raw::c_void;
use std::os::unix::io::RawFd;
use std::ptr;
use std::ptr::NonNull;
use std::time::Duration;
use thiserror::Error;
#[derive(Debug)]
pub struct ThreadLooper {
_marker: std::marker::PhantomData<*mut ()>, foreign: ForeignLooper,
}
#[derive(Debug)]
pub enum Poll {
Wake,
Callback,
Timeout,
Event {
ident: i32,
fd: RawFd,
events: i32,
data: *mut c_void,
},
}
#[derive(Debug, Copy, Clone, Error)]
#[error("Android Looper error")]
pub struct LooperError;
impl ThreadLooper {
pub fn prepare() -> Self {
unsafe {
let ptr = ffi::ALooper_prepare(ffi::ALOOPER_PREPARE_ALLOW_NON_CALLBACKS as _);
let foreign = ForeignLooper::from_ptr(NonNull::new(ptr).expect("looper non null"));
Self {
_marker: std::marker::PhantomData,
foreign,
}
}
}
pub fn for_thread() -> Option<Self> {
Some(Self {
_marker: std::marker::PhantomData,
foreign: ForeignLooper::for_thread()?,
})
}
fn poll_once_ms(&self, ms: i32) -> Result<Poll, LooperError> {
unsafe {
let mut fd: RawFd = -1;
let mut events: i32 = -1;
let mut data: *mut c_void = ptr::null_mut();
match ffi::ALooper_pollOnce(ms, &mut fd, &mut events, &mut data) {
ffi::ALOOPER_POLL_WAKE => Ok(Poll::Wake),
ffi::ALOOPER_POLL_CALLBACK => Ok(Poll::Callback),
ffi::ALOOPER_POLL_TIMEOUT => Ok(Poll::Timeout),
ffi::ALOOPER_POLL_ERROR => Err(LooperError),
ident if ident >= 0 => Ok(Poll::Event {
ident,
fd,
events,
data,
}),
_ => unreachable!(),
}
}
}
#[inline]
pub fn poll_once(&self) -> Result<Poll, LooperError> {
self.poll_once_ms(-1)
}
#[inline]
pub fn poll_once_timeout(&self, timeout: Duration) -> Result<Poll, LooperError> {
self.poll_once_ms(
timeout
.as_millis()
.try_into()
.expect("Supplied timeout is too large"),
)
}
fn poll_all_ms(&self, ms: i32) -> Result<Poll, LooperError> {
unsafe {
let mut fd: RawFd = -1;
let mut events: i32 = -1;
let mut data: *mut c_void = ptr::null_mut();
match ffi::ALooper_pollAll(ms, &mut fd, &mut events, &mut data) {
ffi::ALOOPER_POLL_WAKE => Ok(Poll::Wake),
ffi::ALOOPER_POLL_TIMEOUT => Ok(Poll::Timeout),
ffi::ALOOPER_POLL_ERROR => Err(LooperError),
ident if ident >= 0 => Ok(Poll::Event {
ident,
fd,
events,
data,
}),
_ => unreachable!(),
}
}
}
#[inline]
pub fn poll_all(&self) -> Result<Poll, LooperError> {
self.poll_all_ms(-1)
}
#[inline]
pub fn poll_all_timeout(&self, timeout: Duration) -> Result<Poll, LooperError> {
self.poll_all_ms(
timeout
.as_millis()
.try_into()
.expect("Supplied timeout is too large"),
)
}
pub fn as_foreign(&self) -> &ForeignLooper {
&self.foreign
}
pub fn into_foreign(self) -> ForeignLooper {
self.foreign
}
}
#[derive(Debug)]
pub struct ForeignLooper {
ptr: NonNull<ffi::ALooper>,
}
unsafe impl Send for ForeignLooper {}
unsafe impl Sync for ForeignLooper {}
impl Drop for ForeignLooper {
fn drop(&mut self) {
unsafe { ffi::ALooper_release(self.ptr.as_ptr()) }
}
}
impl Clone for ForeignLooper {
fn clone(&self) -> Self {
unsafe {
ffi::ALooper_acquire(self.ptr.as_ptr());
Self { ptr: self.ptr }
}
}
}
impl ForeignLooper {
#[inline]
pub fn for_thread() -> Option<Self> {
NonNull::new(unsafe { ffi::ALooper_forThread() }).map(|ptr| unsafe { Self::from_ptr(ptr) })
}
#[inline]
pub unsafe fn from_ptr(ptr: NonNull<ffi::ALooper>) -> Self {
ffi::ALooper_acquire(ptr.as_ptr());
Self { ptr }
}
#[inline]
pub fn ptr(&self) -> NonNull<ffi::ALooper> {
self.ptr
}
pub fn wake(&self) {
unsafe { ffi::ALooper_wake(self.ptr.as_ptr()) }
}
pub unsafe fn add_fd(
&self,
fd: RawFd,
ident: i32,
event: i32,
data: *mut c_void,
) -> Result<(), LooperError> {
match ffi::ALooper_addFd(self.ptr.as_ptr(), fd, ident, event, None, data) {
1 => Ok(()),
-1 => Err(LooperError),
_ => unreachable!(),
}
}
pub unsafe fn add_fd_with_callback(
&self,
fd: RawFd,
ident: i32,
event: i32,
callback: Box<dyn FnMut(RawFd) -> bool>,
) -> Result<(), LooperError> {
extern "C" fn cb_handler(fd: RawFd, _events: i32, data: *mut c_void) -> i32 {
unsafe {
let cb: &mut Box<dyn FnMut(RawFd) -> bool> = &mut *(data as *mut _);
cb(fd) as i32
}
}
let data: *mut c_void = Box::into_raw(Box::new(callback)) as *mut _;
match ffi::ALooper_addFd(self.ptr.as_ptr(), fd, ident, event, Some(cb_handler), data) {
1 => Ok(()),
-1 => Err(LooperError),
_ => unreachable!(),
}
}
}