Merge pull request #4031 from Kingtous/feat/plugins

feat: add a rust example for writing a custom plugin
This commit is contained in:
RustDesk 2023-04-13 09:25:46 +08:00 committed by GitHub
commit 1280b7beb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 103 additions and 33 deletions

2
.gitignore vendored
View File

@ -48,3 +48,5 @@ lib/generated_bridge.dart
.vscode-server/
.ssh
.devcontainer/.*
# build cache in examples
examples/**/target/

View File

@ -136,7 +136,7 @@ flutter_rust_bridge = "1.61.1"
[workspace]
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/simple_rc", "libs/portable"]
exclude = ["vdi/host"]
exclude = ["vdi/host", "examples/custom_plugin"]
[package.metadata.winres]
LegalCopyright = "Copyright © 2022 Purslane, Inc."

View File

@ -0,0 +1,23 @@
[package]
name = "custom_plugin"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "custom_plugin"
path = "src/lib.rs"
crate-type = ["cdylib"]
[dependencies]
lazy_static = "1.4.0"
rustdesk = { path = "../../", version = "1.2.0"}
[profile.release]
lto = true
codegen-units = 1
panic = 'abort'
strip = true
#opt-level = 'z' # only have smaller size after strip
rpath = true

View File

@ -0,0 +1,30 @@
use librustdesk::{api::RustDeskApiTable};
/// This file demonstrates how to write a custom plugin for RustDesk.
use std::ffi::{c_char, c_int, CString};
lazy_static::lazy_static! {
pub static ref PLUGIN_NAME: CString = CString::new("A Template Rust Plugin").unwrap();
pub static ref PLUGIN_ID: CString = CString::new("TemplatePlugin").unwrap();
// Do your own logic based on the API provided by RustDesk.
pub static ref API: RustDeskApiTable = RustDeskApiTable::default();
}
#[no_mangle]
fn plugin_name() -> *const c_char {
return PLUGIN_NAME.as_ptr();
}
#[no_mangle]
fn plugin_id() -> *const c_char {
return PLUGIN_ID.as_ptr();
}
#[no_mangle]
fn plugin_init() -> c_int {
return 0 as _;
}
#[no_mangle]
fn plugin_dispose() -> c_int {
return 0 as _;
}

View File

@ -8,8 +8,8 @@ pub type UnloadPluginFunc = fn(*const c_char) -> i32;
#[repr(C)]
pub struct RustDeskApiTable {
pub register_plugin: LoadPluginFunc,
pub unload_plugin: UnloadPluginFunc,
pub(crate) register_plugin: LoadPluginFunc,
pub(crate) unload_plugin: UnloadPluginFunc,
}
#[no_mangle]
@ -22,11 +22,6 @@ fn unload_plugin(path: *const c_char) -> i32 {
PLUGIN_REGISTRAR.unload_plugin(path)
}
#[no_mangle]
fn get_api_table() -> RustDeskApiTable {
RustDeskApiTable::default()
}
impl Default for RustDeskApiTable {
fn default() -> Self {
Self {

View File

@ -1,7 +1,7 @@
mod keyboard;
#[cfg(not(any(target_os = "ios")))]
/// cbindgen:ignore
pub mod platform;
mod keyboard;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub use platform::{get_cursor, get_cursor_data, get_cursor_pos, start_os_service};
#[cfg(not(any(target_os = "ios")))]
@ -20,7 +20,12 @@ pub use self::rendezvous_mediator::*;
pub mod common;
#[cfg(not(any(target_os = "ios")))]
pub mod ipc;
#[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli", feature = "flutter")))]
#[cfg(not(any(
target_os = "android",
target_os = "ios",
feature = "cli",
feature = "flutter"
)))]
pub mod ui;
mod version;
pub use version::*;
@ -44,9 +49,9 @@ mod license;
mod port_forward;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
mod plugins;
pub mod api;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
mod api;
pub mod plugins;
mod tray;

View File

@ -4,7 +4,10 @@ use std::{
sync::{Arc, RwLock},
};
use hbb_common::{anyhow::Error, log::debug};
use hbb_common::{
anyhow::Error,
log::{debug, error},
};
use lazy_static::lazy_static;
use libloading::{Library, Symbol};
@ -82,6 +85,18 @@ impl<P: Plugin> PluginRegistar<P> {
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()
@ -104,7 +119,12 @@ impl<P: Plugin> PluginRegistar<P> {
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(_) => 0,
Some(plugin) => {
if let Some(dispose) = plugin.plugin_vt().dispose {
return dispose();
}
0
}
None => -1,
}
}
@ -150,33 +170,28 @@ impl TryFrom<Library> for PluginImpl {
#[cfg(target_os = "linux")]
fn test_plugin() {
use std::io::Write;
let code = "
const char* plugin_name(){return \"test_name\";};
const char* plugin_id(){return \"test_id\"; }
int plugin_init() {return 0;}
int plugin_dispose() {return 0;}
";
let mut f = std::fs::File::create("test.c").unwrap();
f.write_all(code.as_bytes()).unwrap();
f.flush().unwrap();
let mut cmd = std::process::Command::new("cc");
cmd.arg("-fPIC")
.arg("-shared")
.arg("test.c")
.arg("-o")
.arg("libtest.so");
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("./libtest.so").unwrap() };
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 == "test_name");
assert!(plugin.id == "test_id");
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()