#[cfg(target_os = "android")]
use jni::objects::{JClass, JObject};
#[cfg(target_os = "android")]
use jni::sys::{jboolean, jfloat, jint, jlong, JNI_FALSE, JNI_TRUE};
#[cfg(target_os = "android")]
use jni::JNIEnv;
#[cfg(target_os = "android")]
use ndk::native_window::NativeWindow;
#[cfg(target_os = "android")]
use tracing::{debug, error, info, warn};
#[cfg(target_os = "android")]
struct BlincHandle {
width: u32,
height: u32,
scale_factor: f64,
touch_active: bool,
last_touch: (f32, f32),
native_window_ptr: *mut std::ffi::c_void,
}
#[cfg(target_os = "android")]
impl BlincHandle {
fn new(
width: u32,
height: u32,
scale_factor: f64,
native_window_ptr: *mut std::ffi::c_void,
) -> Self {
Self {
width,
height,
scale_factor,
touch_active: false,
last_touch: (0.0, 0.0),
native_window_ptr,
}
}
}
#[cfg(target_os = "android")]
unsafe impl Send for BlincHandle {}
#[cfg(target_os = "android")]
#[no_mangle]
pub extern "system" fn Java_com_blinc_BlincBridge_nativeInit(
mut env: JNIEnv,
_class: JClass,
surface: JObject,
width: jint,
height: jint,
density: jfloat,
) -> jlong {
android_logger::init_once(
android_logger::Config::default()
.with_max_level(log::LevelFilter::Debug)
.with_tag("BlincJNI"),
);
info!("BlincBridge.nativeInit called");
info!(
"Surface dimensions: {}x{}, density: {}",
width, height, density
);
if width <= 0 || height <= 0 {
error!("Invalid surface dimensions: {}x{}", width, height);
return 0;
}
let scale_factor = if density > 0.0 { density as f64 } else { 1.0 };
let native_window_ptr = match get_native_window_from_surface(&mut env, &surface) {
Ok(ptr) => ptr,
Err(e) => {
error!("Failed to get native window: {}", e);
return 0;
}
};
let handle = Box::new(BlincHandle::new(
width as u32,
height as u32,
scale_factor,
native_window_ptr,
));
let ptr = Box::into_raw(handle);
info!("Created BlincHandle at {:p}", ptr);
ptr as jlong
}
#[cfg(target_os = "android")]
#[no_mangle]
pub extern "system" fn Java_com_blinc_BlincBridge_nativeRenderFrame(
_env: JNIEnv,
_class: JClass,
handle: jlong,
) {
if handle == 0 {
warn!("nativeRenderFrame called with null handle");
return;
}
let _blinc = unsafe { &mut *(handle as *mut BlincHandle) };
debug!("nativeRenderFrame called");
}
#[cfg(target_os = "android")]
#[no_mangle]
pub extern "system" fn Java_com_blinc_BlincBridge_nativeOnTouch(
_env: JNIEnv,
_class: JClass,
handle: jlong,
action: jint,
x: jfloat,
y: jfloat,
) -> jboolean {
if handle == 0 {
warn!("nativeOnTouch called with null handle");
return JNI_FALSE;
}
let blinc = unsafe { &mut *(handle as *mut BlincHandle) };
let logical_x = x / blinc.scale_factor as f32;
let logical_y = y / blinc.scale_factor as f32;
const ACTION_DOWN: i32 = 0;
const ACTION_UP: i32 = 1;
const ACTION_MOVE: i32 = 2;
const ACTION_CANCEL: i32 = 3;
match action {
ACTION_DOWN => {
debug!("Touch down at ({}, {})", logical_x, logical_y);
blinc.touch_active = true;
blinc.last_touch = (logical_x, logical_y);
}
ACTION_UP => {
debug!("Touch up at ({}, {})", logical_x, logical_y);
blinc.touch_active = false;
blinc.last_touch = (logical_x, logical_y);
}
ACTION_MOVE => {
if blinc.touch_active {
debug!("Touch move to ({}, {})", logical_x, logical_y);
blinc.last_touch = (logical_x, logical_y);
}
}
ACTION_CANCEL => {
debug!("Touch cancelled");
blinc.touch_active = false;
}
_ => {
debug!("Unknown touch action: {}", action);
}
}
JNI_TRUE
}
#[cfg(target_os = "android")]
#[no_mangle]
pub extern "system" fn Java_com_blinc_BlincBridge_nativeResize(
_env: JNIEnv,
_class: JClass,
handle: jlong,
width: jint,
height: jint,
) {
if handle == 0 {
warn!("nativeResize called with null handle");
return;
}
let blinc = unsafe { &mut *(handle as *mut BlincHandle) };
info!("Surface resized to {}x{}", width, height);
blinc.width = width as u32;
blinc.height = height as u32;
}
#[cfg(target_os = "android")]
#[no_mangle]
pub extern "system" fn Java_com_blinc_BlincBridge_nativeDestroy(
_env: JNIEnv,
_class: JClass,
handle: jlong,
) {
if handle == 0 {
warn!("nativeDestroy called with null handle");
return;
}
info!("Destroying BlincHandle at {:p}", handle as *const ());
let blinc = unsafe { Box::from_raw(handle as *mut BlincHandle) };
if !blinc.native_window_ptr.is_null() {
unsafe { ANativeWindow_release(blinc.native_window_ptr) };
}
info!("BlincHandle destroyed");
}
#[cfg(target_os = "android")]
extern "C" {
fn ANativeWindow_fromSurface(
env: *mut std::ffi::c_void,
surface: *mut std::ffi::c_void,
) -> *mut std::ffi::c_void;
fn ANativeWindow_release(window: *mut std::ffi::c_void);
}
#[cfg(target_os = "android")]
fn get_native_window_from_surface(
env: &mut JNIEnv,
surface: &JObject,
) -> Result<*mut std::ffi::c_void, String> {
let surface_ptr = surface.as_raw();
let native_window = unsafe {
ANativeWindow_fromSurface(
env.get_raw() as *mut std::ffi::c_void,
surface_ptr as *mut _,
)
};
if native_window.is_null() {
return Err("ANativeWindow_fromSurface returned null".to_string());
}
Ok(native_window)
}
#[cfg(not(target_os = "android"))]
pub fn jni_bridge_placeholder() {
}