Merge pull request #4200 from fufesou/feat/plugin_framework

Feat/plugin framework
This commit is contained in:
RustDesk 2023-04-26 16:37:58 +08:00 committed by GitHub
commit e6ac69abfe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 110 additions and 49 deletions

View File

@ -1608,12 +1608,16 @@ class FFI {
if (message.field0 == "close") { if (message.field0 == "close") {
break; break;
} }
Map<String, dynamic>? event;
try { try {
Map<String, dynamic> event = json.decode(message.field0); event = json.decode(message.field0);
await cb(event);
} catch (e) { } catch (e) {
debugPrint('json.decode fail1(): $e, ${message.field0}'); debugPrint('json.decode fail1(): $e, ${message.field0}');
} }
if (event != null) {
await cb(event);
}
} else if (message is EventToUI_Rgba) { } else if (message is EventToUI_Rgba) {
if (useTextureRender) { if (useTextureRender) {
if (_waitForImage[id]!) { if (_waitForImage[id]!) {

View File

@ -1,11 +1,19 @@
import 'dart:convert';
import 'package:flutter/material.dart';
void handlePluginEvent( void handlePluginEvent(
Map<String, dynamic> evt, Map<String, dynamic> evt,
String peer, String peer,
Function(Map<String, dynamic> e) handleMsgBox, Function(Map<String, dynamic> e) handleMsgBox,
) { ) {
if (evt['content']?['c'] == null) return; Map<String, dynamic>? content;
final t = evt['content']?['t']; try {
if (t == 'MsgBox') { content = json.decode(evt['content']);
handleMsgBox(evt['content']?['c']); } catch (e) {
debugPrint(
'Json decode plugin event content failed: $e, ${evt['content']}');
}
if (content?['t'] == 'MsgBox') {
handleMsgBox(content?['c']);
} }
} }

View File

@ -605,7 +605,7 @@ message PluginRequest {
bytes content = 2; bytes content = 2;
} }
message PluginResponse { message PluginFailure {
string id = 1; string id = 1;
string name = 2; string name = 2;
string msg = 3; string msg = 3;
@ -633,7 +633,7 @@ message Misc {
SwitchBack switch_back = 22; SwitchBack switch_back = 22;
Resolution change_resolution = 24; Resolution change_resolution = 24;
PluginRequest plugin_request = 25; PluginRequest plugin_request = 25;
PluginResponse plugin_response = 26; PluginFailure plugin_failure = 26;
} }
} }

View File

@ -1301,12 +1301,16 @@ impl<T: InvokeUiSession> Remote<T> {
#[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")))]
Some(misc::Union::PluginRequest(p)) => { Some(misc::Union::PluginRequest(p)) => {
allow_err!(crate::plugin::handle_server_event(&p.id, &self.handler.id, &p.content)); allow_err!(crate::plugin::handle_server_event(
&p.id,
&self.handler.id,
&p.content
));
// to-do: show message box on UI when error occurs? // to-do: show message box on UI when error occurs?
} }
#[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")))]
Some(misc::Union::PluginResponse(p)) => { Some(misc::Union::PluginFailure(p)) => {
let name = if p.name.is_empty() { let name = if p.name.is_empty() {
"plugin".to_string() "plugin".to_string()
} else { } else {

View File

@ -36,13 +36,13 @@ lazy_static::lazy_static! {
}; };
} }
#[derive(Deserialize)] #[derive(Debug, Deserialize)]
struct ConfigToUi { struct ConfigToUi {
channel: u16, channel: u16,
location: String, location: String,
} }
#[derive(Deserialize)] #[derive(Debug, Deserialize)]
struct MsgToConfig { struct MsgToConfig {
id: String, id: String,
r#type: String, r#type: String,
@ -99,7 +99,7 @@ pub(super) extern "C" fn cb_msg(
} }
MSG_TO_UI_TARGET => { MSG_TO_UI_TARGET => {
let content_slice = unsafe { std::slice::from_raw_parts(content as *const u8, len) }; let content_slice = unsafe { std::slice::from_raw_parts(content as *const u8, len) };
let channel = u16::from_be_bytes([content_slice[0], content_slice[1]]); let channel = u16::from_le_bytes([content_slice[0], content_slice[1]]);
let content = std::string::String::from_utf8(content_slice[2..].to_vec()) let content = std::string::String::from_utf8(content_slice[2..].to_vec())
.unwrap_or("".to_string()); .unwrap_or("".to_string());
push_event_to_ui(channel, &peer, &content); push_event_to_ui(channel, &peer, &content);
@ -165,7 +165,7 @@ fn push_event_to_ui(channel: u16, peer: &str, content: &str) {
let _res = flutter::push_global_event(v as _, event.to_string()); let _res = flutter::push_global_event(v as _, event.to_string());
} }
} }
if is_peer_channel(channel) { if !peer.is_empty() && is_peer_channel(channel) {
let _res = flutter::push_session_event( let _res = flutter::push_session_event(
&peer, &peer,
MSG_TO_UI_TYPE_PLUGIN_EVENT, MSG_TO_UI_TYPE_PLUGIN_EVENT,
@ -176,21 +176,26 @@ fn push_event_to_ui(channel: u16, peer: &str, content: &str) {
fn push_option_to_ui(channel: u16, peer: &str, msg: &MsgToConfig, ui: &ConfigToUi) { fn push_option_to_ui(channel: u16, peer: &str, msg: &MsgToConfig, ui: &ConfigToUi) {
let v = [ let v = [
("name", MSG_TO_UI_TYPE_PLUGIN_OPTION), ("id", &msg.id as &str),
("id", &msg.id),
("location", &ui.location), ("location", &ui.location),
("key", &msg.key), ("key", &msg.key),
("value", &msg.value), ("value", &msg.value),
]; ];
let event = serde_json::to_string(&HashMap::from(v)).unwrap_or("".to_string());
// Send main and cm
let mut m = HashMap::from(v);
m.insert("name", MSG_TO_UI_TYPE_PLUGIN_OPTION);
let event = serde_json::to_string(&m).unwrap_or("".to_string());
for (k, v) in MSG_TO_UI_FLUTTER_CHANNELS.iter() { for (k, v) in MSG_TO_UI_FLUTTER_CHANNELS.iter() {
if channel & k != 0 { if channel & k != 0 {
let _res = flutter::push_global_event(v as _, event.to_string()); let _res = flutter::push_global_event(v as _, event.to_string());
} }
} }
let mut v = v.to_vec();
v.push(("peer", &peer)); // Send remote, transfer and forward
if is_peer_channel(channel) { if !peer.is_empty() && is_peer_channel(channel) {
let _res = flutter::push_session_event(&peer, MSG_TO_UI_TYPE_PLUGIN_OPTION, v.to_vec()); let mut v = v.to_vec();
v.push(("peer", &peer));
let _res = flutter::push_session_event(&peer, MSG_TO_UI_TYPE_PLUGIN_OPTION, v);
} }
} }

View File

@ -6,7 +6,7 @@ use hbb_common::{
allow_err, bail, allow_err, bail,
dlopen::symbor::Library, dlopen::symbor::Library,
lazy_static, log, lazy_static, log,
message_proto::{Message, Misc, PluginResponse}, message_proto::{Message, Misc, PluginFailure, PluginRequest},
ResultType, ResultType,
}; };
use std::{ use std::{
@ -89,20 +89,43 @@ 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);
/// The main function of the plugin. /// 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"
/// peer: The peer id. /// peer: The peer id.
/// args: The arguments. /// args: The arguments.
/// len: The length of the arguments.
/// ///
/// Return null ptr if success. /// Return null ptr if success.
/// Return the error message if failed. `i32-String` without dash, i32 is a signed little-endian number, the String is utf8 string. /// Return the error message if failed. `i32-String` without dash, i32 is a signed little-endian number, the String is utf8 string.
/// The plugin allocate memory with `libc::malloc` and return the pointer. /// The plugin allocate memory with `libc::malloc` and return the pointer.
type PluginFuncCall = extern "C" fn( type PluginFuncClientCall = extern "C" fn(
method: *const c_char, method: *const c_char,
peer: *const c_char, peer: *const c_char,
args: *const c_void, args: *const c_void,
len: usize, len: usize,
) -> *const c_void; ) -> *const c_void;
/// The main function of the plugin on the server(remote) side.
///
/// method: The method. "handle_ui" or "handle_peer"
/// peer: The peer id.
/// args: The arguments.
/// len: The length of the arguments.
/// out: The output.
/// The plugin allocate memory with `libc::malloc` and return the pointer.
/// out_len: The length of the output.
///
/// Return null ptr if success.
/// Return the error message if failed. `i32-String` without dash, i32 is a signed little-endian number, the String is utf8 string.
/// The plugin allocate memory with `libc::malloc` and return the pointer.
type PluginFuncServerCall = extern "C" fn(
method: *const c_char,
peer: *const c_char,
args: *const c_void,
len: usize,
out: *mut *mut c_void,
out_len: *mut usize,
) -> *const c_void;
/// The plugin callbacks. /// The plugin callbacks.
/// msg: The callback to send message to peer or ui. /// msg: The callback to send message to peer or ui.
@ -222,7 +245,8 @@ make_plugin!(
reset: PluginFuncReset, reset: PluginFuncReset,
clear: PluginFuncClear, clear: PluginFuncClear,
desc: PluginFuncDesc, desc: PluginFuncDesc,
call: PluginFuncCall client_call: PluginFuncClientCall,
server_call: PluginFuncServerCall
); );
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
@ -357,7 +381,7 @@ fn handle_event(method: &[u8], id: &str, peer: &str, event: &[u8]) -> ResultType
peer.push('\0'); peer.push('\0');
match PLUGINS.read().unwrap().get(id) { match PLUGINS.read().unwrap().get(id) {
Some(plugin) => { Some(plugin) => {
let ret = (plugin.call)( let ret = (plugin.client_call)(
method.as_ptr() as _, method.as_ptr() as _,
peer.as_ptr() as _, peer.as_ptr() as _,
event.as_ptr() as _, event.as_ptr() as _,
@ -392,19 +416,25 @@ pub fn handle_server_event(id: &str, peer: &str, event: &[u8]) -> ResultType<()>
} }
#[inline] #[inline]
pub fn handle_client_event(id: &str, peer: &str, event: &[u8]) -> Option<Message> { pub fn handle_client_event(id: &str, peer: &str, event: &[u8]) -> Message {
let mut peer: String = peer.to_owned(); let mut peer: String = peer.to_owned();
peer.push('\0'); peer.push('\0');
match PLUGINS.read().unwrap().get(id) { match PLUGINS.read().unwrap().get(id) {
Some(plugin) => { Some(plugin) => {
let ret = (plugin.call)( let mut out = std::ptr::null_mut();
let mut out_len: usize = 0;
let ret = (plugin.server_call)(
METHOD_HANDLE_PEER.as_ptr() as _, METHOD_HANDLE_PEER.as_ptr() as _,
peer.as_ptr() as _, peer.as_ptr() as _,
event.as_ptr() as _, event.as_ptr() as _,
event.len(), event.len(),
&mut out as _,
&mut out_len as _,
); );
if ret.is_null() { if ret.is_null() {
None let msg = make_plugin_request(id, out, out_len);
free_c_ptr(out as _);
msg
} else { } else {
let (code, msg) = get_code_msg_from_ret(ret); let (code, msg) = get_code_msg_from_ret(ret);
free_c_ptr(ret as _); free_c_ptr(ret as _);
@ -421,17 +451,13 @@ pub fn handle_client_event(id: &str, peer: &str, event: &[u8]) -> Option<Message
} }
.to_owned(); .to_owned();
match code { match code {
ERR_CALL_NOT_SUPPORTED_METHOD => Some(make_plugin_response( ERR_CALL_NOT_SUPPORTED_METHOD => {
id, make_plugin_failure(id, &name, "Plugin method is not supported")
&name, }
"plugin method is not supported", ERR_CALL_INVALID_ARGS => {
)), make_plugin_failure(id, &name, "Plugin arguments is invalid")
ERR_CALL_INVALID_ARGS => Some(make_plugin_response( }
id, _ => make_plugin_failure(id, &name, &msg),
&name,
"plugin arguments is invalid",
)),
_ => Some(make_plugin_response(id, &name, &msg)),
} }
} else { } else {
log::error!( log::error!(
@ -440,17 +466,33 @@ pub fn handle_client_event(id: &str, peer: &str, event: &[u8]) -> Option<Message
code, code,
msg msg
); );
None let msg = make_plugin_request(id, out, out_len);
free_c_ptr(out as _);
msg
} }
} }
} }
None => Some(make_plugin_response(id, "", "plugin not found")), None => make_plugin_failure(id, "", "Plugin not found"),
} }
} }
fn make_plugin_response(id: &str, name: &str, msg: &str) -> Message { fn make_plugin_request(id: &str, content: *const c_void, len: usize) -> Message {
let mut misc = Misc::new(); let mut misc = Misc::new();
misc.set_plugin_response(PluginResponse { misc.set_plugin_request(PluginRequest {
id: id.to_owned(),
content: unsafe { std::slice::from_raw_parts(content as *const u8, len) }
.clone()
.into(),
..Default::default()
});
let mut msg_out = Message::new();
msg_out.set_misc(misc);
msg_out
}
fn make_plugin_failure(id: &str, name: &str, msg: &str) -> Message {
let mut misc = Misc::new();
misc.set_plugin_failure(PluginFailure {
id: id.to_owned(), id: id.to_owned(),
name: name.to_owned(), name: name.to_owned(),
msg: msg.to_owned(), msg: msg.to_owned(),

View File

@ -1813,11 +1813,9 @@ impl Connection {
#[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")))]
Some(misc::Union::PluginRequest(p)) => { Some(misc::Union::PluginRequest(p)) => {
if let Some(msg) = let msg =
crate::plugin::handle_client_event(&p.id, &self.lr.my_id, &p.content) crate::plugin::handle_client_event(&p.id, &self.lr.my_id, &p.content);
{ self.send(msg).await;
self.send(msg).await;
}
} }
_ => {} _ => {}
}, },