Skip to content

Commit

Permalink
ndk/input_queue: Replace InputQueueError with std::io::Error and …
Browse files Browse the repository at this point in the history
…add missing docs

These `AInputQueue` functions all return [standard `errno.h` error
codes] which can and should be communicated back to the user instead of
appearing as some opaque magic struct.

In addition import the NDK documentation for all these functions and
make the scope of `unsafe` blocks smaller.

[standard `errno.h` error codes]: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/jni/android_view_InputQueue.cpp;l=112;drc=7b3193e1e86ab5c0fcb784cb8616864045dd2515
  • Loading branch information
MarijnS95 committed Jun 11, 2022
1 parent 3e2175f commit 145c328
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 37 deletions.
3 changes: 2 additions & 1 deletion ndk-examples/examples/looper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ fn main() {
let input_queue = input_queue.as_ref().expect("Input queue not attached");
assert!(input_queue.has_events().unwrap());
// Consume as many events as possible
while let Some(event) = input_queue.get_event() {

while let Ok(event) = input_queue.get_event() { // Returns std::io::ErrorKind::WouldBlock if no event is available
// Pass the event by a possible IME (Input Method Editor, ie. an open keyboard) first
if let Some(event) = input_queue.pre_dispatch(event) {
info!("Input event {:?}", event);
Expand Down
92 changes: 56 additions & 36 deletions ndk/src/input_queue.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
// TODO: mod docs
//! Bindings for [`AInputQueue`]
//!
//! [`AInputQueue`]: https://developer.android.com/ndk/reference/group/input#ainputqueue
use std::io::{Error, Result};
use std::os::raw::c_int;
use std::ptr;
use std::ptr::NonNull;
use std::ptr::{self, NonNull};

use crate::event::InputEvent;
#[cfg(doc)]
use crate::event::KeyEvent;
use crate::looper::ForeignLooper;

// TODO docs
/// A native [`AInputQueue *`]
///
/// An input queue is the facility through which you retrieve input events.
///
/// [`AInputQueue *`]: https://developer.android.com/ndk/reference/group/input#ainputqueue
#[derive(Debug)]
pub struct InputQueue {
ptr: NonNull<ffi::AInputQueue>,
}

// It gets shared between threads in android_native_app_glue
// It gets shared between threads in `ndk-glue`
unsafe impl Send for InputQueue {}
unsafe impl Sync for InputQueue {}

#[derive(Debug)]
pub struct InputQueueError;

impl InputQueue {
/// Construct an `InputQueue` from the native pointer.
/// Construct an [`InputQueue`] from the native pointer.
///
/// # Safety
/// By calling this function, you assert that the pointer is a valid pointer to an NDK `AInputQueue`.
/// By calling this function, you assert that the pointer is a valid pointer to an NDK [`ffi::AInputQueue`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::AInputQueue>) -> Self {
Self { ptr }
}
Expand All @@ -33,45 +38,61 @@ impl InputQueue {
self.ptr
}

pub fn get_event(&self) -> Option<InputEvent> {
unsafe {
let mut out_event = ptr::null_mut();
if ffi::AInputQueue_getEvent(self.ptr.as_ptr(), &mut out_event) < 0 {
None
} else {
/// Returns the next available [`InputEvent`] from the queue.
///
/// Returns [`std::io::ErrorKind::WouldBlock`] if no event is available.
pub fn get_event(&self) -> Result<InputEvent> {
let mut out_event = ptr::null_mut();
match unsafe { ffi::AInputQueue_getEvent(self.ptr.as_ptr(), &mut out_event) } {
0 => {
debug_assert!(!out_event.is_null());
Some(InputEvent::from_ptr(NonNull::new_unchecked(out_event)))
Ok(unsafe { InputEvent::from_ptr(NonNull::new_unchecked(out_event)) })
}
r if r < 0 => Err(Error::from_raw_os_error(-r)),
r => unreachable!("AInputQueue_getEvent returned positive integer {}", r),
}
}

pub fn has_events(&self) -> Result<bool, InputQueueError> {
unsafe {
match ffi::AInputQueue_hasEvents(self.ptr.as_ptr()) {
0 => Ok(false),
1 => Ok(true),
x if x < 0 => Err(InputQueueError),
x => unreachable!("AInputQueue_hasEvents returned {}", x),
}
/// Returns [`true`] if there are one or more events available in the input queue.
pub fn has_events(&self) -> Result<bool> {
match unsafe { ffi::AInputQueue_hasEvents(self.ptr.as_ptr()) } {
0 => Ok(false),
1 => Ok(true),
r if r < 0 => Err(Error::from_raw_os_error(-r)),
r => unreachable!("AInputQueue_hasEvents returned non-boolean {}", r),
}
}

/// Sends the key for standard pre-dispatching that is, possibly deliver it to the current IME
/// to be consumed before the app.
///
/// Returns [`Some`] if it was not pre-dispatched, meaning you can process it right now. If
/// [`None`] is returned, you must abandon the current event processing and allow the event to
/// appear again in the event queue (if it does not get consumed during pre-dispatching).
///
/// Also returns [`None`] if `event` is not a [`KeyEvent`].
pub fn pre_dispatch(&self, event: InputEvent) -> Option<InputEvent> {
unsafe {
if ffi::AInputQueue_preDispatchEvent(self.ptr.as_ptr(), event.ptr().as_ptr()) == 0 {
Some(event)
} else {
None
}
if unsafe {
ffi::AInputQueue_preDispatchEvent(self.ptr.as_ptr(), event.ptr().as_ptr()) == 0
} {
Some(event)
} else {
None
}
}

/// Report that dispatching has finished with the given [`InputEvent`].
///
/// This must be called after receiving an event with [`InputQueue::get_event()`].
pub fn finish_event(&self, event: InputEvent, handled: bool) {
unsafe {
ffi::AInputQueue_finishEvent(self.ptr.as_ptr(), event.ptr().as_ptr(), handled as c_int);
ffi::AInputQueue_finishEvent(self.ptr.as_ptr(), event.ptr().as_ptr(), handled as c_int)
}
}

/// Add this input queue to a [`ForeignLooper`] for processing.
///
/// See [`ForeignLooper::add_fd()`] for information on the `ident`, `callback`, and `data` params.
pub fn attach_looper(&self, looper: &ForeignLooper, id: i32) {
unsafe {
ffi::AInputQueue_attachLooper(
Expand All @@ -80,13 +101,12 @@ impl InputQueue {
id,
None,
std::ptr::null_mut(),
);
)
}
}

/// Remove this input queue from the [`ForeignLooper`] it is currently attached to.
pub fn detach_looper(&self) {
unsafe {
ffi::AInputQueue_detachLooper(self.ptr.as_ptr());
}
unsafe { ffi::AInputQueue_detachLooper(self.ptr.as_ptr()) }
}
}

0 comments on commit 145c328

Please sign in to comment.