mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-27 23:19:02 +08:00
Merge pull request #4031 from Kingtous/feat/plugins
feat: add a rust example for writing a custom plugin
This commit is contained in:
commit
1280b7beb4
2
.gitignore
vendored
2
.gitignore
vendored
@ -48,3 +48,5 @@ lib/generated_bridge.dart
|
||||
.vscode-server/
|
||||
.ssh
|
||||
.devcontainer/.*
|
||||
# build cache in examples
|
||||
examples/**/target/
|
@ -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."
|
||||
|
23
examples/custom_plugin/Cargo.toml
Normal file
23
examples/custom_plugin/Cargo.toml
Normal 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
|
30
examples/custom_plugin/src/lib.rs
Normal file
30
examples/custom_plugin/src/lib.rs
Normal 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 _;
|
||||
}
|
@ -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 {
|
||||
|
13
src/lib.rs
13
src/lib.rs
@ -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;
|
||||
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user