use jni::{objects::JObject, JNIEnv}; use jni_toolbox::jni; use crate::{api::{Controller, TextChange}, errors::ControllerError}; use super::null_check; /// Get the name of the buffer. #[jni(package = "mp.code", class = "BufferController", ptr)] fn get_name(controller: &mut crate::buffer::Controller) -> String { controller.path().to_string() //TODO: &str is built into the newer version } /// Get the contents of the buffers. #[jni(package = "mp.code", class = "BufferController", ptr)] fn get_content(controller: &mut crate::buffer::Controller) -> Result<String, ControllerError> { super::tokio().block_on(controller.content()) } /// Try to fetch a [TextChange], or return null if there's nothing. #[jni(package = "mp.code", class = "BufferController", ptr)] fn try_recv(controller: &mut crate::buffer::Controller) -> Result<Option<TextChange>, ControllerError> { super::tokio().block_on(controller.try_recv()) } /// Block until it receives a [TextChange]. #[jni(package = "mp.code", class = "BufferController", ptr)] fn recv(controller: &mut crate::buffer::Controller) -> Result<TextChange, ControllerError> { super::tokio().block_on(controller.recv()) } /// Send a [TextChange] to the server. #[jni(package = "mp.code", class = "BufferController")] fn send(controller: &mut crate::buffer::Controller, change: TextChange) -> Result<(), ControllerError> { super::tokio().block_on(controller.send(change)) } /// Register a callback for buffer changes. #[jni(package = "mp.code", class = "BufferController")] fn callback<'local>(env: &mut JNIEnv<'local>, controller: &mut crate::buffer::Controller, cb: JObject<'local>) { null_check!(env, cb, {}); let Ok(cb_ref) = env.new_global_ref(cb) else { env.throw_new("mp/code/exceptions/JNIException", "Failed to pin callback reference!") .expect("Failed to throw exception!"); return; }; controller.callback(move |controller: crate::buffer::Controller| { let jvm = super::jvm(); let mut env = jvm.attach_current_thread_permanently() .expect("failed attaching to main JVM thread"); if let Err(e) = env.with_local_frame(5, |env| { use jni_toolbox::IntoJavaObject; let jcontroller = controller.into_java(env)?; if let Err(e) = env.call_method( &cb_ref, "accept", "(Ljava/lang/Object;)V", &[jni::objects::JValueGen::Object(&jcontroller)] ) { tracing::error!("error invoking callback: {e:?}"); }; Ok::<(), jni::errors::Error>(()) }) { tracing::error!("error invoking callback: {e}"); let _ = env.exception_describe(); } }); } /// Clear the callback for buffer changes. #[jni(package = "mp.code", class = "BufferController")] fn clear_callback(controller: &mut crate::buffer::Controller) { controller.clear_callback() } /// Block until there is a new value available. #[jni(package = "mp.code", class = "BufferController")] fn poll(controller: &mut crate::buffer::Controller) -> Result<(), ControllerError> { super::tokio().block_on(controller.poll()) } /// Stop the controller. #[jni(package = "mp.code", class = "BufferController")] fn stop(controller: &mut crate::buffer::Controller) -> bool { controller.stop() } /// Called by the Java GC to drop a [crate::buffer::Controller]. #[jni(package = "mp.code", class = "BufferController")] fn free(input: jni::sys::jlong) { let _ = unsafe { Box::from_raw(input as *mut crate::buffer::Controller) }; }