Compare commits

...

20 Commits

Author SHA1 Message Date
Robert Bragg c9faa9c44e Merge pull request #151 from rust-mobile/release-0.5.2
Release 0.5.2
2024-01-30 13:09:08 +00:00
Robert Bragg 4b9b8d754b Force cargo-ndk to only be built with stable toolchain
This fixes CI builds with rust 0.68 because cargo ndk depends on
cargo platform which depends on 0.70.
2024-01-30 12:42:36 +00:00
Robert Bragg 526d39c1f3 Release 0.5.2 2024-01-30 12:15:21 +00:00
Robert Bragg 4ffa3ac2e1 Merge pull request #147 from ArthurCose/motion-event-mask
native-activity/input: OR with `EVENT_ACTION_MASK` when extracting action
2024-01-30 12:05:30 +00:00
Robert Bragg 967882f3d9 Merge pull request #149 from rust-mobile/release-0.5.1
Release 0.5.1
2023-12-20 22:11:18 +00:00
Robert Bragg 35e080baf0 Release 0.5.1 2023-12-20 22:03:15 +00:00
Robert Bragg 5cb67a2b89 Remove ndk dev-dependency added in #142
Although this crate has some examples that depend on the ndk, they
aren't regular Cargo examples, they are completely standalone apps
that depend on dev-dependencies.
2023-12-20 17:12:25 +00:00
Arthur Cosentino 672360c5e6 Fix multitouch MotionActions processing as unknown in native activities 2023-12-13 09:05:59 -05:00
Robert Bragg 9fce890219 Merge pull request #143 from rust-mobile/readme-update-versions
README: Update crate version in `Cargo.toml` example
2023-11-20 16:13:38 +00:00
Robert Bragg 2deec162b5 Merge pull request #145 from fornwall/android-main-thread-name
Name spawned threads
2023-11-20 16:10:27 +00:00
Fredrik Fornwall eeeb80209f Fix error after merge conflict 2023-11-20 15:36:04 +01:00
Fredrik Fornwall 6c3583dc24 Merge branch 'main' into android-main-thread-name 2023-11-20 14:35:37 +01:00
Robert Bragg bfd8bfd04c Merge pull request #133 from rust-mobile/marijn/bail-log-thread-on-read_line-error
Stop log-forwarding thread on IO errors
2023-11-20 13:24:23 +00:00
Marijn Suijten af341897a2 Generalize log-forwarding setup and stop thread on IO errors
When `read_line()` starts returning `Err` the current `if let Ok`
condition ignores those, likely causing the `loop` to spin indefinitely
while this function keeps returning errors.

Note that we don't currently store the join handle for this thread
anywhere, so won't see the error surface either (just like how the join
handle for the main thread is never checked).  Perhaps we should call
`log::error!()` to make the user aware that their IO logging has
mysteriously terminated.
2023-11-20 14:15:54 +01:00
Marijn Suijten a84722ff23 Clean up a let-else that is possible in Rust 1.68 2023-11-20 13:30:29 +01:00
Fredrik Fornwall d9af67008a Rename threads 2023-11-20 12:48:37 +01:00
Fredrik Fornwall c2f467c174 Name spawned threads
Name spawned threads to make things more clear during debugging and
profiling.
2023-11-18 19:20:15 +01:00
Marijn Suijten e14d2c1deb README: Fix MSRV badge 2023-11-04 22:40:15 +01:00
Marijn Suijten 100d5bc1d4 README: Update crate version in Cargo.toml example 2023-10-28 20:25:08 +02:00
Thierry Berger 98aef99419 Disable ndk default features to remove raw-window-handle 0.6 (#142)
The `ndk` crate enables `raw-window-handle 0.6` by default (because of
https://github.com/rust-mobile/ndk/pull/434#issuecomment-1752089087)
which might not be used by consumers of the `android-activity` crate
at all, or might (still) be a mismatching version. Even if the `rwh_0x`
features are additive, figuring that out leads to cryptic errors and it
is best to turn off these defaults completely and leave it to the user
to turn it back on in their own `[dependencies]` section if desired.
2023-10-25 23:15:21 +02:00
10 changed files with 124 additions and 101 deletions
+5 -5
View File
@@ -17,13 +17,13 @@ jobs:
fail-fast: false
matrix:
# See top README for MSRV policy
rust_version: [1.68.0, stable]
rust-version: [1.68.0, stable]
steps:
- uses: actions/checkout@v4
- uses: hecrj/setup-rust-action@v2
with:
rust-version: ${{ matrix.rust_version }}
rust-version: ${{ matrix.rust-version }}
- name: Install Rust targets
run: >
@@ -34,7 +34,7 @@ jobs:
i686-linux-android
- name: Install cargo-ndk
run: cargo install cargo-ndk
run: cargo +stable install cargo-ndk
- name: Build game-activity
working-directory: android-activity
@@ -57,7 +57,7 @@ jobs:
build --features native-activity
- name: Build agdk-mainloop example
if: matrix.rust_version == 'stable'
if: matrix.rust-version == 'stable'
working-directory: examples/agdk-mainloop
run: >
cargo ndk
@@ -68,7 +68,7 @@ jobs:
-o app/src/main/jniLibs/ -- build
- name: Build na-mainloop example
if: matrix.rust_version == 'stable'
if: matrix.rust-version == 'stable'
working-directory: examples/na-mainloop
run: >
cargo ndk
+8 -8
View File
@@ -3,7 +3,7 @@
[![ci](https://github.com/rust-mobile/android-activity/actions/workflows/ci.yml/badge.svg)](https://github.com/rust-mobile/android-activity/actions/workflows/ci.yml)
[![crates.io](https://img.shields.io/crates/v/android-activity.svg)](https://crates.io/crates/android-activity)
[![Docs](https://docs.rs/android-activity/badge.svg)](https://docs.rs/android-activity)
[![MSRV](https://img.shields.io/badge/rustc-1.64.0+-ab6000.svg)](https://blog.rust-lang.org/2022/09/22/Rust-1.64.0.html)
[![MSRV](https://img.shields.io/badge/rustc-1.68.0+-ab6000.svg)](https://blog.rust-lang.org/2023/03/09/Rust-1.68.0.html)
## Overview
@@ -36,8 +36,8 @@ Cargo.toml
```toml
[dependencies]
log = "0.4"
android_logger = "0.11"
android-activity = { version = "0.4", features = [ "native-activity" ] }
android_logger = "0.13"
android-activity = { version = "0.5", features = [ "native-activity" ] }
[lib]
crate_type = ["cdylib"]
@@ -126,8 +126,8 @@ Middleware libraries can instead look at using the [ndk-context](https://crates.
The steps to switch a simple standalone application over from `ndk-glue` to `android-activity` (still based on `NativeActivity`) should be:
1. Remove `ndk-glue` from your Cargo.toml
2. Add a dependency on `android-activity`, like `android-activity = { version="0.4", features = [ "native-activity" ] }`
3. Optionally add a dependency on `android_logger = "0.11.0"`
2. Add a dependency on `android-activity`, like `android-activity = { version="0.5", features = [ "native-activity" ] }`
3. Optionally add a dependency on `android_logger = "0.13.0"`
4. Update the `main` entry point to look like this:
```rust
@@ -157,8 +157,8 @@ Prior to working on android-activity, the existing glue crates available for bui
## MSRV
We aim to (at least) support stable releases of Rust from the last three months. Rust has a 6 week release cycle which means we will support the last three stable releases.
For example, when Rust 1.69 is released we would limit our `rust_version` to 1.67.
For example, when Rust 1.69 is released we would limit our `rust-version` to 1.67.
We will only bump the `rust_version` at the point where we either depend on a new features or a dependency has increased its MSRV, and we won't be greedy. In other words we will only set the MSRV to the lowest version that's _needed_.
We will only bump the `rust-version` at the point where we either depend on a new features or a dependency has increased its MSRV, and we won't be greedy. In other words we will only set the MSRV to the lowest version that's _needed_.
MSRV updates are not considered to be inherently semver breaking (unless a new feature is exposed in the public API) and so a `rust_version` change may happen in patch releases.
MSRV updates are not considered to be inherently semver breaking (unless a new feature is exposed in the public API) and so a `rust-version` change may happen in patch releases.
+34
View File
@@ -6,6 +6,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.5.2] - 2024-01-30
### Fixed
- NativeActivity: OR with `EVENT_ACTION_MASK` when extracting action from `MotionEvent` - fixing multi-touch input ([#146](https://github.com/rust-mobile/android-activity/issues/146), [#147](https://github.com/rust-mobile/android-activity/pull/147))
## [0.5.1] - 2023-12-20
### Changed
- Avoids depending on default features for `ndk` crate to avoid pulling in any `raw-window-handle` dependencies ([#142](https://github.com/rust-mobile/android-activity/pull/142))
**Note:** Technically, this could be observed as a breaking change in case you
were depending on the `rwh_06` feature that was enabled by default in the
`ndk` crate. This could be observed via the `NativeWindow` type (exposed via
`AndroidApp::native_window()`) no longer implementing `rwh_06::HasWindowHandle`.
In the unlikely case that you were depending on the `ndk`'s `rwh_06` API
being enabled by default via `android-activity`'s `ndk` dependency, your crate
should explicitly enable the `rwh_06` feature for the `ndk` crate.
As far as could be seen though, it's not expected that anything was
depending on this (e.g. anything based on Winit enables the `ndk` feature
based on an equivalent `winit` feature).
The benefit of the change is that it can help avoid a redundant
`raw-window-handle 0.6` dependency in projects that still need to use older
(non-default) `raw-window-handle` versions. (Though note that this may be
awkward to achieve in practice since other crates that depend on the `ndk`
are still likely to use default features and also pull in
`raw-window-handles 0.6`)
- The IO thread now gets named `stdio-to-logcat` and main thread is named `android_main` ([#145](https://github.com/rust-mobile/android-activity/pull/145))
- Improved IO error handling in `stdio-to-logcat` IO loop. ([#133](https://github.com/rust-mobile/android-activity/pull/133))
## [0.5.0] - 2023-10-16
### Added
- Added `MotionEvent::action_button()` exposing the button associated with button press/release actions ()
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "android-activity"
version = "0.5.0"
version = "0.5.2"
edition = "2021"
keywords = ["android", "ndk"]
readme = "../README.md"
@@ -34,7 +34,7 @@ jni-sys = "0.3"
cesu8 = "1"
jni = "0.21"
ndk-sys = "0.5.0"
ndk = "0.8.0"
ndk = { version = "0.8.0", default-features = false }
ndk-context = "0.1"
android-properties = "0.2"
num_enum = "0.7"
+18 -45
View File
@@ -1,21 +1,17 @@
#![cfg(feature = "game-activity")]
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::marker::PhantomData;
use std::ops::Deref;
use std::os::unix::prelude::*;
use std::panic::catch_unwind;
use std::ptr;
use std::ptr::NonNull;
use std::sync::Weak;
use std::sync::{Arc, Mutex, RwLock};
use std::time::Duration;
use std::{ptr, thread};
use libc::c_void;
use log::{error, trace, Level};
use log::{error, trace};
use jni_sys::*;
@@ -29,9 +25,9 @@ use ndk::native_window::NativeWindow;
use crate::error::InternalResult;
use crate::input::{Axis, KeyCharacterMap, KeyCharacterMapBinding};
use crate::jni_utils::{self, CloneJavaVM};
use crate::util::{abort_on_panic, android_log, log_panic};
use crate::util::{abort_on_panic, forward_stdio_to_logcat, log_panic, try_get_path_from_ptr};
use crate::{
util, AndroidApp, ConfigurationRef, InputStatus, MainEvent, PollEvent, Rect, WindowManagerFlags,
AndroidApp, ConfigurationRef, InputStatus, MainEvent, PollEvent, Rect, WindowManagerFlags,
};
mod ffi;
@@ -617,21 +613,21 @@ impl AndroidAppInner {
pub fn internal_data_path(&self) -> Option<std::path::PathBuf> {
unsafe {
let app_ptr = self.native_app.as_ptr();
util::try_get_path_from_ptr((*(*app_ptr).activity).internalDataPath)
try_get_path_from_ptr((*(*app_ptr).activity).internalDataPath)
}
}
pub fn external_data_path(&self) -> Option<std::path::PathBuf> {
unsafe {
let app_ptr = self.native_app.as_ptr();
util::try_get_path_from_ptr((*(*app_ptr).activity).externalDataPath)
try_get_path_from_ptr((*(*app_ptr).activity).externalDataPath)
}
}
pub fn obb_path(&self) -> Option<std::path::PathBuf> {
unsafe {
let app_ptr = self.native_app.as_ptr();
util::try_get_path_from_ptr((*(*app_ptr).activity).obbPath)
try_get_path_from_ptr((*(*app_ptr).activity).obbPath)
}
}
}
@@ -768,18 +764,16 @@ impl<'a> From<Arc<InputReceiver>> for InputIteratorInner<'a> {
let buffered = unsafe {
let app_ptr = receiver.native_app.as_ptr();
let input_buffer = ffi::android_app_swap_input_buffers(app_ptr);
if input_buffer.is_null() {
None
} else {
let buffer = InputBuffer::from_ptr(NonNull::new_unchecked(input_buffer));
NonNull::new(input_buffer).map(|input_buffer| {
let buffer = InputBuffer::from_ptr(input_buffer);
let keys_iter = KeyEventsLendingIterator::new(&buffer);
let motion_iter = MotionEventsLendingIterator::new(&buffer);
Some(BufferedEvents::<'a> {
BufferedEvents::<'a> {
buffer,
keys_iter,
motion_iter,
})
}
}
})
};
let native_app = receiver.native_app.clone();
@@ -913,33 +907,7 @@ extern "Rust" {
#[no_mangle]
pub unsafe extern "C" fn _rust_glue_entry(native_app: *mut ffi::android_app) {
abort_on_panic(|| {
// Maybe make this stdout/stderr redirection an optional / opt-in feature?...
let file = {
let mut logpipe: [RawFd; 2] = Default::default();
libc::pipe2(logpipe.as_mut_ptr(), libc::O_CLOEXEC);
libc::dup2(logpipe[1], libc::STDOUT_FILENO);
libc::dup2(logpipe[1], libc::STDERR_FILENO);
libc::close(logpipe[1]);
File::from_raw_fd(logpipe[0])
};
thread::spawn(move || {
let tag = CStr::from_bytes_with_nul(b"RustStdoutStderr\0").unwrap();
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);
}
}
}
});
let _join_log_forwarder = forward_stdio_to_logcat();
let jvm = unsafe {
let jvm = (*(*native_app).activity).vm;
@@ -955,6 +923,11 @@ pub unsafe extern "C" fn _rust_glue_entry(native_app: *mut ffi::android_app) {
};
unsafe {
// Name thread - this needs to happen here after attaching to a JVM thread,
// since that changes the thread name to something like "Thread-2".
let thread_name = std::ffi::CStr::from_bytes_with_nul(b"android_main\0").unwrap();
libc::pthread_setname_np(libc::pthread_self(), thread_name.as_ptr());
let app = AndroidApp::from_ptr(NonNull::new(native_app).unwrap(), jvm.clone());
// We want to specifically catch any panic from the application's android_main
+2
View File
@@ -112,6 +112,8 @@
//! [`GameActivity`]: https://developer.android.com/games/agdk/integrate-game-activity
//! [Looper]: https://developer.android.com/reference/android/os/Looper
#![deny(clippy::manual_let_else)]
use std::hash::Hash;
use std::sync::Arc;
use std::sync::RwLock;
+7 -33
View File
@@ -3,23 +3,17 @@
//! synchronization between the two threads.
use std::{
ffi::{CStr, CString},
fs::File,
io::{BufRead, BufReader},
ops::Deref,
os::unix::prelude::{FromRawFd, RawFd},
panic::catch_unwind,
ptr::{self, NonNull},
sync::{Arc, Condvar, Mutex, Weak},
};
use log::Level;
use ndk::{configuration::Configuration, input_queue::InputQueue, native_window::NativeWindow};
use crate::{
jni_utils::CloneJavaVM,
util::android_log,
util::{abort_on_panic, log_panic},
util::{abort_on_panic, forward_stdio_to_logcat, log_panic},
ConfigurationRef,
};
@@ -834,32 +828,7 @@ extern "C" fn ANativeActivity_onCreate(
saved_state_size: libc::size_t,
) {
abort_on_panic(|| {
// Maybe make this stdout/stderr redirection an optional / opt-in feature?...
let file = unsafe {
let mut logpipe: [RawFd; 2] = Default::default();
libc::pipe2(logpipe.as_mut_ptr(), libc::O_CLOEXEC);
libc::dup2(logpipe[1], libc::STDOUT_FILENO);
libc::dup2(logpipe[1], libc::STDERR_FILENO);
libc::close(logpipe[1]);
File::from_raw_fd(logpipe[0])
};
std::thread::spawn(move || {
let tag = CStr::from_bytes_with_nul(b"RustStdoutStderr\0").unwrap();
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);
}
}
}
});
let _join_log_forwarder = forward_stdio_to_logcat();
log::trace!(
"Creating: {:p}, saved_state = {:p}, save_state_size = {}",
@@ -899,6 +868,11 @@ extern "C" fn ANativeActivity_onCreate(
rust_glue.notify_main_thread_running();
unsafe {
// Name thread - this needs to happen here after attaching to a JVM thread,
// since that changes the thread name to something like "Thread-2".
let thread_name = std::ffi::CStr::from_bytes_with_nul(b"android_main\0").unwrap();
libc::pthread_setname_np(libc::pthread_self(), thread_name.as_ptr());
// We want to specifically catch any panic from the application's android_main
// so we can finish + destroy the Activity gracefully via the JVM
catch_unwind(|| {
@@ -55,7 +55,8 @@ impl<'a> MotionEvent<'a> {
// `MotionAction` enum that we share between backends, which may also
// capture unknown variants added in new versions of Android.
let action =
unsafe { ndk_sys::AMotionEvent_getAction(self.ndk_event.ptr().as_ptr()) as u32 };
unsafe { ndk_sys::AMotionEvent_getAction(self.ndk_event.ptr().as_ptr()) as u32 }
& ndk_sys::AMOTION_EVENT_ACTION_MASK;
action.into()
}
+1 -5
View File
@@ -485,11 +485,7 @@ impl<'a> InputIteratorInner<'a> {
where
F: FnOnce(&input::InputEvent) -> InputStatus,
{
// XXX: would use `let Some(queue) = &self.receiver.queue else { return
// false; }` but we're stuck supporting Rust 1.64 for Winit currently
let queue = if let Some(queue) = &self.receiver.queue {
queue
} else {
let Some(queue) = &self.receiver.queue else {
log::trace!("no queue available for events");
return false;
};
+45 -2
View File
@@ -1,7 +1,12 @@
use log::Level;
use log::{error, Level};
use std::{
ffi::{CStr, CString},
os::raw::c_char,
fs::File,
io::{BufRead as _, BufReader, Result},
os::{
fd::{FromRawFd as _, RawFd},
raw::c_char,
},
};
pub fn try_get_path_from_ptr(path: *const c_char) -> Option<std::path::PathBuf> {
@@ -31,6 +36,44 @@ pub(crate) fn android_log(level: Level, tag: &CStr, msg: &CStr) {
}
}
pub(crate) fn forward_stdio_to_logcat() -> std::thread::JoinHandle<Result<()>> {
// XXX: make this stdout/stderr redirection an optional / opt-in feature?...
let file = unsafe {
let mut logpipe: [RawFd; 2] = Default::default();
libc::pipe2(logpipe.as_mut_ptr(), libc::O_CLOEXEC);
libc::dup2(logpipe[1], libc::STDOUT_FILENO);
libc::dup2(logpipe[1], libc::STDERR_FILENO);
libc::close(logpipe[1]);
File::from_raw_fd(logpipe[0])
};
std::thread::Builder::new()
.name("stdio-to-logcat".to_string())
.spawn(move || -> Result<()> {
let tag = CStr::from_bytes_with_nul(b"RustStdoutStderr\0").unwrap();
let mut reader = BufReader::new(file);
let mut buffer = String::new();
loop {
buffer.clear();
let len = match reader.read_line(&mut buffer) {
Ok(len) => len,
Err(e) => {
error!("Logcat forwarder failed to read stdin/stderr: {e:?}");
break Err(e);
}
};
if len == 0 {
break Ok(());
} else if let Ok(msg) = CString::new(buffer.clone()) {
android_log(Level::Info, tag, &msg);
}
}
})
.expect("Failed to start stdout/stderr to logcat forwarder thread")
}
pub(crate) fn log_panic(panic: Box<dyn std::any::Any + Send>) {
let rust_panic = unsafe { CStr::from_bytes_with_nul_unchecked(b"RustPanic\0") };