use crate::{ api::controller::AsyncReceiver, errors::{ConnectionError, ControllerError, RemoteError}, ffi::java::null_check, Workspace, }; use jni::{objects::JObject, JNIEnv}; use jni_toolbox::jni; /// Get the workspace id. #[jni(package = "mp.code", class = "Workspace")] fn get_workspace_id(workspace: &mut Workspace) -> String { workspace.id() } /// Get a cursor controller by name and returns a pointer to it. #[jni(package = "mp.code", class = "Workspace")] fn get_cursor(workspace: &mut Workspace) -> crate::cursor::Controller { workspace.cursor() } /// Get a buffer controller by name and returns a pointer to it. #[jni(package = "mp.code", class = "Workspace")] fn get_buffer(workspace: &mut Workspace, path: String) -> Option<crate::buffer::Controller> { workspace.buffer_by_name(&path) } /// Get the filetree. #[jni(package = "mp.code", class = "Workspace")] fn get_file_tree(workspace: &mut Workspace, filter: Option<String>, strict: bool) -> Vec<String> { workspace.filetree(filter.as_deref(), strict) } /// Gets a list of the active buffers. #[jni(package = "mp.code", class = "Workspace")] fn active_buffers(workspace: &mut Workspace) -> Vec<String> { workspace.buffer_list() } /// Gets a list of the active buffers. #[jni(package = "mp.code", class = "Workspace")] fn user_list(workspace: &mut Workspace) -> Vec<String> { workspace.user_list() } /// Create a new buffer. #[jni(package = "mp.code", class = "Workspace")] fn create_buffer(workspace: &mut Workspace, path: String) -> Result<(), RemoteError> { super::tokio().block_on(workspace.create(&path)) } /// Attach to a buffer and return a pointer to its [crate::buffer::Controller]. #[jni(package = "mp.code", class = "Workspace")] fn attach_to_buffer( workspace: &mut Workspace, path: String, ) -> Result<crate::buffer::Controller, ConnectionError> { super::tokio().block_on(workspace.attach(&path)) } /// Detach from a buffer. #[jni(package = "mp.code", class = "Workspace")] fn detach_from_buffer(workspace: &mut Workspace, path: String) -> bool { workspace.detach(&path) } /// Update the local buffer list. #[jni(package = "mp.code", class = "Workspace")] fn fetch_buffers(workspace: &mut Workspace) -> Result<(), RemoteError> { super::tokio().block_on(workspace.fetch_buffers()) } /// Update the local user list. #[jni(package = "mp.code", class = "Workspace")] fn fetch_users(workspace: &mut Workspace) -> Result<(), RemoteError> { super::tokio().block_on(workspace.fetch_users()) } /// List users attached to a buffer. #[jni(package = "mp.code", class = "Workspace")] fn list_buffer_users( workspace: &mut Workspace, path: String, ) -> Result<Vec<crate::api::User>, RemoteError> { super::tokio().block_on(workspace.list_buffer_users(&path)) } /// Delete a buffer. #[jni(package = "mp.code", class = "Workspace")] fn delete_buffer(workspace: &mut Workspace, path: String) -> Result<(), RemoteError> { super::tokio().block_on(workspace.delete(&path)) } /// Block and receive a workspace event. #[jni(package = "mp.code", class = "Workspace")] fn recv(workspace: &mut Workspace) -> Result<crate::api::Event, ControllerError> { super::tokio().block_on(workspace.recv()) } /// Receive a workspace event if present. #[jni(package = "mp.code", class = "Workspace")] fn try_recv(workspace: &mut Workspace) -> Result<Option<crate::api::Event>, ControllerError> { super::tokio().block_on(workspace.try_recv()) } /// Block until a workspace event is available. #[jni(package = "mp.code", class = "Workspace")] fn poll(workspace: &mut Workspace) -> Result<(), ControllerError> { super::tokio().block_on(workspace.poll()) } /// Clear previously registered callback. #[jni(package = "mp.code", class = "Workspace")] fn clear_callback(workspace: &mut Workspace) { workspace.clear_callback(); } /// Register a callback for workspace events. #[jni(package = "mp.code", class = "Workspace")] fn callback<'local>( env: &mut JNIEnv<'local>, controller: &mut crate::Workspace, 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 |workspace: crate::Workspace| { 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 jworkspace = workspace.into_java_object(env)?; if let Err(e) = env.call_method( &cb_ref, "accept", "(Ljava/lang/Object;)V", &[jni::objects::JValueGen::Object(&jworkspace)], ) { tracing::error!("error invoking callback: {e:?}"); }; Ok::<(), jni::errors::Error>(()) }) { tracing::error!("error invoking callback: {e}"); let _ = env.exception_describe(); } }); } /// Called by the Java GC to drop a [Workspace]. #[jni(package = "mp.code", class = "Workspace")] fn free(input: jni::sys::jlong) { let _ = unsafe { Box::from_raw(input as *mut crate::Workspace) }; }