#![allow(non_snake_case)]
use crate::boundary::{
MutationBatch, RendererApi, STATUS_OK, STATUS_PROTOCOL_ERROR, current_arena,
install_renderer_api,
};
use jni::JNIEnv;
use jni::JavaVM;
use jni::objects::{GlobalRef, JByteBuffer, JClass, JLongArray, JStaticMethodID};
use jni::signature::{Primitive, ReturnType};
use jni::sys::jobject;
use jni::sys::{JNI_ERR, JNI_VERSION_1_6, jint, jlong};
use std::ffi::c_int;
use std::os::raw::c_void;
use std::sync::OnceLock;
unsafe extern "C" {
fn compose_rust_android_main();
}
pub const OUT_SLOTS: usize = 5;
struct FrameRequestUpcall {
class: GlobalRef,
method: JStaticMethodID,
}
static VM: OnceLock<JavaVM> = OnceLock::new();
static UPCALL: OnceLock<FrameRequestUpcall> = OnceLock::new();
extern "C" fn platform_run() -> c_int {
STATUS_OK as c_int
}
extern "C" fn request_frame() {
let (Some(vm), Some(upcall)) = (VM.get(), UPCALL.get()) else {
return;
};
let Ok(mut env) = vm.attach_current_thread_permanently() else {
return;
};
let class = <&JClass>::from(upcall.class.as_obj());
let result = unsafe {
env.call_static_method_unchecked(
class,
upcall.method,
ReturnType::Primitive(Primitive::Void),
&[],
)
};
if result.is_err() {
let _ = env.exception_clear();
}
}
fn direct_address(env: &JNIEnv<'_>, buffer: &JByteBuffer<'_>, length: jint) -> Option<*const u8> {
if length < 0 {
return None;
}
let address = env.get_direct_buffer_address(buffer).ok()?;
let capacity = env.get_direct_buffer_capacity(buffer).ok()?;
if capacity < length as usize {
return None;
}
Some(address.cast_const())
}
fn report(env: &mut JNIEnv<'_>, out: &JLongArray<'_>, batch: &MutationBatch) {
let (base, capacity) = current_arena();
let offset = if batch.ptr.is_null() || base.is_null() {
0
} else {
(batch.ptr as usize).wrapping_sub(base as usize)
};
let values: [jlong; OUT_SLOTS] = [
offset as jlong,
jlong::from(batch.len),
batch.result,
base as usize as jlong,
capacity as jlong,
];
let _ = env.set_long_array_region(out, 0, &values);
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_dev_darkpyonix_composerust_ui_platform_HostBridge_nativeInit(
mut env: JNIEnv<'_>,
_class: JClass<'_>,
handshake: JByteBuffer<'_>,
handshake_length: jint,
out: JLongArray<'_>,
) -> jint {
let Some(address) = direct_address(&env, &handshake, handshake_length) else {
return STATUS_PROTOCOL_ERROR;
};
let mut batch = MutationBatch::default();
let status = unsafe {
crate::boundary::compose_rust_host_init(address, handshake_length as u32, &raw mut batch)
};
report(&mut env, &out, &batch);
status
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_dev_darkpyonix_composerust_ui_platform_HostBridge_nativeDispatchEvent(
mut env: JNIEnv<'_>,
_class: JClass<'_>,
event: JByteBuffer<'_>,
event_length: jint,
out: JLongArray<'_>,
) -> jint {
let Some(address) = direct_address(&env, &event, event_length) else {
return STATUS_PROTOCOL_ERROR;
};
let mut batch = MutationBatch::default();
let status = unsafe {
crate::boundary::compose_rust_host_dispatch_event(
address,
event_length as u32,
&raw mut batch,
)
};
report(&mut env, &out, &batch);
status
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_dev_darkpyonix_composerust_ui_platform_HostBridge_nativeRenderFrame(
mut env: JNIEnv<'_>,
_class: JClass<'_>,
frame_time_nanos: jlong,
out: JLongArray<'_>,
) -> jint {
let mut batch = MutationBatch::default();
let status = unsafe {
crate::boundary::compose_rust_host_render_frame(frame_time_nanos as u64, &raw mut batch)
};
report(&mut env, &out, &batch);
status
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_dev_darkpyonix_composerust_ui_platform_HostBridge_nativeReleaseBatch(
_env: JNIEnv<'_>,
_class: JClass<'_>,
) -> jint {
let mut batch = MutationBatch::default();
unsafe { crate::boundary::compose_rust_host_release_batch(&raw mut batch) };
STATUS_OK
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_dev_darkpyonix_composerust_ui_platform_HostBridge_nativeShutdown(
_env: JNIEnv<'_>,
_class: JClass<'_>,
) -> jint {
crate::boundary::compose_rust_host_shutdown();
STATUS_OK
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_dev_darkpyonix_composerust_ui_platform_HostBridge_nativeArenaBuffer(
mut env: JNIEnv<'_>,
_class: JClass<'_>,
) -> jobject {
let (base, capacity) = current_arena();
if base.is_null() || capacity == 0 {
return std::ptr::null_mut();
}
match unsafe { env.new_direct_byte_buffer(base.cast_mut(), capacity) } {
Ok(buffer) => buffer.into_raw(),
Err(_) => std::ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "system" fn JNI_OnLoad(
vm: *mut jni::sys::JavaVM,
_reserved: *mut c_void,
) -> jint {
let Ok(vm) = (unsafe { JavaVM::from_raw(vm) }) else {
return JNI_ERR;
};
let Ok(mut env) = vm.attach_current_thread_permanently() else {
return JNI_ERR;
};
let Ok(class) = env.find_class(BRIDGE_CLASS) else {
return JNI_ERR;
};
let Ok(method) = env.get_static_method_id(&class, "onFrameRequested", "()V") else {
return JNI_ERR;
};
let Ok(global) = env.new_global_ref(&class) else {
return JNI_ERR;
};
let _ = UPCALL.set(FrameRequestUpcall {
class: global,
method,
});
let _ = VM.set(vm);
let _ = install_renderer_api(RendererApi {
run: platform_run,
request_frame,
});
unsafe { compose_rust_android_main() };
JNI_VERSION_1_6
}
const BRIDGE_CLASS: &str = "dev/darkpyonix/composerust/ui/platform/HostBridge";