feat: add session related function
This commit is contained in:
parent
c140bcfed6
commit
952598af25
@ -147,7 +147,7 @@ pub struct FlutterHandler {
|
||||
renderer: Arc<RwLock<VideoRenderer>>,
|
||||
peer_info: Arc<RwLock<PeerInfo>>,
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
hooks: Arc<RwLock<HashMap<String, crate::api::SessionHook>>>,
|
||||
hooks: Arc<RwLock<HashMap<String, SessionHook>>>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "flutter_texture_render"))]
|
||||
@ -160,7 +160,7 @@ pub struct FlutterHandler {
|
||||
pub rgba_valid: Arc<AtomicBool>,
|
||||
peer_info: Arc<RwLock<PeerInfo>>,
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
hooks: Arc<RwLock<HashMap<String, crate::api::SessionHook>>>,
|
||||
hooks: Arc<RwLock<HashMap<String, SessionHook>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "flutter_texture_render")]
|
||||
@ -294,7 +294,7 @@ impl FlutterHandler {
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
pub(crate) fn add_session_hook(&self, key: String, hook: crate::api::SessionHook) -> bool {
|
||||
pub(crate) fn add_session_hook(&self, key: String, hook: SessionHook) -> bool {
|
||||
let mut hooks = self.hooks.write().unwrap();
|
||||
if hooks.contains_key(&key) {
|
||||
// Already has the hook with this key.
|
||||
@ -501,6 +501,14 @@ impl InvokeUiSession for FlutterHandler {
|
||||
#[inline]
|
||||
#[cfg(not(feature = "flutter_texture_render"))]
|
||||
fn on_rgba(&self, rgba: &mut scrap::ImageRgb) {
|
||||
// Give a chance for plugins or etc to hook a rgba data.
|
||||
for (key, hook) in self.hooks.read().unwrap().iter() {
|
||||
match hook {
|
||||
SessionHook::OnSessionRgba(cb) => {
|
||||
cb(key.to_owned(), rgba);
|
||||
},
|
||||
}
|
||||
}
|
||||
// If the current rgba is not fetched by flutter, i.e., is valid.
|
||||
// We give up sending a new event to flutter.
|
||||
if self.rgba_valid.load(Ordering::Relaxed) {
|
||||
@ -1056,3 +1064,9 @@ pub fn stop_global_event_stream(app_type: String) {
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn get_rgba() {}
|
||||
|
||||
/// Hooks for session.
|
||||
#[derive(Clone)]
|
||||
pub enum SessionHook {
|
||||
OnSessionRgba(fn(String, &mut scrap::ImageRgb)),
|
||||
}
|
@ -6,8 +6,8 @@ use std::{ffi::{c_char, c_void}, os::raw::c_uint};
|
||||
/// The data is owned by librustdesk.
|
||||
#[repr(C)]
|
||||
pub struct NativeReturnValue{
|
||||
return_type: c_uint,
|
||||
data: *const c_void
|
||||
pub return_type: c_uint,
|
||||
pub data: *const c_void
|
||||
}
|
||||
|
||||
pub(super) extern "C" fn cb_native_data(method: *const c_char, json: *const c_char, raw: *const c_void, raw_len: usize) -> NativeReturnValue {
|
||||
|
@ -61,7 +61,7 @@ pub(self) trait PluginNativeHandler {
|
||||
) -> Option<NR>;
|
||||
}
|
||||
|
||||
pub(crate) trait Callable {
|
||||
pub trait Callable {
|
||||
fn call(
|
||||
&self,
|
||||
method: &String,
|
||||
|
@ -1,10 +1,22 @@
|
||||
use crate::{call_if_method, define_method_prefix, return_if_not_method};
|
||||
use std::sync::{atomic::AtomicU64, Arc, RwLock};
|
||||
|
||||
use crate::{
|
||||
call_if_method, define_method_prefix, flutter::FlutterHandler, return_if_not_method,
|
||||
ui_session_interface::Session,
|
||||
};
|
||||
|
||||
use super::PluginNativeHandler;
|
||||
|
||||
#[derive(Default)]
|
||||
/// Session related handler for librustdesk core.
|
||||
pub struct PluginNativeSessionHandler;
|
||||
pub struct PluginNativeSessionHandler {
|
||||
sessions: Arc<RwLock<Vec<Session<FlutterHandler>>>>,
|
||||
id: AtomicU64,
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref SESSION_HANDLER: Arc<PluginNativeSessionHandler> = Arc::new(PluginNativeSessionHandler::default());
|
||||
}
|
||||
|
||||
impl PluginNativeHandler for PluginNativeSessionHandler {
|
||||
define_method_prefix!("session_");
|
||||
@ -14,6 +26,48 @@ impl PluginNativeHandler for PluginNativeSessionHandler {
|
||||
method: &str,
|
||||
data: &serde_json::Map<String, serde_json::Value>,
|
||||
) -> Option<super::NR> {
|
||||
match method {
|
||||
"create_session" => {
|
||||
return Some(super::NR {
|
||||
return_type: 1,
|
||||
data: SESSION_HANDLER.create_session() as _,
|
||||
});
|
||||
}
|
||||
"add_session_hook" => {
|
||||
if let Some(id) = data.get("id") {
|
||||
if let Some(id) = id.as_u64() {
|
||||
SESSION_HANDLER.add_session_hook(id);
|
||||
return Some(super::NR {
|
||||
return_type: 0,
|
||||
data: std::ptr::null(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
"remove_session_hook" => {
|
||||
if let Some(id) = data.get("id") {
|
||||
if let Some(id) = id.as_u64() {
|
||||
SESSION_HANDLER.remove_session_hook(id);
|
||||
return Some(super::NR {
|
||||
return_type: 0,
|
||||
data: std::ptr::null(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
"remove_session" => {
|
||||
if let Some(id) = data.get("id") {
|
||||
if let Some(id) = id.as_u64() {
|
||||
SESSION_HANDLER.remove_session(id);
|
||||
return Some(super::NR {
|
||||
return_type: 0,
|
||||
data: std::ptr::null(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@ -29,7 +83,59 @@ impl PluginNativeHandler for PluginNativeSessionHandler {
|
||||
}
|
||||
|
||||
impl PluginNativeSessionHandler {
|
||||
fn create_session() {}
|
||||
fn create_session(&self) -> u64 {
|
||||
let mut sessions = self.sessions.write().unwrap();
|
||||
let unique_id = self.id.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
let mut session: Session<FlutterHandler> = Session::default();
|
||||
session.id = self.get_hook_key(unique_id);
|
||||
sessions.push(session);
|
||||
return unique_id;
|
||||
}
|
||||
|
||||
fn add_session_hook() {}
|
||||
fn add_session_hook(&self, session_id: u64) {
|
||||
let sessions = self.sessions.read().unwrap();
|
||||
let session_id = self.get_hook_key(session_id);
|
||||
for session in sessions.iter() {
|
||||
if session.id == session_id {
|
||||
session.ui_handler.add_session_hook(
|
||||
session_id.to_owned(),
|
||||
crate::flutter::SessionHook::OnSessionRgba(session_rgba_cb),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_session_hook(&self, session_id: u64) {
|
||||
let sessions = self.sessions.read().unwrap();
|
||||
let session_id = self.get_hook_key(session_id);
|
||||
for session in sessions.iter() {
|
||||
if session.id == session_id {
|
||||
session.ui_handler.remove_session_hook(&session_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_session(&self, session_id: u64) {
|
||||
let mut sessions = self.sessions.write().unwrap();
|
||||
let session_id = self.get_hook_key(session_id);
|
||||
for i in 0..sessions.len() {
|
||||
if sessions[i].id == session_id {
|
||||
sessions.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_hook_key(&self, id: u64) -> String {
|
||||
format!("{}_{}", self.method_prefix(), id)
|
||||
}
|
||||
|
||||
// The callback function for rgba data
|
||||
fn session_rgba_cb(&self, key: String, rgb: &mut scrap::ImageRgb) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn session_rgba_cb(key: String, rgb: &mut scrap::ImageRgb) {
|
||||
SESSION_HANDLER.session_rgba_cb(key, rgb);
|
||||
}
|
||||
|
201
src/plugins.rs
201
src/plugins.rs
@ -1,201 +0,0 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ffi::{c_char, CStr},
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use hbb_common::{
|
||||
anyhow::Error,
|
||||
log::{debug, error},
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use libloading::{Library, Symbol};
|
||||
|
||||
lazy_static! {
|
||||
pub static ref PLUGIN_REGISTRAR: Arc<PluginRegistar<PluginImpl>> =
|
||||
Arc::new(PluginRegistar::<PluginImpl>::default());
|
||||
}
|
||||
// API needed to be implemented by plugins.
|
||||
pub type PluginInitFunc = fn() -> i32;
|
||||
// API needed to be implemented by plugins.
|
||||
pub type PluginIdFunc = fn() -> *const c_char;
|
||||
// API needed to be implemented by plugins.
|
||||
pub type PluginNameFunc = fn() -> *const c_char;
|
||||
// API needed to be implemented by plugins.
|
||||
pub type PluginDisposeFunc = fn() -> i32;
|
||||
|
||||
pub trait Plugin {
|
||||
// Return: the unique ID which identifies this plugin.
|
||||
fn plugin_id(&self) -> String;
|
||||
// Return: the name which is human-readable.
|
||||
fn plugin_name(&self) -> String;
|
||||
// Return: the virtual table of the plugin.
|
||||
fn plugin_vt(&self) -> &RustDeskPluginTable;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct RustDeskPluginTable {
|
||||
pub init: Option<PluginInitFunc>, // NonNull
|
||||
pub dispose: Option<PluginDisposeFunc>, // NonNull
|
||||
}
|
||||
|
||||
pub struct PluginImpl {
|
||||
vt: RustDeskPluginTable,
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
_inner: Option<Library>,
|
||||
}
|
||||
|
||||
impl Default for PluginImpl {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
_inner: None,
|
||||
vt: Default::default(),
|
||||
id: Default::default(),
|
||||
name: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for PluginImpl {
|
||||
fn plugin_id(&self) -> String {
|
||||
self.id.to_owned()
|
||||
}
|
||||
|
||||
fn plugin_name(&self) -> String {
|
||||
self.name.to_owned()
|
||||
}
|
||||
|
||||
fn plugin_vt(&self) -> &RustDeskPluginTable {
|
||||
&self.vt
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct PluginRegistar<P: Plugin> {
|
||||
plugins: Arc<RwLock<HashMap<String, P>>>,
|
||||
}
|
||||
|
||||
impl<P: Plugin> PluginRegistar<P> {
|
||||
pub fn load_plugin(&self, path: *const c_char) -> i32 {
|
||||
let p = unsafe { CStr::from_ptr(path) };
|
||||
let lib_path = p.to_str().unwrap_or("").to_owned();
|
||||
let lib = unsafe { libloading::Library::new(lib_path.as_str()) };
|
||||
match lib {
|
||||
Ok(lib) => match lib.try_into() {
|
||||
Ok(plugin) => {
|
||||
let plugin: PluginImpl = plugin;
|
||||
// try to initialize this plugin
|
||||
if let Some(init) = plugin.plugin_vt().init {
|
||||
let init_ret = init();
|
||||
if init_ret != 0 {
|
||||
error!(
|
||||
"Error when initializing the plugin {} with error code {}.",
|
||||
plugin.name, init_ret
|
||||
);
|
||||
return init_ret;
|
||||
}
|
||||
}
|
||||
PLUGIN_REGISTRAR
|
||||
.plugins
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(lib_path, plugin);
|
||||
return 0;
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Load plugin failed: {}", err);
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
eprintln!("Load plugin failed: {}", err);
|
||||
}
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
||||
pub fn unload_plugin(&self, path: *const c_char) -> i32 {
|
||||
let p = unsafe { CStr::from_ptr(path) };
|
||||
let lib_path = p.to_str().unwrap_or("").to_owned();
|
||||
match PLUGIN_REGISTRAR.plugins.write().unwrap().remove(&lib_path) {
|
||||
Some(plugin) => {
|
||||
if let Some(dispose) = plugin.plugin_vt().dispose {
|
||||
return dispose();
|
||||
}
|
||||
0
|
||||
}
|
||||
None => -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Library> for PluginImpl {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(library: Library) -> Result<Self, Self::Error> {
|
||||
let init: Symbol<PluginInitFunc> = unsafe { library.get(b"plugin_init")? };
|
||||
let dispose: Symbol<PluginDisposeFunc> = unsafe { library.get(b"plugin_dispose")? };
|
||||
let id_func: Symbol<PluginIdFunc> = unsafe { library.get(b"plugin_id")? };
|
||||
let id_string = unsafe {
|
||||
std::ffi::CStr::from_ptr(id_func())
|
||||
.to_str()
|
||||
.unwrap_or("")
|
||||
.to_owned()
|
||||
};
|
||||
let name_func: Symbol<PluginNameFunc> = unsafe { library.get(b"plugin_name")? };
|
||||
let name_string = unsafe {
|
||||
std::ffi::CStr::from_ptr(name_func())
|
||||
.to_str()
|
||||
.unwrap_or("")
|
||||
.to_owned()
|
||||
};
|
||||
debug!(
|
||||
"Successfully loaded the plugin called {} with id {}.",
|
||||
name_string, id_string
|
||||
);
|
||||
Ok(Self {
|
||||
vt: RustDeskPluginTable {
|
||||
init: Some(*init),
|
||||
dispose: Some(*dispose),
|
||||
},
|
||||
id: id_string,
|
||||
name: name_string,
|
||||
_inner: Some(library),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_plugin() {
|
||||
use std::io::Write;
|
||||
let mut cmd = std::process::Command::new("cargo");
|
||||
cmd.current_dir("./examples/custom_plugin");
|
||||
// Strip this shared library.
|
||||
cmd.env("RUSTFLAGS", "-C link-arg=-s");
|
||||
cmd.arg("build");
|
||||
// Spawn the compiler process.
|
||||
let mut child = cmd.spawn().unwrap();
|
||||
// Wait for the compiler to finish.
|
||||
let status = child.wait().unwrap();
|
||||
assert!(status.success());
|
||||
// Load the library.
|
||||
let lib = unsafe {
|
||||
Library::new("./examples/custom_plugin/target/debug/libcustom_plugin.so").unwrap()
|
||||
};
|
||||
let plugin: PluginImpl = lib.try_into().unwrap();
|
||||
assert!(plugin._inner.is_some());
|
||||
assert!(plugin.name == "A Template Rust Plugin");
|
||||
assert!(plugin.id == "TemplatePlugin");
|
||||
println!(
|
||||
"plugin vt size: {}",
|
||||
std::mem::size_of::<RustDeskPluginTable>()
|
||||
);
|
||||
assert!(PLUGIN_REGISTRAR
|
||||
.plugins
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert("test".to_owned(), plugin)
|
||||
.is_none());
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user