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/ | .vscode-server/ | ||||||
| .ssh | .ssh | ||||||
| .devcontainer/.* | .devcontainer/.* | ||||||
|  | # build cache in examples | ||||||
|  | examples/**/target/ | ||||||
| @ -136,7 +136,7 @@ flutter_rust_bridge = "1.61.1" | |||||||
| 
 | 
 | ||||||
| [workspace] | [workspace] | ||||||
| members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/simple_rc", "libs/portable"] | 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] | [package.metadata.winres] | ||||||
| LegalCopyright = "Copyright © 2022 Purslane, Inc." | 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)] | #[repr(C)] | ||||||
| pub struct RustDeskApiTable { | pub struct RustDeskApiTable { | ||||||
|     pub register_plugin: LoadPluginFunc, |     pub(crate) register_plugin: LoadPluginFunc, | ||||||
|     pub unload_plugin: UnloadPluginFunc, |     pub(crate) unload_plugin: UnloadPluginFunc, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| @ -22,11 +22,6 @@ fn unload_plugin(path: *const c_char) -> i32 { | |||||||
|     PLUGIN_REGISTRAR.unload_plugin(path) |     PLUGIN_REGISTRAR.unload_plugin(path) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[no_mangle] |  | ||||||
| fn get_api_table() -> RustDeskApiTable { |  | ||||||
|     RustDeskApiTable::default() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Default for RustDeskApiTable { | impl Default for RustDeskApiTable { | ||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         Self { |         Self { | ||||||
|  | |||||||
							
								
								
									
										13
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/lib.rs
									
									
									
									
									
								
							| @ -1,7 +1,7 @@ | |||||||
|  | mod keyboard; | ||||||
| #[cfg(not(any(target_os = "ios")))] | #[cfg(not(any(target_os = "ios")))] | ||||||
| /// cbindgen:ignore
 | /// cbindgen:ignore
 | ||||||
| pub mod platform; | pub mod platform; | ||||||
| mod keyboard; |  | ||||||
| #[cfg(not(any(target_os = "android", target_os = "ios")))] | #[cfg(not(any(target_os = "android", target_os = "ios")))] | ||||||
| pub use platform::{get_cursor, get_cursor_data, get_cursor_pos, start_os_service}; | pub use platform::{get_cursor, get_cursor_data, get_cursor_pos, start_os_service}; | ||||||
| #[cfg(not(any(target_os = "ios")))] | #[cfg(not(any(target_os = "ios")))] | ||||||
| @ -20,7 +20,12 @@ pub use self::rendezvous_mediator::*; | |||||||
| pub mod common; | pub mod common; | ||||||
| #[cfg(not(any(target_os = "ios")))] | #[cfg(not(any(target_os = "ios")))] | ||||||
| pub mod ipc; | 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; | pub mod ui; | ||||||
| mod version; | mod version; | ||||||
| pub use version::*; | pub use version::*; | ||||||
| @ -44,9 +49,9 @@ mod license; | |||||||
| mod port_forward; | mod port_forward; | ||||||
| 
 | 
 | ||||||
| #[cfg(not(any(target_os = "android", target_os = "ios")))] | #[cfg(not(any(target_os = "android", target_os = "ios")))] | ||||||
| mod plugins; | pub mod api; | ||||||
| #[cfg(not(any(target_os = "android", target_os = "ios")))] | #[cfg(not(any(target_os = "android", target_os = "ios")))] | ||||||
| mod api; | pub mod plugins; | ||||||
| 
 | 
 | ||||||
| mod tray; | mod tray; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,7 +4,10 @@ use std::{ | |||||||
|     sync::{Arc, RwLock}, |     sync::{Arc, RwLock}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use hbb_common::{anyhow::Error, log::debug}; | use hbb_common::{ | ||||||
|  |     anyhow::Error, | ||||||
|  |     log::{debug, error}, | ||||||
|  | }; | ||||||
| use lazy_static::lazy_static; | use lazy_static::lazy_static; | ||||||
| use libloading::{Library, Symbol}; | use libloading::{Library, Symbol}; | ||||||
| 
 | 
 | ||||||
| @ -82,6 +85,18 @@ impl<P: Plugin> PluginRegistar<P> { | |||||||
|         match lib { |         match lib { | ||||||
|             Ok(lib) => match lib.try_into() { |             Ok(lib) => match lib.try_into() { | ||||||
|                 Ok(plugin) => { |                 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 |                     PLUGIN_REGISTRAR | ||||||
|                         .plugins |                         .plugins | ||||||
|                         .write() |                         .write() | ||||||
| @ -104,7 +119,12 @@ impl<P: Plugin> PluginRegistar<P> { | |||||||
|         let p = unsafe { CStr::from_ptr(path) }; |         let p = unsafe { CStr::from_ptr(path) }; | ||||||
|         let lib_path = p.to_str().unwrap_or("").to_owned(); |         let lib_path = p.to_str().unwrap_or("").to_owned(); | ||||||
|         match PLUGIN_REGISTRAR.plugins.write().unwrap().remove(&lib_path) { |         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, |             None => -1, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -150,33 +170,28 @@ impl TryFrom<Library> for PluginImpl { | |||||||
| #[cfg(target_os = "linux")] | #[cfg(target_os = "linux")] | ||||||
| fn test_plugin() { | fn test_plugin() { | ||||||
|     use std::io::Write; |     use std::io::Write; | ||||||
| 
 |     let mut cmd = std::process::Command::new("cargo"); | ||||||
|     let code = " |     cmd.current_dir("./examples/custom_plugin"); | ||||||
|     const char* plugin_name(){return \"test_name\";};
 |     // Strip this shared library.
 | ||||||
|     const char* plugin_id(){return \"test_id\"; }
 |     cmd.env("RUSTFLAGS", "-C link-arg=-s"); | ||||||
|     int plugin_init() {return 0;} |     cmd.arg("build"); | ||||||
|     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"); |  | ||||||
|     // Spawn the compiler process.
 |     // Spawn the compiler process.
 | ||||||
|     let mut child = cmd.spawn().unwrap(); |     let mut child = cmd.spawn().unwrap(); | ||||||
|     // Wait for the compiler to finish.
 |     // Wait for the compiler to finish.
 | ||||||
|     let status = child.wait().unwrap(); |     let status = child.wait().unwrap(); | ||||||
|     assert!(status.success()); |     assert!(status.success()); | ||||||
|     // Load the library.
 |     // 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(); |     let plugin: PluginImpl = lib.try_into().unwrap(); | ||||||
|     assert!(plugin._inner.is_some()); |     assert!(plugin._inner.is_some()); | ||||||
|     assert!(plugin.name == "test_name"); |     assert!(plugin.name == "A Template Rust Plugin"); | ||||||
|     assert!(plugin.id == "test_id"); |     assert!(plugin.id == "TemplatePlugin"); | ||||||
|  |     println!( | ||||||
|  |         "plugin vt size: {}", | ||||||
|  |         std::mem::size_of::<RustDeskPluginTable>() | ||||||
|  |     ); | ||||||
|     assert!(PLUGIN_REGISTRAR |     assert!(PLUGIN_REGISTRAR | ||||||
|         .plugins |         .plugins | ||||||
|         .write() |         .write() | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user