Merge pull request #4221 from Kingtous/feat/plugins
feat: add native handlers for plugins
This commit is contained in:
commit
114b004192
87
src/api.rs
87
src/api.rs
@ -1,87 +0,0 @@
|
|||||||
use std::ffi::{c_char};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
flutter::{FlutterHandler, SESSIONS},
|
|
||||||
plugins::PLUGIN_REGISTRAR,
|
|
||||||
ui_session_interface::Session,
|
|
||||||
};
|
|
||||||
|
|
||||||
// API provided by RustDesk.
|
|
||||||
pub type LoadPluginFunc = fn(*const c_char) -> i32;
|
|
||||||
pub type UnloadPluginFunc = fn(*const c_char) -> i32;
|
|
||||||
pub type AddSessionFunc = fn(session_id: String) -> bool;
|
|
||||||
pub type RemoveSessionFunc = fn(session_id: &String) -> bool;
|
|
||||||
pub type AddSessionHookFunc = fn(session_id: String, key: String, hook: SessionHook) -> bool;
|
|
||||||
pub type RemoveSessionHookFunc = fn(session_id: String, key: &String) -> bool;
|
|
||||||
|
|
||||||
/// Hooks for session.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum SessionHook {
|
|
||||||
OnSessionRgba(fn(String, Vec<i8>) -> Vec<i8>),
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[repr(C)]
|
|
||||||
pub struct RustDeskApiTable {
|
|
||||||
pub(crate) load_plugin: LoadPluginFunc,
|
|
||||||
pub(crate) unload_plugin: UnloadPluginFunc,
|
|
||||||
pub add_session: AddSessionFunc,
|
|
||||||
pub remove_session: RemoveSessionFunc,
|
|
||||||
pub add_session_hook: AddSessionHookFunc,
|
|
||||||
pub remove_session_hook: RemoveSessionHookFunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_plugin(path: *const c_char) -> i32 {
|
|
||||||
PLUGIN_REGISTRAR.load_plugin(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unload_plugin(path: *const c_char) -> i32 {
|
|
||||||
PLUGIN_REGISTRAR.unload_plugin(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_session(session_id: String) -> bool {
|
|
||||||
// let mut sessions = SESSIONS.write().unwrap();
|
|
||||||
// if sessions.contains_key(&session.id) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// let _ = sessions.insert(session.id.to_owned(), session);
|
|
||||||
// true
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_session(session_id: &String) -> bool {
|
|
||||||
let mut sessions = SESSIONS.write().unwrap();
|
|
||||||
if !sessions.contains_key(session_id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let _ = sessions.remove(session_id);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_session_hook(session_id: String, key: String, hook: SessionHook) -> bool {
|
|
||||||
let sessions = SESSIONS.read().unwrap();
|
|
||||||
if let Some(session) = sessions.get(&session_id) {
|
|
||||||
return session.add_session_hook(key, hook);
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_session_hook(session_id: String, key: &String) -> bool {
|
|
||||||
let sessions = SESSIONS.read().unwrap();
|
|
||||||
if let Some(session) = sessions.get(&session_id) {
|
|
||||||
return session.remove_session_hook(key);
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for RustDeskApiTable {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
load_plugin,
|
|
||||||
unload_plugin,
|
|
||||||
add_session,
|
|
||||||
remove_session,
|
|
||||||
add_session_hook,
|
|
||||||
remove_session_hook,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -147,7 +147,7 @@ pub struct FlutterHandler {
|
|||||||
renderer: Arc<RwLock<VideoRenderer>>,
|
renderer: Arc<RwLock<VideoRenderer>>,
|
||||||
peer_info: Arc<RwLock<PeerInfo>>,
|
peer_info: Arc<RwLock<PeerInfo>>,
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[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"))]
|
#[cfg(not(feature = "flutter_texture_render"))]
|
||||||
@ -160,7 +160,7 @@ pub struct FlutterHandler {
|
|||||||
pub rgba_valid: Arc<AtomicBool>,
|
pub rgba_valid: Arc<AtomicBool>,
|
||||||
peer_info: Arc<RwLock<PeerInfo>>,
|
peer_info: Arc<RwLock<PeerInfo>>,
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[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")]
|
#[cfg(feature = "flutter_texture_render")]
|
||||||
@ -294,7 +294,7 @@ impl FlutterHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[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();
|
let mut hooks = self.hooks.write().unwrap();
|
||||||
if hooks.contains_key(&key) {
|
if hooks.contains_key(&key) {
|
||||||
// Already has the hook with this key.
|
// Already has the hook with this key.
|
||||||
@ -501,6 +501,15 @@ impl InvokeUiSession for FlutterHandler {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(not(feature = "flutter_texture_render"))]
|
#[cfg(not(feature = "flutter_texture_render"))]
|
||||||
fn on_rgba(&self, rgba: &mut scrap::ImageRgb) {
|
fn on_rgba(&self, rgba: &mut scrap::ImageRgb) {
|
||||||
|
// Give a chance for plugins or etc to hook a rgba data.
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
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.
|
// If the current rgba is not fetched by flutter, i.e., is valid.
|
||||||
// We give up sending a new event to flutter.
|
// We give up sending a new event to flutter.
|
||||||
if self.rgba_valid.load(Ordering::Relaxed) {
|
if self.rgba_valid.load(Ordering::Relaxed) {
|
||||||
@ -1056,3 +1065,9 @@ pub fn stop_global_event_stream(app_type: String) {
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn get_rgba() {}
|
unsafe extern "C" fn get_rgba() {}
|
||||||
|
|
||||||
|
/// Hooks for session.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum SessionHook {
|
||||||
|
OnSessionRgba(fn(String, &mut scrap::ImageRgb)),
|
||||||
|
}
|
@ -48,13 +48,6 @@ mod license;
|
|||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
mod port_forward;
|
mod port_forward;
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
||||||
#[cfg(any(feature = "flutter"))]
|
|
||||||
pub mod api;
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
||||||
#[cfg(any(feature = "flutter"))]
|
|
||||||
pub mod plugins;
|
|
||||||
|
|
||||||
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
|
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
pub mod plugin;
|
pub mod plugin;
|
||||||
|
@ -17,6 +17,8 @@ pub const ERR_CALL_NOT_SUPPORTED_METHOD: i32 = 10202;
|
|||||||
// failed on calling
|
// failed on calling
|
||||||
pub const ERR_CALL_INVALID_ARGS: i32 = 10301;
|
pub const ERR_CALL_INVALID_ARGS: i32 = 10301;
|
||||||
pub const ERR_PEER_ID_MISMATCH: i32 = 10302;
|
pub const ERR_PEER_ID_MISMATCH: i32 = 10302;
|
||||||
|
// no handlers on calling
|
||||||
|
pub const ERR_NOT_HANDLED: i32 = 10401;
|
||||||
|
|
||||||
// ======================================================
|
// ======================================================
|
||||||
// errors that should be handled by the plugin
|
// errors that should be handled by the plugin
|
||||||
|
@ -8,6 +8,8 @@ mod errno;
|
|||||||
pub mod ipc;
|
pub mod ipc;
|
||||||
mod plog;
|
mod plog;
|
||||||
mod plugins;
|
mod plugins;
|
||||||
|
pub mod native;
|
||||||
|
mod native_handlers;
|
||||||
|
|
||||||
pub use plugins::{
|
pub use plugins::{
|
||||||
handle_client_event, handle_listen_event, handle_server_event, handle_ui_event, load_plugin,
|
handle_client_event, handle_listen_event, handle_server_event, handle_ui_event, load_plugin,
|
||||||
|
40
src/plugin/native.rs
Normal file
40
src/plugin/native.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use std::{
|
||||||
|
ffi::{c_char, c_int, c_void},
|
||||||
|
os::raw::c_uint,
|
||||||
|
};
|
||||||
|
|
||||||
|
use hbb_common::log::error;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
cstr_to_string,
|
||||||
|
errno::ERR_NOT_HANDLED,
|
||||||
|
native_handlers::{Callable, NATIVE_HANDLERS_REGISTRAR},
|
||||||
|
};
|
||||||
|
/// The native returned value from librustdesk native.
|
||||||
|
///
|
||||||
|
/// [Note]
|
||||||
|
/// The data is owned by librustdesk.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct NativeReturnValue {
|
||||||
|
pub return_type: c_int,
|
||||||
|
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 {
|
||||||
|
let ret = match cstr_to_string(method) {
|
||||||
|
Ok(method) => NATIVE_HANDLERS_REGISTRAR.call(&method, json, raw, raw_len),
|
||||||
|
Err(err) => {
|
||||||
|
error!("cb_native_data error: {}", err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return ret.unwrap_or(NativeReturnValue {
|
||||||
|
return_type: ERR_NOT_HANDLED,
|
||||||
|
data: std::ptr::null(),
|
||||||
|
});
|
||||||
|
}
|
27
src/plugin/native_handlers/macros.rs
Normal file
27
src/plugin/native_handlers/macros.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! return_if_not_method {
|
||||||
|
($call: ident, $prefix: ident) => {
|
||||||
|
if $call.starts_with($prefix) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! call_if_method {
|
||||||
|
($call: ident ,$method: literal, $block: block) => {
|
||||||
|
if ($call != $method) {
|
||||||
|
$block
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! define_method_prefix {
|
||||||
|
($prefix: literal) => {
|
||||||
|
#[inline]
|
||||||
|
fn method_prefix(&self) -> &'static str {
|
||||||
|
$prefix
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
124
src/plugin/native_handlers/mod.rs
Normal file
124
src/plugin/native_handlers/mod.rs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
use std::{
|
||||||
|
ffi::c_void,
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
vec,
|
||||||
|
};
|
||||||
|
|
||||||
|
use hbb_common::libc::c_char;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use serde_json::Map;
|
||||||
|
|
||||||
|
use crate::return_if_not_method;
|
||||||
|
|
||||||
|
use self::session::PluginNativeSessionHandler;
|
||||||
|
|
||||||
|
use super::cstr_to_string;
|
||||||
|
|
||||||
|
mod macros;
|
||||||
|
pub mod session;
|
||||||
|
|
||||||
|
pub type NR = super::native::NativeReturnValue;
|
||||||
|
pub type PluginNativeHandlerRegistrar = NativeHandlerRegistrar<Box<dyn Callable + Send + Sync>>;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref NATIVE_HANDLERS_REGISTRAR: Arc<PluginNativeHandlerRegistrar> =
|
||||||
|
Arc::new(PluginNativeHandlerRegistrar::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct NativeHandlerRegistrar<H> {
|
||||||
|
handlers: Arc<RwLock<Vec<H>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PluginNativeHandlerRegistrar {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
handlers: Arc::new(RwLock::new(vec![Box::new(
|
||||||
|
// Add prebuilt native handlers here.
|
||||||
|
PluginNativeSessionHandler::default(),
|
||||||
|
)])),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(self) trait PluginNativeHandler {
|
||||||
|
/// The method prefix handled by this handler.s
|
||||||
|
fn method_prefix(&self) -> &'static str;
|
||||||
|
|
||||||
|
/// Try to handle the method with the given data.
|
||||||
|
///
|
||||||
|
/// Returns: None for the message does not be handled by this handler.
|
||||||
|
fn on_message(&self, method: &str, data: &Map<String, serde_json::Value>) -> Option<NR>;
|
||||||
|
|
||||||
|
/// Try to handle the method with the given data and extra void binary data.
|
||||||
|
///
|
||||||
|
/// Returns: None for the message does not be handled by this handler.
|
||||||
|
fn on_message_raw(
|
||||||
|
&self,
|
||||||
|
method: &str,
|
||||||
|
data: &Map<String, serde_json::Value>,
|
||||||
|
raw: *const c_void,
|
||||||
|
raw_len: usize,
|
||||||
|
) -> Option<NR>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Callable {
|
||||||
|
fn call(
|
||||||
|
&self,
|
||||||
|
method: &String,
|
||||||
|
json: *const c_char,
|
||||||
|
raw: *const c_void,
|
||||||
|
raw_len: usize,
|
||||||
|
) -> Option<NR> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Callable for T
|
||||||
|
where
|
||||||
|
T: PluginNativeHandler + Send + Sync,
|
||||||
|
{
|
||||||
|
fn call(
|
||||||
|
&self,
|
||||||
|
method: &String,
|
||||||
|
json: *const c_char,
|
||||||
|
raw: *const c_void,
|
||||||
|
raw_len: usize,
|
||||||
|
) -> Option<NR> {
|
||||||
|
let prefix = self.method_prefix();
|
||||||
|
return_if_not_method!(method, prefix);
|
||||||
|
match cstr_to_string(json) {
|
||||||
|
Ok(s) => {
|
||||||
|
if let Ok(json) = serde_json::from_str(s.as_str()) {
|
||||||
|
let method_suffix = &method[prefix.len()..];
|
||||||
|
if raw != std::ptr::null() && raw_len > 0 {
|
||||||
|
return self.on_message_raw(method_suffix, &json, raw, raw_len);
|
||||||
|
} else {
|
||||||
|
return self.on_message(method_suffix, &json);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Callable for PluginNativeHandlerRegistrar {
|
||||||
|
fn call(
|
||||||
|
&self,
|
||||||
|
method: &String,
|
||||||
|
json: *const c_char,
|
||||||
|
raw: *const c_void,
|
||||||
|
raw_len: usize,
|
||||||
|
) -> Option<NR> {
|
||||||
|
for handler in self.handlers.read().unwrap().iter() {
|
||||||
|
let ret = handler.call(method, json, raw, raw_len);
|
||||||
|
if ret.is_some() {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
141
src/plugin/native_handlers/session.rs
Normal file
141
src/plugin/native_handlers/session.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
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 {
|
||||||
|
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_");
|
||||||
|
|
||||||
|
fn on_message(
|
||||||
|
&self,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_message_raw(
|
||||||
|
&self,
|
||||||
|
method: &str,
|
||||||
|
data: &serde_json::Map<String, serde_json::Value>,
|
||||||
|
raw: *const std::ffi::c_void,
|
||||||
|
raw_len: usize,
|
||||||
|
) -> Option<super::NR> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PluginNativeSessionHandler {
|
||||||
|
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(&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);
|
||||||
|
}
|
@ -14,7 +14,7 @@ use std::{
|
|||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
ffi::{c_char, c_void},
|
ffi::{c_char, c_void},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock}, os::raw::c_uint,
|
||||||
};
|
};
|
||||||
|
|
||||||
const METHOD_HANDLE_UI: &[u8; 10] = b"handle_ui\0";
|
const METHOD_HANDLE_UI: &[u8; 10] = b"handle_ui\0";
|
||||||
@ -91,6 +91,14 @@ type CallbackGetId = extern "C" fn() -> *const c_char;
|
|||||||
/// level: "error", "warn", "info", "debug", "trace".
|
/// level: "error", "warn", "info", "debug", "trace".
|
||||||
/// msg: The message.
|
/// msg: The message.
|
||||||
type CallbackLog = extern "C" fn(level: *const c_char, msg: *const c_char);
|
type CallbackLog = extern "C" fn(level: *const c_char, msg: *const c_char);
|
||||||
|
|
||||||
|
/// Callback to the librustdesk core.
|
||||||
|
///
|
||||||
|
/// method: the method name of this callback.
|
||||||
|
/// json: the json data for the parameters. The argument *must* be non-null.
|
||||||
|
/// raw: the binary data for this call, nullable.
|
||||||
|
/// raw_len: the length of this binary data, only valid when we pass raw data to `raw`.
|
||||||
|
type CallbackNative = extern "C" fn(method: *const c_char, json: *const c_char, raw: *const c_void, raw_len: usize) -> super::native::NativeReturnValue;
|
||||||
/// The main function of the plugin on the client(self) side.
|
/// The main function of the plugin on the client(self) side.
|
||||||
///
|
///
|
||||||
/// method: The method. "handle_ui" or "handle_peer"
|
/// method: The method. "handle_ui" or "handle_peer"
|
||||||
@ -140,6 +148,7 @@ struct Callbacks {
|
|||||||
get_conf: CallbackGetConf,
|
get_conf: CallbackGetConf,
|
||||||
get_id: CallbackGetId,
|
get_id: CallbackGetId,
|
||||||
log: CallbackLog,
|
log: CallbackLog,
|
||||||
|
native: CallbackNative
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The plugin initialize data.
|
/// The plugin initialize data.
|
||||||
@ -334,6 +343,7 @@ fn load_plugin_path(path: &str) -> ResultType<()> {
|
|||||||
get_conf: config::cb_get_conf,
|
get_conf: config::cb_get_conf,
|
||||||
get_id: config::cb_get_local_peer_id,
|
get_id: config::cb_get_local_peer_id,
|
||||||
log: super::plog::plugin_log,
|
log: super::plog::plugin_log,
|
||||||
|
native: super::native::cb_native_data
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
plugin.init(&init_data, path)?;
|
plugin.init(&init_data, path)?;
|
||||||
|
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