diff --git a/Cargo.lock b/Cargo.lock index 299d99c14..45f809680 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5133,7 +5133,6 @@ dependencies = [ "include_dir", "jni 0.19.0", "lazy_static", - "libloading", "libpulse-binding", "libpulse-simple-binding", "mac_address", diff --git a/Cargo.toml b/Cargo.toml index cd76b7d01..d70c202c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ hwcodec = ["scrap/hwcodec"] mediacodec = ["scrap/mediacodec"] linux_headless = ["pam", "users"] virtual_display_driver = ["virtual_display"] +plugin_framework = [] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -69,7 +70,6 @@ hex = "0.4" reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false } chrono = "0.4" cidr-utils = "0.5" -libloading = "0.7" [target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies] cpal = "0.15" diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 42d2d7c09..b4eb9bf4b 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -600,6 +600,11 @@ message SwitchSidesResponse { message SwitchBack {} +message Plugin { + string id = 1; + bytes content = 2; +} + message Misc { oneof union { ChatMessage chat_message = 4; @@ -621,6 +626,7 @@ message Misc { SwitchSidesRequest switch_sides_request = 21; SwitchBack switch_back = 22; Resolution change_resolution = 24; + Plugin plugin = 25; } } diff --git a/src/flutter.rs b/src/flutter.rs index 34fe469fc..d2cba9dd7 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -29,20 +29,20 @@ use std::{ /// tag "main" for [Desktop Main Page] and [Mobile (Client and Server)] (the mobile don't need multiple windows, only one global event stream is needed) /// tag "cm" only for [Desktop CM Page] -pub(super) const APP_TYPE_MAIN: &str = "main"; +pub(crate) const APP_TYPE_MAIN: &str = "main"; #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub(super) const APP_TYPE_CM: &str = "cm"; +pub(crate) const APP_TYPE_CM: &str = "cm"; #[cfg(any(target_os = "android", target_os = "ios"))] -pub(super) const APP_TYPE_CM: &str = "main"; +pub(crate) const APP_TYPE_CM: &str = "main"; -pub(super) const APP_TYPE_DESKTOP_REMOTE: &str = "remote"; -pub(super) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer"; -pub(super) const APP_TYPE_DESKTOP_PORT_FORWARD: &str = "port forward"; +pub(crate) const APP_TYPE_DESKTOP_REMOTE: &str = "remote"; +pub(crate) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer"; +pub(crate) const APP_TYPE_DESKTOP_PORT_FORWARD: &str = "port forward"; lazy_static::lazy_static! { pub(crate) static ref CUR_SESSION_ID: RwLock = Default::default(); pub(crate) static ref SESSIONS: RwLock>> = Default::default(); - pub(crate) static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel + static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel } #[cfg(all(target_os = "windows", feature = "flutter_texture_render"))] @@ -249,14 +249,18 @@ impl FlutterHandler { /// /// * `name` - The name of the event. /// * `event` - Fields of the event content. - fn push_event(&self, name: &str, event: Vec<(&str, &str)>) { + pub fn push_event(&self, name: &str, event: Vec<(&str, &str)>) -> Option { let mut h: HashMap<&str, &str> = event.iter().cloned().collect(); assert!(h.get("name").is_none()); h.insert("name", name); let out = serde_json::ser::to_string(&h).unwrap_or("".to_owned()); - if let Some(stream) = &*self.event_stream.read().unwrap() { - stream.add(EventToUI::Event(out)); - } + Some( + self.event_stream + .read() + .unwrap() + .as_ref()? + .add(EventToUI::Event(out)), + ) } pub(crate) fn close_event_stream(&mut self) { @@ -627,7 +631,7 @@ impl InvokeUiSession for FlutterHandler { } fn on_voice_call_closed(&self, reason: &str) { - self.push_event("on_voice_call_closed", [("reason", reason)].into()) + let _res = self.push_event("on_voice_call_closed", [("reason", reason)].into()); } fn on_voice_call_waiting(&self) { @@ -1007,3 +1011,29 @@ pub fn session_register_texture(id: *const char, ptr: usize) { #[no_mangle] #[cfg(not(feature = "flutter_texture_render"))] pub fn session_register_texture(_id: *const char, _ptr: usize) {} + +pub fn push_session_event(peer: &str, name: &str, event: Vec<(&str, &str)>) -> Option { + SESSIONS.read().unwrap().get(peer)?.push_event(name, event) +} + +pub fn push_global_event(channel: &str, event: String) -> Option { + Some(GLOBAL_EVENT_STREAM.read().unwrap().get(channel)?.add(event)) +} + +pub fn start_global_event_stream(s: StreamSink, app_type: String) -> ResultType<()> { + if let Some(_) = GLOBAL_EVENT_STREAM + .write() + .unwrap() + .insert(app_type.clone(), s) + { + log::warn!( + "Global event stream of type {} is started before, but now removed", + app_type + ); + } + Ok(()) +} + +pub fn stop_global_event_stream(app_type: String) { + let _ = GLOBAL_EVENT_STREAM.write().unwrap().remove(&app_type); +} diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 3fdc5e48f..fa1cf6cc8 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -53,27 +53,6 @@ pub enum EventToUI { Rgba, } -pub fn start_global_event_stream(s: StreamSink, app_type: String) -> ResultType<()> { - if let Some(_) = flutter::GLOBAL_EVENT_STREAM - .write() - .unwrap() - .insert(app_type.clone(), s) - { - log::warn!( - "Global event stream of type {} is started before, but now removed", - app_type - ); - } - Ok(()) -} - -pub fn stop_global_event_stream(app_type: String) { - let _ = flutter::GLOBAL_EVENT_STREAM - .write() - .unwrap() - .remove(&app_type); -} - pub fn host_stop_system_key_propagate(_stopped: bool) { #[cfg(windows)] crate::platform::windows::stop_system_key_propagate(_stopped); @@ -338,7 +317,13 @@ pub fn session_handle_flutter_key_event( down_or_up: bool, ) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.handle_flutter_key_event(&name, platform_code, position_code, lock_modes, down_or_up); + session.handle_flutter_key_event( + &name, + platform_code, + position_code, + lock_modes, + down_or_up, + ); } } @@ -739,20 +724,18 @@ pub fn main_load_recent_peers() { .drain(..) .map(|(id, _, p)| peer_to_map(id, p)) .collect(); - if let Some(s) = flutter::GLOBAL_EVENT_STREAM - .read() - .unwrap() - .get(flutter::APP_TYPE_MAIN) - { - let data = HashMap::from([ - ("name", "load_recent_peers".to_owned()), - ( - "peers", - serde_json::ser::to_string(&peers).unwrap_or("".to_owned()), - ), - ]); - s.add(serde_json::ser::to_string(&data).unwrap_or("".to_owned())); - }; + + let data = HashMap::from([ + ("name", "load_recent_peers".to_owned()), + ( + "peers", + serde_json::ser::to_string(&peers).unwrap_or("".to_owned()), + ), + ]); + let _res = flutter::push_global_event( + flutter::APP_TYPE_MAIN, + serde_json::ser::to_string(&data).unwrap_or("".to_owned()), + ); } } @@ -790,38 +773,33 @@ pub fn main_load_fav_peers() { } }) .collect(); - if let Some(s) = flutter::GLOBAL_EVENT_STREAM - .read() - .unwrap() - .get(flutter::APP_TYPE_MAIN) - { - let data = HashMap::from([ - ("name", "load_fav_peers".to_owned()), - ( - "peers", - serde_json::ser::to_string(&peers).unwrap_or("".to_owned()), - ), - ]); - s.add(serde_json::ser::to_string(&data).unwrap_or("".to_owned())); - }; + + let data = HashMap::from([ + ("name", "load_fav_peers".to_owned()), + ( + "peers", + serde_json::ser::to_string(&peers).unwrap_or("".to_owned()), + ), + ]); + let _res = flutter::push_global_event( + flutter::APP_TYPE_MAIN, + serde_json::ser::to_string(&data).unwrap_or("".to_owned()), + ); } } pub fn main_load_lan_peers() { - if let Some(s) = flutter::GLOBAL_EVENT_STREAM - .read() - .unwrap() - .get(flutter::APP_TYPE_MAIN) - { - let data = HashMap::from([ - ("name", "load_lan_peers".to_owned()), - ( - "peers", - serde_json::to_string(&get_lan_peers()).unwrap_or_default(), - ), - ]); - s.add(serde_json::ser::to_string(&data).unwrap_or("".to_owned())); - }; + let data = HashMap::from([ + ("name", "load_lan_peers".to_owned()), + ( + "peers", + serde_json::to_string(&get_lan_peers()).unwrap_or_default(), + ), + ]); + let _res = flutter::push_global_event( + flutter::APP_TYPE_MAIN, + serde_json::ser::to_string(&data).unwrap_or("".to_owned()), + ); } pub fn main_remove_discovered(id: String) { @@ -835,10 +813,9 @@ fn main_broadcast_message(data: &HashMap<&str, &str>) { flutter::APP_TYPE_DESKTOP_PORT_FORWARD, ]; + let event = serde_json::ser::to_string(&data).unwrap_or("".to_owned()); for app in apps { - if let Some(s) = flutter::GLOBAL_EVENT_STREAM.read().unwrap().get(app) { - s.add(serde_json::ser::to_string(data).unwrap_or("".to_owned())); - }; + let _res = flutter::push_global_event(app, event.clone()); } } @@ -1222,18 +1199,15 @@ unsafe extern "C" fn translate(name: *const c_char, locale: *const c_char) -> *c } fn handle_query_onlines(onlines: Vec, offlines: Vec) { - if let Some(s) = flutter::GLOBAL_EVENT_STREAM - .read() - .unwrap() - .get(flutter::APP_TYPE_MAIN) - { - let data = HashMap::from([ - ("name", "callback_query_onlines".to_owned()), - ("onlines", onlines.join(",")), - ("offlines", offlines.join(",")), - ]); - s.add(serde_json::ser::to_string(&data).unwrap_or("".to_owned())); - }; + let data = HashMap::from([ + ("name", "callback_query_onlines".to_owned()), + ("onlines", onlines.join(",")), + ("offlines", offlines.join(",")), + ]); + let _res = flutter::push_global_event( + flutter::APP_TYPE_MAIN, + serde_json::ser::to_string(&data).unwrap_or("".to_owned()), + ); } pub fn query_onlines(ids: Vec) { diff --git a/src/lib.rs b/src/lib.rs index 3f48be87d..ad9229ab5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,7 @@ mod license; #[cfg(not(any(target_os = "android", target_os = "ios")))] mod port_forward; +#[cfg(all(feature = "flutter", feature = "plugin_framework"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] #[cfg(any(feature = "flutter"))] pub mod api; diff --git a/src/plugin/callback_msg.rs b/src/plugin/callback_msg.rs new file mode 100644 index 000000000..62962cc83 --- /dev/null +++ b/src/plugin/callback_msg.rs @@ -0,0 +1,112 @@ +use super::cstr_to_string; +use crate::flutter::{self, APP_TYPE_CM, APP_TYPE_MAIN, SESSIONS}; +use hbb_common::{lazy_static, libc, log, message_proto::Plugin, ResultType}; +use serde_derive::{Deserialize, Serialize}; +use serde_json; +use std::{ + collections::HashMap, + ffi::{c_char, CStr}, + sync::Arc, +}; + +const MSG_TO_PEER_TARGET: &str = "peer"; +const MSG_TO_UI_TARGET: &str = "ui"; + +#[allow(dead_code)] +const MSG_TO_UI_FLUTTER_CHANNEL_MAIN: u16 = 0x01 << 0; +#[allow(dead_code)] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +const MSG_TO_UI_FLUTTER_CHANNEL_CM: u16 = 0x01 << 1; +#[cfg(any(target_os = "android", target_os = "ios"))] +const MSG_TO_UI_FLUTTER_CHANNEL_CM: u16 = 0x01; +const MSG_TO_UI_FLUTTER_CHANNEL_REMOTE: u16 = 0x01 << 2; +#[allow(dead_code)] +const MSG_TO_UI_FLUTTER_CHANNEL_TRANSFER: u16 = 0x01 << 3; +#[allow(dead_code)] +const MSG_TO_UI_FLUTTER_CHANNEL_FORWARD: u16 = 0x01 << 4; + +lazy_static::lazy_static! { + static ref MSG_TO_UI_FLUTTER_CHANNELS: Arc> = { + let channels = HashMap::from([ + (MSG_TO_UI_FLUTTER_CHANNEL_MAIN, APP_TYPE_MAIN.to_string()), + (MSG_TO_UI_FLUTTER_CHANNEL_CM, APP_TYPE_CM.to_string()), + ]); + Arc::new(channels) + }; +} + +/// Callback to send message to peer or ui. +/// peer, target, id are utf8 strings(null terminated). +/// +/// peer: The peer id. +/// target: "peer" or "ui". +/// id: The id of this plugin. +/// content: The content. +/// len: The length of the content. +pub fn callback_msg( + peer: *const c_char, + target: *const c_char, + id: *const c_char, + content: *const c_char, + len: usize, +) { + macro_rules! callback_msg_field { + ($field: ident) => { + let $field = match cstr_to_string($field) { + Err(e) => { + log::error!("Failed to convert {} to string, {}", stringify!($field), e); + return; + } + Ok(v) => v, + }; + }; + } + callback_msg_field!(peer); + callback_msg_field!(target); + callback_msg_field!(id); + + match &target as _ { + MSG_TO_PEER_TARGET => { + if let Some(session) = SESSIONS.write().unwrap().get_mut(&peer) { + let content_slice = + unsafe { std::slice::from_raw_parts(content as *const u8, len) }; + let content_vec = Vec::from(content_slice); + let plugin = Plugin { + id, + content: bytes::Bytes::from(content_vec), + ..Default::default() + }; + session.send_plugin(plugin); + } + } + MSG_TO_UI_TARGET => { + 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 content = std::string::String::from_utf8(content_slice[2..].to_vec()) + .unwrap_or("".to_string()); + let mut m = HashMap::new(); + m.insert("name", "plugin"); + m.insert("peer", &peer); + m.insert("content", &content); + let event = serde_json::to_string(&m).unwrap_or("".to_string()); + for (k, v) in MSG_TO_UI_FLUTTER_CHANNELS.iter() { + if channel & k != 0 { + let _res = flutter::push_global_event(v as _, event.clone()); + } + } + if channel & MSG_TO_UI_FLUTTER_CHANNEL_REMOTE != 0 + || channel & MSG_TO_UI_FLUTTER_CHANNEL_TRANSFER != 0 + || channel & MSG_TO_UI_FLUTTER_CHANNEL_FORWARD != 0 + { + let _res = flutter::push_session_event( + APP_TYPE_CM, + "plugin", + vec![("peer", &peer), ("content", &content)], + ); + } + } + _ => { + log::error!("Unknown target {}", target); + } + } +} diff --git a/src/plugin/config.rs b/src/plugin/config.rs new file mode 100644 index 000000000..e69de29bb diff --git a/src/plugin/desc.rs b/src/plugin/desc.rs new file mode 100644 index 000000000..8d345b7d2 --- /dev/null +++ b/src/plugin/desc.rs @@ -0,0 +1,126 @@ +use hbb_common::ResultType; +use serde_derive::Deserialize; +use serde_json; +use std::collections::HashMap; +use std::ffi::{c_char, CStr}; + +#[derive(Debug, Deserialize)] +pub struct UiButton { + key: String, + text: String, + icon: String, + tooltip: String, + action: String, // The action to be triggered when the button is clicked. +} + +#[derive(Debug, Deserialize)] +pub struct UiCheckbox { + key: String, + text: String, + tooltip: String, + action: String, // The action to be triggered when the checkbox is checked or unchecked. +} + +#[derive(Debug, Deserialize)] +#[serde(tag = "t", content = "c")] +pub enum UiType { + Button(UiButton), + Checkbox(UiCheckbox), +} + +#[derive(Debug, Deserialize)] +pub struct Location { + core: String, + ui: HashMap, +} + +#[derive(Debug, Deserialize)] +pub struct ConfigItem { + key: String, + value: String, + default: String, + description: String, +} + +#[derive(Debug, Deserialize)] +pub struct Configs { + pub local: Vec, + pub session: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct Config { + pub host: Configs, + pub client: Configs, +} + +#[derive(Debug, Deserialize)] +pub struct Desc { + id: String, + name: String, + version: String, + description: String, + author: String, + home: String, + license: String, + published: String, + released: String, + github: String, + location: Location, + config: Config, +} + +impl Desc { + pub fn from_cstr(s: *const c_char) -> ResultType { + let s = unsafe { CStr::from_ptr(s) }; + Ok(serde_json::from_str(s.to_str()?)?) + } + + pub fn id(&self) -> &str { + &self.id + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn version(&self) -> &str { + &self.version + } + + pub fn description(&self) -> &str { + &self.description + } + + pub fn author(&self) -> &str { + &self.author + } + + pub fn home(&self) -> &str { + &self.home + } + + pub fn license(&self) -> &str { + &self.license + } + + pub fn published(&self) -> &str { + &self.published + } + + pub fn released(&self) -> &str { + &self.released + } + + pub fn github(&self) -> &str { + &self.github + } + + pub fn location(&self) -> &Location { + &self.location + } + + pub fn config(&self) -> &Config { + &self.config + } +} diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs new file mode 100644 index 000000000..ab233a503 --- /dev/null +++ b/src/plugin/mod.rs @@ -0,0 +1,19 @@ +use hbb_common::{dlopen::symbor::Library, log, ResultType}; +use std::{ + ffi::{c_char, CStr}, + path::Path, +}; + +mod callback_msg; +mod config; +pub mod desc; +mod plugins; + +pub use plugins::load_plugins; + +#[inline] +fn cstr_to_string(cstr: *const c_char) -> ResultType { + Ok(String::from_utf8(unsafe { + CStr::from_ptr(cstr).to_bytes().to_vec() + })?) +} diff --git a/src/plugin/plugins.rs b/src/plugin/plugins.rs new file mode 100644 index 000000000..d93ac5938 --- /dev/null +++ b/src/plugin/plugins.rs @@ -0,0 +1,162 @@ +use std::{ + collections::HashMap, + ffi::{c_char, CStr}, + path::PathBuf, + sync::{Arc, RwLock}, +}; + +use super::{callback_msg, desc::Desc}; +use hbb_common::{ + anyhow::Error, + bail, + dlopen::symbor::Library, + lazy_static, libc, log, + log::{debug, error}, + ResultType, +}; + +lazy_static::lazy_static! { + pub static ref PLUGINS: Arc>> = Default::default(); +} + +/// Initialize the plugins. +/// Return 0 if success. +pub type PluginFuncInit = fn() -> i32; +/// Reset the plugin. +/// Return 0 if success. +pub type PluginFuncReset = fn() -> i32; +/// Clear the plugin. +/// Return 0 if success. +pub type PluginFuncClear = fn() -> i32; +/// Get the description of the plugin. +/// Return the description. The plugin allocate memory with `libc::malloc` and return the pointer. +pub type PluginFuncDesc = fn() -> *const c_char; +/// Callback to send message to peer or ui. +/// peer, target, id are utf8 strings(null terminated). +/// +/// peer: The peer id. +/// target: "peer" or "ui". +/// id: The id of this plugin. +/// content: The content. +/// len: The length of the content. +type PluginFuncCallbackMsg = fn( + peer: *const c_char, + target: *const c_char, + id: *const c_char, + content: *const c_char, + len: usize, +); +pub type PluginFuncSetCallbackMsg = fn(PluginFuncCallbackMsg); +/// The main function of the plugin. +/// method: The method. "handle_ui" or "handle_peer" +/// args: The arguments. +/// out: The output. The plugin allocate memory with `libc::malloc` and set the pointer to `out`. +/// out_len: The length of the output. +/// Return 0 if success. +pub type PluginFuncCall = fn( + method: *const c_char, + args: *const c_char, + out: *mut *mut c_char, + out_len: *mut usize, +) -> i32; + +macro_rules! make_plugin { + ($($field:ident : $tp:ty),+) => { + pub struct Plugin { + _lib: Library, + path: String, + desc: Option, + $($field: $tp),+ + } + + impl Plugin { + fn new(path: &str) -> ResultType { + let lib = match Library::open(path) { + Ok(lib) => lib, + Err(e) => { + bail!("Failed to load library {}, {}", path, e); + } + }; + + $(let $field = match unsafe { lib.symbol::<$tp>(stringify!($field)) } { + Ok(m) => { + log::info!("method found {}", stringify!($field)); + *m + }, + Err(e) => { + bail!("Failed to load {} func {}, {}", path, stringify!($field), e); + } + } + ;)+ + + Ok(Self { + _lib: lib, + path: path.to_string(), + desc: None, + $( $field ),+ + }) + } + } + } +} + +make_plugin!( + fn_init: PluginFuncInit, + fn_reset: PluginFuncReset, + fn_clear: PluginFuncClear, + fn_desc: PluginFuncDesc, + fn_set_cb_msg: PluginFuncSetCallbackMsg, + fn_call: PluginFuncCall +); + +pub fn load_plugins(dir: &str) -> ResultType<()> { + for entry in std::fs::read_dir(dir)? { + match entry { + Ok(entry) => { + let path = entry.path(); + if path.is_file() { + let path = path.to_str().unwrap_or(""); + if path.ends_with(".so") { + if let Err(e) = load_plugin(path) { + log::error!("{e}"); + } + } + } + } + Err(e) => { + log::error!("Failed to read dir entry, {}", e); + } + } + } + Ok(()) +} + +pub fn unload_plugin(id: &str) { + if let Some(plugin) = PLUGINS.write().unwrap().remove(id) { + let _ret = (plugin.fn_clear)(); + } +} + +pub fn reload_plugin(id: &str) -> ResultType<()> { + let path = match PLUGINS.read().unwrap().get(id) { + Some(plugin) => plugin.path.clone(), + None => bail!("Plugin {} not found", id), + }; + unload_plugin(id); + load_plugin(&path) +} + +fn load_plugin(path: &str) -> ResultType<()> { + let mut plugin = Plugin::new(path)?; + let desc = (plugin.fn_desc)(); + let desc_res = Desc::from_cstr(desc); + unsafe { + libc::free(desc as _); + } + let desc = desc_res?; + let id = desc.id().to_string(); + (plugin.fn_set_cb_msg)(callback_msg::callback_msg); + plugin.desc = Some(desc); + PLUGINS.write().unwrap().insert(id, plugin); + Ok(()) +} diff --git a/src/server.rs b/src/server.rs index 9c7fb2260..6a0371757 100644 --- a/src/server.rs +++ b/src/server.rs @@ -451,17 +451,13 @@ pub async fn start_ipc_url_server() { Ok(Some(data)) => match data { #[cfg(feature = "flutter")] Data::UrlLink(url) => { - if let Some(stream) = crate::flutter::GLOBAL_EVENT_STREAM - .read() - .unwrap() - .get(crate::flutter::APP_TYPE_MAIN) - { - let mut m = HashMap::new(); - m.insert("name", "on_url_scheme_received"); - m.insert("url", url.as_str()); - stream.add(serde_json::to_string(&m).unwrap()); - } else { - log::warn!("No main window app found!"); + let mut m = HashMap::new(); + m.insert("name", "on_url_scheme_received"); + m.insert("url", url.as_str()); + let event = serde_json::to_string(&m).unwrap(); + match crate::flutter::push_global_event(crate::flutter::APP_TYPE_MAIN, event) { + None => log::warn!("No main window app found!"), + Some(..) => {} } } _ => { diff --git a/src/server/dbus.rs b/src/server/dbus.rs index 081db3e8f..79aa62a1e 100644 --- a/src/server/dbus.rs +++ b/src/server/dbus.rs @@ -72,21 +72,17 @@ fn handle_client_message(builder: &mut IfaceBuilder<()>) { #[cfg(feature = "flutter")] { use crate::flutter::{self, APP_TYPE_MAIN}; - - if let Some(stream) = flutter::GLOBAL_EVENT_STREAM - .write() - .unwrap() - .get(APP_TYPE_MAIN) - { - let data = HashMap::from([ - ("name", "new_connection"), - ("uni_links", _uni_links.as_str()), - ]); - if !stream.add(serde_json::ser::to_string(&data).unwrap_or("".to_string())) { - log::error!("failed to add dbus message to flutter global dbus stream."); + let data = HashMap::from([ + ("name", "new_connection"), + ("uni_links", _uni_links.as_str()), + ]); + let event = serde_json::ser::to_string(&data).unwrap_or("".to_string()); + match crate::flutter::push_global_event(flutter::APP_TYPE_MAIN, event) { + None => log::error!("failed to find main event stream"), + Some(false) => { + log::error!("failed to add dbus message to flutter global dbus stream.") } - } else { - log::error!("failed to find main event stream"); + Some(true) => {} } } return Ok((DBUS_METHOD_RETURN_SUCCESS.to_string(),)); diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index db0aab0ae..2747a6f00 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -240,6 +240,14 @@ impl Session { self.send(Data::Message(msg)); } + pub fn send_plugin(&self, plugin: Plugin) { + let mut misc = Misc::new(); + misc.set_plugin(plugin); + let mut msg_out = Message::new(); + msg_out.set_misc(misc); + self.send(Data::Message(msg_out)); + } + pub fn get_audit_server(&self, typ: String) -> String { if self.lc.read().unwrap().conn_id <= 0 || LocalConfig::get_option("access_token").is_empty()