Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
1
third-party/vendor/ndk/.cargo-checksum.json
vendored
Normal file
1
third-party/vendor/ndk/.cargo-checksum.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"files":{"CHANGELOG.md":"fb266dc992ce98787c6afa9f76d7875080f66c6b4fbf5b6127d539bca710f838","Cargo.toml":"dcf8924779fc545761ebbd778ddfef0f41ace80bb50267e4853b5eedfd183a21","README.md":"9adf2ca8412635302cbfa7d9a55537e92b3c1e3e3e2b963f4ecc478d774b4c96","src/asset.rs":"c7c07a9ca655fbd8f1c61cf9039689aa5ac5c35e170d440bef750a430e6b9210","src/audio.rs":"62658a036a49d1b474f0ee426c3bded79e5d06c1a1932329078927911d72b672","src/bitmap.rs":"51d95ef13563e47f245c0c102f2da86f5b2dd868a625a4d5ae1f22c661fafcfc","src/configuration.rs":"25ff6224868b09f89cdff84bc7a80c06de7f7ab71524561fd1b70ab852805c44","src/event.rs":"bfd9f82ca0b058675056264369fc54af77519e41a5a4164e4f51519fbea2881f","src/hardware_buffer.rs":"c42e33c5b06ab692567471e7a345a9e3791cd915352455cc0a0a3edea594a31b","src/hardware_buffer_format.rs":"bc54a2333c248cb3f8160c54ff27bf2fad2f50eb0d6ec7360e5cec59bf48167a","src/input_queue.rs":"d888e2bea21fc9853c35afc06146013604ce466714382ebdc26352ba52bb98bb","src/lib.rs":"e5437e6cac64916c0856569e7b8dcc626718a5766d2ec2c1252cfcb9f0bae77a","src/looper.rs":"c1f48c56d5d02b781dbb751d143eddd7a2689243d654ff9afd210b413e033b34","src/media/error.rs":"caeec11e39cb3433a9afa259677eeadf42f73601b6ff6ee597f9ee914c7fbc5c","src/media/image_reader.rs":"489951386fe71209d174a19dfa9444383337825a4371cf342274182703811646","src/media/media_codec.rs":"fd23d4cdec99bbfbeac8313cca402fd4d06d9cdf7f0302e38cfcb1fd135cddc9","src/media/mod.rs":"b3f79d99d1f4b4dd6f9d70be4c6666765489fcb038767446e9088ca0283e64cf","src/native_activity.rs":"c8d1b4b36944e2f64d74fbaa69cc5da199cfa12749297f7f353368344c78173a","src/native_window.rs":"e563f4634a5a83ad30c64873e3e8ed5e40ffb70f86081ad00a8738b9d7590d4a","src/surface_texture.rs":"ad323d5c2dff6f72c1ed9a86ff5cec88351ec9f3e22289e1499434e66e80213c","src/trace.rs":"f8013db7e2ea18cf60a59487e3028844098c3cc21d935da46509aded1e7bf97f","src/utils.rs":"90baded328bd0cabcbea799649a1ffa6b7f7aaa9009b941902a98245c220b646"},"package":"451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0"}
|
||||
67
third-party/vendor/ndk/CHANGELOG.md
vendored
Normal file
67
third-party/vendor/ndk/CHANGELOG.md
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# Unreleased
|
||||
|
||||
# 0.7.0 (2022-07-24)
|
||||
|
||||
- hardware_buffer: Make `HardwareBuffer::as_ptr()` public for interop with Vulkan. (#213)
|
||||
- **Breaking:** `Configuration::country()` now returns `None` when the country is unset (akin to `Configuration::language()`). (#220)
|
||||
- Add `MediaCodec` and `MediaFormat` bindings. (#216)
|
||||
- **Breaking:** Upgrade to [`ndk-sys 0.4.0`](../ndk-sys/CHANGELOG.md#040-2022-07-XXXX) and use new `enum` newtype wrappers. (#245)
|
||||
- native_window: Use `release`/`acquire` for `Drop` and `Clone` respectively. (#207)
|
||||
- **Breaking:** audio: Rename from `aaudio` to `audio` and drop `A` prefix. (#273)
|
||||
- Implement `HasRawWindowHandle` directly on `NativeWindow`. (#274, #319)
|
||||
- **Breaking:** native_activity: Replace `CStr` return types with `Path`. (#279)
|
||||
- native_window: Add `format()` getter and `set_buffers_geometry()` setter. (#276)
|
||||
- native_activity: Add `set_window_format()` setter. (#277)
|
||||
- native_activity: Add `set_window_flags()` to change window behavior. (#278)
|
||||
- Add `SurfaceTexture` bindings. (#267)
|
||||
- Improve library and structure documentation, linking back to the NDK docs more rigorously. (#290)
|
||||
- **Breaking:** input_queue: `get_event()` now returns a `Result` with `std::io::Error`; `InputQueueError` has been removed. (#292)
|
||||
- **Breaking:** input_queue: `has_events()` now returns a `bool` directly without being wrapped in `Result`. (#294)
|
||||
- **Breaking:** hardware_buffer: `HardwareBufferError` has been removed and replaced with `std::io::Error` in return types. (#295)
|
||||
- Fixed `HardwareBuffer` leak on buffers returned from `AndroidBitmap::get_hardware_buffer()`. (#296)
|
||||
- **Breaking:** Update `jni` crate (used in public API) from `0.18` to `0.19`. (#300)
|
||||
- hardware_buffer: Made `HardwareBufferDesc` fields `pub`. (#313)
|
||||
- **Breaking:** Remove `hardware_buffer` and `trace` features in favour of using `api-level-26` or `api-level-23` directly. (#320)
|
||||
|
||||
# 0.6.0 (2022-01-05)
|
||||
|
||||
- **Breaking:** Upgrade to [`ndk-sys 0.3.0`](../ndk-sys/CHANGELOG.md#030-2022-01-05) and migrate to `jni-sys` types that it now directly uses in its bindings. (#209 / #214)
|
||||
|
||||
# 0.5.0 (2021-11-22)
|
||||
|
||||
- **Breaking:** Replace `add_fd_with_callback` `ident` with constant value `ALOOPER_POLL_CALLBACK`,
|
||||
as per https://developer.android.com/ndk/reference/group/looper#alooper_addfd.
|
||||
- **Breaking:** Accept unboxed closure in `add_fd_with_callback`.
|
||||
- aaudio: Replace "Added in" comments with missing `#[cfg(feature)]`.
|
||||
- aaudio: Add missing `fn get_allowed_capture_policy()`.
|
||||
- configuration: Add missing `api-level-30` feature to `fn screen_round()`.
|
||||
|
||||
# 0.4.0 (2021-08-02)
|
||||
|
||||
- **Breaking:** Model looper file descriptor events integer as `bitflags`.
|
||||
|
||||
# 0.3.0 (2021-01-30)
|
||||
|
||||
- **Breaking:** Looper `ident` not passed in `data` pointer anymore.
|
||||
`attach_looper` now only sets the `ident` field when attaching an
|
||||
`InputQueue` to a `ForeignLooper`.
|
||||
If you are relying on `Poll::Event::data` to tell event fd and
|
||||
input queue apart, please use `Poll::Event::ident` and the new
|
||||
constants introduced in `ndk-glue`!
|
||||
|
||||
# 0.2.1 (2020-10-15)
|
||||
|
||||
- Fix documentation build on docs.rs
|
||||
|
||||
# 0.2.0 (2020-09-15)
|
||||
|
||||
- **Breaking:** Updated to use [ndk-sys 0.2.0](../ndk-sys/CHANGELOG.md#020-2020-09-15)
|
||||
- Added `media` bindings
|
||||
- Added `bitmap` and `hardware_buffer` bindings
|
||||
- Added `aaudio` bindings
|
||||
- Fixed assets directory path to be relative to the manifest
|
||||
- Added `trace` feature for native tracing
|
||||
|
||||
# 0.1.0 (2020-04-22)
|
||||
|
||||
- Initial release! 🎉
|
||||
98
third-party/vendor/ndk/Cargo.toml
vendored
Normal file
98
third-party/vendor/ndk/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "ndk"
|
||||
version = "0.7.0"
|
||||
authors = ["The Rust Windowing contributors"]
|
||||
description = "Safe Rust bindings to the Android NDK"
|
||||
homepage = "https://github.com/rust-windowing/android-ndk-rs"
|
||||
documentation = "https://docs.rs/android-ndk"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"android",
|
||||
"ndk",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/rust-windowing/android-ndk-rs"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = [
|
||||
"jni",
|
||||
"jni-glue",
|
||||
"all",
|
||||
]
|
||||
rustdoc-args = [
|
||||
"--cfg",
|
||||
"docsrs",
|
||||
]
|
||||
targets = [
|
||||
"aarch64-linux-android",
|
||||
"armv7-linux-androideabi",
|
||||
"i686-linux-android",
|
||||
"x86_64-linux-android",
|
||||
]
|
||||
|
||||
[dependencies.bitflags]
|
||||
version = "1.2.1"
|
||||
|
||||
[dependencies.ffi]
|
||||
version = "0.4.0"
|
||||
package = "ndk-sys"
|
||||
|
||||
[dependencies.jni]
|
||||
version = "0.19"
|
||||
optional = true
|
||||
|
||||
[dependencies.jni-glue]
|
||||
version = "0.0.10"
|
||||
optional = true
|
||||
|
||||
[dependencies.jni-sys]
|
||||
version = "0.3.0"
|
||||
|
||||
[dependencies.num_enum]
|
||||
version = "0.5.1"
|
||||
|
||||
[dependencies.raw-window-handle]
|
||||
version = "0.5"
|
||||
|
||||
[dependencies.thiserror]
|
||||
version = "1.0.23"
|
||||
|
||||
[features]
|
||||
all = [
|
||||
"audio",
|
||||
"bitmap",
|
||||
"media",
|
||||
"api-level-30",
|
||||
]
|
||||
api-level-23 = []
|
||||
api-level-24 = ["api-level-23"]
|
||||
api-level-25 = ["api-level-24"]
|
||||
api-level-26 = ["api-level-25"]
|
||||
api-level-27 = ["api-level-26"]
|
||||
api-level-28 = ["api-level-27"]
|
||||
api-level-29 = ["api-level-28"]
|
||||
api-level-30 = ["api-level-29"]
|
||||
audio = [
|
||||
"ffi/audio",
|
||||
"api-level-26",
|
||||
]
|
||||
bitmap = ["ffi/bitmap"]
|
||||
media = ["ffi/media"]
|
||||
test = [
|
||||
"ffi/test",
|
||||
"jni",
|
||||
"jni-glue",
|
||||
"all",
|
||||
]
|
||||
175
third-party/vendor/ndk/README.md
vendored
Normal file
175
third-party/vendor/ndk/README.md
vendored
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
# Rust on Android
|
||||
|
||||
[](https://github.com/rust-windowing/android-ndk-rs/actions)  
|
||||
|
||||
Libraries and tools for Rust programming on Android targets:
|
||||
|
||||
Name | Description | Badges
|
||||
--- | --- | ---
|
||||
[`ndk-sys`](./ndk-sys) | Raw FFI bindings to the NDK | [](https://crates.io/crates/ndk-sys) [](https://docs.rs/ndk-sys)
|
||||
[`ndk`](./ndk) | Safe abstraction of the bindings | [](https://crates.io/crates/ndk) [](https://docs.rs/ndk)
|
||||
[`ndk-context`](./ndk-context) | Android handles | [](https://crates.io/crates/ndk-context)
|
||||
[`ndk-glue`](./ndk-glue) | Startup code | [](https://crates.io/crates/ndk-glue) [](https://docs.rs/ndk-glue)
|
||||
[`ndk-build`](./ndk-build) | Everything for building apk's | [](https://crates.io/crates/ndk-build) [](https://docs.rs/ndk-build)
|
||||
[`cargo-apk`](./cargo-apk) | Build tool | [](https://crates.io/crates/cargo-apk) [](https://docs.rs/cargo-apk)
|
||||
|
||||
See [`ndk-examples`](./ndk-examples) for examples using the NDK and the README files of the crates for more details.
|
||||
|
||||
## Supported NDK versions
|
||||
|
||||
`android-ndk-rs` aims to support at least the `LTS` and `Rolling Release` branches of the NDK, as described on [their wiki](https://github.com/android/ndk/wiki#supported-downloads). Additionally the `Beta Release` might be supported to prepare for an upcoming release.
|
||||
|
||||
As of writing (2021-07-24) the following NDKs are tested:
|
||||
|
||||
Branch | Version | Status | Working
|
||||
-|-|:-:|:-:
|
||||
r18 | 18.1.5063045 | _Deprecated_ | :x:
|
||||
r19 | 19.2.5345600 | _Deprecated_ | :heavy_check_mark:
|
||||
r20 | 20.1.5948944 | _Deprecated_ | :heavy_check_mark:
|
||||
r21 | 21.4.7075529 | _Deprecated_ | :heavy_check_mark:
|
||||
r22 | 22.1.7171670 | _Deprecated_ | :heavy_check_mark:
|
||||
r23 | beta 1/2 | _Deprecated_ | :heavy_check_mark:
|
||||
r23 | 23.0.7272597-beta3 | _Deprecated_ | :heavy_check_mark: Workaround in [#189](https://github.com/rust-windowing/android-ndk-rs/pull/189)
|
||||
r23 | 23.1.7779620 | LTS | :heavy_check_mark: Workaround in [#189](https://github.com/rust-windowing/android-ndk-rs/pull/189)
|
||||
r24 | 24.0.7856742-beta1 | Rolling Release | :heavy_check_mark: Workaround in [#189](https://github.com/rust-windowing/android-ndk-rs/pull/189)
|
||||
|
||||
|
||||
## Quick start: `Hello World` crate on Android
|
||||
|
||||
Quick start setting up a new project with support for Android, using the `ndk-glue` layer for communicating with the Android framework through [`NativeActivity`](https://developer.android.com/reference/android/app/NativeActivity) and `cargo-apk` for packaging a crate in an Android `.apk` file.
|
||||
|
||||
This short guide can also be used as a reference for converting existing crates to be runnable on Android.
|
||||
|
||||
### 1. Install the Android NDK and SDK
|
||||
|
||||
Make sure the Android NDK is installed, together with a target platform (`30` by default), `build-tools` and `platform-tools`, using either the [`sdkmanager`](https://developer.android.com/studio/command-line/sdkmanager) or [Android Studio](https://developer.android.com/studio/projects/install-ndk).
|
||||
|
||||
### 2. Create a new library crate
|
||||
|
||||
```console
|
||||
$ cargo new hello_world_android --lib
|
||||
```
|
||||
|
||||
Never name your project `android` as this results in a target binary named `libandroid.so` which is also the name of Android's framework library: this will fail to link.
|
||||
|
||||
### 3. Configure crate for use on Android
|
||||
|
||||
Add the `ndk-glue` dependency to your crate.
|
||||
|
||||
`Cargo.toml`
|
||||
```toml
|
||||
# This dependency will only be included when targeting Android
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
ndk-glue = "xxx" # Substitute this with the latest ndk-glue version you wish to use
|
||||
```
|
||||
|
||||
Then configure the library target to be compiled to a Rust `lib` (for use in an executable on desktop) and a `cdylib` to create a native binary that can be bundled in the final `.apk` and loaded by Android.
|
||||
|
||||
`Cargo.toml`
|
||||
```toml
|
||||
[lib]
|
||||
crate-type = ["lib", "cdylib"]
|
||||
```
|
||||
|
||||
### 4. Wrap entry point with `ndk-glue`
|
||||
|
||||
Create a `main` function holding your code in the library portion of the crate, and wrap it in the `ndk_glue::main` attribute macro when targeting Android.
|
||||
|
||||
`src/lib.rs`
|
||||
```rust
|
||||
#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))]
|
||||
pub fn main() {
|
||||
println!("Hello World");
|
||||
}
|
||||
```
|
||||
|
||||
See the [`ndk-macro` documentation](./ndk-macro/README.md) for more options.
|
||||
|
||||
Additionally, to make this crate runnable outside of Android, create a binary that calls the main function in the library.
|
||||
|
||||
`src/main.rs`
|
||||
```rust
|
||||
fn main() {
|
||||
hello_world_android::main()
|
||||
}
|
||||
```
|
||||
|
||||
As a sanity check, run this binary to make sure everything is set up correctly:
|
||||
|
||||
```console
|
||||
$ cargo run
|
||||
```
|
||||
|
||||
### 5. Run the crate on your Android device
|
||||
|
||||
Install `cargo apk` for building, running and debugging your application:
|
||||
```console
|
||||
$ cargo install cargo-apk
|
||||
```
|
||||
|
||||
We can now directly execute our `Hello World` application on a real connected device or an emulator:
|
||||
```console
|
||||
$ cargo apk run
|
||||
```
|
||||
|
||||
If the crate includes a runnable binary as suggested above, you will likely be greeted by the following error:
|
||||
|
||||
```console
|
||||
$ cargo apk run
|
||||
error: extra arguments to `rustc` can only be passed to one target, consider filtering
|
||||
the package by passing, e.g., `--lib` or `--bin NAME` to specify a single target
|
||||
Error: Command `cargo rustc --target aarch64-linux-android -- -L hello_world_android/target/cargo-apk-temp-extra-link-libraries` had a non-zero exit code.
|
||||
```
|
||||
|
||||
To solve this, add `--lib` to the run invocation, like so:
|
||||
```console
|
||||
$ cargo apk run --lib
|
||||
```
|
||||
|
||||
### 6. Inspect the output
|
||||
|
||||
`ndk-glue` redirects stdout and stderr to Android `logcat`, including the `println!("Hello World")` by the example above. See [Logging and stdout](##Logging-and-stdout) below how to access it.
|
||||
|
||||
## Rendering to the window
|
||||
|
||||
Android native apps have no easy access to [Android's User Interface](https://developer.android.com/guide/topics/ui) functionality (bar [JNI](##jni) interop). Applications can instead draw pixels directly to the window using [`ANativeWindow_lock`](https://developer.android.com/ndk/reference/group/a-native-window#group___a_native_window_1ga0b0e3b7d442dee83e1a1b42e5b0caee6), or use a graphics API like OpenGL or Vulkan for high performance rendering.
|
||||
|
||||
## Logging and stdout
|
||||
Stdout is redirected to the android log api when using `ndk-glue`. Any logger that logs to
|
||||
stdout, like `println!`, should therefore work.
|
||||
|
||||
To filter on this output in `logcat`:
|
||||
```console
|
||||
$ adb logcat RustStdoutStderr:D *:S
|
||||
```
|
||||
|
||||
### Android logger
|
||||
Enable the `"logger"` feature on the `ndk-glue` macro and configure its log tag and debug level through the attribute macro:
|
||||
|
||||
`src/lib.rs`
|
||||
```rust
|
||||
#[cfg_attr(target_os = "android", ndk_glue::main(logger(level = "debug", tag = "my-tag")))]
|
||||
pub fn main() {
|
||||
log!("hello world");
|
||||
}
|
||||
```
|
||||
|
||||
## App/APK configuration
|
||||
Android APKs contain a file called `AndroidManifest.xml`, which has things like permission requests and feature declarations, plus configuration of activities, intents, resource folders and more. This file is autogenerated by `cargo-apk`. To control what goes in it through Cargo.toml, refer to [`cargo-apk`'s README](./cargo-apk/README.md).
|
||||
|
||||
## Overriding crate paths
|
||||
The macro `ndk_glue::main` tries to determine crate names from current _Cargo.toml_.
|
||||
You can override this names with specific paths like so:
|
||||
```rust
|
||||
#[ndk_glue::main(
|
||||
ndk_glue = "path::to::ndk_glue",
|
||||
)]
|
||||
fn main() {}
|
||||
```
|
||||
|
||||
## JNI
|
||||
Java Native Interface (JNI) allows executing Java code in a VM from native applications. To access
|
||||
the JNI use the `AndroidContext` from the `ndk-context` crate. `ndk-examples` contains a `jni_audio`
|
||||
example which will print out all output audio devices in the log.
|
||||
|
||||
- [`jni`](https://crates.io/crates/jni), JNI bindings for Rust
|
||||
274
third-party/vendor/ndk/src/asset.rs
vendored
Normal file
274
third-party/vendor/ndk/src/asset.rs
vendored
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
//! Bindings for [`AAsset`], [`AAssetDir`] and [`AAssetManager`]
|
||||
//!
|
||||
//! [`AAsset`]: https://developer.android.com/ndk/reference/group/asset#aasset
|
||||
//! [`AAssetDir`]: https://developer.android.com/ndk/reference/group/asset#aassetdir
|
||||
//! [`AAssetManager`]: https://developer.android.com/ndk/reference/group/asset#aassetmanager
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::io;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
/// A native [`AAssetManager *`]
|
||||
///
|
||||
/// [`AAssetManager *`]: https://developer.android.com/ndk/reference/group/asset#aassetmanager
|
||||
#[derive(Debug)]
|
||||
pub struct AssetManager {
|
||||
ptr: NonNull<ffi::AAssetManager>,
|
||||
}
|
||||
|
||||
// AAssetManager is thread safe.
|
||||
// See https://developer.android.com/ndk/reference/group/asset#aassetmanager
|
||||
unsafe impl Send for AssetManager {}
|
||||
unsafe impl Sync for AssetManager {}
|
||||
|
||||
impl AssetManager {
|
||||
/// Create an `AssetManager` from a pointer
|
||||
///
|
||||
/// # Safety
|
||||
/// By calling this function, you assert that the pointer is a valid pointer to a native
|
||||
/// `AAssetManager`.
|
||||
pub unsafe fn from_ptr(ptr: NonNull<ffi::AAssetManager>) -> Self {
|
||||
Self { ptr }
|
||||
}
|
||||
|
||||
/// Returns the pointer to the native `AAssetManager`.
|
||||
pub fn ptr(&self) -> NonNull<ffi::AAssetManager> {
|
||||
self.ptr
|
||||
}
|
||||
|
||||
/// Open the asset. Returns `None` if opening the asset fails.
|
||||
///
|
||||
/// This currently always opens the asset in the streaming mode.
|
||||
pub fn open(&self, filename: &CStr) -> Option<Asset> {
|
||||
unsafe {
|
||||
let ptr = ffi::AAssetManager_open(
|
||||
self.ptr.as_ptr(),
|
||||
filename.as_ptr(),
|
||||
ffi::AASSET_MODE_STREAMING as i32,
|
||||
);
|
||||
Some(Asset::from_ptr(NonNull::new(ptr)?))
|
||||
}
|
||||
}
|
||||
|
||||
/// Open an asset directory. Returns `None` if opening the directory fails.
|
||||
pub fn open_dir(&self, filename: &CStr) -> Option<AssetDir> {
|
||||
unsafe {
|
||||
let ptr = ffi::AAssetManager_openDir(self.ptr.as_ptr(), filename.as_ptr());
|
||||
Some(AssetDir::from_ptr(NonNull::new(ptr)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A native [`AAssetDir *`]
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use std::ffi::CString;
|
||||
/// # use ndk::asset::AssetManager;
|
||||
/// # let asset_manager: AssetManager = unimplemented!();
|
||||
/// use std::io::Read;
|
||||
///
|
||||
/// let mut my_dir = asset_manager
|
||||
/// .open_dir(&CString::new("my_dir").unwrap())
|
||||
/// .expect("Could not open directory");
|
||||
///
|
||||
/// // Use it as an iterator
|
||||
/// let all_files = my_dir.collect::<Vec<CString>>();
|
||||
///
|
||||
/// // Reset the iterator
|
||||
/// my_dir.rewind();
|
||||
///
|
||||
/// // Use .with_next() to iterate without allocating `CString`s
|
||||
/// while let Some(asset) = my_dir.with_next(|cstr| asset_manager.open(cstr).unwrap()) {
|
||||
/// let mut text = String::new();
|
||||
/// asset.read_to_string(&mut text);
|
||||
/// // ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`AAssetDir *`]: https://developer.android.com/ndk/reference/group/asset#aassetdir
|
||||
#[derive(Debug)]
|
||||
pub struct AssetDir {
|
||||
ptr: NonNull<ffi::AAssetDir>,
|
||||
}
|
||||
|
||||
// It's unclear if AAssetDir is thread safe.
|
||||
// However, AAsset is not, so there's a good chance that AAssetDir is not either.
|
||||
|
||||
impl Drop for AssetDir {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::AAssetDir_close(self.ptr.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AssetDir {
|
||||
/// Construct an `AssetDir` from the native `AAssetDir *`. This gives ownership of the
|
||||
/// `AAssetDir *` to the `AssetDir`, which will handle closing the asset. Avoid using
|
||||
/// the pointer after calling this function.
|
||||
///
|
||||
/// # Safety
|
||||
/// By calling this function, you assert that it points to a valid native `AAssetDir`.
|
||||
pub unsafe fn from_ptr(ptr: NonNull<ffi::AAssetDir>) -> Self {
|
||||
Self { ptr }
|
||||
}
|
||||
|
||||
/// The corresponding native `AAssetDir *`
|
||||
pub fn ptr(&self) -> NonNull<ffi::AAssetDir> {
|
||||
self.ptr
|
||||
}
|
||||
|
||||
/// Get the next filename, if any, and process it. Like `.next()`, but performs no additional
|
||||
/// allocation.
|
||||
///
|
||||
/// The filenames are in the correct format to be passed to `AssetManager::open`
|
||||
pub fn with_next<T>(&mut self, f: impl for<'a> FnOnce(&'a CStr) -> T) -> Option<T> {
|
||||
unsafe {
|
||||
let next_name = ffi::AAssetDir_getNextFileName(self.ptr.as_ptr());
|
||||
if next_name.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(f(CStr::from_ptr(next_name)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset the iteration state
|
||||
pub fn rewind(&mut self) {
|
||||
unsafe {
|
||||
ffi::AAssetDir_rewind(self.ptr.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for AssetDir {
|
||||
type Item = CString;
|
||||
|
||||
fn next(&mut self) -> Option<CString> {
|
||||
self.with_next(|cstr| cstr.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
/// A native [`AAsset *`], opened in streaming mode
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use std::ffi::CString;
|
||||
/// # use ndk::asset::AssetManager;
|
||||
/// # let asset_manager: AssetManager = unimplemented!();
|
||||
/// use std::io::Read;
|
||||
///
|
||||
/// let asset = asset_manager
|
||||
/// .open(&CString::new("path/to/asset").unwrap())
|
||||
/// .expect("Could not open asset");
|
||||
///
|
||||
/// let mut data = vec![];
|
||||
/// asset.read_to_end(&mut data);
|
||||
/// // ... use data ...
|
||||
/// ```
|
||||
///
|
||||
/// [`AAsset *`]: https://developer.android.com/ndk/reference/group/asset#aasset
|
||||
#[derive(Debug)]
|
||||
pub struct Asset {
|
||||
ptr: NonNull<ffi::AAsset>,
|
||||
}
|
||||
|
||||
// AAsset is *not* thread safe.
|
||||
// See https://developer.android.com/ndk/reference/group/asset#aasset
|
||||
|
||||
impl Drop for Asset {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::AAsset_close(self.ptr.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Asset {
|
||||
/// Construct an `Asset` from the native `AAsset *`. This gives ownership of the `AAsset *` to
|
||||
/// the `Asset`, which will handle closing the asset. Avoid using the pointer after calling
|
||||
/// this function.
|
||||
///
|
||||
/// # Safety
|
||||
/// By calling this function, you assert that it points to a valid native `AAsset`, open
|
||||
/// in the streaming mode.
|
||||
pub unsafe fn from_ptr(ptr: NonNull<ffi::AAsset>) -> Self {
|
||||
Self { ptr }
|
||||
}
|
||||
|
||||
/// The corresponding native `AAsset *`
|
||||
pub fn ptr(&self) -> NonNull<ffi::AAsset> {
|
||||
self.ptr
|
||||
}
|
||||
|
||||
/// Returns the total length of the asset, in bytes
|
||||
pub fn get_length(&self) -> usize {
|
||||
unsafe { ffi::AAsset_getLength64(self.ptr.as_ptr()) as usize }
|
||||
}
|
||||
|
||||
/// Returns the remaining length of the asset, in bytes
|
||||
pub fn get_remaining_length(&self) -> usize {
|
||||
unsafe { ffi::AAsset_getRemainingLength64(self.ptr.as_ptr()) as usize }
|
||||
}
|
||||
|
||||
/// Reads all data into a buffer and returns it
|
||||
pub fn get_buffer(&mut self) -> io::Result<&[u8]> {
|
||||
unsafe {
|
||||
let buf_ptr = ffi::AAsset_getBuffer(self.ptr.as_ptr());
|
||||
if buf_ptr.is_null() {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Android Asset error creating buffer",
|
||||
))
|
||||
} else {
|
||||
Ok(std::slice::from_raw_parts(
|
||||
buf_ptr as *const u8,
|
||||
self.get_remaining_length(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//pub fn open_file_descriptor(&self) -> TODO
|
||||
}
|
||||
|
||||
impl io::Read for Asset {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let res = ffi::AAsset_read(
|
||||
self.ptr.as_ptr(),
|
||||
buf.as_mut_ptr() as *mut _,
|
||||
buf.len() as ffi::size_t,
|
||||
);
|
||||
if res >= 0 {
|
||||
Ok(res as usize)
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Android Asset read error",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Seek for Asset {
|
||||
fn seek(&mut self, seek: io::SeekFrom) -> io::Result<u64> {
|
||||
unsafe {
|
||||
let res = match seek {
|
||||
io::SeekFrom::Start(x) => {
|
||||
ffi::AAsset_seek64(self.ptr.as_ptr(), x as i64, ffi::SEEK_SET as i32)
|
||||
}
|
||||
io::SeekFrom::Current(x) => {
|
||||
ffi::AAsset_seek64(self.ptr.as_ptr(), x, ffi::SEEK_CUR as i32)
|
||||
}
|
||||
io::SeekFrom::End(x) => {
|
||||
ffi::AAsset_seek64(self.ptr.as_ptr(), x, ffi::SEEK_END as i32)
|
||||
}
|
||||
};
|
||||
if res < 0 {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Android Asset seek error",
|
||||
))
|
||||
} else {
|
||||
Ok(res as u64)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1367
third-party/vendor/ndk/src/audio.rs
vendored
Normal file
1367
third-party/vendor/ndk/src/audio.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
156
third-party/vendor/ndk/src/bitmap.rs
vendored
Normal file
156
third-party/vendor/ndk/src/bitmap.rs
vendored
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
//! Bindings for [`AndroidBitmap`] functions
|
||||
//!
|
||||
//! These functions operate directly on a JNI [`android.graphics.Bitmap`] instance.
|
||||
//!
|
||||
//! [`AndroidBitmap`]: https://developer.android.com/ndk/reference/group/bitmap
|
||||
//! [`android.graphics.Bitmap`]: https://developer.android.com/reference/android/graphics/Bitmap
|
||||
#![cfg(feature = "bitmap")]
|
||||
|
||||
use jni_sys::{jobject, JNIEnv};
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use std::{convert::TryInto, mem::MaybeUninit};
|
||||
|
||||
#[cfg(feature = "api-level-30")]
|
||||
use crate::hardware_buffer::HardwareBufferRef;
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum BitmapError {
|
||||
Unknown,
|
||||
AllocationFailed = ffi::ANDROID_BITMAP_RESULT_ALLOCATION_FAILED,
|
||||
BadParameter = ffi::ANDROID_BITMAP_RESULT_BAD_PARAMETER,
|
||||
JniException = ffi::ANDROID_BITMAP_RESULT_JNI_EXCEPTION,
|
||||
}
|
||||
|
||||
pub type BitmapResult<T, E = BitmapError> = std::result::Result<T, E>;
|
||||
|
||||
impl BitmapError {
|
||||
pub(crate) fn from_status(status: i32) -> BitmapResult<()> {
|
||||
Err(match status {
|
||||
ffi::ANDROID_BITMAP_RESULT_SUCCESS => return Ok(()),
|
||||
ffi::ANDROID_BITMAP_RESULT_ALLOCATION_FAILED => BitmapError::AllocationFailed,
|
||||
ffi::ANDROID_BITMAP_RESULT_BAD_PARAMETER => BitmapError::BadParameter,
|
||||
ffi::ANDROID_BITMAP_RESULT_JNI_EXCEPTION => BitmapError::JniException,
|
||||
_ => BitmapError::Unknown,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn construct<T>(with_ptr: impl FnOnce(*mut T) -> i32) -> BitmapResult<T> {
|
||||
let mut result = MaybeUninit::uninit();
|
||||
let status = with_ptr(result.as_mut_ptr());
|
||||
BitmapError::from_status(status).map(|()| unsafe { result.assume_init() })
|
||||
}
|
||||
|
||||
// IntoPrimitive, TryFromPrimitive use the deprecated `RGBA_4444` member below,
|
||||
// resulting in deprecation warnings in generated code beyond the `enum`. These
|
||||
// can only be disabled at the module level, and such warnings seem to be gone on
|
||||
// at least the Rust 1.56 nightlies.
|
||||
#[allow(deprecated)]
|
||||
mod temp_allow_deprecated {
|
||||
use super::*;
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, IntoPrimitive, TryFromPrimitive)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum BitmapFormat {
|
||||
NONE = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_NONE.0,
|
||||
RGBA_8888 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_RGBA_8888.0,
|
||||
RGB_565 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_RGB_565.0,
|
||||
#[deprecated = "Deprecated in API level 13. Because of the poor quality of this configuration, it is advised to use ARGB_8888 instead."]
|
||||
RGBA_4444 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_RGBA_4444.0,
|
||||
A_8 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_A_8.0,
|
||||
RGBA_F16 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_RGBA_F16.0,
|
||||
}
|
||||
}
|
||||
pub use temp_allow_deprecated::*;
|
||||
|
||||
/// An immediate wrapper over [`android.graphics.Bitmap`]
|
||||
///
|
||||
/// [`android.graphics.Bitmap`]: https://developer.android.com/reference/android/graphics/Bitmap
|
||||
#[derive(Debug)]
|
||||
pub struct AndroidBitmap {
|
||||
env: *mut JNIEnv,
|
||||
inner: jobject,
|
||||
}
|
||||
|
||||
impl AndroidBitmap {
|
||||
/// Create an [`AndroidBitmap`] wrapper from JNI pointers
|
||||
///
|
||||
/// # Safety
|
||||
/// This function should be called with a healthy JVM pointer and with a non-null
|
||||
/// [`android.graphics.Bitmap`], which must be kept alive on the Java/Kotlin side.
|
||||
///
|
||||
/// [`android.graphics.Bitmap`]: https://developer.android.com/reference/android/graphics/Bitmap
|
||||
pub unsafe fn from_jni(env: *mut JNIEnv, bitmap: jobject) -> Self {
|
||||
Self { env, inner: bitmap }
|
||||
}
|
||||
|
||||
pub fn get_info(&self) -> BitmapResult<AndroidBitmapInfo> {
|
||||
let inner =
|
||||
construct(|res| unsafe { ffi::AndroidBitmap_getInfo(self.env, self.inner, res) })?;
|
||||
|
||||
Ok(AndroidBitmapInfo { inner })
|
||||
}
|
||||
|
||||
pub fn lock_pixels(&self) -> BitmapResult<*mut std::os::raw::c_void> {
|
||||
construct(|res| unsafe { ffi::AndroidBitmap_lockPixels(self.env, self.inner, res) })
|
||||
}
|
||||
|
||||
pub fn unlock_pixels(&self) -> BitmapResult<()> {
|
||||
let status = unsafe { ffi::AndroidBitmap_unlockPixels(self.env, self.inner) };
|
||||
BitmapError::from_status(status)
|
||||
}
|
||||
|
||||
/// Retrieve the native object associated with a `HARDWARE` [`AndroidBitmap`].
|
||||
///
|
||||
/// Client must not modify it while an [`AndroidBitmap`] is wrapping it.
|
||||
#[cfg(feature = "api-level-30")]
|
||||
pub fn get_hardware_buffer(&self) -> BitmapResult<HardwareBufferRef> {
|
||||
unsafe {
|
||||
let result =
|
||||
construct(|res| ffi::AndroidBitmap_getHardwareBuffer(self.env, self.inner, res))?;
|
||||
let non_null = if cfg!(debug_assertions) {
|
||||
std::ptr::NonNull::new(result).expect("result should never be null")
|
||||
} else {
|
||||
std::ptr::NonNull::new_unchecked(result)
|
||||
};
|
||||
Ok(HardwareBufferRef::from_ptr(non_null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A native [`AndroidBitmapInfo`]
|
||||
///
|
||||
/// [`AndroidBitmapInfo`]: https://developer.android.com/ndk/reference/struct/android-bitmap-info#struct_android_bitmap_info
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct AndroidBitmapInfo {
|
||||
inner: ffi::AndroidBitmapInfo,
|
||||
}
|
||||
|
||||
// TODO: flesh out when API 30 is released
|
||||
#[cfg(feature = "api-level-30")]
|
||||
pub type AndroidBitmapInfoFlags = u32;
|
||||
|
||||
impl AndroidBitmapInfo {
|
||||
pub fn width(&self) -> u32 {
|
||||
self.inner.width
|
||||
}
|
||||
|
||||
pub fn height(&self) -> u32 {
|
||||
self.inner.height
|
||||
}
|
||||
|
||||
pub fn stride(&self) -> u32 {
|
||||
self.inner.stride
|
||||
}
|
||||
|
||||
pub fn format(&self) -> BitmapFormat {
|
||||
let format = self.inner.format as u32;
|
||||
format.try_into().unwrap()
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-30")]
|
||||
pub fn flags(&self) -> AndroidBitmapInfoFlags {
|
||||
self.inner.flags
|
||||
}
|
||||
}
|
||||
573
third-party/vendor/ndk/src/configuration.rs
vendored
Normal file
573
third-party/vendor/ndk/src/configuration.rs
vendored
Normal file
|
|
@ -0,0 +1,573 @@
|
|||
//! Bindings for [`AConfiguration`]
|
||||
//!
|
||||
//! See also the [NDK docs](https://developer.android.com/ndk/reference/group/configuration) for
|
||||
//! [`AConfiguration`], as well as the [docs for providing
|
||||
//! resources](https://developer.android.com/guide/topics/resources/providing-resources.html),
|
||||
//! which explain many of the configuration values. The [`android.content.res.Configuration`
|
||||
//! javadoc](https://developer.android.com/reference/android/content/res/Configuration.html) may
|
||||
//! also have useful information.
|
||||
//!
|
||||
//! [`AConfiguration`]: https://developer.android.com/ndk/reference/group/configuration#aconfiguration
|
||||
|
||||
use crate::asset::AssetManager;
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use std::convert::TryInto;
|
||||
use std::fmt;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
/// A native [`AConfiguration *`]
|
||||
///
|
||||
/// [`Configuration`] is an opaque type used to get and set various subsystem configurations.
|
||||
///
|
||||
/// [`AConfiguration *`]: https://developer.android.com/ndk/reference/group/configuration#aconfiguration
|
||||
pub struct Configuration {
|
||||
ptr: NonNull<ffi::AConfiguration>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Configuration {}
|
||||
unsafe impl Sync for Configuration {}
|
||||
|
||||
impl Drop for Configuration {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::AConfiguration_delete(self.ptr.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Configuration {
|
||||
fn clone(&self) -> Self {
|
||||
let mut new = Self::new();
|
||||
new.copy(self);
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Configuration {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.diff(other).0 == 0
|
||||
}
|
||||
}
|
||||
impl Eq for Configuration {}
|
||||
|
||||
impl fmt::Debug for Configuration {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Configuration")
|
||||
.field("mcc", &self.mcc())
|
||||
.field("mnc", &self.mnc())
|
||||
.field("lang", &self.language())
|
||||
.field("country", &self.country())
|
||||
.field("orientation", &self.orientation())
|
||||
.field("touchscreen", &self.touchscreen())
|
||||
.field("density", &self.density())
|
||||
.field("keyboard", &self.keyboard())
|
||||
.field("navigation", &self.navigation())
|
||||
.field("keys_hidden", &self.keys_hidden())
|
||||
.field("nav_hidden", &self.nav_hidden())
|
||||
.field("sdk_version", &self.sdk_version())
|
||||
.field("screen_size", &self.screen_size())
|
||||
.field("screen_long", &self.screen_long())
|
||||
.field("ui_mode_type", &self.ui_mode_type())
|
||||
.field("ui_mode_night", &self.ui_mode_night())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Configuration {
|
||||
/// Construct a `Configuration` from a pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// By calling this function, you assert that it is a valid pointer to a native
|
||||
/// `AConfiguration`, and give ownership of it to the `Configuration` instance.
|
||||
pub unsafe fn from_ptr(ptr: NonNull<ffi::AConfiguration>) -> Self {
|
||||
Self { ptr }
|
||||
}
|
||||
|
||||
/// Create a new `Configuration`, with the same contents as the `AConfiguration` referenced by
|
||||
/// the pointer.
|
||||
///
|
||||
/// This is useful if you have a pointer, but not ownership of it.
|
||||
///
|
||||
/// # Safety
|
||||
/// By calling this function, you assert that it is a valid pointer to a native
|
||||
/// `AConfiguration`.
|
||||
pub unsafe fn clone_from_ptr(ptr: NonNull<ffi::AConfiguration>) -> Self {
|
||||
let conf = Self::new();
|
||||
ffi::AConfiguration_copy(conf.ptr.as_ptr(), ptr.as_ptr());
|
||||
conf
|
||||
}
|
||||
|
||||
/// The pointer to the native `AConfiguration`. Keep in mind that the `Configuration` object
|
||||
/// still has ownership, and will free it when dropped.
|
||||
pub fn ptr(&self) -> NonNull<ffi::AConfiguration> {
|
||||
self.ptr
|
||||
}
|
||||
|
||||
pub fn from_asset_manager(am: &AssetManager) -> Self {
|
||||
let config = Self::new();
|
||||
unsafe {
|
||||
ffi::AConfiguration_fromAssetManager(config.ptr().as_mut(), am.ptr().as_mut());
|
||||
}
|
||||
config
|
||||
}
|
||||
|
||||
/// Create a new `Configuration`, with none of the values set.
|
||||
pub fn new() -> Self {
|
||||
unsafe {
|
||||
Self {
|
||||
ptr: NonNull::new(ffi::AConfiguration_new()).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `dest.copy(&src)` copies the contents of `src` to `dest`
|
||||
pub fn copy(&mut self, other: &Self) {
|
||||
unsafe { ffi::AConfiguration_copy(self.ptr.as_ptr(), other.ptr.as_ptr()) }
|
||||
}
|
||||
|
||||
/// Information about what fields differ between the two configurations
|
||||
pub fn diff(&self, other: &Self) -> DiffResult {
|
||||
unsafe {
|
||||
DiffResult(ffi::AConfiguration_diff(self.ptr.as_ptr(), other.ptr.as_ptr()) as u32)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns false if anything in `self` conflicts with `requested`
|
||||
pub fn matches(&self, requested: &Self) -> bool {
|
||||
unsafe { ffi::AConfiguration_match(self.ptr.as_ptr(), requested.ptr.as_ptr()) != 0 }
|
||||
}
|
||||
|
||||
/// Returns the country code, as a [`String`] of two characters, if set
|
||||
pub fn country(&self) -> Option<String> {
|
||||
let mut chars = [0u8; 2];
|
||||
unsafe {
|
||||
ffi::AConfiguration_getCountry(self.ptr.as_ptr(), chars.as_mut_ptr().cast());
|
||||
}
|
||||
if chars[0] == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(std::str::from_utf8(chars.as_slice()).unwrap().to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the screen density in dpi.
|
||||
///
|
||||
/// On some devices it can return values outside of the density enum.
|
||||
pub fn density(&self) -> Option<u32> {
|
||||
let density = unsafe { ffi::AConfiguration_getDensity(self.ptr.as_ptr()) as u32 };
|
||||
match density {
|
||||
ffi::ACONFIGURATION_DENSITY_DEFAULT => Some(160),
|
||||
ffi::ACONFIGURATION_DENSITY_ANY => None,
|
||||
ffi::ACONFIGURATION_DENSITY_NONE => None,
|
||||
density => Some(density),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the keyboard type.
|
||||
pub fn keyboard(&self) -> Keyboard {
|
||||
unsafe {
|
||||
(ffi::AConfiguration_getKeyboard(self.ptr.as_ptr()) as u32)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns keyboard visibility/availability.
|
||||
pub fn keys_hidden(&self) -> KeysHidden {
|
||||
unsafe {
|
||||
(ffi::AConfiguration_getKeysHidden(self.ptr.as_ptr()) as u32)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the language, as a [`String`] of two characters, if set
|
||||
pub fn language(&self) -> Option<String> {
|
||||
let mut chars = [0u8; 2];
|
||||
unsafe {
|
||||
ffi::AConfiguration_getLanguage(self.ptr.as_ptr(), chars.as_mut_ptr().cast());
|
||||
}
|
||||
if chars[0] == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(std::str::from_utf8(chars.as_slice()).unwrap().to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the layout direction
|
||||
pub fn layout_direction(&self) -> LayoutDir {
|
||||
unsafe {
|
||||
(ffi::AConfiguration_getLayoutDirection(self.ptr.as_ptr()) as u32)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the mobile country code.
|
||||
pub fn mcc(&self) -> i32 {
|
||||
unsafe { ffi::AConfiguration_getMcc(self.ptr.as_ptr()) }
|
||||
}
|
||||
|
||||
/// Returns the mobile network code, if one is defined
|
||||
pub fn mnc(&self) -> Option<i32> {
|
||||
unsafe {
|
||||
match ffi::AConfiguration_getMnc(self.ptr.as_ptr()) {
|
||||
0 => None,
|
||||
x if x == ffi::ACONFIGURATION_MNC_ZERO as i32 => Some(0),
|
||||
x => Some(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nav_hidden(&self) -> NavHidden {
|
||||
unsafe {
|
||||
(ffi::AConfiguration_getNavHidden(self.ptr.as_ptr()) as u32)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn navigation(&self) -> Navigation {
|
||||
unsafe {
|
||||
(ffi::AConfiguration_getNavigation(self.ptr.as_ptr()) as u32)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn orientation(&self) -> Orientation {
|
||||
unsafe {
|
||||
(ffi::AConfiguration_getOrientation(self.ptr.as_ptr()) as u32)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn screen_height_dp(&self) -> Option<i32> {
|
||||
unsafe {
|
||||
let height = ffi::AConfiguration_getScreenHeightDp(self.ptr.as_ptr());
|
||||
if height == ffi::ACONFIGURATION_SCREEN_HEIGHT_DP_ANY as i32 {
|
||||
None
|
||||
} else {
|
||||
Some(height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn screen_width_dp(&self) -> Option<i32> {
|
||||
unsafe {
|
||||
let width = ffi::AConfiguration_getScreenWidthDp(self.ptr.as_ptr());
|
||||
if width == ffi::ACONFIGURATION_SCREEN_WIDTH_DP_ANY as i32 {
|
||||
None
|
||||
} else {
|
||||
Some(width)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn screen_long(&self) -> ScreenLong {
|
||||
unsafe {
|
||||
(ffi::AConfiguration_getScreenLong(self.ptr.as_ptr()) as u32)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-30")]
|
||||
pub fn screen_round(&self) -> ScreenRound {
|
||||
unsafe {
|
||||
(ffi::AConfiguration_getScreenRound(self.ptr.as_ptr()) as u32)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn screen_size(&self) -> ScreenSize {
|
||||
unsafe {
|
||||
(ffi::AConfiguration_getScreenSize(self.ptr.as_ptr()) as u32)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sdk_version(&self) -> i32 {
|
||||
unsafe { ffi::AConfiguration_getSdkVersion(self.ptr.as_ptr()) }
|
||||
}
|
||||
|
||||
pub fn smallest_screen_width_dp(&self) -> Option<i32> {
|
||||
unsafe {
|
||||
let width = ffi::AConfiguration_getSmallestScreenWidthDp(self.ptr.as_ptr());
|
||||
if width == ffi::ACONFIGURATION_SMALLEST_SCREEN_WIDTH_DP_ANY as i32 {
|
||||
None
|
||||
} else {
|
||||
Some(width)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn touchscreen(&self) -> Touchscreen {
|
||||
unsafe {
|
||||
(ffi::AConfiguration_getTouchscreen(self.ptr.as_ptr()) as u32)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ui_mode_night(&self) -> UiModeNight {
|
||||
unsafe {
|
||||
(ffi::AConfiguration_getUiModeNight(self.ptr.as_ptr()) as u32)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ui_mode_type(&self) -> UiModeType {
|
||||
unsafe {
|
||||
(ffi::AConfiguration_getUiModeType(self.ptr.as_ptr()) as u32)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A bitfield representing the differences between two [`Configuration`]s
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct DiffResult(pub u32);
|
||||
|
||||
impl DiffResult {
|
||||
pub fn mcc(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_MCC != 0
|
||||
}
|
||||
pub fn mnc(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_MNC != 0
|
||||
}
|
||||
pub fn locale(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_LOCALE != 0
|
||||
}
|
||||
pub fn touchscreen(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_TOUCHSCREEN != 0
|
||||
}
|
||||
pub fn keyboard(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_KEYBOARD != 0
|
||||
}
|
||||
pub fn keyboard_hidden(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_KEYBOARD_HIDDEN != 0
|
||||
}
|
||||
pub fn navigation(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_NAVIGATION != 0
|
||||
}
|
||||
pub fn orientation(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_ORIENTATION != 0
|
||||
}
|
||||
pub fn density(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_DENSITY != 0
|
||||
}
|
||||
pub fn screen_size(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_SCREEN_SIZE != 0
|
||||
}
|
||||
pub fn version(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_VERSION != 0
|
||||
}
|
||||
pub fn screen_layout(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_SCREEN_LAYOUT != 0
|
||||
}
|
||||
pub fn ui_mode(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_UI_MODE != 0
|
||||
}
|
||||
pub fn smallest_screen_size(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_SMALLEST_SCREEN_SIZE != 0
|
||||
}
|
||||
pub fn layout_dir(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_LAYOUTDIR != 0
|
||||
}
|
||||
pub fn screen_round(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_SCREEN_ROUND != 0
|
||||
}
|
||||
pub fn color_mode(self) -> bool {
|
||||
self.0 & ffi::ACONFIGURATION_COLOR_MODE != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum Orientation {
|
||||
Any = ffi::ACONFIGURATION_ORIENTATION_ANY,
|
||||
Port = ffi::ACONFIGURATION_ORIENTATION_PORT,
|
||||
Land = ffi::ACONFIGURATION_ORIENTATION_LAND,
|
||||
Square = ffi::ACONFIGURATION_ORIENTATION_SQUARE,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum Touchscreen {
|
||||
Any = ffi::ACONFIGURATION_TOUCHSCREEN_ANY,
|
||||
NoTouch = ffi::ACONFIGURATION_TOUCHSCREEN_NOTOUCH,
|
||||
Stylus = ffi::ACONFIGURATION_TOUCHSCREEN_STYLUS,
|
||||
Finger = ffi::ACONFIGURATION_TOUCHSCREEN_FINGER,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum Density {
|
||||
Default = ffi::ACONFIGURATION_DENSITY_DEFAULT,
|
||||
Low = ffi::ACONFIGURATION_DENSITY_LOW,
|
||||
Medium = ffi::ACONFIGURATION_DENSITY_MEDIUM,
|
||||
TV = ffi::ACONFIGURATION_DENSITY_TV,
|
||||
High = ffi::ACONFIGURATION_DENSITY_HIGH,
|
||||
XHigh = ffi::ACONFIGURATION_DENSITY_XHIGH,
|
||||
XXHigh = ffi::ACONFIGURATION_DENSITY_XXHIGH,
|
||||
XXXHigh = ffi::ACONFIGURATION_DENSITY_XXXHIGH,
|
||||
Any = ffi::ACONFIGURATION_DENSITY_ANY,
|
||||
None = ffi::ACONFIGURATION_DENSITY_NONE,
|
||||
}
|
||||
|
||||
impl Density {
|
||||
/// The DPI associated with the density class.
|
||||
/// See [the Android screen density
|
||||
/// docs](https://developer.android.com/training/multiscreen/screendensities#TaskProvideAltBmp)
|
||||
///
|
||||
/// There are some `Density` values that have no associated DPI; these values return `None`.
|
||||
pub fn dpi(self) -> Option<u32> {
|
||||
match self {
|
||||
Self::Default => Some(160), // Or should it be None?
|
||||
Self::Low => Some(120),
|
||||
Self::Medium => Some(160),
|
||||
Self::High => Some(240),
|
||||
Self::XHigh => Some(320),
|
||||
Self::XXHigh => Some(480),
|
||||
Self::XXXHigh => Some(640),
|
||||
Self::TV => Some(213),
|
||||
Self::Any => None,
|
||||
Self::None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The Hi-DPI factor associated with the density class. This is the factor by which an
|
||||
/// image/resource should be scaled to match its size across devices. The baseline is a 160dpi
|
||||
/// screen (i.e., Hi-DPI factor = DPI / 160).
|
||||
/// See [the Android screen density
|
||||
/// docs](https://developer.android.com/training/multiscreen/screendensities#TaskProvideAltBmp)
|
||||
///
|
||||
/// There are some `Density` values that have no associated DPI; these values return `None`.
|
||||
pub fn approx_hidpi_factor(self) -> Option<f64> {
|
||||
match self {
|
||||
Self::Default => Some(1.), // Or should it be None?
|
||||
Self::Low => Some(0.75),
|
||||
Self::Medium => Some(1.),
|
||||
Self::High => Some(1.5),
|
||||
Self::XHigh => Some(2.),
|
||||
Self::XXHigh => Some(3.),
|
||||
Self::XXXHigh => Some(4.),
|
||||
Self::TV => Some(4. / 3.),
|
||||
Self::Any => None,
|
||||
Self::None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum Keyboard {
|
||||
Any = ffi::ACONFIGURATION_KEYBOARD_ANY,
|
||||
NoKeys = ffi::ACONFIGURATION_KEYBOARD_NOKEYS,
|
||||
Qwerty = ffi::ACONFIGURATION_KEYBOARD_QWERTY,
|
||||
TwelveKey = ffi::ACONFIGURATION_KEYBOARD_12KEY,
|
||||
}
|
||||
|
||||
// FIXME is it a bitmask?
|
||||
// FIXME are they all bitmasks?
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum Navigation {
|
||||
Any = ffi::ACONFIGURATION_NAVIGATION_ANY,
|
||||
NoNav = ffi::ACONFIGURATION_NAVIGATION_NONAV,
|
||||
DPad = ffi::ACONFIGURATION_NAVIGATION_DPAD,
|
||||
Trackball = ffi::ACONFIGURATION_NAVIGATION_TRACKBALL,
|
||||
Wheel = ffi::ACONFIGURATION_NAVIGATION_WHEEL,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum KeysHidden {
|
||||
Any = ffi::ACONFIGURATION_KEYSHIDDEN_ANY,
|
||||
No = ffi::ACONFIGURATION_KEYSHIDDEN_NO,
|
||||
Yes = ffi::ACONFIGURATION_KEYSHIDDEN_YES,
|
||||
Soft = ffi::ACONFIGURATION_KEYSHIDDEN_SOFT,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum NavHidden {
|
||||
Any = ffi::ACONFIGURATION_NAVHIDDEN_ANY,
|
||||
No = ffi::ACONFIGURATION_NAVHIDDEN_NO,
|
||||
Yes = ffi::ACONFIGURATION_NAVHIDDEN_YES,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum ScreenSize {
|
||||
Any = ffi::ACONFIGURATION_SCREENSIZE_ANY,
|
||||
Small = ffi::ACONFIGURATION_SCREENSIZE_SMALL,
|
||||
Normal = ffi::ACONFIGURATION_SCREENSIZE_NORMAL,
|
||||
Large = ffi::ACONFIGURATION_SCREENSIZE_LARGE,
|
||||
XLarge = ffi::ACONFIGURATION_SCREENSIZE_XLARGE,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum ScreenLong {
|
||||
Any = ffi::ACONFIGURATION_SCREENLONG_ANY,
|
||||
No = ffi::ACONFIGURATION_SCREENLONG_NO,
|
||||
Yes = ffi::ACONFIGURATION_SCREENLONG_YES,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum ScreenRound {
|
||||
Any = ffi::ACONFIGURATION_SCREENROUND_ANY,
|
||||
No = ffi::ACONFIGURATION_SCREENROUND_NO,
|
||||
Yes = ffi::ACONFIGURATION_SCREENROUND_YES,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum WideColorGamut {
|
||||
Any = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_ANY,
|
||||
No = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_NO,
|
||||
Yes = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_YES,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum HDR {
|
||||
Any = ffi::ACONFIGURATION_HDR_ANY,
|
||||
No = ffi::ACONFIGURATION_HDR_NO,
|
||||
Yes = ffi::ACONFIGURATION_HDR_YES,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum LayoutDir {
|
||||
Any = ffi::ACONFIGURATION_LAYOUTDIR_ANY,
|
||||
Ltr = ffi::ACONFIGURATION_LAYOUTDIR_LTR,
|
||||
Rtl = ffi::ACONFIGURATION_LAYOUTDIR_RTL,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum UiModeType {
|
||||
Any = ffi::ACONFIGURATION_UI_MODE_TYPE_ANY,
|
||||
Normal = ffi::ACONFIGURATION_UI_MODE_TYPE_NORMAL,
|
||||
Desk = ffi::ACONFIGURATION_UI_MODE_TYPE_DESK,
|
||||
Car = ffi::ACONFIGURATION_UI_MODE_TYPE_CAR,
|
||||
Television = ffi::ACONFIGURATION_UI_MODE_TYPE_TELEVISION,
|
||||
Applicance = ffi::ACONFIGURATION_UI_MODE_TYPE_APPLIANCE,
|
||||
Watch = ffi::ACONFIGURATION_UI_MODE_TYPE_WATCH,
|
||||
VrHeadset = ffi::ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum UiModeNight {
|
||||
Any = ffi::ACONFIGURATION_UI_MODE_NIGHT_ANY,
|
||||
No = ffi::ACONFIGURATION_UI_MODE_NIGHT_NO,
|
||||
Yes = ffi::ACONFIGURATION_UI_MODE_NIGHT_YES,
|
||||
}
|
||||
1492
third-party/vendor/ndk/src/event.rs
vendored
Normal file
1492
third-party/vendor/ndk/src/event.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
524
third-party/vendor/ndk/src/hardware_buffer.rs
vendored
Normal file
524
third-party/vendor/ndk/src/hardware_buffer.rs
vendored
Normal file
|
|
@ -0,0 +1,524 @@
|
|||
//! Bindings for [`AHardwareBuffer`]
|
||||
//!
|
||||
//! [`AHardwareBuffer`]: https://developer.android.com/ndk/reference/group/a-hardware-buffer#ahardwarebuffer
|
||||
|
||||
#![cfg(feature = "api-level-26")]
|
||||
|
||||
use crate::utils::status_to_io_result;
|
||||
|
||||
pub use super::hardware_buffer_format::HardwareBufferFormat;
|
||||
use jni_sys::{jobject, JNIEnv};
|
||||
use std::{
|
||||
convert::TryInto, io::Result, mem::MaybeUninit, ops::Deref, os::raw::c_void,
|
||||
os::unix::io::RawFd, ptr::NonNull,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct HardwareBufferUsage(pub ffi::AHardwareBuffer_UsageFlags);
|
||||
|
||||
impl HardwareBufferUsage {
|
||||
pub const CPU_READ_NEVER: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_NEVER);
|
||||
pub const CPU_READ_RARELY: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_RARELY);
|
||||
pub const CPU_READ_OFTEN: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
|
||||
pub const CPU_READ_MASK: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_MASK);
|
||||
|
||||
pub const CPU_WRITE_NEVER: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER);
|
||||
pub const CPU_WRITE_RARELY: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY);
|
||||
pub const CPU_WRITE_OFTEN: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN);
|
||||
pub const CPU_WRITE_MASK: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK);
|
||||
|
||||
pub const GPU_SAMPLED_IMAGE: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE);
|
||||
pub const GPU_FRAMEBUFFER: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER);
|
||||
pub const COMPOSER_OVERLAY: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY);
|
||||
pub const PROTECTED_CONTENT: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
|
||||
pub const VIDEO_ENCODE: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VIDEO_ENCODE);
|
||||
pub const SENSOR_DIRECT_DATA: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA);
|
||||
pub const GPU_DATA_BUFFER: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER);
|
||||
pub const GPU_CUBE_MAP: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP);
|
||||
pub const GPU_MIPMAP_COMPLETE: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE);
|
||||
|
||||
pub const VENDOR_0: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_0);
|
||||
pub const VENDOR_1: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_1);
|
||||
pub const VENDOR_2: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_2);
|
||||
pub const VENDOR_3: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_3);
|
||||
pub const VENDOR_4: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_4);
|
||||
pub const VENDOR_5: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_5);
|
||||
pub const VENDOR_6: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_6);
|
||||
pub const VENDOR_7: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_7);
|
||||
pub const VENDOR_8: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_8);
|
||||
pub const VENDOR_9: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_9);
|
||||
pub const VENDOR_10: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_10);
|
||||
pub const VENDOR_11: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_11);
|
||||
pub const VENDOR_12: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_12);
|
||||
pub const VENDOR_13: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_13);
|
||||
pub const VENDOR_14: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_14);
|
||||
pub const VENDOR_15: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_15);
|
||||
pub const VENDOR_16: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_16);
|
||||
pub const VENDOR_17: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_17);
|
||||
pub const VENDOR_18: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_18);
|
||||
pub const VENDOR_19: Self =
|
||||
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_19);
|
||||
}
|
||||
|
||||
pub type Rect = ffi::ARect;
|
||||
|
||||
fn construct<T>(with_ptr: impl FnOnce(*mut T) -> i32) -> Result<T> {
|
||||
let mut result = MaybeUninit::uninit();
|
||||
let status = with_ptr(result.as_mut_ptr());
|
||||
status_to_io_result(status, unsafe { result.assume_init() })
|
||||
}
|
||||
|
||||
/// A native [`AHardwareBuffer *`]
|
||||
///
|
||||
/// [`HardwareBuffer`] objects represent chunks of memory that can be accessed by various hardware
|
||||
/// components in the system.
|
||||
///
|
||||
/// It can be easily converted to the Java counterpart [`android.hardware.HardwareBuffer`] and
|
||||
/// passed between processes using Binder. All operations involving [`HardwareBuffer`] and
|
||||
/// [`android.hardware.HardwareBuffer`] are zero-copy, i.e., passing [`HardwareBuffer`] to another
|
||||
/// process creates a shared view of the same region of memory.
|
||||
///
|
||||
/// [`HardwareBuffer`] can be bound to EGL/OpenGL and Vulkan primitives. For EGL, use the extension
|
||||
/// function [`eglGetNativeClientBufferANDROID`] to obtain an `EGLClientBuffer` and pass it
|
||||
/// directly to [`eglCreateImageKHR`]. Refer to the EGL extensions
|
||||
/// [`EGL_ANDROID_get_native_client_buffer`] and [`EGL_ANDROID_image_native_buffer`] for more
|
||||
/// information. In Vulkan, the contents of the [`HardwareBuffer`] can be accessed as [external
|
||||
/// memory]. See the [`VK_ANDROID_external_memory_android_hardware_buffer`] extension for details.
|
||||
///
|
||||
/// [`AHardwareBuffer *`]: https://developer.android.com/ndk/reference/group/a-hardware-buffer#ahardwarebuffer
|
||||
/// [`android.hardware.HardwareBuffer`]: https://developer.android.com/reference/android/hardware/HardwareBuffer
|
||||
/// [`eglGetNativeClientBufferANDROID`]: https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_get_native_client_buffer.txt
|
||||
/// [`eglCreateImageKHR`]: https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_image_base.txt
|
||||
/// [`EGL_ANDROID_get_native_client_buffer`]: https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_get_native_client_buffer.txt
|
||||
/// [`EGL_ANDROID_image_native_buffer`]: https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_image_native_buffer.txt
|
||||
/// [external memory]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_KHR_external_memory.html
|
||||
/// [`VK_ANDROID_external_memory_android_hardware_buffer`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_ANDROID_external_memory_android_hardware_buffer.html
|
||||
#[derive(Debug)]
|
||||
pub struct HardwareBuffer {
|
||||
inner: NonNull<ffi::AHardwareBuffer>,
|
||||
}
|
||||
|
||||
impl HardwareBuffer {
|
||||
/// Create an _unowned_ [`HardwareBuffer`] from a native pointer
|
||||
///
|
||||
/// To wrap a strong reference (that is `release`d on [`Drop`]), call
|
||||
/// [`HardwareBufferRef::from_ptr()`] instead.
|
||||
///
|
||||
/// # Safety
|
||||
/// By calling this function, you assert that it is a valid pointer to an NDK
|
||||
/// [`ffi::AHardwareBuffer`] that is kept alive externally, or retrieve a strong reference
|
||||
/// using [`HardwareBuffer::acquire()`].
|
||||
pub unsafe fn from_ptr(ptr: NonNull<ffi::AHardwareBuffer>) -> Self {
|
||||
Self { inner: ptr }
|
||||
}
|
||||
|
||||
/// Returns the underlying [`ffi::AHardwareBuffer`] pointer
|
||||
///
|
||||
/// See the top-level [`HardwareBuffer`] struct documentation for (graphics) APIs that accept
|
||||
/// this pointer.
|
||||
pub fn as_ptr(&self) -> *mut ffi::AHardwareBuffer {
|
||||
self.inner.as_ptr()
|
||||
}
|
||||
|
||||
/// Allocates a buffer that matches the passed [`HardwareBufferDesc`].
|
||||
///
|
||||
/// If allocation succeeds, the buffer can be used according to the usage flags specified in
|
||||
/// its description. If a buffer is used in ways not compatible with its usage flags, the
|
||||
/// results are undefined and may include program termination.
|
||||
pub fn allocate(desc: HardwareBufferDesc) -> Result<HardwareBufferRef> {
|
||||
unsafe {
|
||||
let ptr = construct(|res| ffi::AHardwareBuffer_allocate(&desc.into_native(), res))?;
|
||||
|
||||
Ok(HardwareBufferRef::from_ptr(NonNull::new_unchecked(ptr)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a [`HardwareBuffer`] from JNI pointers
|
||||
///
|
||||
/// # Safety
|
||||
/// By calling this function, you assert that these are valid pointers to JNI objects.
|
||||
///
|
||||
/// This method does not acquire any additional reference to the AHardwareBuffer that is
|
||||
/// returned. To keep the [`HardwareBuffer`] alive after the [Java `HardwareBuffer`] object
|
||||
/// is closed, explicitly or by the garbage collector, be sure to retrieve a strong reference
|
||||
/// using [`HardwareBuffer::acquire()`].
|
||||
///
|
||||
/// [Java `HardwareBuffer`]: https://developer.android.com/reference/android/hardware/HardwareBuffer
|
||||
pub unsafe fn from_jni(env: *mut JNIEnv, hardware_buffer: jobject) -> Self {
|
||||
let ptr = ffi::AHardwareBuffer_fromHardwareBuffer(env, hardware_buffer);
|
||||
|
||||
Self::from_ptr(NonNull::new_unchecked(ptr))
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// By calling this function, you assert that `env` is a valid pointer to a [`JNIEnv`].
|
||||
pub unsafe fn to_jni(&self, env: *mut JNIEnv) -> jobject {
|
||||
ffi::AHardwareBuffer_toHardwareBuffer(env, self.as_ptr())
|
||||
}
|
||||
|
||||
/// Return a description of the [`HardwareBuffer`] in the passed [`HardwareBufferDesc`] struct.
|
||||
pub fn describe(&self) -> HardwareBufferDesc {
|
||||
let desc = unsafe {
|
||||
let mut result = MaybeUninit::uninit();
|
||||
ffi::AHardwareBuffer_describe(self.as_ptr(), result.as_mut_ptr());
|
||||
result.assume_init()
|
||||
};
|
||||
|
||||
HardwareBufferDesc {
|
||||
width: desc.width,
|
||||
height: desc.height,
|
||||
layers: desc.layers,
|
||||
format: desc.format.try_into().unwrap(),
|
||||
usage: HardwareBufferUsage(ffi::AHardwareBuffer_UsageFlags(desc.usage)),
|
||||
stride: desc.stride,
|
||||
}
|
||||
}
|
||||
|
||||
/// Test whether the given format and usage flag combination is allocatable.
|
||||
///
|
||||
/// If this function returns [`true`], it means that a buffer with the given description can
|
||||
/// be allocated on this implementation, unless resource exhaustion occurs. If this function
|
||||
/// returns [`false`], it means that the allocation of the given description will never
|
||||
/// succeed.
|
||||
///
|
||||
/// The return value of this function may depend on all fields in the description, except
|
||||
/// [`HardwareBufferDesc::stride`], which is always ignored. For example, some implementations
|
||||
/// have implementation-defined limits on texture size and layer count.
|
||||
#[cfg(feature = "api-level-29")]
|
||||
pub fn is_supported(desc: HardwareBufferDesc) -> bool {
|
||||
let res = unsafe { ffi::AHardwareBuffer_isSupported(&desc.into_native()) };
|
||||
res == 1
|
||||
}
|
||||
|
||||
/// Lock the [`HardwareBuffer`] for direct CPU access.
|
||||
///
|
||||
/// This function can lock the buffer for either reading or writing. It may block if the
|
||||
/// hardware needs to finish rendering, if CPU caches need to be synchronized, or possibly for
|
||||
/// other implementation-specific reasons.
|
||||
///
|
||||
/// The [`HardwareBuffer`] must have one layer, otherwise the call will fail.
|
||||
///
|
||||
/// If `fence` is not [`None`], it specifies a fence file descriptor on which to wait before
|
||||
/// locking the buffer. If it's [`None`], the caller is responsible for ensuring that writes
|
||||
/// to the buffer have completed before calling this function. Using this parameter is more
|
||||
/// efficient than waiting on the fence and then calling this function.
|
||||
///
|
||||
/// The `usage` parameter may only specify `HardwareBufferUsage::CPU_*`. If set, then the
|
||||
/// address of the buffer in virtual memory is returned. The flags must also be compatible with
|
||||
/// usage flags specified at buffer creation: if a read flag is passed, the buffer must have
|
||||
/// been created with [`HardwareBufferUsage::CPU_READ_RARELY`] or
|
||||
/// [`HardwareBufferUsage::CPU_READ_OFTEN`]. If a write flag is passed, it must have been
|
||||
/// created with [`HardwareBufferUsage::CPU_WRITE_RARELY`] or
|
||||
/// [`HardwareBufferUsage::CPU_WRITE_OFTEN`].
|
||||
///
|
||||
/// If `rect` is not [`None`], the caller promises to modify only data in the area specified by
|
||||
/// `rect`. If rect is [`None`], the caller may modify the contents of the entire buffer. The
|
||||
/// content of the buffer outside of the specified rect is NOT modified by this call.
|
||||
///
|
||||
/// It is legal for several different threads to lock a buffer for read access; none of the
|
||||
/// threads are blocked.
|
||||
///
|
||||
/// Locking a buffer simultaneously for write or read/write is undefined, but will neither
|
||||
/// terminate the process nor block the caller. This function may return an error or leave the
|
||||
/// buffer's content in an indeterminate state.
|
||||
///
|
||||
/// If the buffer has [`HardwareBufferFormat::BLOB`], it is legal lock it for reading and
|
||||
/// writing in multiple threads and/or processes simultaneously, and the contents of the buffer
|
||||
/// behave like shared memory.
|
||||
pub fn lock(
|
||||
&self,
|
||||
usage: HardwareBufferUsage,
|
||||
fence: Option<RawFd>,
|
||||
rect: Option<Rect>,
|
||||
) -> Result<*mut c_void> {
|
||||
let fence = fence.unwrap_or(-1);
|
||||
let rect = match rect {
|
||||
Some(rect) => &rect,
|
||||
None => std::ptr::null(),
|
||||
};
|
||||
construct(|res| unsafe {
|
||||
ffi::AHardwareBuffer_lock(self.as_ptr(), usage.0 .0, fence, rect, res)
|
||||
})
|
||||
}
|
||||
|
||||
/// Lock a [`HardwareBuffer`] for direct CPU access.
|
||||
///
|
||||
/// This function is the same as the above [`lock()`][Self::lock()] function, but passes back
|
||||
/// additional information about the bytes per pixel and the bytes per stride of the locked
|
||||
/// buffer. If the bytes per pixel or bytes per stride are unknown or variable, or if the
|
||||
/// underlying mapper implementation does not support returning additional information, then
|
||||
/// this call will fail with [`std::io::Error::kind()`] = [`std::io::ErrorKind::Unsupported`].
|
||||
#[cfg(feature = "api-level-29")]
|
||||
pub fn lock_and_get_info(
|
||||
&self,
|
||||
usage: HardwareBufferUsage,
|
||||
fence: Option<RawFd>,
|
||||
rect: Option<Rect>,
|
||||
) -> Result<LockedPlaneInfo> {
|
||||
let fence = fence.unwrap_or(-1);
|
||||
let rect = match rect {
|
||||
Some(rect) => &rect,
|
||||
None => std::ptr::null(),
|
||||
};
|
||||
let mut virtual_address = MaybeUninit::uninit();
|
||||
let mut bytes_per_pixel = MaybeUninit::uninit();
|
||||
let mut bytes_per_stride = MaybeUninit::uninit();
|
||||
let status = unsafe {
|
||||
ffi::AHardwareBuffer_lockAndGetInfo(
|
||||
self.as_ptr(),
|
||||
usage.0 .0,
|
||||
fence,
|
||||
rect,
|
||||
virtual_address.as_mut_ptr(),
|
||||
bytes_per_pixel.as_mut_ptr(),
|
||||
bytes_per_stride.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
status_to_io_result(status, ()).map(|()| unsafe {
|
||||
LockedPlaneInfo {
|
||||
virtual_address: virtual_address.assume_init(),
|
||||
bytes_per_pixel: bytes_per_pixel.assume_init() as u32,
|
||||
bytes_per_stride: bytes_per_stride.assume_init() as u32,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Lock a potentially multi-planar [`HardwareBuffer`] for direct CPU access.
|
||||
///
|
||||
/// This function is similar to [`lock()`][Self::lock()], but can lock multi-planar formats.
|
||||
/// Note, that multi-planar should not be confused with multi-layer images, which this locking
|
||||
/// function does not support.
|
||||
///
|
||||
/// YUV formats are always represented by three separate planes of data, one for each color
|
||||
/// plane. The order of planes in the array is guaranteed such that plane #0 is always `Y`,
|
||||
/// plane #1 is always `U` (`Cb`), and plane #2 is always `V` (`Cr`). All other formats are
|
||||
/// represented by a single plane.
|
||||
///
|
||||
/// Additional information always accompanies the buffers, describing the row stride and the
|
||||
/// pixel stride for each plane.
|
||||
///
|
||||
/// In case the buffer cannot be locked, this will return zero planes.
|
||||
///
|
||||
/// See the [`lock()`][Self::lock()] documentation for all other locking semantics.
|
||||
#[cfg(feature = "api-level-29")]
|
||||
pub fn lock_planes(
|
||||
&self,
|
||||
usage: HardwareBufferUsage,
|
||||
fence: Option<RawFd>,
|
||||
rect: Option<Rect>,
|
||||
) -> Result<HardwareBufferPlanes> {
|
||||
let fence = fence.unwrap_or(-1);
|
||||
let rect = match rect {
|
||||
Some(rect) => &rect,
|
||||
None => std::ptr::null(),
|
||||
};
|
||||
let planes = construct(|res| unsafe {
|
||||
ffi::AHardwareBuffer_lockPlanes(self.as_ptr(), usage.0 .0, fence, rect, res)
|
||||
})?;
|
||||
|
||||
Ok(HardwareBufferPlanes {
|
||||
inner: planes,
|
||||
index: 0,
|
||||
})
|
||||
}
|
||||
|
||||
/// Unlock the [`HardwareBuffer`] from direct CPU access.
|
||||
///
|
||||
/// Must be called after all changes to the buffer are completed by the caller. The function
|
||||
/// will block until all work is completed. See [`unlock_async()`][Self::unlock_async()] for
|
||||
/// a non-blocking variant that returns a file descriptor to be signaled on unlocking instead.
|
||||
pub fn unlock(&self) -> Result<()> {
|
||||
let status = unsafe { ffi::AHardwareBuffer_unlock(self.as_ptr(), std::ptr::null_mut()) };
|
||||
status_to_io_result(status, ())
|
||||
}
|
||||
|
||||
/// Unlock the [`HardwareBuffer`] from direct CPU access.
|
||||
///
|
||||
/// Returns a fence file descriptor that will become signaled when unlocking is completed, or
|
||||
/// [`None`] if unlocking is already finished. The caller is responsible for closing the file
|
||||
/// descriptor once it's no longer needed. See [`unlock()`][Self::unlock()] for a variant that
|
||||
/// blocks instead.
|
||||
pub fn unlock_async(&self) -> Result<Option<RawFd>> {
|
||||
let fence = construct(|res| unsafe { ffi::AHardwareBuffer_unlock(self.as_ptr(), res) })?;
|
||||
Ok(match fence {
|
||||
-1 => None,
|
||||
fence => Some(fence),
|
||||
})
|
||||
}
|
||||
|
||||
/// Receive a [`HardwareBuffer`] from an `AF_UNIX` socket.
|
||||
///
|
||||
/// `AF_UNIX` sockets are wrapped by [`std::os::unix::net::UnixListener`] in Rust.
|
||||
pub fn recv_handle_from_unix_socket(socket_fd: RawFd) -> Result<Self> {
|
||||
unsafe {
|
||||
let ptr =
|
||||
construct(|res| ffi::AHardwareBuffer_recvHandleFromUnixSocket(socket_fd, res))?;
|
||||
|
||||
Ok(Self::from_ptr(NonNull::new_unchecked(ptr)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Send the [`HardwareBuffer`] to an `AF_UNIX` socket.
|
||||
///
|
||||
/// `AF_UNIX` sockets are wrapped by [`std::os::unix::net::UnixListener`] in Rust.
|
||||
pub fn send_handle_to_unix_socket(&self, socket_fd: RawFd) -> Result<()> {
|
||||
let status =
|
||||
unsafe { ffi::AHardwareBuffer_sendHandleToUnixSocket(self.as_ptr(), socket_fd) };
|
||||
status_to_io_result(status, ())
|
||||
}
|
||||
|
||||
/// Acquire a reference on the given [`HardwareBuffer`] object.
|
||||
///
|
||||
/// This prevents the object from being deleted until the last strong reference, represented
|
||||
/// by [`HardwareBufferRef`], is [`drop()`]ped.
|
||||
pub fn acquire(&self) -> HardwareBufferRef {
|
||||
unsafe {
|
||||
ffi::AHardwareBuffer_acquire(self.as_ptr());
|
||||
HardwareBufferRef::from_ptr(self.inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`HardwareBuffer`] with an owned reference, that is released when dropped.
|
||||
/// It behaves much like a strong [`std::rc::Rc`] reference.
|
||||
#[derive(Debug)]
|
||||
pub struct HardwareBufferRef {
|
||||
inner: HardwareBuffer,
|
||||
}
|
||||
|
||||
impl HardwareBufferRef {
|
||||
/// Create an _owned_ [`HardwareBuffer`] from a native pointer
|
||||
///
|
||||
/// To wrap a weak reference (that is **not** `release`d on [`Drop`]), call
|
||||
/// [`HardwareBuffer::from_ptr()`] instead.
|
||||
///
|
||||
/// # Safety
|
||||
/// By calling this function, you assert that it is a valid pointer to an NDK
|
||||
/// [`ffi::AHardwareBuffer`].
|
||||
pub unsafe fn from_ptr(ptr: NonNull<ffi::AHardwareBuffer>) -> Self {
|
||||
Self {
|
||||
inner: HardwareBuffer { inner: ptr },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for HardwareBufferRef {
|
||||
type Target = HardwareBuffer;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for HardwareBufferRef {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::AHardwareBuffer_release(self.inner.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for HardwareBufferRef {
|
||||
fn clone(&self) -> Self {
|
||||
self.acquire()
|
||||
}
|
||||
}
|
||||
|
||||
/// Buffer description.
|
||||
///
|
||||
/// Used for allocating new buffers and querying parameters of existing ones.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct HardwareBufferDesc {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub layers: u32,
|
||||
pub format: HardwareBufferFormat,
|
||||
pub usage: HardwareBufferUsage,
|
||||
pub stride: u32,
|
||||
}
|
||||
|
||||
impl HardwareBufferDesc {
|
||||
fn into_native(self) -> ffi::AHardwareBuffer_Desc {
|
||||
ffi::AHardwareBuffer_Desc {
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
layers: self.layers,
|
||||
format: self.format.try_into().unwrap(),
|
||||
usage: self.usage.0 .0,
|
||||
stride: self.stride,
|
||||
rfu0: 0,
|
||||
rfu1: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A native [`AHardwareBuffer_Plane`]
|
||||
///
|
||||
/// Contains the same fields as [`ffi::AHardwareBuffer_Plane`].
|
||||
///
|
||||
/// [`AHardwareBuffer_Plane`]: https://developer.android.com/ndk/reference/group/a-hardware-buffer#ahardwarebuffer_plane
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct LockedPlaneInfo {
|
||||
pub virtual_address: *mut c_void,
|
||||
pub bytes_per_pixel: u32,
|
||||
pub bytes_per_stride: u32,
|
||||
}
|
||||
|
||||
/// Iterator over [`ffi::AHardwareBuffer_Planes`], containing a list of [`LockedPlaneInfo`].
|
||||
#[derive(Debug)]
|
||||
pub struct HardwareBufferPlanes {
|
||||
inner: ffi::AHardwareBuffer_Planes,
|
||||
index: u32,
|
||||
}
|
||||
|
||||
impl Iterator for HardwareBufferPlanes {
|
||||
type Item = LockedPlaneInfo;
|
||||
|
||||
fn next(&mut self) -> Option<LockedPlaneInfo> {
|
||||
if self.index == self.inner.planeCount {
|
||||
None
|
||||
} else {
|
||||
let plane = self.inner.planes[self.index as usize];
|
||||
self.index += 1;
|
||||
Some(LockedPlaneInfo {
|
||||
virtual_address: plane.data,
|
||||
bytes_per_pixel: plane.pixelStride,
|
||||
bytes_per_stride: plane.rowStride,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
38
third-party/vendor/ndk/src/hardware_buffer_format.rs
vendored
Normal file
38
third-party/vendor/ndk/src/hardware_buffer_format.rs
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
//! Bindings for [`AHardwareBuffer_Format`]
|
||||
//!
|
||||
//! [`AHardwareBuffer_Format`]: https://developer.android.com/ndk/reference/group/a-hardware-buffer#ahardwarebuffer_format
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
|
||||
/// Buffer pixel formats.
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum HardwareBufferFormat {
|
||||
/// Matches deprecated [`ffi::ANativeWindow_LegacyFormat::WINDOW_FORMAT_RGBA_8888`].
|
||||
R8G8B8A8_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM.0,
|
||||
/// Matches deprecated [`ffi::ANativeWindow_LegacyFormat::WINDOW_FORMAT_RGBX_8888`].
|
||||
R8G8B8X8_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM.0,
|
||||
#[cfg(feature = "api-level-26")]
|
||||
R8G8B8_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM.0,
|
||||
/// Matches deprecated [`ffi::ANativeWindow_LegacyFormat::WINDOW_FORMAT_RGB_565`].
|
||||
R5G6B5_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM.0,
|
||||
R16G16B16A16_FLOAT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT.0,
|
||||
#[cfg(feature = "api-level-26")]
|
||||
R10G10B10A2_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM.0,
|
||||
#[cfg(feature = "api-level-26")]
|
||||
BLOB = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_BLOB.0,
|
||||
#[cfg(feature = "api-level-26")]
|
||||
D16_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D16_UNORM.0,
|
||||
#[cfg(feature = "api-level-26")]
|
||||
D24_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D24_UNORM.0,
|
||||
#[cfg(feature = "api-level-26")]
|
||||
D24_UNORM_S8_UINT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT.0,
|
||||
#[cfg(feature = "api-level-26")]
|
||||
D32_FLOAT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D32_FLOAT.0,
|
||||
#[cfg(feature = "api-level-26")]
|
||||
D32_FLOAT_S8_UINT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT.0,
|
||||
#[cfg(feature = "api-level-26")]
|
||||
S8_UINT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_S8_UINT.0,
|
||||
#[cfg(feature = "api-level-26")]
|
||||
Y8Cb8Cr8_420 = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420.0,
|
||||
}
|
||||
114
third-party/vendor/ndk/src/input_queue.rs
vendored
Normal file
114
third-party/vendor/ndk/src/input_queue.rs
vendored
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
//! Bindings for [`AInputQueue`]
|
||||
//!
|
||||
//! [`AInputQueue`]: https://developer.android.com/ndk/reference/group/input#ainputqueue
|
||||
|
||||
use std::io::Result;
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr::{self, NonNull};
|
||||
|
||||
use crate::event::InputEvent;
|
||||
#[cfg(doc)]
|
||||
use crate::event::KeyEvent;
|
||||
use crate::looper::ForeignLooper;
|
||||
use crate::utils::status_to_io_result;
|
||||
|
||||
/// 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 `ndk-glue`
|
||||
unsafe impl Send for InputQueue {}
|
||||
unsafe impl Sync for InputQueue {}
|
||||
|
||||
impl InputQueue {
|
||||
/// Construct an [`InputQueue`] from the native pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// 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 }
|
||||
}
|
||||
|
||||
pub fn ptr(&self) -> NonNull<ffi::AInputQueue> {
|
||||
self.ptr
|
||||
}
|
||||
|
||||
/// Returns the next available [`InputEvent`] from the queue.
|
||||
///
|
||||
/// Returns [`None`] if no event is available.
|
||||
pub fn get_event(&self) -> Result<Option<InputEvent>> {
|
||||
let mut out_event = ptr::null_mut();
|
||||
let status = unsafe { ffi::AInputQueue_getEvent(self.ptr.as_ptr(), &mut out_event) };
|
||||
match status_to_io_result(status, ()) {
|
||||
Ok(()) => {
|
||||
debug_assert!(!out_event.is_null());
|
||||
Ok(Some(unsafe {
|
||||
InputEvent::from_ptr(NonNull::new_unchecked(out_event))
|
||||
}))
|
||||
}
|
||||
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(None),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns [`true`] if there are one or more events available in the input queue.
|
||||
pub fn has_events(&self) -> bool {
|
||||
match unsafe { ffi::AInputQueue_hasEvents(self.ptr.as_ptr()) } {
|
||||
0 => false,
|
||||
1 => true,
|
||||
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> {
|
||||
match unsafe { ffi::AInputQueue_preDispatchEvent(self.ptr.as_ptr(), event.ptr().as_ptr()) }
|
||||
{
|
||||
0 => Some(event),
|
||||
1 => None,
|
||||
r => unreachable!("AInputQueue_preDispatchEvent returned non-boolean {}", r),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(
|
||||
self.ptr.as_ptr(),
|
||||
looper.ptr().as_ptr(),
|
||||
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()) }
|
||||
}
|
||||
}
|
||||
23
third-party/vendor/ndk/src/lib.rs
vendored
Normal file
23
third-party/vendor/ndk/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//! # Android NDK
|
||||
//!
|
||||
//! Bindings to the [Android NDK].
|
||||
//!
|
||||
//! [Android NDK]: https://developer.android.com/ndk/reference
|
||||
#![warn(missing_debug_implementations, trivial_casts)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
|
||||
pub mod asset;
|
||||
pub mod audio;
|
||||
pub mod bitmap;
|
||||
pub mod configuration;
|
||||
pub mod event;
|
||||
pub mod hardware_buffer;
|
||||
pub mod hardware_buffer_format;
|
||||
pub mod input_queue;
|
||||
pub mod looper;
|
||||
pub mod media;
|
||||
pub mod native_activity;
|
||||
pub mod native_window;
|
||||
pub mod surface_texture;
|
||||
pub mod trace;
|
||||
mod utils;
|
||||
310
third-party/vendor/ndk/src/looper.rs
vendored
Normal file
310
third-party/vendor/ndk/src/looper.rs
vendored
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
//! Bindings for [`ALooper`]
|
||||
//!
|
||||
//! In Android, [`ALooper`]s are inherently thread-local. Due to this, there are two different
|
||||
//! [`ALooper`] interfaces exposed in this module:
|
||||
//!
|
||||
//! * [`ThreadLooper`], which has methods for the operations performable with a looper in one's own
|
||||
//! thread; and
|
||||
//! * [`ForeignLooper`], which has methods for the operations performable with any thread's looper.
|
||||
//!
|
||||
//! [`ALooper`]: https://developer.android.com/ndk/reference/group/looper#alooper
|
||||
|
||||
use bitflags::bitflags;
|
||||
use std::convert::TryInto;
|
||||
use std::mem::ManuallyDrop;
|
||||
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;
|
||||
|
||||
/// A thread-local native [`ALooper *`]. This promises that there is a looper associated with the
|
||||
/// current thread.
|
||||
///
|
||||
/// [`ALooper *`]: https://developer.android.com/ndk/reference/group/looper#alooper
|
||||
#[derive(Debug)]
|
||||
pub struct ThreadLooper {
|
||||
_marker: std::marker::PhantomData<*mut ()>, // Not send or sync
|
||||
foreign: ForeignLooper,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Flags for file descriptor events that a looper can monitor.
|
||||
pub struct FdEvent: u32 {
|
||||
const INPUT = ffi::ALOOPER_EVENT_INPUT;
|
||||
const OUTPUT = ffi::ALOOPER_EVENT_OUTPUT;
|
||||
const ERROR = ffi::ALOOPER_EVENT_ERROR;
|
||||
const HANGUP = ffi::ALOOPER_EVENT_HANGUP;
|
||||
const INVALID = ffi::ALOOPER_EVENT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/// The poll result from a [`ThreadLooper`].
|
||||
#[derive(Debug)]
|
||||
pub enum Poll {
|
||||
/// This looper was woken using [`ForeignLooper::wake()`]
|
||||
Wake,
|
||||
/// For [`ThreadLooper::poll_once*()`][ThreadLooper::poll_once()], an event was received and processed using a callback.
|
||||
Callback,
|
||||
/// For [`ThreadLooper::poll_*_timeout()`][ThreadLooper::poll_once_timeout()], the requested timeout was reached before any events.
|
||||
Timeout,
|
||||
/// An event was received
|
||||
Event {
|
||||
ident: i32,
|
||||
fd: RawFd,
|
||||
events: FdEvent,
|
||||
data: *mut c_void,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Error)]
|
||||
#[error("Android Looper error")]
|
||||
pub struct LooperError;
|
||||
|
||||
impl ThreadLooper {
|
||||
/// Prepares a looper for the current thread and returns it
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the looper associated with the current thread, if any.
|
||||
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> {
|
||||
let mut fd: RawFd = -1;
|
||||
let mut events: i32 = -1;
|
||||
let mut data: *mut c_void = ptr::null_mut();
|
||||
match unsafe { 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: FdEvent::from_bits(events as u32)
|
||||
.expect("poll event contains unknown bits"),
|
||||
data,
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Polls the looper, blocking on processing an event.
|
||||
#[inline]
|
||||
pub fn poll_once(&self) -> Result<Poll, LooperError> {
|
||||
self.poll_once_ms(-1)
|
||||
}
|
||||
|
||||
/// Polls the looper, blocking on processing an event, but with a timeout. Give a timeout of 0
|
||||
/// to make this non-blocking.
|
||||
///
|
||||
/// It panics if the timeout is larger than expressible as an [`i32`] of milliseconds (roughly 25
|
||||
/// days).
|
||||
#[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> {
|
||||
let mut fd: RawFd = -1;
|
||||
let mut events: i32 = -1;
|
||||
let mut data: *mut c_void = ptr::null_mut();
|
||||
match unsafe { 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: FdEvent::from_bits(events as u32)
|
||||
.expect("poll event contains unknown bits"),
|
||||
data,
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Repeatedly polls the looper, blocking on processing an event.
|
||||
///
|
||||
/// This function will never return [`Poll::Callback`].
|
||||
#[inline]
|
||||
pub fn poll_all(&self) -> Result<Poll, LooperError> {
|
||||
self.poll_all_ms(-1)
|
||||
}
|
||||
|
||||
/// Repeatedly polls the looper, blocking on processing an event, but with a timeout. Give a
|
||||
/// timeout of 0 to make this non-blocking.
|
||||
///
|
||||
/// This function will never return [`Poll::Callback`].
|
||||
///
|
||||
/// It panics if the timeout is larger than expressible as an [`i32`] of milliseconds (roughly 25
|
||||
/// days).
|
||||
#[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"),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a reference to the [`ForeignLooper`] that is associated with the current thread.
|
||||
pub fn as_foreign(&self) -> &ForeignLooper {
|
||||
&self.foreign
|
||||
}
|
||||
|
||||
pub fn into_foreign(self) -> ForeignLooper {
|
||||
self.foreign
|
||||
}
|
||||
}
|
||||
|
||||
/// A native [`ALooper *`], not necessarily allocated with the current thread.
|
||||
///
|
||||
/// [`ALooper *`]: https://developer.android.com/ndk/reference/group/looper#alooper
|
||||
#[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 {
|
||||
/// Returns the looper associated with the current thread, if any.
|
||||
#[inline]
|
||||
pub fn for_thread() -> Option<Self> {
|
||||
NonNull::new(unsafe { ffi::ALooper_forThread() }).map(|ptr| unsafe { Self::from_ptr(ptr) })
|
||||
}
|
||||
|
||||
/// Construct a [`ForeignLooper`] object from the given pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// By calling this function, you guarantee that the pointer is a valid, non-null pointer to an
|
||||
/// NDK [`ffi::ALooper`].
|
||||
#[inline]
|
||||
pub unsafe fn from_ptr(ptr: NonNull<ffi::ALooper>) -> Self {
|
||||
ffi::ALooper_acquire(ptr.as_ptr());
|
||||
Self { ptr }
|
||||
}
|
||||
|
||||
/// Returns a pointer to the NDK `ALooper` object.
|
||||
#[inline]
|
||||
pub fn ptr(&self) -> NonNull<ffi::ALooper> {
|
||||
self.ptr
|
||||
}
|
||||
|
||||
/// Wakes the looper. An event of [`Poll::Wake`] will be sent.
|
||||
pub fn wake(&self) {
|
||||
unsafe { ffi::ALooper_wake(self.ptr.as_ptr()) }
|
||||
}
|
||||
|
||||
/// Adds a file descriptor to be polled, without a callback.
|
||||
///
|
||||
/// See also [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/looper.html#alooper_addfd).
|
||||
|
||||
// `ALooper_addFd` won't dereference `data`; it will only pass it on to the event.
|
||||
// Optionally dereferencing it there already enforces `unsafe` context.
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn add_fd(
|
||||
&self,
|
||||
fd: RawFd,
|
||||
ident: i32,
|
||||
events: FdEvent,
|
||||
data: *mut c_void,
|
||||
) -> Result<(), LooperError> {
|
||||
match unsafe {
|
||||
ffi::ALooper_addFd(
|
||||
self.ptr.as_ptr(),
|
||||
fd,
|
||||
ident,
|
||||
events.bits() as i32,
|
||||
None,
|
||||
data,
|
||||
)
|
||||
} {
|
||||
1 => Ok(()),
|
||||
-1 => Err(LooperError),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a file descriptor to be polled, with a callback.
|
||||
///
|
||||
/// The callback takes as an argument the file descriptor, and should return true to continue
|
||||
/// receiving callbacks, or false to have the callback unregistered.
|
||||
///
|
||||
/// See also [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/looper.html#alooper_addfd).
|
||||
pub fn add_fd_with_callback<F: FnMut(RawFd) -> bool>(
|
||||
&self,
|
||||
fd: RawFd,
|
||||
events: FdEvent,
|
||||
callback: F,
|
||||
) -> Result<(), LooperError> {
|
||||
extern "C" fn cb_handler<F: FnMut(RawFd) -> bool>(
|
||||
fd: RawFd,
|
||||
_events: i32,
|
||||
data: *mut c_void,
|
||||
) -> i32 {
|
||||
unsafe {
|
||||
let mut cb = ManuallyDrop::new(Box::<F>::from_raw(data as *mut _));
|
||||
let keep_registered = cb(fd);
|
||||
if !keep_registered {
|
||||
ManuallyDrop::into_inner(cb);
|
||||
}
|
||||
keep_registered as i32
|
||||
}
|
||||
}
|
||||
let data = Box::into_raw(Box::new(callback)) as *mut _;
|
||||
match unsafe {
|
||||
ffi::ALooper_addFd(
|
||||
self.ptr.as_ptr(),
|
||||
fd,
|
||||
ffi::ALOOPER_POLL_CALLBACK,
|
||||
events.bits() as i32,
|
||||
Some(cb_handler::<F>),
|
||||
data,
|
||||
)
|
||||
} {
|
||||
1 => Ok(()),
|
||||
-1 => Err(LooperError),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
82
third-party/vendor/ndk/src/media/error.rs
vendored
Normal file
82
third-party/vendor/ndk/src/media/error.rs
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
use super::Result;
|
||||
use thiserror::Error;
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum MediaErrorResult {
|
||||
CodecErrorInsufficientResource = ffi::media_status_t::AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE.0,
|
||||
CodecErrorReclaimed = ffi::media_status_t::AMEDIACODEC_ERROR_RECLAIMED.0,
|
||||
ErrorUnknown = ffi::media_status_t::AMEDIA_ERROR_UNKNOWN.0,
|
||||
ErrorMalformed = ffi::media_status_t::AMEDIA_ERROR_MALFORMED.0,
|
||||
ErrorUnsupported = ffi::media_status_t::AMEDIA_ERROR_UNSUPPORTED.0,
|
||||
ErrorInvalidObject = ffi::media_status_t::AMEDIA_ERROR_INVALID_OBJECT.0,
|
||||
ErrorInvalidParameter = ffi::media_status_t::AMEDIA_ERROR_INVALID_PARAMETER.0,
|
||||
ErrorInvalidOperation = ffi::media_status_t::AMEDIA_ERROR_INVALID_OPERATION.0,
|
||||
ErrorEndOfStream = ffi::media_status_t::AMEDIA_ERROR_END_OF_STREAM.0,
|
||||
ErrorIo = ffi::media_status_t::AMEDIA_ERROR_IO.0,
|
||||
ErrorWouldBlock = ffi::media_status_t::AMEDIA_ERROR_WOULD_BLOCK.0,
|
||||
DrmErrorBase = ffi::media_status_t::AMEDIA_DRM_ERROR_BASE.0,
|
||||
DrmNotProvisioned = ffi::media_status_t::AMEDIA_DRM_NOT_PROVISIONED.0,
|
||||
DrmResourceBusy = ffi::media_status_t::AMEDIA_DRM_RESOURCE_BUSY.0,
|
||||
DrmDeviceRevoked = ffi::media_status_t::AMEDIA_DRM_DEVICE_REVOKED.0,
|
||||
DrmShortBuffer = ffi::media_status_t::AMEDIA_DRM_SHORT_BUFFER.0,
|
||||
DrmSessionNotOpened = ffi::media_status_t::AMEDIA_DRM_SESSION_NOT_OPENED.0,
|
||||
DrmTamperDetected = ffi::media_status_t::AMEDIA_DRM_TAMPER_DETECTED.0,
|
||||
DrmVerifyFailed = ffi::media_status_t::AMEDIA_DRM_VERIFY_FAILED.0,
|
||||
DrmNeedKey = ffi::media_status_t::AMEDIA_DRM_NEED_KEY.0,
|
||||
DrmLicenseExpired = ffi::media_status_t::AMEDIA_DRM_LICENSE_EXPIRED.0,
|
||||
ImgreaderErrorBase = ffi::media_status_t::AMEDIA_IMGREADER_ERROR_BASE.0,
|
||||
ImgreaderNoBufferAvailable = ffi::media_status_t::AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE.0,
|
||||
ImgreaderMaxImagesAcquired = ffi::media_status_t::AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED.0,
|
||||
ImgreaderCannotLockImage = ffi::media_status_t::AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE.0,
|
||||
ImgreaderCannotUnlockImage = ffi::media_status_t::AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE.0,
|
||||
ImgreaderImageNotLocked = ffi::media_status_t::AMEDIA_IMGREADER_IMAGE_NOT_LOCKED.0,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum NdkMediaError {
|
||||
#[error("error Media result ({0:?})")]
|
||||
ErrorResult(MediaErrorResult),
|
||||
#[error("unknown Media error result ({0:?})")]
|
||||
UnknownResult(ffi::media_status_t),
|
||||
}
|
||||
|
||||
impl NdkMediaError {
|
||||
pub(crate) fn from_status(status: ffi::media_status_t) -> Result<()> {
|
||||
use MediaErrorResult::*;
|
||||
let result = match status {
|
||||
ffi::media_status_t::AMEDIA_OK => return Ok(()),
|
||||
ffi::media_status_t::AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE => {
|
||||
CodecErrorInsufficientResource
|
||||
}
|
||||
ffi::media_status_t::AMEDIACODEC_ERROR_RECLAIMED => CodecErrorReclaimed,
|
||||
ffi::media_status_t::AMEDIA_ERROR_UNKNOWN => ErrorUnknown,
|
||||
ffi::media_status_t::AMEDIA_ERROR_MALFORMED => ErrorMalformed,
|
||||
ffi::media_status_t::AMEDIA_ERROR_UNSUPPORTED => ErrorUnsupported,
|
||||
ffi::media_status_t::AMEDIA_ERROR_INVALID_OBJECT => ErrorInvalidObject,
|
||||
ffi::media_status_t::AMEDIA_ERROR_INVALID_PARAMETER => ErrorInvalidParameter,
|
||||
ffi::media_status_t::AMEDIA_ERROR_INVALID_OPERATION => ErrorInvalidOperation,
|
||||
ffi::media_status_t::AMEDIA_ERROR_END_OF_STREAM => ErrorEndOfStream,
|
||||
ffi::media_status_t::AMEDIA_ERROR_IO => ErrorIo,
|
||||
ffi::media_status_t::AMEDIA_ERROR_WOULD_BLOCK => ErrorWouldBlock,
|
||||
ffi::media_status_t::AMEDIA_DRM_ERROR_BASE => DrmErrorBase,
|
||||
ffi::media_status_t::AMEDIA_DRM_NOT_PROVISIONED => DrmNotProvisioned,
|
||||
ffi::media_status_t::AMEDIA_DRM_RESOURCE_BUSY => DrmResourceBusy,
|
||||
ffi::media_status_t::AMEDIA_DRM_DEVICE_REVOKED => DrmDeviceRevoked,
|
||||
ffi::media_status_t::AMEDIA_DRM_SHORT_BUFFER => DrmShortBuffer,
|
||||
ffi::media_status_t::AMEDIA_DRM_SESSION_NOT_OPENED => DrmSessionNotOpened,
|
||||
ffi::media_status_t::AMEDIA_DRM_TAMPER_DETECTED => DrmTamperDetected,
|
||||
ffi::media_status_t::AMEDIA_DRM_VERIFY_FAILED => DrmVerifyFailed,
|
||||
ffi::media_status_t::AMEDIA_DRM_NEED_KEY => DrmNeedKey,
|
||||
ffi::media_status_t::AMEDIA_DRM_LICENSE_EXPIRED => DrmLicenseExpired,
|
||||
ffi::media_status_t::AMEDIA_IMGREADER_ERROR_BASE => ImgreaderErrorBase,
|
||||
ffi::media_status_t::AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE => ImgreaderNoBufferAvailable,
|
||||
ffi::media_status_t::AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED => ImgreaderMaxImagesAcquired,
|
||||
ffi::media_status_t::AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE => ImgreaderCannotLockImage,
|
||||
ffi::media_status_t::AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE => ImgreaderCannotUnlockImage,
|
||||
ffi::media_status_t::AMEDIA_IMGREADER_IMAGE_NOT_LOCKED => ImgreaderImageNotLocked,
|
||||
_ => return Err(NdkMediaError::UnknownResult(status)),
|
||||
};
|
||||
Err(NdkMediaError::ErrorResult(result))
|
||||
}
|
||||
}
|
||||
366
third-party/vendor/ndk/src/media/image_reader.rs
vendored
Normal file
366
third-party/vendor/ndk/src/media/image_reader.rs
vendored
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
//! Bindings for [`AImageReader`] and [`AImage`]
|
||||
//!
|
||||
//! [`AImageReader`]: https://developer.android.com/ndk/reference/group/media#aimagereader
|
||||
//! [`AImage`]: https://developer.android.com/ndk/reference/group/media#aimage
|
||||
#![cfg(feature = "api-level-24")]
|
||||
|
||||
use super::NdkMediaError;
|
||||
use super::{construct, construct_never_null, error::MediaErrorResult, Result};
|
||||
use crate::native_window::NativeWindow;
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
ffi::c_void,
|
||||
fmt::{self, Debug, Formatter},
|
||||
mem::MaybeUninit,
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
||||
#[cfg(feature = "api-level-26")]
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
#[cfg(feature = "api-level-26")]
|
||||
use crate::hardware_buffer::{HardwareBuffer, HardwareBufferUsage};
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum ImageFormat {
|
||||
RGBA_8888 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGBA_8888.0,
|
||||
RGBX_8888 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGBX_8888.0,
|
||||
RGB_888 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGB_888.0,
|
||||
RGB_565 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGB_565.0,
|
||||
RGBA_FP16 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGBA_FP16.0,
|
||||
YUV_420_888 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_YUV_420_888.0,
|
||||
JPEG = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_JPEG.0,
|
||||
RAW16 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RAW16.0,
|
||||
RAW_PRIVATE = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RAW_PRIVATE.0,
|
||||
RAW10 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RAW10.0,
|
||||
RAW12 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RAW12.0,
|
||||
DEPTH16 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_DEPTH16.0,
|
||||
DEPTH_POINT_CLOUD = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_DEPTH_POINT_CLOUD.0,
|
||||
PRIVATE = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_PRIVATE.0,
|
||||
Y8 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_Y8.0,
|
||||
HEIC = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_HEIC.0,
|
||||
DEPTH_JPEG = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_DEPTH_JPEG.0,
|
||||
}
|
||||
|
||||
pub type ImageListener = Box<dyn FnMut(&ImageReader)>;
|
||||
|
||||
#[cfg(feature = "api-level-26")]
|
||||
pub type BufferRemovedListener = Box<dyn FnMut(&ImageReader, &HardwareBuffer)>;
|
||||
|
||||
/// A native [`AImageReader *`]
|
||||
///
|
||||
/// [`AImageReader *`]: https://developer.android.com/ndk/reference/group/media#aimagereader
|
||||
pub struct ImageReader {
|
||||
inner: NonNull<ffi::AImageReader>,
|
||||
image_cb: Option<Box<ImageListener>>,
|
||||
#[cfg(feature = "api-level-26")]
|
||||
buffer_removed_cb: Option<Box<BufferRemovedListener>>,
|
||||
}
|
||||
|
||||
impl Debug for ImageReader {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.debug_struct("ImageReader")
|
||||
.field("inner", &self.inner)
|
||||
.field(
|
||||
"image_cb",
|
||||
match &self.image_cb {
|
||||
Some(_) => &"Some(_)",
|
||||
None => &"None",
|
||||
},
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl ImageReader {
|
||||
fn from_ptr(inner: NonNull<ffi::AImageReader>) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
image_cb: None,
|
||||
#[cfg(feature = "api-level-26")]
|
||||
buffer_removed_cb: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_ptr(&self) -> *mut ffi::AImageReader {
|
||||
self.inner.as_ptr()
|
||||
}
|
||||
|
||||
pub fn new(width: i32, height: i32, format: ImageFormat, max_images: i32) -> Result<Self> {
|
||||
let inner = construct_never_null(|res| unsafe {
|
||||
ffi::AImageReader_new(width, height, format as i32, max_images, res)
|
||||
})?;
|
||||
|
||||
Ok(Self::from_ptr(inner))
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-26")]
|
||||
pub fn new_with_usage(
|
||||
width: i32,
|
||||
height: i32,
|
||||
format: ImageFormat,
|
||||
usage: HardwareBufferUsage,
|
||||
max_images: i32,
|
||||
) -> Result<Self> {
|
||||
let inner = construct_never_null(|res| unsafe {
|
||||
ffi::AImageReader_newWithUsage(
|
||||
width,
|
||||
height,
|
||||
format as i32,
|
||||
usage.0 .0,
|
||||
max_images,
|
||||
res,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(Self::from_ptr(inner))
|
||||
}
|
||||
|
||||
pub fn set_image_listener(&mut self, listener: ImageListener) -> Result<()> {
|
||||
let mut boxed = Box::new(listener);
|
||||
let ptr: *mut ImageListener = &mut *boxed;
|
||||
// keep listener alive until Drop or new listener is assigned
|
||||
self.image_cb = Some(boxed);
|
||||
|
||||
unsafe extern "C" fn on_image_available(
|
||||
context: *mut c_void,
|
||||
reader: *mut ffi::AImageReader,
|
||||
) {
|
||||
let reader = ImageReader::from_ptr(NonNull::new_unchecked(reader));
|
||||
let listener: *mut ImageListener = context as *mut _;
|
||||
(*listener)(&reader);
|
||||
std::mem::forget(reader);
|
||||
}
|
||||
|
||||
let mut listener = ffi::AImageReader_ImageListener {
|
||||
context: ptr as _,
|
||||
onImageAvailable: Some(on_image_available),
|
||||
};
|
||||
let status = unsafe { ffi::AImageReader_setImageListener(self.as_ptr(), &mut listener) };
|
||||
NdkMediaError::from_status(status)
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-26")]
|
||||
pub fn set_buffer_removed_listener(&mut self, listener: BufferRemovedListener) -> Result<()> {
|
||||
let mut boxed = Box::new(listener);
|
||||
let ptr: *mut BufferRemovedListener = &mut *boxed;
|
||||
// keep listener alive until Drop or new listener is assigned
|
||||
self.buffer_removed_cb = Some(boxed);
|
||||
|
||||
unsafe extern "C" fn on_buffer_removed(
|
||||
context: *mut c_void,
|
||||
reader: *mut ffi::AImageReader,
|
||||
buffer: *mut ffi::AHardwareBuffer,
|
||||
) {
|
||||
let reader = ImageReader::from_ptr(NonNull::new_unchecked(reader));
|
||||
let buffer = HardwareBuffer::from_ptr(NonNull::new_unchecked(buffer));
|
||||
let listener: *mut BufferRemovedListener = context as *mut _;
|
||||
(*listener)(&reader, &buffer);
|
||||
std::mem::forget(reader);
|
||||
}
|
||||
|
||||
let mut listener = ffi::AImageReader_BufferRemovedListener {
|
||||
context: ptr as _,
|
||||
onBufferRemoved: Some(on_buffer_removed),
|
||||
};
|
||||
let status =
|
||||
unsafe { ffi::AImageReader_setBufferRemovedListener(self.as_ptr(), &mut listener) };
|
||||
NdkMediaError::from_status(status)
|
||||
}
|
||||
|
||||
pub fn get_window(&self) -> Result<NativeWindow> {
|
||||
unsafe {
|
||||
let ptr = construct_never_null(|res| ffi::AImageReader_getWindow(self.as_ptr(), res))?;
|
||||
Ok(NativeWindow::from_ptr(ptr))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_width(&self) -> Result<i32> {
|
||||
construct(|res| unsafe { ffi::AImageReader_getWidth(self.as_ptr(), res) })
|
||||
}
|
||||
|
||||
pub fn get_height(&self) -> Result<i32> {
|
||||
construct(|res| unsafe { ffi::AImageReader_getHeight(self.as_ptr(), res) })
|
||||
}
|
||||
|
||||
pub fn get_format(&self) -> Result<ImageFormat> {
|
||||
let format = construct(|res| unsafe { ffi::AImageReader_getFormat(self.as_ptr(), res) })?;
|
||||
Ok((format as u32).try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn get_max_images(&self) -> Result<i32> {
|
||||
construct(|res| unsafe { ffi::AImageReader_getMaxImages(self.as_ptr(), res) })
|
||||
}
|
||||
|
||||
pub fn acquire_next_image(&self) -> Result<Option<Image>> {
|
||||
let res = construct_never_null(|res| unsafe {
|
||||
ffi::AImageReader_acquireNextImage(self.as_ptr(), res)
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(inner) => Ok(Some(Image { inner })),
|
||||
Err(NdkMediaError::ErrorResult(MediaErrorResult::ImgreaderNoBufferAvailable)) => {
|
||||
Ok(None)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// If the returned file descriptor is not `None`, it must be awaited before attempting to access the Image returned.
|
||||
/// <https://developer.android.com/ndk/reference/group/media#aimagereader_acquirenextimageasync>
|
||||
#[cfg(feature = "api-level-26")]
|
||||
pub unsafe fn acquire_next_image_async(&self) -> Result<(Image, Option<RawFd>)> {
|
||||
let mut fence = MaybeUninit::uninit();
|
||||
let inner = construct_never_null(|res| {
|
||||
ffi::AImageReader_acquireNextImageAsync(self.as_ptr(), res, fence.as_mut_ptr())
|
||||
})?;
|
||||
|
||||
let image = Image { inner };
|
||||
|
||||
Ok(match fence.assume_init() {
|
||||
-1 => (image, None),
|
||||
fence => (image, Some(fence)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn acquire_latest_image(&self) -> Result<Option<Image>> {
|
||||
let res = construct_never_null(|res| unsafe {
|
||||
ffi::AImageReader_acquireLatestImage(self.as_ptr(), res)
|
||||
});
|
||||
|
||||
if let Err(NdkMediaError::ErrorResult(MediaErrorResult::ImgreaderNoBufferAvailable)) = res {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(Some(Image { inner: res? }))
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// If the returned file descriptor is not `None`, it must be awaited before attempting to access the Image returned.
|
||||
/// <https://developer.android.com/ndk/reference/group/media#aimagereader_acquirelatestimageasync>
|
||||
#[cfg(feature = "api-level-26")]
|
||||
pub fn acquire_latest_image_async(&self) -> Result<(Image, Option<RawFd>)> {
|
||||
let mut fence = MaybeUninit::uninit();
|
||||
let inner = construct_never_null(|res| unsafe {
|
||||
ffi::AImageReader_acquireLatestImageAsync(self.as_ptr(), res, fence.as_mut_ptr())
|
||||
})?;
|
||||
|
||||
let image = Image { inner };
|
||||
|
||||
Ok(match unsafe { fence.assume_init() } {
|
||||
-1 => (image, None),
|
||||
fence => (image, Some(fence)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ImageReader {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::AImageReader_delete(self.as_ptr()) };
|
||||
}
|
||||
}
|
||||
|
||||
/// A native [`AImage *`]
|
||||
///
|
||||
/// [`AImage *`]: https://developer.android.com/ndk/reference/group/media#aimage
|
||||
#[derive(Debug)]
|
||||
pub struct Image {
|
||||
inner: NonNull<ffi::AImage>,
|
||||
}
|
||||
|
||||
pub type CropRect = ffi::AImageCropRect;
|
||||
|
||||
impl Image {
|
||||
fn as_ptr(&self) -> *mut ffi::AImage {
|
||||
self.inner.as_ptr()
|
||||
}
|
||||
|
||||
pub fn get_plane_data(&self, plane_idx: i32) -> Result<&[u8]> {
|
||||
let mut result_ptr = MaybeUninit::uninit();
|
||||
let mut result_len = MaybeUninit::uninit();
|
||||
let status = unsafe {
|
||||
ffi::AImage_getPlaneData(
|
||||
self.as_ptr(),
|
||||
plane_idx,
|
||||
result_ptr.as_mut_ptr(),
|
||||
result_len.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
NdkMediaError::from_status(status).map(|()| unsafe {
|
||||
std::slice::from_raw_parts(result_ptr.assume_init(), result_len.assume_init() as _)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_plane_pixel_stride(&self, plane_idx: i32) -> Result<i32> {
|
||||
construct(|res| unsafe { ffi::AImage_getPlanePixelStride(self.as_ptr(), plane_idx, res) })
|
||||
}
|
||||
|
||||
pub fn get_plane_row_stride(&self, plane_idx: i32) -> Result<i32> {
|
||||
construct(|res| unsafe { ffi::AImage_getPlaneRowStride(self.as_ptr(), plane_idx, res) })
|
||||
}
|
||||
|
||||
pub fn get_crop_rect(&self) -> Result<CropRect> {
|
||||
construct(|res| unsafe { ffi::AImage_getCropRect(self.as_ptr(), res) })
|
||||
}
|
||||
|
||||
pub fn get_width(&self) -> Result<i32> {
|
||||
construct(|res| unsafe { ffi::AImage_getWidth(self.as_ptr(), res) })
|
||||
}
|
||||
|
||||
pub fn get_height(&self) -> Result<i32> {
|
||||
construct(|res| unsafe { ffi::AImage_getHeight(self.as_ptr(), res) })
|
||||
}
|
||||
|
||||
pub fn get_format(&self) -> Result<ImageFormat> {
|
||||
let format = construct(|res| unsafe { ffi::AImage_getFormat(self.as_ptr(), res) })?;
|
||||
Ok((format as u32).try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn get_timestamp(&self) -> Result<i64> {
|
||||
construct(|res| unsafe { ffi::AImage_getTimestamp(self.as_ptr(), res) })
|
||||
}
|
||||
|
||||
pub fn get_number_of_planes(&self) -> Result<i32> {
|
||||
construct(|res| unsafe { ffi::AImage_getNumberOfPlanes(self.as_ptr(), res) })
|
||||
}
|
||||
|
||||
/// Get the hardware buffer handle of the input image intended for GPU and/or hardware access.
|
||||
///
|
||||
/// Note that no reference on the returned [`HardwareBuffer`] handle is acquired automatically.
|
||||
/// Once the [`Image`] or the parent [`ImageReader`] is deleted, the [`HardwareBuffer`] handle
|
||||
/// from previous [`Image::get_hardware_buffer()`] becomes invalid.
|
||||
///
|
||||
/// If the caller ever needs to hold on a reference to the [`HardwareBuffer`] handle after the
|
||||
/// [`Image`] or the parent [`ImageReader`] is deleted, it must call
|
||||
/// [`HardwareBuffer::acquire()`] to acquire an extra reference, and [`drop()`] it when
|
||||
/// finished using it in order to properly deallocate the underlying memory managed by
|
||||
/// [`HardwareBuffer`]. If the caller has acquired an extra reference on a [`HardwareBuffer`]
|
||||
/// returned from this function, it must also register a listener using
|
||||
/// [`ImageReader::set_buffer_removed_listener()`] to be notified when the buffer is no longer
|
||||
/// used by [`ImageReader`].
|
||||
#[cfg(feature = "api-level-26")]
|
||||
pub fn get_hardware_buffer(&self) -> Result<HardwareBuffer> {
|
||||
unsafe {
|
||||
let ptr =
|
||||
construct_never_null(|res| ffi::AImage_getHardwareBuffer(self.as_ptr(), res))?;
|
||||
Ok(HardwareBuffer::from_ptr(ptr))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-26")]
|
||||
pub fn delete_async(self, release_fence_fd: RawFd) {
|
||||
unsafe { ffi::AImage_deleteAsync(self.as_ptr(), release_fence_fd) };
|
||||
std::mem::forget(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Image {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::AImage_delete(self.as_ptr()) };
|
||||
}
|
||||
}
|
||||
494
third-party/vendor/ndk/src/media/media_codec.rs
vendored
Normal file
494
third-party/vendor/ndk/src/media/media_codec.rs
vendored
Normal file
|
|
@ -0,0 +1,494 @@
|
|||
//! Bindings for [`AMediaFormat`] and [`AMediaCodec`]
|
||||
//!
|
||||
//! [`AMediaFormat`]: https://developer.android.com/ndk/reference/group/media#amediaformat
|
||||
//! [`AMediaCodec`]: https://developer.android.com/ndk/reference/group/media#amediacodec
|
||||
|
||||
use super::{get_unlikely_to_be_null, NdkMediaError, Result};
|
||||
use crate::native_window::NativeWindow;
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
ffi::{CStr, CString},
|
||||
fmt::Display,
|
||||
ptr::{self, NonNull},
|
||||
slice,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum MediaCodecDirection {
|
||||
Decoder,
|
||||
Encoder,
|
||||
}
|
||||
|
||||
/// A native [`AMediaFormat *`]
|
||||
///
|
||||
/// [`AMediaFormat *`]: https://developer.android.com/ndk/reference/group/media#amediaformat
|
||||
#[derive(Debug)]
|
||||
pub struct MediaFormat {
|
||||
inner: NonNull<ffi::AMediaFormat>,
|
||||
}
|
||||
|
||||
impl Display for MediaFormat {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let c_str = unsafe { CStr::from_ptr(ffi::AMediaFormat_toString(self.as_ptr())) };
|
||||
f.write_str(c_str.to_str().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MediaFormat {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl MediaFormat {
|
||||
fn as_ptr(&self) -> *mut ffi::AMediaFormat {
|
||||
self.inner.as_ptr()
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: NonNull::new(unsafe { ffi::AMediaFormat_new() }).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn i32(&self, key: &str) -> Option<i32> {
|
||||
let name = CString::new(key).unwrap();
|
||||
let mut out = 0;
|
||||
unsafe { ffi::AMediaFormat_getInt32(self.as_ptr(), name.as_ptr(), &mut out) }.then(|| out)
|
||||
}
|
||||
|
||||
pub fn i64(&self, key: &str) -> Option<i64> {
|
||||
let name = CString::new(key).unwrap();
|
||||
let mut out = 0;
|
||||
unsafe { ffi::AMediaFormat_getInt64(self.as_ptr(), name.as_ptr(), &mut out) }.then(|| out)
|
||||
}
|
||||
|
||||
pub fn f32(&self, key: &str) -> Option<f32> {
|
||||
let name = CString::new(key).unwrap();
|
||||
let mut out = 0.0;
|
||||
unsafe { ffi::AMediaFormat_getFloat(self.as_ptr(), name.as_ptr(), &mut out) }.then(|| out)
|
||||
}
|
||||
|
||||
pub fn usize(&self, key: &str) -> Option<usize> {
|
||||
let name = CString::new(key).unwrap();
|
||||
let mut out = 0;
|
||||
unsafe { ffi::AMediaFormat_getSize(self.as_ptr(), name.as_ptr(), &mut out) }
|
||||
.then(|| out as usize)
|
||||
}
|
||||
|
||||
pub fn buffer(&self, key: &str) -> Option<&[u8]> {
|
||||
let name = CString::new(key).unwrap();
|
||||
let mut out_buffer = ptr::null_mut();
|
||||
let mut out_size = 0;
|
||||
unsafe {
|
||||
ffi::AMediaFormat_getBuffer(
|
||||
self.as_ptr(),
|
||||
name.as_ptr(),
|
||||
&mut out_buffer,
|
||||
&mut out_size,
|
||||
)
|
||||
}
|
||||
.then(|| unsafe { slice::from_raw_parts(out_buffer.cast(), out_size as usize) })
|
||||
}
|
||||
|
||||
pub fn str(&self, key: &str) -> Option<&str> {
|
||||
let name = CString::new(key).unwrap();
|
||||
let mut out = ptr::null();
|
||||
unsafe { ffi::AMediaFormat_getString(self.as_ptr(), name.as_ptr(), &mut out) }
|
||||
.then(|| unsafe { CStr::from_ptr(out) }.to_str().unwrap())
|
||||
}
|
||||
|
||||
pub fn set_i32(&self, key: &str, value: i32) {
|
||||
let name = CString::new(key).unwrap();
|
||||
unsafe { ffi::AMediaFormat_setInt32(self.as_ptr(), name.as_ptr(), value) };
|
||||
}
|
||||
|
||||
pub fn set_i64(&self, key: &str, value: i64) {
|
||||
let name = CString::new(key).unwrap();
|
||||
unsafe { ffi::AMediaFormat_setInt64(self.as_ptr(), name.as_ptr(), value) };
|
||||
}
|
||||
|
||||
pub fn set_f32(&self, key: &str, value: f32) {
|
||||
let name = CString::new(key).unwrap();
|
||||
unsafe { ffi::AMediaFormat_setFloat(self.as_ptr(), name.as_ptr(), value) };
|
||||
}
|
||||
|
||||
pub fn set_str(&self, key: &str, value: &str) {
|
||||
let name = CString::new(key).unwrap();
|
||||
let c_string = CString::new(value).unwrap();
|
||||
unsafe { ffi::AMediaFormat_setString(self.as_ptr(), name.as_ptr(), c_string.as_ptr()) };
|
||||
}
|
||||
|
||||
pub fn set_buffer(&self, key: &str, value: &[u8]) {
|
||||
let name = CString::new(key).unwrap();
|
||||
unsafe {
|
||||
ffi::AMediaFormat_setBuffer(
|
||||
self.as_ptr(),
|
||||
name.as_ptr(),
|
||||
value.as_ptr().cast(),
|
||||
value.len() as ffi::size_t,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-28")]
|
||||
pub fn f64(&self, key: &str) -> Option<f64> {
|
||||
let name = CString::new(key).unwrap();
|
||||
let mut out = 0.0;
|
||||
unsafe { ffi::AMediaFormat_getDouble(self.as_ptr(), name.as_ptr(), &mut out) }.then(|| out)
|
||||
}
|
||||
|
||||
/// Returns (left, top, right, bottom)
|
||||
#[cfg(feature = "api-level-28")]
|
||||
pub fn rect(&self, key: &str) -> Option<(i32, i32, i32, i32)> {
|
||||
let name = CString::new(key).unwrap();
|
||||
let mut left = 0;
|
||||
let mut top = 0;
|
||||
let mut right = 0;
|
||||
let mut bottom = 0;
|
||||
unsafe {
|
||||
ffi::AMediaFormat_getRect(
|
||||
self.as_ptr(),
|
||||
name.as_ptr(),
|
||||
&mut left,
|
||||
&mut top,
|
||||
&mut right,
|
||||
&mut bottom,
|
||||
)
|
||||
}
|
||||
.then(|| (left, top, right, bottom))
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-28")]
|
||||
pub fn set_f64(&self, key: &str, value: f64) {
|
||||
let name = CString::new(key).unwrap();
|
||||
unsafe { ffi::AMediaFormat_setDouble(self.as_ptr(), name.as_ptr(), value) };
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-28")]
|
||||
pub fn set_rect(&self, key: &str, left: i32, top: i32, right: i32, bottom: i32) {
|
||||
let name = CString::new(key).unwrap();
|
||||
unsafe {
|
||||
ffi::AMediaFormat_setRect(self.as_ptr(), name.as_ptr(), left, top, right, bottom)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-28")]
|
||||
pub fn set_usize(&self, key: &str, value: usize) {
|
||||
let name = CString::new(key).unwrap();
|
||||
unsafe { ffi::AMediaFormat_setSize(self.as_ptr(), name.as_ptr(), value as ffi::size_t) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MediaFormat {
|
||||
fn drop(&mut self) {
|
||||
let status = unsafe { ffi::AMediaFormat_delete(self.as_ptr()) };
|
||||
NdkMediaError::from_status(status).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// A native [`AMediaCodec *`]
|
||||
///
|
||||
/// [`AMediaCodec *`]: https://developer.android.com/ndk/reference/group/media#amediacodec
|
||||
#[derive(Debug)]
|
||||
pub struct MediaCodec {
|
||||
inner: NonNull<ffi::AMediaCodec>,
|
||||
}
|
||||
|
||||
impl MediaCodec {
|
||||
fn as_ptr(&self) -> *mut ffi::AMediaCodec {
|
||||
self.inner.as_ptr()
|
||||
}
|
||||
|
||||
pub fn from_codec_name(name: &str) -> Option<Self> {
|
||||
let c_string = CString::new(name).unwrap();
|
||||
Some(Self {
|
||||
inner: NonNull::new(unsafe { ffi::AMediaCodec_createCodecByName(c_string.as_ptr()) })?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_decoder_type(mime_type: &str) -> Option<Self> {
|
||||
let c_string = CString::new(mime_type).unwrap();
|
||||
Some(Self {
|
||||
inner: NonNull::new(unsafe {
|
||||
ffi::AMediaCodec_createDecoderByType(c_string.as_ptr())
|
||||
})?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_encoder_type(mime_type: &str) -> Option<Self> {
|
||||
let c_string = CString::new(mime_type).unwrap();
|
||||
Some(Self {
|
||||
inner: NonNull::new(unsafe {
|
||||
ffi::AMediaCodec_createEncoderByType(c_string.as_ptr())
|
||||
})?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn configure(
|
||||
&self,
|
||||
format: &MediaFormat,
|
||||
surface: Option<&NativeWindow>,
|
||||
direction: MediaCodecDirection,
|
||||
) -> Result<()> {
|
||||
let status = unsafe {
|
||||
ffi::AMediaCodec_configure(
|
||||
self.as_ptr(),
|
||||
format.as_ptr(),
|
||||
surface.map_or(ptr::null_mut(), |s| s.ptr().as_ptr()),
|
||||
ptr::null_mut(),
|
||||
if direction == MediaCodecDirection::Encoder {
|
||||
ffi::AMEDIACODEC_CONFIGURE_FLAG_ENCODE as u32
|
||||
} else {
|
||||
0
|
||||
},
|
||||
)
|
||||
};
|
||||
NdkMediaError::from_status(status)
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-26")]
|
||||
pub fn create_input_surface(&self) -> Result<NativeWindow> {
|
||||
use super::construct_never_null;
|
||||
unsafe {
|
||||
let ptr = construct_never_null(|res| {
|
||||
ffi::AMediaCodec_createInputSurface(self.as_ptr(), res)
|
||||
})?;
|
||||
Ok(NativeWindow::from_ptr(ptr))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-26")]
|
||||
pub fn create_persistent_input_surface() -> Result<NativeWindow> {
|
||||
use super::construct_never_null;
|
||||
unsafe {
|
||||
let ptr =
|
||||
construct_never_null(|res| ffi::AMediaCodec_createPersistentInputSurface(res))?;
|
||||
Ok(NativeWindow::from_ptr(ptr))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns [`None`] if timeout is reached.
|
||||
pub fn dequeue_input_buffer(&self, timeout: Duration) -> Result<Option<InputBuffer>> {
|
||||
let result = unsafe {
|
||||
ffi::AMediaCodec_dequeueInputBuffer(
|
||||
self.as_ptr(),
|
||||
timeout
|
||||
.as_micros()
|
||||
.try_into()
|
||||
.expect("Supplied timeout is too large"),
|
||||
)
|
||||
};
|
||||
|
||||
if result == ffi::AMEDIACODEC_INFO_TRY_AGAIN_LATER as ffi::ssize_t {
|
||||
Ok(None)
|
||||
} else if result >= 0 {
|
||||
Ok(Some(InputBuffer {
|
||||
codec: self,
|
||||
index: result as ffi::size_t,
|
||||
}))
|
||||
} else {
|
||||
NdkMediaError::from_status(ffi::media_status_t(result as _)).map(|()| None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns [`None`] if timeout is reached.
|
||||
pub fn dequeue_output_buffer(&self, timeout: Duration) -> Result<Option<OutputBuffer>> {
|
||||
let mut info: ffi::AMediaCodecBufferInfo = unsafe { std::mem::zeroed() };
|
||||
|
||||
let result = unsafe {
|
||||
ffi::AMediaCodec_dequeueOutputBuffer(
|
||||
self.as_ptr(),
|
||||
&mut info,
|
||||
timeout
|
||||
.as_micros()
|
||||
.try_into()
|
||||
.expect("Supplied timeout is too large"),
|
||||
)
|
||||
};
|
||||
|
||||
if result == ffi::AMEDIACODEC_INFO_TRY_AGAIN_LATER as ffi::ssize_t {
|
||||
Ok(None)
|
||||
} else if result >= 0 {
|
||||
Ok(Some(OutputBuffer {
|
||||
codec: self,
|
||||
index: result as ffi::size_t,
|
||||
info,
|
||||
}))
|
||||
} else {
|
||||
NdkMediaError::from_status(ffi::media_status_t(result as _)).map(|()| None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flush(&self) -> Result<()> {
|
||||
let status = unsafe { ffi::AMediaCodec_flush(self.as_ptr()) };
|
||||
NdkMediaError::from_status(status)
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-28")]
|
||||
pub fn input_format(&self) -> MediaFormat {
|
||||
let inner =
|
||||
get_unlikely_to_be_null(|| unsafe { ffi::AMediaCodec_getInputFormat(self.as_ptr()) });
|
||||
MediaFormat { inner }
|
||||
}
|
||||
|
||||
pub fn output_format(&self) -> MediaFormat {
|
||||
let inner =
|
||||
get_unlikely_to_be_null(|| unsafe { ffi::AMediaCodec_getOutputFormat(self.as_ptr()) });
|
||||
MediaFormat { inner }
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-28")]
|
||||
pub fn name(&self) -> Result<String> {
|
||||
use super::construct;
|
||||
unsafe {
|
||||
let name_ptr = construct(|name| ffi::AMediaCodec_getName(self.as_ptr(), name))?;
|
||||
let name = CStr::from_ptr(name_ptr).to_str().unwrap().to_owned();
|
||||
ffi::AMediaCodec_releaseName(self.as_ptr(), name_ptr);
|
||||
|
||||
Ok(name)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue_input_buffer(
|
||||
&self,
|
||||
buffer: InputBuffer,
|
||||
offset: usize,
|
||||
size: usize,
|
||||
time: u64,
|
||||
flags: u32,
|
||||
) -> Result<()> {
|
||||
let status = unsafe {
|
||||
ffi::AMediaCodec_queueInputBuffer(
|
||||
self.as_ptr(),
|
||||
buffer.index as ffi::size_t,
|
||||
offset as ffi::off_t,
|
||||
size as ffi::size_t,
|
||||
time,
|
||||
flags,
|
||||
)
|
||||
};
|
||||
NdkMediaError::from_status(status)
|
||||
}
|
||||
|
||||
pub fn release_output_buffer(&self, buffer: OutputBuffer, render: bool) -> Result<()> {
|
||||
let status = unsafe {
|
||||
ffi::AMediaCodec_releaseOutputBuffer(self.as_ptr(), buffer.index as ffi::size_t, render)
|
||||
};
|
||||
NdkMediaError::from_status(status)
|
||||
}
|
||||
|
||||
pub fn release_output_buffer_at_time(
|
||||
&self,
|
||||
buffer: OutputBuffer,
|
||||
timestamp_ns: i64,
|
||||
) -> Result<()> {
|
||||
let status = unsafe {
|
||||
ffi::AMediaCodec_releaseOutputBufferAtTime(
|
||||
self.as_ptr(),
|
||||
buffer.index as ffi::size_t,
|
||||
timestamp_ns,
|
||||
)
|
||||
};
|
||||
NdkMediaError::from_status(status)
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-26")]
|
||||
pub fn set_input_surface(&self, surface: &NativeWindow) -> Result<()> {
|
||||
let status =
|
||||
unsafe { ffi::AMediaCodec_setInputSurface(self.as_ptr(), surface.ptr().as_ptr()) };
|
||||
NdkMediaError::from_status(status)
|
||||
}
|
||||
|
||||
pub fn set_output_surface(&self, surface: &NativeWindow) -> Result<()> {
|
||||
let status =
|
||||
unsafe { ffi::AMediaCodec_setOutputSurface(self.as_ptr(), surface.ptr().as_ptr()) };
|
||||
NdkMediaError::from_status(status)
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-26")]
|
||||
pub fn set_parameters(&self, params: MediaFormat) -> Result<()> {
|
||||
let status = unsafe { ffi::AMediaCodec_setParameters(self.as_ptr(), params.as_ptr()) };
|
||||
NdkMediaError::from_status(status)
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-26")]
|
||||
pub fn set_signal_end_of_input_stream(&self) -> Result<()> {
|
||||
let status = unsafe { ffi::AMediaCodec_signalEndOfInputStream(self.as_ptr()) };
|
||||
NdkMediaError::from_status(status)
|
||||
}
|
||||
|
||||
pub fn start(&self) -> Result<()> {
|
||||
let status = unsafe { ffi::AMediaCodec_start(self.as_ptr()) };
|
||||
NdkMediaError::from_status(status)
|
||||
}
|
||||
|
||||
pub fn stop(&self) -> Result<()> {
|
||||
let status = unsafe { ffi::AMediaCodec_stop(self.as_ptr()) };
|
||||
NdkMediaError::from_status(status)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MediaCodec {
|
||||
fn drop(&mut self) {
|
||||
let status = unsafe { ffi::AMediaCodec_delete(self.as_ptr()) };
|
||||
NdkMediaError::from_status(status).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InputBuffer<'a> {
|
||||
codec: &'a MediaCodec,
|
||||
index: ffi::size_t,
|
||||
}
|
||||
|
||||
impl InputBuffer<'_> {
|
||||
pub fn buffer_mut(&mut self) -> &mut [u8] {
|
||||
unsafe {
|
||||
let mut out_size = 0;
|
||||
let buffer_ptr =
|
||||
ffi::AMediaCodec_getInputBuffer(self.codec.as_ptr(), self.index, &mut out_size);
|
||||
assert!(!buffer_ptr.is_null());
|
||||
slice::from_raw_parts_mut(buffer_ptr, out_size as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OutputBuffer<'a> {
|
||||
codec: &'a MediaCodec,
|
||||
index: ffi::size_t,
|
||||
info: ffi::AMediaCodecBufferInfo,
|
||||
}
|
||||
|
||||
impl OutputBuffer<'_> {
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
unsafe {
|
||||
let mut _out_size = 0;
|
||||
let buffer_ptr =
|
||||
ffi::AMediaCodec_getOutputBuffer(self.codec.as_ptr(), self.index, &mut _out_size);
|
||||
assert!(!buffer_ptr.is_null());
|
||||
slice::from_raw_parts(
|
||||
buffer_ptr.add(self.info.offset as usize),
|
||||
self.info.size as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-28")]
|
||||
pub fn format(&self) -> MediaFormat {
|
||||
let inner = get_unlikely_to_be_null(|| unsafe {
|
||||
ffi::AMediaCodec_getBufferFormat(self.codec.as_ptr(), self.index)
|
||||
});
|
||||
MediaFormat { inner }
|
||||
}
|
||||
|
||||
pub fn flags(&self) -> u32 {
|
||||
self.info.flags
|
||||
}
|
||||
|
||||
pub fn presentation_time_us(&self) -> i64 {
|
||||
self.info.presentationTimeUs
|
||||
}
|
||||
}
|
||||
41
third-party/vendor/ndk/src/media/mod.rs
vendored
Normal file
41
third-party/vendor/ndk/src/media/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//! Bindings for the NDK media classes.
|
||||
//!
|
||||
//! See also [the NDK docs](https://developer.android.com/ndk/reference/group/media)
|
||||
#![cfg(feature = "media")]
|
||||
|
||||
mod error;
|
||||
pub mod image_reader;
|
||||
pub mod media_codec;
|
||||
|
||||
pub use error::NdkMediaError;
|
||||
use std::{mem::MaybeUninit, ptr::NonNull};
|
||||
|
||||
pub type Result<T, E = NdkMediaError> = std::result::Result<T, E>;
|
||||
|
||||
fn construct<T>(with_ptr: impl FnOnce(*mut T) -> ffi::media_status_t) -> Result<T> {
|
||||
let mut result = MaybeUninit::uninit();
|
||||
let status = with_ptr(result.as_mut_ptr());
|
||||
NdkMediaError::from_status(status).map(|()| unsafe { result.assume_init() })
|
||||
}
|
||||
|
||||
fn construct_never_null<T>(
|
||||
with_ptr: impl FnOnce(*mut *mut T) -> ffi::media_status_t,
|
||||
) -> Result<NonNull<T>> {
|
||||
let result = construct(with_ptr)?;
|
||||
let non_null = if cfg!(debug_assertions) {
|
||||
NonNull::new(result).expect("result should never be null")
|
||||
} else {
|
||||
unsafe { NonNull::new_unchecked(result) }
|
||||
};
|
||||
Ok(non_null)
|
||||
}
|
||||
|
||||
/// Function is not expected to ever return `null`, but this
|
||||
/// cannot be validated through the Android documentation.
|
||||
///
|
||||
/// As such this function always asserts on `null` values,
|
||||
/// even when `cfg!(debug_assertions)` is disabled.
|
||||
fn get_unlikely_to_be_null<T>(get_ptr: impl FnOnce() -> *mut T) -> NonNull<T> {
|
||||
let result = get_ptr();
|
||||
NonNull::new(result).expect("result should never be null")
|
||||
}
|
||||
250
third-party/vendor/ndk/src/native_activity.rs
vendored
Normal file
250
third-party/vendor/ndk/src/native_activity.rs
vendored
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
//! Bindings for [`ANativeActivity`]
|
||||
//!
|
||||
//! [`ANativeActivity`]: https://developer.android.com/ndk/reference/group/native-activity#anativeactivity
|
||||
|
||||
use super::hardware_buffer_format::HardwareBufferFormat;
|
||||
use bitflags::bitflags;
|
||||
use std::{
|
||||
ffi::{CStr, OsStr},
|
||||
os::{raw::c_void, unix::prelude::OsStrExt},
|
||||
path::Path,
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
||||
bitflags! {
|
||||
/// Window flags, as per the Java API at [`android.view.WindowManager.LayoutParams`].
|
||||
///
|
||||
/// <https://developer.android.com/ndk/reference/group/native-activity#group___native_activity_1ga2f1398dba5e4a5616b83437528bdb28e>
|
||||
///
|
||||
/// [`android.view.WindowManager.LayoutParams`]: https://developer.android.com/reference/android/view/WindowManager.LayoutParams
|
||||
pub struct WindowFlags: u32 {
|
||||
const ALLOW_LOCK_WHILE_SCREEN_ON = ffi::AWINDOW_FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
|
||||
const DIM_BEHIND = ffi::AWINDOW_FLAG_DIM_BEHIND;
|
||||
#[deprecated = "Deprecated. Blurring is no longer supported."]
|
||||
const BLUR_BEHIND = ffi::AWINDOW_FLAG_BLUR_BEHIND;
|
||||
const NOT_FOCUSABLE = ffi::AWINDOW_FLAG_NOT_FOCUSABLE;
|
||||
const NOT_TOUCHABLE = ffi::AWINDOW_FLAG_NOT_TOUCHABLE;
|
||||
const NOT_TOUCH_MODAL = ffi::AWINDOW_FLAG_NOT_TOUCH_MODAL;
|
||||
#[deprecated = "This constant was deprecated in API level 20. This flag has no effect."]
|
||||
const TOUCHABLE_WHEN_WAKING = ffi::AWINDOW_FLAG_TOUCHABLE_WHEN_WAKING;
|
||||
const KEEP_SCREEN_ON = ffi::AWINDOW_FLAG_KEEP_SCREEN_ON;
|
||||
const LAYOUT_IN_SCREEN = ffi::AWINDOW_FLAG_LAYOUT_IN_SCREEN;
|
||||
const LAYOUT_NO_LIMITS = ffi::AWINDOW_FLAG_LAYOUT_NO_LIMITS;
|
||||
const FULLSCREEN = ffi::AWINDOW_FLAG_FULLSCREEN;
|
||||
#[cfg_attr(feature = "api-level-30", deprecated = "This constant was deprecated in API level 30. This value became API \"by accident\", and shouldn't be used by 3rd party applications.")]
|
||||
const FORCE_NOT_FULLSCREEN = ffi::AWINDOW_FLAG_FORCE_NOT_FULLSCREEN;
|
||||
#[deprecated = "This constant was deprecated in API level 17. This flag is no longer used."]
|
||||
const DITHER = ffi::AWINDOW_FLAG_DITHER;
|
||||
const SECURE = ffi::AWINDOW_FLAG_SECURE;
|
||||
const SCALED = ffi::AWINDOW_FLAG_SCALED;
|
||||
const IGNORE_CHEEK_PRESSES = ffi::AWINDOW_FLAG_IGNORE_CHEEK_PRESSES;
|
||||
const LAYOUT_INSET_DECOR = ffi::AWINDOW_FLAG_LAYOUT_INSET_DECOR;
|
||||
const ALT_FOCUSABLE_IM = ffi::AWINDOW_FLAG_ALT_FOCUSABLE_IM;
|
||||
const WATCH_OUTSIDE_TOUCH = ffi::AWINDOW_FLAG_WATCH_OUTSIDE_TOUCH;
|
||||
const SHOW_WHEN_LOCKED = ffi::AWINDOW_FLAG_SHOW_WHEN_LOCKED;
|
||||
const SHOW_WALLPAPER = ffi::AWINDOW_FLAG_SHOW_WALLPAPER;
|
||||
const TURN_SCREEN_ON = ffi::AWINDOW_FLAG_TURN_SCREEN_ON;
|
||||
#[cfg_attr(feature = "api-level-26", deprecated = "This constant was deprecated in API level 26. Use `SHOW_WHEN_LOCKED` instead.")]
|
||||
const DISMISS_KEYGUARD = ffi::AWINDOW_FLAG_DISMISS_KEYGUARD;
|
||||
const ATTACHED_IN_DECOR = 0x40000000;
|
||||
}
|
||||
}
|
||||
|
||||
/// A native [`ANativeActivity *`]
|
||||
///
|
||||
/// This is either provided in [`ffi::ANativeActivity_onCreate()`], or accessible through
|
||||
/// `ndk_glue::native_activity()`.
|
||||
///
|
||||
/// [`ANativeActivity *`]: https://developer.android.com/ndk/reference/struct/a-native-activity
|
||||
#[derive(Debug)]
|
||||
pub struct NativeActivity {
|
||||
ptr: NonNull<ffi::ANativeActivity>,
|
||||
}
|
||||
|
||||
// It gets shared between threads in `ndk-glue`
|
||||
unsafe impl Send for NativeActivity {}
|
||||
unsafe impl Sync for NativeActivity {}
|
||||
|
||||
impl NativeActivity {
|
||||
/// Create a `NativeActivity` from a pointer
|
||||
///
|
||||
/// # Safety
|
||||
/// By calling this function, you assert that it is a valid pointer to a native
|
||||
/// `ANativeActivity`.
|
||||
pub unsafe fn from_ptr(ptr: NonNull<ffi::ANativeActivity>) -> Self {
|
||||
Self { ptr }
|
||||
}
|
||||
|
||||
/// The pointer to the native `ANativeActivity`
|
||||
pub fn ptr(&self) -> NonNull<ffi::ANativeActivity> {
|
||||
self.ptr
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods that relate to fields of the struct itself
|
||||
///
|
||||
/// The relevant NDK docs can be found
|
||||
/// [here](https://developer.android.com/ndk/reference/struct/a-native-activity).
|
||||
impl NativeActivity {
|
||||
/// The platform's SDK version code
|
||||
pub fn sdk_version(&self) -> i32 {
|
||||
unsafe { self.ptr.as_ref().sdkVersion }
|
||||
}
|
||||
|
||||
/// Path to this application's internal data directory
|
||||
pub fn internal_data_path(&self) -> &Path {
|
||||
OsStr::from_bytes(unsafe { CStr::from_ptr(self.ptr.as_ref().internalDataPath) }.to_bytes())
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
/// Path to this application's external (removable, mountable) data directory
|
||||
pub fn external_data_path(&self) -> &Path {
|
||||
OsStr::from_bytes(unsafe { CStr::from_ptr(self.ptr.as_ref().externalDataPath) }.to_bytes())
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
/// This app's asset manager, which can be used to access assets from the `.apk` file.
|
||||
pub fn asset_manager(&self) -> crate::asset::AssetManager {
|
||||
unsafe {
|
||||
crate::asset::AssetManager::from_ptr(
|
||||
NonNull::new(self.ptr.as_ref().assetManager).unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Instance data associated with the activity
|
||||
pub fn instance(&self) -> *mut c_void {
|
||||
unsafe { self.ptr.as_ref().instance }
|
||||
}
|
||||
|
||||
/// Set the instance data associated with the activity
|
||||
///
|
||||
/// # Safety
|
||||
/// This can invalidate assumptions held by `ndk-glue`, as well as cause data
|
||||
/// races with concurrent access to the instance data.
|
||||
pub unsafe fn set_instance(&mut self, data: *mut c_void) {
|
||||
// FIXME Does this create undefined behavior by creating a mutable reference to what could
|
||||
// also be accessed immutably at the same time?
|
||||
//
|
||||
// I think that as long as we warn the users to avoid concurrent access, and we pass along
|
||||
// the `unsafe` burden, it's OK.
|
||||
self.ptr.as_mut().instance = data;
|
||||
}
|
||||
|
||||
/// This process's `JavaVM` object.
|
||||
///
|
||||
/// Usage with [__jni__](https://crates.io/crates/jni) crate:
|
||||
/// ```no_run
|
||||
/// # use ndk::native_activity::NativeActivity;
|
||||
/// # let native_activity: NativeActivity = unimplemented!();
|
||||
/// let vm_ptr = native_activity.vm();
|
||||
/// let vm = unsafe { jni::JavaVM::from_raw(vm_ptr) }.unwrap();
|
||||
/// let env = vm.attach_current_thread();
|
||||
/// // Do JNI with env ...
|
||||
/// ```
|
||||
///
|
||||
/// Usage with [__jni-glue__](https://crates.io/crates/jni-glue) crate:
|
||||
/// ```no_run
|
||||
/// # use ndk::native_activity::NativeActivity;
|
||||
/// # let native_activity: NativeActivity = unimplemented!();
|
||||
/// let vm_ptr = native_activity.vm();
|
||||
/// let vm = unsafe { jni_glue::VM::from_jni_local(&*vm_ptr) };
|
||||
/// vm.with_env(|env| {
|
||||
/// // Do JNI with env ...
|
||||
/// });
|
||||
/// ```
|
||||
pub fn vm(&self) -> *mut jni_sys::JavaVM {
|
||||
unsafe { self.ptr.as_ref() }.vm
|
||||
}
|
||||
|
||||
/// The [`android.app.NativeActivity`] instance
|
||||
///
|
||||
/// In the JNI, this is named `clazz`; however, as the docs say, "it should really be named
|
||||
/// 'activity' instead of 'clazz', since it's a reference to the NativeActivity instance".
|
||||
///
|
||||
/// [`android.app.NativeActivity`]: https://developer.android.com/reference/android/app/NativeActivity
|
||||
pub fn activity(&self) -> jni_sys::jobject {
|
||||
unsafe { self.ptr.as_ref() }.clazz
|
||||
}
|
||||
|
||||
/// Path to the directory with the application's OBB files.
|
||||
///
|
||||
/// # Safety
|
||||
/// Only available as of Honeycomb (Android 3.0+, API level 11+)
|
||||
pub unsafe fn obb_path(&self) -> &Path {
|
||||
OsStr::from_bytes(CStr::from_ptr(self.ptr.as_ref().obbPath).to_bytes()).as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods that relate to `ANativeActivity_*` functions.
|
||||
///
|
||||
/// The relevant NDK docs can be found
|
||||
/// [here](https://developer.android.com/ndk/reference/group/native-activity).
|
||||
impl NativeActivity {
|
||||
/// Sends a destroy event to the activity and stops it.
|
||||
pub fn finish(&self) {
|
||||
unsafe { ffi::ANativeActivity_finish(self.ptr.as_ptr()) }
|
||||
}
|
||||
|
||||
/// Shows the IME (the on-screen keyboard).
|
||||
///
|
||||
/// If `force` is true, the `SHOW_FORCED` flag is used; otherwise, the `SHOW_IMPLICIT` flag is
|
||||
/// used. Depending on the value of this flag, the `hide_soft_input` method with behave
|
||||
/// differently. See [the relevant
|
||||
/// javadoc](https://developer.android.com/reference/android/view/inputmethod/InputMethodManager#constants_2)
|
||||
/// for more information.
|
||||
pub fn show_soft_input(&self, force: bool) {
|
||||
let flag = if force {
|
||||
ffi::ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED
|
||||
} else {
|
||||
ffi::ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT
|
||||
};
|
||||
unsafe { ffi::ANativeActivity_showSoftInput(self.ptr.as_ptr(), flag) }
|
||||
}
|
||||
|
||||
/// Hides the IME (the on-screen keyboard).
|
||||
///
|
||||
/// If `not_always` is true, the `HIDE_NOT_ALWAYS` flag is used; otherwise, the
|
||||
/// `HIDE_IMPLICIT_ONLY` flag is used. Depending on the value of this flag and the way the IME
|
||||
/// was shown, it may or may not be hidden. See [the relevant
|
||||
/// javadoc](https://developer.android.com/reference/android/view/inputmethod/InputMethodManager#constants_2)
|
||||
/// for more information.
|
||||
pub fn hide_soft_input(&self, not_always: bool) {
|
||||
let flag = if not_always {
|
||||
ffi::ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS
|
||||
} else {
|
||||
ffi::ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY
|
||||
};
|
||||
unsafe { ffi::ANativeActivity_hideSoftInput(self.ptr.as_ptr(), flag) }
|
||||
}
|
||||
|
||||
/// Change the window format of the given activity.
|
||||
///
|
||||
/// Calls [`getWindow().setFormat()`] of the given activity. Note that this method can be
|
||||
/// called from any thread; it will send a message to the main thread of the process where the
|
||||
/// Java finish call will take place.
|
||||
///
|
||||
/// [`getWindow().setFormat()`]: https://developer.android.com/reference/android/view/Window#setFormat(int)
|
||||
pub fn set_window_format(&self, format: HardwareBufferFormat) {
|
||||
let format: u32 = format.into();
|
||||
unsafe { ffi::ANativeActivity_setWindowFormat(self.ptr.as_ptr(), format as i32) }
|
||||
}
|
||||
|
||||
/// Change the window flags of the given activity.
|
||||
///
|
||||
/// Calls [`getWindow().setFlags()`] of the given activity.
|
||||
///
|
||||
/// Note that this method can be called from any thread; it will send a message to the main
|
||||
/// thread of the process where the Java finish call will take place.
|
||||
///
|
||||
/// [`getWindow().setFlags()`]: https://developer.android.com/reference/android/view/Window#setFlags(int,%20int)
|
||||
pub fn set_window_flags(&self, add_flags: WindowFlags, remove_flags: WindowFlags) {
|
||||
unsafe {
|
||||
ffi::ANativeActivity_setWindowFlags(
|
||||
self.ptr.as_ptr(),
|
||||
add_flags.bits(),
|
||||
remove_flags.bits(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
129
third-party/vendor/ndk/src/native_window.rs
vendored
Normal file
129
third-party/vendor/ndk/src/native_window.rs
vendored
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
//! Bindings for [`ANativeWindow`]
|
||||
//!
|
||||
//! [`ANativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window#anativewindow
|
||||
|
||||
use crate::utils::status_to_io_result;
|
||||
|
||||
pub use super::hardware_buffer_format::HardwareBufferFormat;
|
||||
use jni_sys::{jobject, JNIEnv};
|
||||
use raw_window_handle::{AndroidNdkWindowHandle, HasRawWindowHandle, RawWindowHandle};
|
||||
use std::{convert::TryFrom, ffi::c_void, io::Result, ptr::NonNull};
|
||||
|
||||
// [`NativeWindow`] represents the producer end of an image queue
|
||||
///
|
||||
/// It is the C counterpart of the [`android.view.Surface`] object in Java, and can be converted
|
||||
/// both ways. Depending on the consumer, images submitted to [`NativeWindow`] can be shown on the
|
||||
/// display or sent to other consumers, such as video encoders.
|
||||
///
|
||||
/// [`android.view.Surface`]: https://developer.android.com/reference/android/view/Surface
|
||||
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct NativeWindow {
|
||||
ptr: NonNull<ffi::ANativeWindow>,
|
||||
}
|
||||
|
||||
unsafe impl Send for NativeWindow {}
|
||||
unsafe impl Sync for NativeWindow {}
|
||||
|
||||
impl Drop for NativeWindow {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::ANativeWindow_release(self.ptr.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for NativeWindow {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe { ffi::ANativeWindow_acquire(self.ptr.as_ptr()) }
|
||||
Self { ptr: self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl HasRawWindowHandle for NativeWindow {
|
||||
fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
let mut handle = AndroidNdkWindowHandle::empty();
|
||||
handle.a_native_window = self.ptr.as_ptr() as *mut c_void;
|
||||
RawWindowHandle::AndroidNdk(handle)
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeWindow {
|
||||
/// Assumes ownership of `ptr`
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must be a valid pointer to an Android [`ffi::ANativeWindow`].
|
||||
pub unsafe fn from_ptr(ptr: NonNull<ffi::ANativeWindow>) -> Self {
|
||||
Self { ptr }
|
||||
}
|
||||
|
||||
/// Acquires ownership of `ptr`
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must be a valid pointer to an Android [`ffi::ANativeWindow`].
|
||||
pub unsafe fn clone_from_ptr(ptr: NonNull<ffi::ANativeWindow>) -> Self {
|
||||
ffi::ANativeWindow_acquire(ptr.as_ptr());
|
||||
Self::from_ptr(ptr)
|
||||
}
|
||||
|
||||
pub fn ptr(&self) -> NonNull<ffi::ANativeWindow> {
|
||||
self.ptr
|
||||
}
|
||||
|
||||
pub fn height(&self) -> i32 {
|
||||
unsafe { ffi::ANativeWindow_getHeight(self.ptr.as_ptr()) }
|
||||
}
|
||||
|
||||
pub fn width(&self) -> i32 {
|
||||
unsafe { ffi::ANativeWindow_getWidth(self.ptr.as_ptr()) }
|
||||
}
|
||||
|
||||
/// Return the current pixel format ([`HardwareBufferFormat`]) of the window surface.
|
||||
pub fn format(&self) -> HardwareBufferFormat {
|
||||
let value = unsafe { ffi::ANativeWindow_getFormat(self.ptr.as_ptr()) };
|
||||
let value = u32::try_from(value).unwrap();
|
||||
HardwareBufferFormat::try_from(value).unwrap()
|
||||
}
|
||||
|
||||
/// Change the format and size of the window buffers.
|
||||
///
|
||||
/// The width and height control the number of pixels in the buffers, not the dimensions of the
|
||||
/// window on screen. If these are different than the window's physical size, then its buffer
|
||||
/// will be scaled to match that size when compositing it to the screen. The width and height
|
||||
/// must be either both zero or both non-zero.
|
||||
///
|
||||
/// For all of these parameters, if `0` or [`None`] is supplied then the window's base value
|
||||
/// will come back in force.
|
||||
pub fn set_buffers_geometry(
|
||||
&self,
|
||||
width: i32,
|
||||
height: i32,
|
||||
format: Option<HardwareBufferFormat>,
|
||||
) -> Result<()> {
|
||||
let format: u32 = format.map_or(0, |f| f.into());
|
||||
let status = unsafe {
|
||||
ffi::ANativeWindow_setBuffersGeometry(self.ptr.as_ptr(), width, height, format as i32)
|
||||
};
|
||||
status_to_io_result(status, ())
|
||||
}
|
||||
|
||||
/// Return the [`NativeWindow`] associated with a JNI [`android.view.Surface`] pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// By calling this function, you assert that `env` is a valid pointer to a [`JNIEnv`] and
|
||||
/// `surface` is a valid pointer to an [`android.view.Surface`].
|
||||
///
|
||||
/// [`android.view.Surface`]: https://developer.android.com/reference/android/view/Surface
|
||||
pub unsafe fn from_surface(env: *mut JNIEnv, surface: jobject) -> Option<Self> {
|
||||
let ptr = ffi::ANativeWindow_fromSurface(env, surface);
|
||||
Some(Self::from_ptr(NonNull::new(ptr)?))
|
||||
}
|
||||
|
||||
/// Return a JNI [`android.view.Surface`] pointer derived from this [`NativeWindow`].
|
||||
///
|
||||
/// # Safety
|
||||
/// By calling this function, you assert that `env` is a valid pointer to a [`JNIEnv`].
|
||||
///
|
||||
/// [`android.view.Surface`]: https://developer.android.com/reference/android/view/Surface
|
||||
#[cfg(feature = "api-level-26")]
|
||||
pub unsafe fn to_surface(&self, env: *mut JNIEnv) -> jobject {
|
||||
ffi::ANativeWindow_toSurface(env, self.ptr().as_ptr())
|
||||
}
|
||||
}
|
||||
159
third-party/vendor/ndk/src/surface_texture.rs
vendored
Normal file
159
third-party/vendor/ndk/src/surface_texture.rs
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
//! Bindings for [`ASurfaceTexture`]
|
||||
//!
|
||||
//! See <https://source.android.com/devices/graphics/arch-st> for an architectural overview of
|
||||
//! [`SurfaceTexture`] internals.
|
||||
//!
|
||||
//! [`ASurfaceTexture`]: https://developer.android.com/ndk/reference/group/surface-texture
|
||||
#![cfg(feature = "api-level-28")]
|
||||
|
||||
use crate::{native_window::NativeWindow, utils::status_to_io_result};
|
||||
use jni_sys::{jobject, JNIEnv};
|
||||
use std::{convert::TryInto, io::Result, ptr::NonNull, time::Duration};
|
||||
|
||||
/// An opaque type to manage [`android.graphics.SurfaceTexture`] from native code
|
||||
///
|
||||
/// [`android.graphics.SurfaceTexture`]: https://developer.android.com/reference/android/graphics/SurfaceTexture
|
||||
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct SurfaceTexture {
|
||||
ptr: NonNull<ffi::ASurfaceTexture>,
|
||||
}
|
||||
|
||||
unsafe impl Send for SurfaceTexture {}
|
||||
|
||||
impl Drop for SurfaceTexture {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::ASurfaceTexture_release(self.ptr.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl SurfaceTexture {
|
||||
/// Assumes ownership of `ptr`
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must be a valid pointer to an Android [`ffi::ASurfaceTexture`].
|
||||
pub unsafe fn from_ptr(ptr: NonNull<ffi::ASurfaceTexture>) -> Self {
|
||||
Self { ptr }
|
||||
}
|
||||
|
||||
/// Get a reference to the native [`SurfaceTexture`] from the corresponding Java object.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function should be called with a healthy JVM pointer and with a non-null
|
||||
/// [`android.graphics.SurfaceTexture`], which must be kept alive on the Java/Kotlin side.
|
||||
///
|
||||
/// The caller must keep a reference to the Java [`android.graphics.SurfaceTexture`] during the
|
||||
/// lifetime of the returned [`SurfaceTexture`]. Failing to do so could result in the
|
||||
/// [`SurfaceTexture`] to stop functioning properly once the Java object gets finalized.
|
||||
/// However, this will not result in program termination.
|
||||
///
|
||||
/// [`android.graphics.SurfaceTexture`]: https://developer.android.com/reference/android/graphics/SurfaceTexture
|
||||
pub unsafe fn from_surface_texture(env: *mut JNIEnv, surface_texture: jobject) -> Option<Self> {
|
||||
let a_surface_texture_ptr = ffi::ASurfaceTexture_fromSurfaceTexture(env, surface_texture);
|
||||
let s = NonNull::new(a_surface_texture_ptr)?;
|
||||
Some(SurfaceTexture::from_ptr(s))
|
||||
}
|
||||
|
||||
/// Returns a pointer to the native [`ffi::ASurfaceTexture`].
|
||||
pub fn ptr(&self) -> NonNull<ffi::ASurfaceTexture> {
|
||||
self.ptr
|
||||
}
|
||||
|
||||
/// Returns a reference to a [`NativeWindow`] (i.e. the Producer) for this [`SurfaceTexture`].
|
||||
///
|
||||
/// This is equivalent to Java's:
|
||||
/// ```java
|
||||
/// Surface sur = new Surface(surfaceTexture);
|
||||
/// ```
|
||||
pub fn acquire_native_window(&self) -> Option<NativeWindow> {
|
||||
let native_window = unsafe { ffi::ASurfaceTexture_acquireANativeWindow(self.ptr.as_ptr()) };
|
||||
let n = NonNull::new(native_window)?;
|
||||
Some(unsafe { NativeWindow::from_ptr(n) })
|
||||
}
|
||||
|
||||
/// Attach the [`SurfaceTexture`] to the OpenGL ES context that is current on the calling
|
||||
/// thread.
|
||||
///
|
||||
/// A new OpenGL ES texture object is created and populated with the [`SurfaceTexture`] image
|
||||
/// frame that was current at the time of the last call to
|
||||
/// [`detach_from_gl_context()`][Self::detach_from_gl_context()]. This new texture is bound to
|
||||
/// the `GL_TEXTURE_EXTERNAL_OES` texture target.
|
||||
///
|
||||
/// This can be used to access the [`SurfaceTexture`] image contents from multiple OpenGL ES
|
||||
/// contexts. Note, however, that the image contents are only accessible from one OpenGL ES
|
||||
/// context at a time.
|
||||
pub fn attach_to_gl_context(&self, tex_name: u32) -> Result<()> {
|
||||
let status = unsafe { ffi::ASurfaceTexture_attachToGLContext(self.ptr.as_ptr(), tex_name) };
|
||||
status_to_io_result(status, ())
|
||||
}
|
||||
|
||||
/// Detach the [`SurfaceTexture`] from the OpenGL ES context that owns the OpenGL ES texture
|
||||
/// object.
|
||||
///
|
||||
/// This call must be made with the OpenGL ES context current on the calling thread. The OpenGL
|
||||
/// ES texture object will be deleted as a result of this call. After calling this method all
|
||||
/// calls to [`update_tex_image()`][Self::update_tex_image()] will fail until a successful call
|
||||
/// to [`attach_to_gl_context()`][Self::attach_to_gl_context()] is made.
|
||||
///
|
||||
/// This can be used to access the [`SurfaceTexture`] image contents from multiple OpenGL ES
|
||||
/// contexts. Note, however, that the image contents are only accessible from one OpenGL ES
|
||||
/// context at a time.
|
||||
pub fn detach_from_gl_context(&self) -> Result<()> {
|
||||
let status = unsafe { ffi::ASurfaceTexture_detachFromGLContext(self.ptr.as_ptr()) };
|
||||
status_to_io_result(status, ())
|
||||
}
|
||||
|
||||
/// Retrieve the 4x4 texture coordinate transform matrix associated with the texture image set
|
||||
/// by the most recent call to [`update_tex_image()`][Self::update_tex_image()].
|
||||
///
|
||||
/// This transform matrix maps 2D homogeneous texture coordinates of the form `(s, t, 0, 1)`
|
||||
/// with `s` and `t` in the inclusive range `[0, 1]` to the texture coordinate that should be
|
||||
/// used to sample that location from the texture. Sampling the texture outside of the range of
|
||||
/// this transform is undefined.
|
||||
///
|
||||
/// The matrix is stored in column-major order so that it may be passed directly to OpenGL ES
|
||||
/// via the [`glLoadMatrixf()`] or [`glUniformMatrix4fv()`] functions.
|
||||
///
|
||||
/// [`glLoadMatrixf()`]: https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glLoadMatrix.xml
|
||||
/// [`gluniformmatrix4fv()`]: https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glUniform.xhtml
|
||||
pub fn transform_matrix(&self) -> [f32; 16] {
|
||||
let mut r = [0f32; 16];
|
||||
unsafe { ffi::ASurfaceTexture_getTransformMatrix(self.ptr.as_ptr(), r.as_mut_ptr()) };
|
||||
r
|
||||
}
|
||||
|
||||
/// Retrieve the timestamp associated with the texture image set by the most recent call to
|
||||
/// [`update_tex_image()`][Self::update_tex_image()].
|
||||
///
|
||||
/// This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp
|
||||
/// should be unaffected by time-of-day adjustments, and for a camera should be strictly
|
||||
/// monotonic but for a [`MediaPlayer`] may be reset when the position is set. The specific
|
||||
/// meaning and zero point of the timestamp depends on the source providing images to the
|
||||
/// [`SurfaceTexture`]. Unless otherwise specified by the image source, timestamps cannot
|
||||
/// generally be compared across [`SurfaceTexture`] instances, or across multiple program
|
||||
/// invocations. It is mostly useful for determining time offsets between subsequent frames.
|
||||
///
|
||||
/// For EGL/Vulkan producers, this timestamp is the desired present time set with the
|
||||
/// [`EGL_ANDROID_presentation_time`] or [`VK_GOOGLE_display_timing`] extensions.
|
||||
///
|
||||
/// [`MediaPlayer`]: https://developer.android.com/reference/android/media/MediaPlayer
|
||||
/// [`EGL_ANDROID_presentation_time`]: https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_presentation_time.txt
|
||||
/// [`VK_GOOGLE_display_timing`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_GOOGLE_display_timing.html
|
||||
pub fn timestamp(&self) -> Duration {
|
||||
Duration::from_nanos(
|
||||
unsafe { ffi::ASurfaceTexture_getTimestamp(self.ptr.as_ptr()) }
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Update the texture image to the most recent frame from the image stream.
|
||||
///
|
||||
/// This may only be called while the OpenGL ES context that owns the texture is current on the
|
||||
/// calling thread. It will implicitly bind its texture to the `GL_TEXTURE_EXTERNAL_OES`
|
||||
/// texture target.
|
||||
pub fn update_tex_image(&self) -> Result<()> {
|
||||
let status = unsafe { ffi::ASurfaceTexture_updateTexImage(self.ptr.as_ptr()) };
|
||||
status_to_io_result(status, ())
|
||||
}
|
||||
}
|
||||
92
third-party/vendor/ndk/src/trace.rs
vendored
Normal file
92
third-party/vendor/ndk/src/trace.rs
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
//! Bindings for the NDK tracing API.
|
||||
//!
|
||||
//! See also [the NDK docs](https://developer.android.com/ndk/reference/group/tracing)
|
||||
#![cfg(feature = "api-level-23")]
|
||||
use std::ffi::{CString, NulError};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub fn is_trace_enabled() -> bool {
|
||||
unsafe { ffi::ATrace_isEnabled() }
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Section {
|
||||
// Section is !Sync and !Send
|
||||
_pd: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
impl Section {
|
||||
pub fn new(name: &str) -> Result<Self, NulError> {
|
||||
let section_name = CString::new(name)?;
|
||||
unsafe { ffi::ATrace_beginSection(section_name.as_ptr()) };
|
||||
|
||||
Ok(Self { _pd: PhantomData })
|
||||
}
|
||||
|
||||
pub fn end(self) {
|
||||
std::mem::drop(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Section {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::ATrace_endSection() };
|
||||
}
|
||||
}
|
||||
|
||||
/// Unique identifier for distinguishing simultaneous events
|
||||
#[derive(Debug)]
|
||||
#[cfg(feature = "api-level-29")]
|
||||
pub struct Cookie(pub i32);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg(feature = "api-level-29")]
|
||||
pub struct AsyncSection {
|
||||
section_name: CString,
|
||||
cookie: Cookie,
|
||||
// AsyncSection is !Sync
|
||||
_pd: PhantomData<&'static ()>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-29")]
|
||||
impl AsyncSection {
|
||||
pub fn new(name: &str, cookie: Cookie) -> Result<Self, NulError> {
|
||||
let section_name = CString::new(name)?;
|
||||
unsafe { ffi::ATrace_beginAsyncSection(section_name.as_ptr(), cookie.0) };
|
||||
|
||||
Ok(Self {
|
||||
section_name,
|
||||
cookie,
|
||||
_pd: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn end(self) {
|
||||
std::mem::drop(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-29")]
|
||||
impl Drop for AsyncSection {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::ATrace_endAsyncSection(self.section_name.as_ptr(), self.cookie.0) };
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-29")]
|
||||
#[derive(Debug)]
|
||||
pub struct Counter {
|
||||
name: CString,
|
||||
}
|
||||
|
||||
#[cfg(feature = "api-level-29")]
|
||||
impl Counter {
|
||||
pub fn new(name: &str) -> Result<Self, NulError> {
|
||||
let name = CString::new(name)?;
|
||||
Ok(Self { name })
|
||||
}
|
||||
|
||||
pub fn set_value(&self, value: i64) {
|
||||
unsafe { ffi::ATrace_setCounter(self.name.as_ptr(), value) }
|
||||
}
|
||||
}
|
||||
14
third-party/vendor/ndk/src/utils.rs
vendored
Normal file
14
third-party/vendor/ndk/src/utils.rs
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//! Internal utilities
|
||||
use std::io::{Error, Result};
|
||||
|
||||
/// Turns standard `<errno.h>` status codes - typically rewrapped by Android's [`Errors.h`] - into
|
||||
/// Rust's [`std::io::Error`].
|
||||
///
|
||||
/// [`Errors.h`]: https://cs.android.com/android/platform/superproject/+/master:system/core/libutils/include/utils/Errors.h
|
||||
pub(crate) fn status_to_io_result<T>(status: i32, value: T) -> Result<T> {
|
||||
match status {
|
||||
0 => Ok(value),
|
||||
r if r < 0 => Err(Error::from_raw_os_error(-r)),
|
||||
r => unreachable!("Status is positive integer {}", r),
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue