rustdesk/src/flutter.rs

488 lines
15 KiB
Rust
Raw Normal View History

use std::{
2022-08-17 17:23:55 +08:00
collections::HashMap,
2022-09-01 16:21:41 +08:00
sync::{Arc, RwLock},
};
2022-05-19 23:45:44 +08:00
use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer};
2022-09-05 10:27:33 +08:00
use hbb_common::{
bail, config::LocalConfig, message_proto::*, rendezvous_proto::ConnType, ResultType,
};
use serde_json::json;
use crate::ui_session_interface::{io_loop, InvokeUiSession, Session};
2022-09-01 09:48:53 +08:00
use crate::{client::*, flutter_ffi::EventToUI};
2022-05-12 17:35:25 +08:00
pub(super) const APP_TYPE_MAIN: &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";
2022-05-12 17:35:25 +08:00
lazy_static::lazy_static! {
pub static ref SESSIONS: RwLock<HashMap<String,Session<FlutterHandler>>> = Default::default();
pub static ref GLOBAL_EVENT_STREAM: RwLock<HashMap<String, StreamSink<String>>> = Default::default(); // rust to dart event channel
2022-05-12 17:35:25 +08:00
}
#[derive(Default, Clone)]
pub struct FlutterHandler {
pub event_stream: Arc<RwLock<Option<StreamSink<EventToUI>>>>,
2022-05-12 17:35:25 +08:00
}
impl FlutterHandler {
2022-05-28 03:56:42 +08:00
/// Push an event to the event queue.
/// An event is stored as json in the event queue.
///
/// # Arguments
///
/// * `name` - The name of the event.
/// * `event` - Fields of the event content.
2022-05-12 17:35:25 +08:00
fn push_event(&self, name: &str, event: Vec<(&str, &str)>) {
let mut h: HashMap<&str, &str> = event.iter().cloned().collect();
assert!(h.get("name").is_none());
h.insert("name", name);
2022-05-31 22:09:36 +08:00
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));
2022-08-27 16:03:44 +08:00
}
2022-05-12 17:35:25 +08:00
}
}
2022-05-12 17:35:25 +08:00
impl InvokeUiSession for FlutterHandler {
fn set_cursor_data(&self, cd: CursorData) {
let colors = hbb_common::compress::decompress(&cd.colors);
self.push_event(
"cursor_data",
vec![
("id", &cd.id.to_string()),
("hotx", &cd.hotx.to_string()),
("hoty", &cd.hoty.to_string()),
("width", &cd.width.to_string()),
("height", &cd.height.to_string()),
(
"colors",
&serde_json::ser::to_string(&colors).unwrap_or("".to_owned()),
),
],
);
2022-05-12 17:35:25 +08:00
}
fn set_cursor_id(&self, id: String) {
self.push_event("cursor_id", vec![("id", &id.to_string())]);
2022-05-12 17:35:25 +08:00
}
fn set_cursor_position(&self, cp: CursorPosition) {
self.push_event(
"cursor_position",
vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())],
);
2022-05-12 17:35:25 +08:00
}
/// unused in flutter, use switch_display or set_peer_info
fn set_display(&self, _x: i32, _y: i32, _w: i32, _h: i32) {}
fn update_privacy_mode(&self) {
self.push_event("update_privacy_mode", [].into());
2022-07-11 18:23:58 +08:00
}
fn set_permission(&self, name: &str, value: bool) {
2022-09-01 09:48:53 +08:00
self.push_event("permission", vec![(name, &value.to_string())]);
}
2022-09-05 10:27:33 +08:00
// unused in flutter
fn close_success(&self) {}
2022-08-04 17:24:02 +08:00
fn update_quality_status(&self, status: QualityStatus) {
const NULL: String = String::new();
self.push_event(
"update_quality_status",
vec![
("speed", &status.speed.map_or(NULL, |it| it)),
("fps", &status.fps.map_or(NULL, |it| it.to_string())),
("delay", &status.delay.map_or(NULL, |it| it.to_string())),
(
"target_bitrate",
&status.target_bitrate.map_or(NULL, |it| it.to_string()),
),
(
"codec_format",
&status.codec_format.map_or(NULL, |it| it.to_string()),
),
],
);
}
2022-05-12 17:35:25 +08:00
fn set_connection_type(&self, is_secured: bool, direct: bool) {
2022-05-12 17:35:25 +08:00
self.push_event(
"connection_ready",
2022-05-12 17:35:25 +08:00
vec![
("secure", &is_secured.to_string()),
("direct", &direct.to_string()),
2022-05-12 17:35:25 +08:00
],
);
}
fn job_error(&self, id: i32, err: String, file_num: i32) {
2022-09-05 10:27:33 +08:00
self.push_event(
"job_error",
vec![
("id", &id.to_string()),
("err", &err),
("file_num", &file_num.to_string()),
],
);
2022-05-12 17:35:25 +08:00
}
fn job_done(&self, id: i32, file_num: i32) {
2022-05-12 17:35:25 +08:00
self.push_event(
2022-09-01 09:48:53 +08:00
"job_done",
vec![("id", &id.to_string()), ("file_num", &file_num.to_string())],
2022-05-12 17:35:25 +08:00
);
}
2022-09-05 10:27:33 +08:00
// unused in flutter
fn clear_all_jobs(&self) {}
fn load_last_job(&self, _cnt: i32, job_json: &str) {
self.push_event("load_last_job", vec![("value", job_json)]);
}
2022-09-05 10:27:33 +08:00
fn update_folder_files(
&self,
id: i32,
2022-09-05 10:27:33 +08:00
entries: &Vec<FileEntry>,
path: String,
2022-09-05 10:27:33 +08:00
is_local: bool,
only_count: bool,
) {
2022-09-05 10:27:33 +08:00
// TODO opt
if only_count {
self.push_event(
"update_folder_files",
vec![("info", &make_fd_flutter(id, entries, only_count))],
);
} else {
self.push_event(
"file_dir",
vec![
("value", &make_fd_to_json(id, path, entries)),
("is_local", "false"),
],
);
}
}
2022-09-05 10:27:33 +08:00
// unused in flutter
fn update_transfer_list(&self) {}
2022-05-12 17:35:25 +08:00
2022-09-05 10:27:33 +08:00
// unused in flutter // TEST flutter
fn confirm_delete_files(&self, _id: i32, _i: i32, _name: String) {}
2022-05-12 17:35:25 +08:00
fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool) {
2022-09-01 09:48:53 +08:00
self.push_event(
"override_file_confirm",
vec![
("id", &id.to_string()),
("file_num", &file_num.to_string()),
("read_path", &to),
("is_upload", &is_upload.to_string()),
],
);
2022-05-12 17:35:25 +08:00
}
fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64) {
2022-09-01 09:48:53 +08:00
self.push_event(
"job_progress",
vec![
("id", &id.to_string()),
("file_num", &file_num.to_string()),
("speed", &speed.to_string()),
("finished_size", &finished_size.to_string()),
],
);
}
2022-09-05 10:27:33 +08:00
// unused in flutter
fn adapt_size(&self) {}
fn on_rgba(&self, data: &[u8]) {
if let Some(stream) = &*self.event_stream.read().unwrap() {
stream.add(EventToUI::Rgba(ZeroCopyBuffer(data.to_owned())));
}
2022-05-12 17:35:25 +08:00
}
2022-09-01 16:21:41 +08:00
fn set_peer_info(&self, pi: &PeerInfo) {
let mut displays = Vec::new();
for ref d in pi.displays.iter() {
let mut h: HashMap<&str, i32> = Default::default();
h.insert("x", d.x);
h.insert("y", d.y);
h.insert("width", d.width);
h.insert("height", d.height);
displays.push(h);
}
2022-09-01 16:21:41 +08:00
let displays = serde_json::ser::to_string(&displays).unwrap_or("".to_owned());
self.push_event(
"peer_info",
2022-05-12 17:35:25 +08:00
vec![
2022-09-01 16:21:41 +08:00
("username", &pi.username),
("hostname", &pi.hostname),
("platform", &pi.platform),
("sas_enabled", &pi.sas_enabled.to_string()),
("displays", &displays),
2022-09-01 16:21:41 +08:00
("version", &pi.version),
("current_display", &pi.current_display.to_string()),
2022-05-12 17:35:25 +08:00
],
);
}
fn msgbox(&self, msgtype: &str, title: &str, text: &str, retry: bool) {
let has_retry = if retry { "true" } else { "" };
self.push_event(
"msgbox",
2022-05-19 21:45:25 +08:00
vec![
("type", msgtype),
("title", title),
("text", text),
("hasRetry", has_retry),
2022-05-19 21:45:25 +08:00
],
2022-05-17 20:56:36 +08:00
);
}
2022-09-01 09:48:53 +08:00
fn new_message(&self, msg: String) {
self.push_event("chat_client_mode", vec![("text", &msg)]);
}
2022-09-01 09:48:53 +08:00
fn switch_display(&self, display: &SwitchDisplay) {
self.push_event(
"switch_display",
2022-05-12 17:35:25 +08:00
vec![
2022-09-01 09:48:53 +08:00
("display", &display.to_string()),
("x", &display.x.to_string()),
("y", &display.y.to_string()),
("width", &display.width.to_string()),
("height", &display.height.to_string()),
2022-05-12 17:35:25 +08:00
],
);
}
2022-09-01 09:48:53 +08:00
fn update_block_input_state(&self, on: bool) {
self.push_event(
"update_block_input_state",
[("input_state", if on { "on" } else { "off" })].into(),
);
2022-05-12 17:35:25 +08:00
}
2022-09-01 09:48:53 +08:00
#[cfg(any(target_os = "android", target_os = "ios"))]
fn clipboard(&self, content: String) {
self.push_event("clipboard", vec![("content", &content)]);
2022-05-12 17:35:25 +08:00
}
}
2022-05-17 20:56:36 +08:00
/// Create a new remote session with the given id.
///
/// # Arguments
///
/// * `id` - The identifier of the remote session with prefix. Regex: [\w]*[\_]*[\d]+
/// * `is_file_transfer` - If the session is used for file transfer.
/// * `is_port_forward` - If the session is used for port forward.
pub fn session_add(id: &str, is_file_transfer: bool, is_port_forward: bool) -> ResultType<()> {
let session_id = get_session_id(id.to_owned());
LocalConfig::set_remote_id(&session_id);
let session: Session<FlutterHandler> = Session {
id: session_id.clone(),
..Default::default()
};
// TODO rdp
let conn_type = if is_file_transfer {
ConnType::FILE_TRANSFER
} else if is_port_forward {
ConnType::PORT_FORWARD
} else {
ConnType::DEFAULT_CONN
};
session
.lc
.write()
.unwrap()
.initialize(session_id, conn_type);
2022-09-05 10:27:33 +08:00
if let Some(same_id_session) = SESSIONS.write().unwrap().insert(id.to_owned(), session) {
same_id_session.close();
2022-05-17 20:56:36 +08:00
}
2022-07-11 18:23:58 +08:00
Ok(())
}
/// start a session with the given id.
///
/// # Arguments
///
/// * `id` - The identifier of the remote session with prefix. Regex: [\w]*[\_]*[\d]+
/// * `events2ui` - The events channel to ui.
pub fn session_start_(id: &str, event_stream: StreamSink<EventToUI>) -> ResultType<()> {
if let Some(session) = SESSIONS.write().unwrap().get_mut(id) {
*session.event_stream.write().unwrap() = Some(event_stream);
let session = session.clone();
std::thread::spawn(move || {
2022-09-13 16:50:22 +08:00
crate::client::disable_keyboard_listening();
io_loop(session);
});
Ok(())
} else {
bail!("No session with peer id {}", id)
2022-07-11 18:23:58 +08:00
}
2022-05-12 17:35:25 +08:00
}
// Server Side
#[cfg(not(any(target_os = "ios")))]
2022-05-12 17:35:25 +08:00
pub mod connection_manager {
use std::collections::HashMap;
2022-05-12 17:35:25 +08:00
2022-09-05 20:05:23 +08:00
use hbb_common::log;
#[cfg(any(target_os = "android"))]
2022-05-12 17:35:25 +08:00
use scrap::android::call_main_service_set_by_name;
use crate::ui_cm_interface::InvokeUiCM;
2022-05-12 17:35:25 +08:00
2022-05-31 22:09:36 +08:00
use super::GLOBAL_EVENT_STREAM;
#[derive(Clone)]
struct FlutterHandler {}
2022-08-17 17:23:55 +08:00
impl InvokeUiCM for FlutterHandler {
//TODO port_forward
fn add_connection(&self, client: &crate::ui_cm_interface::Client) {
2022-08-17 17:23:55 +08:00
let client_json = serde_json::to_string(&client).unwrap_or("".into());
// send to Android service, active notification no matter UI is shown or not.
#[cfg(any(target_os = "android"))]
if let Err(e) =
call_main_service_set_by_name("add_connection", Some(&client_json), None)
2022-08-17 17:23:55 +08:00
{
log::debug!("call_service_set_by_name fail,{}", e);
}
// send to UI, refresh widget
2022-09-13 22:37:16 +08:00
self.push_event("add_connection", vec![("client", &client_json)]);
2022-08-17 17:23:55 +08:00
}
fn remove_connection(&self, id: i32) {
self.push_event("on_client_remove", vec![("id", &id.to_string())]);
2022-05-12 17:35:25 +08:00
}
fn new_message(&self, id: i32, text: String) {
self.push_event(
"chat_server_mode",
vec![("id", &id.to_string()), ("text", &text)],
);
2022-05-12 17:35:25 +08:00
}
fn change_theme(&self, dark: String) {
self.push_event("theme", vec![("dark", &dark)]);
}
fn change_language(&self) {
self.push_event("language", vec![]);
}
2022-05-12 17:35:25 +08:00
}
impl FlutterHandler {
fn push_event(&self, name: &str, event: Vec<(&str, &str)>) {
let mut h: HashMap<&str, &str> = event.iter().cloned().collect();
assert!(h.get("name").is_none());
h.insert("name", name);
2022-05-12 17:35:25 +08:00
if let Some(s) = GLOBAL_EVENT_STREAM
.read()
.unwrap()
.get(super::APP_TYPE_MAIN)
{
s.add(serde_json::ser::to_string(&h).unwrap_or("".to_owned()));
};
2022-05-12 17:35:25 +08:00
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn start_listen_ipc_thread() {
2022-09-05 20:32:21 +08:00
use crate::ui_cm_interface::{start_ipc, ConnectionManager};
2022-05-12 17:35:25 +08:00
#[cfg(target_os = "linux")]
2022-09-05 20:32:21 +08:00
std::thread::spawn(crate::ipc::start_pa);
2022-05-12 17:35:25 +08:00
let cm = ConnectionManager {
ui_handler: FlutterHandler {},
};
std::thread::spawn(move || start_ipc(cm));
2022-05-12 17:35:25 +08:00
}
#[cfg(target_os = "android")]
2022-09-05 20:32:21 +08:00
use hbb_common::tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
2022-05-12 17:35:25 +08:00
2022-09-05 20:05:23 +08:00
#[cfg(target_os = "android")]
pub fn start_channel(
rx: UnboundedReceiver<crate::ipc::Data>,
tx: UnboundedSender<crate::ipc::Data>,
) {
use crate::ui_cm_interface::start_listen;
2022-09-05 20:05:23 +08:00
let cm = crate::ui_cm_interface::ConnectionManager {
ui_handler: FlutterHandler {},
};
std::thread::spawn(move || start_listen(cm, rx, tx));
2022-05-12 17:35:25 +08:00
}
}
#[inline]
pub fn get_session_id(id: String) -> String {
return if let Some(index) = id.find('_') {
id[index + 1..].to_string()
} else {
id
};
}
2022-08-29 13:08:42 +08:00
2022-09-05 10:27:33 +08:00
pub fn make_fd_to_json(id: i32, path: String, entries: &Vec<FileEntry>) -> String {
let mut fd_json = serde_json::Map::new();
fd_json.insert("id".into(), json!(id));
fd_json.insert("path".into(), json!(path));
let mut entries_out = vec![];
for entry in entries {
let mut entry_map = serde_json::Map::new();
entry_map.insert("entry_type".into(), json!(entry.entry_type.value()));
entry_map.insert("name".into(), json!(entry.name));
entry_map.insert("size".into(), json!(entry.size));
entry_map.insert("modified_time".into(), json!(entry.modified_time));
entries_out.push(entry_map);
}
fd_json.insert("entries".into(), json!(entries_out));
serde_json::to_string(&fd_json).unwrap_or("".into())
}
pub fn make_fd_flutter(id: i32, entries: &Vec<FileEntry>, only_count: bool) -> String {
let mut m = serde_json::Map::new();
m.insert("id".into(), json!(id));
let mut a = vec![];
let mut n: u64 = 0;
for entry in entries {
n += entry.size;
if only_count {
continue;
}
let mut e = serde_json::Map::new();
e.insert("name".into(), json!(entry.name.to_owned()));
let tmp = entry.entry_type.value();
e.insert("type".into(), json!(if tmp == 0 { 1 } else { tmp }));
e.insert("time".into(), json!(entry.modified_time as f64));
e.insert("size".into(), json!(entry.size as f64));
a.push(e);
}
if only_count {
m.insert("num_entries".into(), json!(entries.len() as i32));
} else {
m.insert("entries".into(), json!(a));
}
m.insert("total_size".into(), json!(n as f64));
serde_json::to_string(&m).unwrap_or("".into())
}