mirror of
https://github.com/hexedtech/codemp.git
synced 2024-11-25 00:24:48 +01:00
feat(java): callback API
Co-authored-by: alemi <me@alemi.dev>
This commit is contained in:
parent
b7680b15c1
commit
795423de2a
10 changed files with 197 additions and 102 deletions
17
dist/java/src/mp/code/Extensions.java
vendored
Normal file
17
dist/java/src/mp/code/Extensions.java
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package mp.code;
|
||||||
|
|
||||||
|
public class Extensions {
|
||||||
|
/**
|
||||||
|
* Hashes the given {@link String} using CodeMP's hashing algorithm (xxh3).
|
||||||
|
* @param input the string to hash
|
||||||
|
* @return the hash
|
||||||
|
*/
|
||||||
|
public static native long hash(String input);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drive the underlying library's asynchronous event loop.
|
||||||
|
* @param block true if it should use the current thread, false if it should
|
||||||
|
* spawn a separate one
|
||||||
|
*/
|
||||||
|
public static native void drive(boolean block);
|
||||||
|
}
|
10
dist/java/src/mp/code/Utils.java
vendored
10
dist/java/src/mp/code/Utils.java
vendored
|
@ -1,10 +0,0 @@
|
||||||
package mp.code;
|
|
||||||
|
|
||||||
public class Utils {
|
|
||||||
/**
|
|
||||||
* Hashes the given {@link String} using CodeMP's hashing algorithm (xxh3).
|
|
||||||
* @param input the string to hash
|
|
||||||
* @return the hash
|
|
||||||
*/
|
|
||||||
public static native long hash(String input);
|
|
||||||
}
|
|
2
dist/java/src/mp/code/Workspace.java
vendored
2
dist/java/src/mp/code/Workspace.java
vendored
|
@ -95,7 +95,7 @@ public class Workspace {
|
||||||
this.argument = argument;
|
this.argument = argument;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String > getUserJoined() {
|
public Optional<String> getUserJoined() {
|
||||||
if(this.type == Type.USER_JOIN) {
|
if(this.type == Type.USER_JOIN) {
|
||||||
return Optional.of(this.argument);
|
return Optional.of(this.argument);
|
||||||
} else return Optional.empty();
|
} else return Optional.empty();
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use jni::{objects::{JClass, JObject, JValueGen}, sys::{jlong, jobject, jstring}, JNIEnv};
|
use jni::{objects::{JClass, JObject, JValueGen}, sys::{jlong, jobject, jstring}, JNIEnv};
|
||||||
|
|
||||||
use crate::{api::Controller, ffi::java::handle_callback};
|
use crate::api::Controller;
|
||||||
|
|
||||||
use super::{JExceptable, RT};
|
use super::JExceptable;
|
||||||
|
|
||||||
/// Gets the name of the buffer.
|
/// Gets the name of the buffer.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -26,7 +26,7 @@ pub extern "system" fn Java_mp_code_BufferController_get_1content(
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
) -> jstring {
|
) -> jstring {
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
||||||
let content = RT.block_on(controller.content())
|
let content = super::tokio().block_on(controller.content())
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
env.new_string(content)
|
env.new_string(content)
|
||||||
.jexcept(&mut env)
|
.jexcept(&mut env)
|
||||||
|
@ -41,7 +41,7 @@ pub extern "system" fn Java_mp_code_BufferController_try_1recv(
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
) -> jobject {
|
) -> jobject {
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
||||||
let change = RT.block_on(controller.try_recv()).jexcept(&mut env);
|
let change = super::tokio().block_on(controller.try_recv()).jexcept(&mut env);
|
||||||
recv_jni(&mut env, change)
|
recv_jni(&mut env, change)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ pub extern "system" fn Java_mp_code_BufferController_recv(
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
) -> jobject {
|
) -> jobject {
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
||||||
let change = RT.block_on(controller.recv()).map(Some).jexcept(&mut env);
|
let change = super::tokio().block_on(controller.recv()).map(Some).jexcept(&mut env);
|
||||||
recv_jni(&mut env, change)
|
recv_jni(&mut env, change)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +88,18 @@ fn recv_jni(env: &mut JNIEnv, change: Option<crate::api::TextChange>) -> jobject
|
||||||
}.as_raw()
|
}.as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clears the callback for buffer changes.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "system" fn Java_mp_code_BufferController_clear_1callback(
|
||||||
|
_env: JNIEnv,
|
||||||
|
_class: JClass,
|
||||||
|
self_ptr: jlong,
|
||||||
|
) {
|
||||||
|
unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) }
|
||||||
|
.clear_callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers a callback for buffer changes.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "system" fn Java_mp_code_BufferController_callback<'local>(
|
pub extern "system" fn Java_mp_code_BufferController_callback<'local>(
|
||||||
mut env: JNIEnv,
|
mut env: JNIEnv,
|
||||||
|
@ -95,7 +107,36 @@ pub extern "system" fn Java_mp_code_BufferController_callback<'local>(
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
cb: JObject<'local>,
|
cb: JObject<'local>,
|
||||||
) {
|
) {
|
||||||
handle_callback!("mp/code/BufferController", env, self_ptr, cb, crate::buffer::Controller);
|
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
||||||
|
|
||||||
|
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 crate::ffi::java::JObjectify;
|
||||||
|
let jcontroller = controller.jobjectify(env)?;
|
||||||
|
let sig = format!("(L{};)V", "java/lang/Object");
|
||||||
|
if let Err(e) = env.call_method(
|
||||||
|
&cb_ref,
|
||||||
|
"invoke",
|
||||||
|
&sig,
|
||||||
|
&[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();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive from Java, converts and sends a [crate::api::TextChange].
|
/// Receive from Java, converts and sends a [crate::api::TextChange].
|
||||||
|
@ -147,7 +188,7 @@ pub extern "system" fn Java_mp_code_BufferController_send<'local>(
|
||||||
}).jexcept(&mut env);
|
}).jexcept(&mut env);
|
||||||
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::buffer::Controller)) };
|
||||||
RT.block_on(controller.send(crate::api::TextChange {
|
super::tokio().block_on(controller.send(crate::api::TextChange {
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
content,
|
content,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use jni::{objects::{JClass, JObject, JString, JValueGen}, sys::{jboolean, jint, jlong, jobject, jobjectArray}, JNIEnv};
|
use jni::{objects::{JClass, JObject, JString, JValueGen}, sys::{jboolean, jint, jlong, jobject, jobjectArray}, JNIEnv};
|
||||||
use crate::{api::Config, client::Client, Workspace};
|
use crate::{api::Config, client::Client, Workspace};
|
||||||
|
|
||||||
use super::{JExceptable, JObjectify, RT};
|
use super::{JExceptable, JObjectify};
|
||||||
|
|
||||||
/// Connect using the given credentials to the default server, and return a [Client] to interact with it.
|
/// Connect using the given credentials to the default server, and return a [Client] to interact with it.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -63,7 +63,7 @@ pub extern "system" fn Java_mp_code_Client_connectToServer<'local>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect_internal(mut env: JNIEnv, config: Config) -> jobject {
|
fn connect_internal(mut env: JNIEnv, config: Config) -> jobject {
|
||||||
RT.block_on(Client::connect(config))
|
super::tokio().block_on(Client::connect(config))
|
||||||
.map(|client| Box::into_raw(Box::new(client)) as jlong)
|
.map(|client| Box::into_raw(Box::new(client)) as jlong)
|
||||||
.map(|ptr| {
|
.map(|ptr| {
|
||||||
env.find_class("mp/code/Client")
|
env.find_class("mp/code/Client")
|
||||||
|
@ -98,7 +98,7 @@ pub extern "system" fn Java_mp_code_Client_join_1workspace<'local>(
|
||||||
let workspace_id = unsafe { env.get_string_unchecked(&input) }
|
let workspace_id = unsafe { env.get_string_unchecked(&input) }
|
||||||
.map(|wid| wid.to_string_lossy().to_string())
|
.map(|wid| wid.to_string_lossy().to_string())
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
RT.block_on(client.join_workspace(workspace_id))
|
super::tokio().block_on(client.join_workspace(workspace_id))
|
||||||
.map(|workspace| spawn_updater(workspace.clone()))
|
.map(|workspace| spawn_updater(workspace.clone()))
|
||||||
.map(|workspace| Box::into_raw(Box::new(workspace)) as jlong)
|
.map(|workspace| Box::into_raw(Box::new(workspace)) as jlong)
|
||||||
.map(|ptr| {
|
.map(|ptr| {
|
||||||
|
@ -120,7 +120,7 @@ pub extern "system" fn Java_mp_code_Client_create_1workspace<'local>(
|
||||||
let workspace_id = unsafe { env.get_string_unchecked(&input) }
|
let workspace_id = unsafe { env.get_string_unchecked(&input) }
|
||||||
.map(|wid| wid.to_string_lossy().to_string())
|
.map(|wid| wid.to_string_lossy().to_string())
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
RT
|
super::tokio()
|
||||||
.block_on(client.create_workspace(workspace_id))
|
.block_on(client.create_workspace(workspace_id))
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ pub extern "system" fn Java_mp_code_Client_delete_1workspace<'local>(
|
||||||
let workspace_id = unsafe { env.get_string_unchecked(&input) }
|
let workspace_id = unsafe { env.get_string_unchecked(&input) }
|
||||||
.map(|wid| wid.to_string_lossy().to_string())
|
.map(|wid| wid.to_string_lossy().to_string())
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
RT
|
super::tokio()
|
||||||
.block_on(client.delete_workspace(workspace_id))
|
.block_on(client.delete_workspace(workspace_id))
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ pub extern "system" fn Java_mp_code_Client_invite_1to_1workspace<'local>(
|
||||||
let user_name = unsafe { env.get_string_unchecked(&usr) }
|
let user_name = unsafe { env.get_string_unchecked(&usr) }
|
||||||
.map(|wid| wid.to_string_lossy().to_string())
|
.map(|wid| wid.to_string_lossy().to_string())
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
RT
|
super::tokio()
|
||||||
.block_on(client.invite_to_workspace(workspace_id, user_name))
|
.block_on(client.invite_to_workspace(workspace_id, user_name))
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,7 @@ pub extern "system" fn Java_mp_code_Client_list_1workspaces<'local>(
|
||||||
invited: jboolean
|
invited: jboolean
|
||||||
) -> jobjectArray {
|
) -> jobjectArray {
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
||||||
let list = RT
|
let list = super::tokio()
|
||||||
.block_on(client.list_workspaces(owned != 0, invited != 0))
|
.block_on(client.list_workspaces(owned != 0, invited != 0))
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
env.find_class("java/lang/String")
|
env.find_class("java/lang/String")
|
||||||
|
@ -210,7 +210,7 @@ pub extern "system" fn Java_mp_code_Client_active_1workspaces<'local>(
|
||||||
// TODO: this stays until we get rid of the arc then i'll have to find a better way
|
// TODO: this stays until we get rid of the arc then i'll have to find a better way
|
||||||
fn spawn_updater(workspace: Workspace) -> Workspace {
|
fn spawn_updater(workspace: Workspace) -> Workspace {
|
||||||
let w = workspace.clone();
|
let w = workspace.clone();
|
||||||
RT.spawn(async move {
|
super::tokio().spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
tokio::time::sleep(std::time::Duration::from_secs(60)).await;
|
tokio::time::sleep(std::time::Duration::from_secs(60)).await;
|
||||||
w.fetch_buffers().await.unwrap();
|
w.fetch_buffers().await.unwrap();
|
||||||
|
@ -264,7 +264,7 @@ pub extern "system" fn Java_mp_code_Client_refresh<'local>(
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
) {
|
) {
|
||||||
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
let client = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Client)) };
|
||||||
RT.block_on(client.refresh())
|
super::tokio().block_on(client.refresh())
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use jni::{objects::{JClass, JObject, JString, JValueGen}, sys::{jlong, jobject}, JNIEnv};
|
use jni::{objects::{JClass, JObject, JString, JValueGen}, sys::{jlong, jobject}, JNIEnv};
|
||||||
use crate::api::Controller;
|
use crate::api::Controller;
|
||||||
|
|
||||||
use super::{handle_callback, JExceptable, RT};
|
use super::JExceptable;
|
||||||
|
|
||||||
/// Try to fetch a [crate::api::Cursor], or returns null if there's nothing.
|
/// Try to fetch a [crate::api::Cursor], or returns null if there's nothing.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -11,7 +11,7 @@ pub extern "system" fn Java_mp_code_CursorController_try_1recv(
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
) -> jobject {
|
) -> jobject {
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
||||||
let cursor = RT.block_on(controller.try_recv()).jexcept(&mut env);
|
let cursor = super::tokio().block_on(controller.try_recv()).jexcept(&mut env);
|
||||||
jni_recv(&mut env, cursor)
|
jni_recv(&mut env, cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ pub extern "system" fn Java_mp_code_CursorController_recv(
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
) -> jobject {
|
) -> jobject {
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
||||||
let cursor = RT.block_on(controller.recv()).map(Some).jexcept(&mut env);
|
let cursor = super::tokio().block_on(controller.recv()).map(Some).jexcept(&mut env);
|
||||||
jni_recv(&mut env, cursor)
|
jni_recv(&mut env, cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +56,18 @@ fn jni_recv(env: &mut JNIEnv, cursor: Option<crate::api::Cursor>) -> jobject {
|
||||||
}.as_raw()
|
}.as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clears the callback for cursor changes.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "system" fn Java_mp_code_CursorController_clear_1callback(
|
||||||
|
_env: JNIEnv,
|
||||||
|
_class: JClass,
|
||||||
|
self_ptr: jlong,
|
||||||
|
) {
|
||||||
|
unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) }
|
||||||
|
.clear_callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers a callback for cursor changes.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "system" fn Java_mp_code_CursorController_callback<'local>(
|
pub extern "system" fn Java_mp_code_CursorController_callback<'local>(
|
||||||
mut env: JNIEnv,
|
mut env: JNIEnv,
|
||||||
|
@ -63,7 +75,36 @@ pub extern "system" fn Java_mp_code_CursorController_callback<'local>(
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
cb: JObject<'local>,
|
cb: JObject<'local>,
|
||||||
) {
|
) {
|
||||||
handle_callback!("mp/code/CursorController", env, self_ptr, cb, crate::cursor::Controller);
|
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
||||||
|
|
||||||
|
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::cursor::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 crate::ffi::java::JObjectify;
|
||||||
|
let jcontroller = controller.jobjectify(env)?;
|
||||||
|
let sig = format!("(L{};)V", "java/lang/Object");
|
||||||
|
if let Err(e) = env.call_method(
|
||||||
|
&cb_ref,
|
||||||
|
"invoke",
|
||||||
|
&sig,
|
||||||
|
&[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();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive from Java, converts and sends a [crate::api::Cursor].
|
/// Receive from Java, converts and sends a [crate::api::Cursor].
|
||||||
|
@ -109,7 +150,7 @@ pub extern "system" fn Java_mp_code_CursorController_send<'local>(
|
||||||
};
|
};
|
||||||
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
let controller = unsafe { Box::leak(Box::from_raw(self_ptr as *mut crate::cursor::Controller)) };
|
||||||
RT.block_on(controller.send(crate::api::Cursor {
|
super::tokio().block_on(controller.send(crate::api::Cursor {
|
||||||
start: (start_row, start_col),
|
start: (start_row, start_col),
|
||||||
end: (end_row, end_col),
|
end: (end_row, end_col),
|
||||||
buffer,
|
buffer,
|
||||||
|
|
34
src/ffi/java/ext.rs
Normal file
34
src/ffi/java/ext.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
use jni::{objects::{JClass, JString}, sys::{jboolean, jlong}, JNIEnv};
|
||||||
|
|
||||||
|
use super::JExceptable;
|
||||||
|
|
||||||
|
/// Calculate the XXH3 hash for a given String.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "system" fn Java_mp_code_Extensions_hash<'local>(
|
||||||
|
mut env: JNIEnv,
|
||||||
|
_class: JClass<'local>,
|
||||||
|
content: JString<'local>,
|
||||||
|
) -> jlong {
|
||||||
|
let content: String = env.get_string(&content)
|
||||||
|
.map(|s| s.into())
|
||||||
|
.jexcept(&mut env);
|
||||||
|
let hash = crate::ext::hash(content.as_bytes());
|
||||||
|
i64::from_ne_bytes(hash.to_ne_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tells the [tokio] runtime how to drive the event loop.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "system" fn Java_mp_code_Extensions_drive(
|
||||||
|
_env: JNIEnv,
|
||||||
|
_class: JClass,
|
||||||
|
block: jboolean
|
||||||
|
) {
|
||||||
|
if block != 0 {
|
||||||
|
super::tokio().block_on(std::future::pending::<()>());
|
||||||
|
} else {
|
||||||
|
std::thread::spawn(|| {
|
||||||
|
super::tokio().block_on(std::future::pending::<()>());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,10 +2,37 @@ pub mod client;
|
||||||
pub mod workspace;
|
pub mod workspace;
|
||||||
pub mod cursor;
|
pub mod cursor;
|
||||||
pub mod buffer;
|
pub mod buffer;
|
||||||
pub mod utils;
|
pub mod ext;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
/// Gets or creates the relevant [tokio::runtime::Runtime].
|
||||||
pub(crate) static ref RT: tokio::runtime::Runtime = tokio::runtime::Runtime::new().expect("could not create tokio runtime");
|
fn tokio() -> &'static tokio::runtime::Runtime {
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
static RT: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
|
||||||
|
RT.get_or_init(||
|
||||||
|
tokio::runtime::Builder::new_current_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.expect("could not create tokio runtime")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A static reference to [jni::JavaVM] that is set on JNI load.
|
||||||
|
static mut JVM: Option<std::sync::Arc<jni::JavaVM>> = None;
|
||||||
|
|
||||||
|
/// Safe accessor for the [jni::JavaVM] static.
|
||||||
|
pub(crate) fn jvm() -> std::sync::Arc<jni::JavaVM> {
|
||||||
|
unsafe { JVM.clone() }.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called upon initialisation of the JVM.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "system" fn JNI_OnLoad(
|
||||||
|
vm: jni::JavaVM,
|
||||||
|
_: *mut std::ffi::c_void
|
||||||
|
) -> jni::sys::jint {
|
||||||
|
unsafe { JVM = Some(std::sync::Arc::new(vm)) };
|
||||||
|
jni::sys::JNI_VERSION_1_1
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set up logging. Useful for debugging.
|
/// Set up logging. Useful for debugging.
|
||||||
|
@ -149,7 +176,7 @@ impl<'local> JObjectify<'local> for crate::cursor::Controller {
|
||||||
class,
|
class,
|
||||||
"(J)V",
|
"(J)V",
|
||||||
&[
|
&[
|
||||||
jni::objects::JValueGen::Long(Box::into_raw(Box::new(&self)) as jni::sys::jlong)
|
jni::objects::JValueGen::Long(Box::into_raw(Box::new(self)) as jni::sys::jlong)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -164,46 +191,8 @@ impl<'local> JObjectify<'local> for crate::buffer::Controller {
|
||||||
class,
|
class,
|
||||||
"(J)V",
|
"(J)V",
|
||||||
&[
|
&[
|
||||||
jni::objects::JValueGen::Long(Box::into_raw(Box::new(&self)) as jni::sys::jlong)
|
jni::objects::JValueGen::Long(Box::into_raw(Box::new(self)) as jni::sys::jlong)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! handle_callback {
|
|
||||||
($jtype:literal, $env:ident, $self_ptr:ident, $cb:ident, $t:ty) => {
|
|
||||||
let controller = unsafe { Box::leak(Box::from_raw($self_ptr as *mut $t)) };
|
|
||||||
|
|
||||||
let Ok(jvm) = $env.get_java_vm() else {
|
|
||||||
$env.throw_new("mp/code/exceptions/JNIException", "Failed to get JVM reference!")
|
|
||||||
.expect("Failed to throw exception!");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
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: $t| {
|
|
||||||
use std::ops::DerefMut;
|
|
||||||
use crate::ffi::java::JObjectify;
|
|
||||||
let mut guard = jvm.attach_current_thread().unwrap();
|
|
||||||
let jcontroller = match controller.jobjectify(guard.deref_mut()) {
|
|
||||||
Err(e) => return tracing::error!("could not convert callback argument: {e:?}"),
|
|
||||||
Ok(x) => x,
|
|
||||||
};
|
|
||||||
let sig = format!("(L{};)V", $jtype);
|
|
||||||
if let Err(e) = guard.call_method(&cb_ref,
|
|
||||||
"invoke",
|
|
||||||
&sig,
|
|
||||||
&[jni::objects::JValueGen::Object(&jcontroller)]
|
|
||||||
) {
|
|
||||||
tracing::error!("error invoking callback: {e:?}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) use handle_callback;
|
|
||||||
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
use jni::{objects::{JClass, JString}, sys::jlong, JNIEnv};
|
|
||||||
|
|
||||||
use super::JExceptable;
|
|
||||||
|
|
||||||
/// Calculate the XXH3 hash for a given String.
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "system" fn Java_mp_code_Utils_hash<'local>(
|
|
||||||
mut env: JNIEnv,
|
|
||||||
_class: JClass<'local>,
|
|
||||||
content: JString<'local>,
|
|
||||||
) -> jlong {
|
|
||||||
let content: String = env.get_string(&content)
|
|
||||||
.map(|s| s.into())
|
|
||||||
.jexcept(&mut env);
|
|
||||||
let hash = crate::ext::hash(content.as_bytes());
|
|
||||||
i64::from_ne_bytes(hash.to_ne_bytes())
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
use jni::{objects::{JClass, JObject, JString, JValueGen}, sys::{jboolean, jlong, jobject, jobjectArray, jstring}, JNIEnv};
|
use jni::{objects::{JClass, JObject, JString, JValueGen}, sys::{jboolean, jlong, jobject, jobjectArray, jstring}, JNIEnv};
|
||||||
use crate::Workspace;
|
use crate::Workspace;
|
||||||
|
|
||||||
use super::{JExceptable, JObjectify, RT};
|
use super::{JExceptable, JObjectify};
|
||||||
|
|
||||||
/// Get the workspace id.
|
/// Get the workspace id.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -55,7 +55,7 @@ pub extern "system" fn Java_mp_code_Workspace_create_1buffer<'local>(
|
||||||
let path = unsafe { env.get_string_unchecked(&input) }
|
let path = unsafe { env.get_string_unchecked(&input) }
|
||||||
.map(|path| path.to_string_lossy().to_string())
|
.map(|path| path.to_string_lossy().to_string())
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
RT.block_on(ws.create(&path))
|
super::tokio().block_on(ws.create(&path))
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ pub extern "system" fn Java_mp_code_Workspace_attach_1to_1buffer<'local>(
|
||||||
let path = unsafe { env.get_string_unchecked(&input) }
|
let path = unsafe { env.get_string_unchecked(&input) }
|
||||||
.map(|path| path.to_string_lossy().to_string())
|
.map(|path| path.to_string_lossy().to_string())
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
RT.block_on(workspace.attach(&path))
|
super::tokio().block_on(workspace.attach(&path))
|
||||||
.map(|buffer| buffer.jobjectify(&mut env).jexcept(&mut env))
|
.map(|buffer| buffer.jobjectify(&mut env).jexcept(&mut env))
|
||||||
.jexcept(&mut env)
|
.jexcept(&mut env)
|
||||||
.as_raw()
|
.as_raw()
|
||||||
|
@ -142,7 +142,7 @@ pub extern "system" fn Java_mp_code_Workspace_fetch_1buffers(
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
) {
|
) {
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
||||||
RT.block_on(workspace.fetch_buffers()).jexcept(&mut env);
|
super::tokio().block_on(workspace.fetch_buffers()).jexcept(&mut env);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the local user list.
|
/// Update the local user list.
|
||||||
|
@ -153,7 +153,7 @@ pub extern "system" fn Java_mp_code_Workspace_fetch_1users(
|
||||||
self_ptr: jlong,
|
self_ptr: jlong,
|
||||||
) {
|
) {
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
||||||
RT.block_on(workspace.fetch_users()).jexcept(&mut env);
|
super::tokio().block_on(workspace.fetch_users()).jexcept(&mut env);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List users attached to a buffer.
|
/// List users attached to a buffer.
|
||||||
|
@ -168,7 +168,7 @@ pub extern "system" fn Java_mp_code_Workspace_list_1buffer_1users<'local>(
|
||||||
let buffer = unsafe { env.get_string_unchecked(&input) }
|
let buffer = unsafe { env.get_string_unchecked(&input) }
|
||||||
.map(|buffer| buffer.to_string_lossy().to_string())
|
.map(|buffer| buffer.to_string_lossy().to_string())
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
let users = RT.block_on(workspace.list_buffer_users(&buffer))
|
let users = super::tokio().block_on(workspace.list_buffer_users(&buffer))
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
|
|
||||||
env.find_class("java/util/UUID")
|
env.find_class("java/util/UUID")
|
||||||
|
@ -194,7 +194,7 @@ pub extern "system" fn Java_mp_code_Workspace_delete_1buffer<'local>(
|
||||||
let buffer = unsafe { env.get_string_unchecked(&input) }
|
let buffer = unsafe { env.get_string_unchecked(&input) }
|
||||||
.map(|buffer| buffer.to_string_lossy().to_string())
|
.map(|buffer| buffer.to_string_lossy().to_string())
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
RT.block_on(workspace.delete(&buffer))
|
super::tokio().block_on(workspace.delete(&buffer))
|
||||||
.jexcept(&mut env);
|
.jexcept(&mut env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ pub extern "system" fn Java_mp_code_Workspace_event(
|
||||||
self_ptr: jlong
|
self_ptr: jlong
|
||||||
) -> jobject {
|
) -> jobject {
|
||||||
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
let workspace = unsafe { Box::leak(Box::from_raw(self_ptr as *mut Workspace)) };
|
||||||
RT.block_on(workspace.event())
|
super::tokio().block_on(workspace.event())
|
||||||
.map(|event| {
|
.map(|event| {
|
||||||
let (name, arg) = match event {
|
let (name, arg) = match event {
|
||||||
crate::api::Event::FileTreeUpdated(arg) => ("FILE_TREE_UPDATED", env.new_string(arg).unwrap_or_default()),
|
crate::api::Event::FileTreeUpdated(arg) => ("FILE_TREE_UPDATED", env.new_string(arg).unwrap_or_default()),
|
||||||
|
@ -247,10 +247,10 @@ pub extern "system" fn Java_mp_code_Workspace_select_1buffer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RT.block_on(crate::ext::select_buffer(
|
super::tokio().block_on(crate::ext::select_buffer(
|
||||||
&controllers,
|
&controllers,
|
||||||
Some(std::time::Duration::from_millis(timeout as u64)),
|
Some(std::time::Duration::from_millis(timeout as u64)),
|
||||||
&RT,
|
super::tokio(),
|
||||||
)).jexcept(&mut env)
|
)).jexcept(&mut env)
|
||||||
.map(|buf| {
|
.map(|buf| {
|
||||||
env.find_class("mp/code/BufferController").and_then(|class|
|
env.find_class("mp/code/BufferController").and_then(|class|
|
||||||
|
|
Loading…
Reference in a new issue