2023-01-17 13:28:33 +08:00
|
|
|
use crate::ui_session_interface::{io_loop, InvokeUiSession, Session};
|
|
|
|
use crate::{client::*, flutter_ffi::EventToUI};
|
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::{
|
2022-12-02 21:34:20 +08:00
|
|
|
bail, config::LocalConfig, get_version_number, message_proto::*, rendezvous_proto::ConnType,
|
|
|
|
ResultType,
|
2022-09-05 10:27:33 +08:00
|
|
|
};
|
|
|
|
use serde_json::json;
|
2023-01-17 13:28:33 +08:00
|
|
|
use std::{
|
|
|
|
collections::HashMap,
|
|
|
|
ffi::CString,
|
|
|
|
os::raw::{c_char, c_int},
|
|
|
|
sync::{Arc, RwLock},
|
|
|
|
};
|
2022-05-12 17:35:25 +08:00
|
|
|
|
2022-08-03 22:03:31 +08:00
|
|
|
pub(super) const APP_TYPE_MAIN: &str = "main";
|
2023-02-07 15:39:46 +08:00
|
|
|
pub(super) const APP_TYPE_CM: &str = "cm";
|
2022-08-03 22:03:31 +08:00
|
|
|
pub(super) const APP_TYPE_DESKTOP_REMOTE: &str = "remote";
|
|
|
|
pub(super) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer";
|
2022-09-07 18:57:49 +08:00
|
|
|
pub(super) const APP_TYPE_DESKTOP_PORT_FORWARD: &str = "port forward";
|
2022-08-03 22:03:31 +08:00
|
|
|
|
2022-05-12 17:35:25 +08:00
|
|
|
lazy_static::lazy_static! {
|
2023-01-20 21:03:30 +08:00
|
|
|
pub static ref CUR_SESSION_ID: RwLock<String> = Default::default();
|
2022-10-25 22:07:29 +08:00
|
|
|
pub static ref SESSIONS: RwLock<HashMap<String, Session<FlutterHandler>>> = Default::default();
|
2022-08-03 22:03:31 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2022-10-09 21:10:41 +08:00
|
|
|
/// FFI for rustdesk core's main entry.
|
|
|
|
/// Return true if the app should continue running with UI(possibly Flutter), false if the app should exit.
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "C" fn rustdesk_core_main() -> bool {
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
|
|
return crate::core_main::core_main().is_some();
|
|
|
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2023-01-20 01:25:15 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "C" fn handle_applicationShouldOpenUntitledFile() {
|
|
|
|
hbb_common::log::debug!("icon clicked on finder");
|
2023-02-07 00:18:25 +08:00
|
|
|
let x = std::env::args().nth(1).unwrap_or_default();
|
|
|
|
if x == "--server" || x == "--cm" {
|
2023-01-20 01:25:15 +08:00
|
|
|
crate::platform::macos::check_main_window();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-09 21:10:41 +08:00
|
|
|
#[cfg(windows)]
|
|
|
|
#[no_mangle]
|
2022-11-29 22:00:27 +08:00
|
|
|
pub extern "C" fn rustdesk_core_main_args(args_len: *mut c_int) -> *mut *mut c_char {
|
2022-10-09 21:10:41 +08:00
|
|
|
unsafe { std::ptr::write(args_len, 0) };
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
|
|
{
|
|
|
|
if let Some(args) = crate::core_main::core_main() {
|
|
|
|
return rust_args_to_c_args(args, args_len);
|
|
|
|
}
|
|
|
|
return std::ptr::null_mut() as _;
|
|
|
|
}
|
|
|
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
|
|
|
return std::ptr::null_mut() as _;
|
|
|
|
}
|
|
|
|
|
|
|
|
// https://gist.github.com/iskakaushik/1c5b8aa75c77479c33c4320913eebef6
|
2022-11-10 23:26:09 +08:00
|
|
|
#[cfg(windows)]
|
2022-10-09 21:10:41 +08:00
|
|
|
fn rust_args_to_c_args(args: Vec<String>, outlen: *mut c_int) -> *mut *mut c_char {
|
|
|
|
let mut v = vec![];
|
|
|
|
|
|
|
|
// Let's fill a vector with null-terminated strings
|
|
|
|
for s in args {
|
|
|
|
v.push(CString::new(s).unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turning each null-terminated string into a pointer.
|
|
|
|
// `into_raw` takes ownershop, gives us the pointer and does NOT drop the data.
|
|
|
|
let mut out = v.into_iter().map(|s| s.into_raw()).collect::<Vec<_>>();
|
|
|
|
|
|
|
|
// Make sure we're not wasting space.
|
|
|
|
out.shrink_to_fit();
|
|
|
|
assert!(out.len() == out.capacity());
|
|
|
|
|
|
|
|
// Get the pointer to our vector.
|
|
|
|
let len = out.len();
|
|
|
|
let ptr = out.as_mut_ptr();
|
|
|
|
std::mem::forget(out);
|
|
|
|
|
|
|
|
// Let's write back the length the caller can expect
|
|
|
|
unsafe { std::ptr::write(outlen, len as c_int) };
|
|
|
|
|
|
|
|
// Finally return the data
|
|
|
|
ptr
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub unsafe extern "C" fn free_c_args(ptr: *mut *mut c_char, len: c_int) {
|
|
|
|
let len = len as usize;
|
|
|
|
|
|
|
|
// Get back our vector.
|
|
|
|
// Previously we shrank to fit, so capacity == length.
|
|
|
|
let v = Vec::from_raw_parts(ptr, len, len);
|
|
|
|
|
|
|
|
// Now drop one string at a time.
|
|
|
|
for elem in v {
|
|
|
|
let s = CString::from_raw(elem);
|
|
|
|
std::mem::drop(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Afterwards the vector will be dropped and thus freed.
|
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
#[derive(Default, Clone)]
|
|
|
|
pub struct FlutterHandler {
|
|
|
|
pub event_stream: Arc<RwLock<Option<StreamSink<EventToUI>>>>,
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +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());
|
2022-08-31 20:46:30 +08:00
|
|
|
if let Some(stream) = &*self.event_stream.read().unwrap() {
|
2022-08-30 21:15:35 +08:00
|
|
|
stream.add(EventToUI::Event(out));
|
2022-08-27 16:03:44 +08:00
|
|
|
}
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
2022-08-31 20:46:30 +08:00
|
|
|
}
|
2022-05-12 17:35:25 +08:00
|
|
|
|
2022-09-05 19:41:09 +08:00
|
|
|
impl InvokeUiSession for FlutterHandler {
|
2022-08-31 20:46:30 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +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
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +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
|
|
|
}
|
2022-07-11 16:07:49 +08:00
|
|
|
|
2022-09-01 17:36:37 +08:00
|
|
|
/// unused in flutter, use switch_display or set_peer_info
|
2022-12-31 21:41:16 +08:00
|
|
|
fn set_display(&self, _x: i32, _y: i32, _w: i32, _h: i32, _cursor_embedded: bool) {}
|
2022-07-11 16:07:49 +08:00
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
fn update_privacy_mode(&self) {
|
|
|
|
self.push_event("update_privacy_mode", [].into());
|
2022-07-11 18:23:58 +08:00
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +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-07-11 16:07:49 +08:00
|
|
|
}
|
|
|
|
|
2022-09-05 10:27:33 +08:00
|
|
|
// unused in flutter
|
2022-09-01 17:36:37 +08:00
|
|
|
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
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
fn set_connection_type(&self, is_secured: bool, direct: bool) {
|
2022-05-12 17:35:25 +08:00
|
|
|
self.push_event(
|
2022-08-31 20:46:30 +08:00
|
|
|
"connection_ready",
|
2022-05-12 17:35:25 +08:00
|
|
|
vec![
|
2022-08-31 20:46:30 +08:00
|
|
|
("secure", &is_secured.to_string()),
|
|
|
|
("direct", &direct.to_string()),
|
2022-05-12 17:35:25 +08:00
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +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
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +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-08-25 11:50:30 +08:00
|
|
|
}
|
|
|
|
|
2022-09-05 10:27:33 +08:00
|
|
|
fn update_folder_files(
|
2022-08-31 20:46:30 +08:00
|
|
|
&self,
|
|
|
|
id: i32,
|
2022-09-05 10:27:33 +08:00
|
|
|
entries: &Vec<FileEntry>,
|
2022-08-31 20:46:30 +08:00
|
|
|
path: String,
|
2022-11-15 16:49:55 +08:00
|
|
|
#[allow(unused_variables)] is_local: bool,
|
2022-09-05 10:27:33 +08:00
|
|
|
only_count: bool,
|
2022-08-31 20:46:30 +08:00
|
|
|
) {
|
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![
|
2022-12-29 00:02:31 +08:00
|
|
|
("value", &crate::common::make_fd_to_json(id, path, entries)),
|
2022-09-05 10:27:33 +08:00
|
|
|
("is_local", "false"),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
2022-08-25 11:50:30 +08:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2022-08-31 20:46:30 +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
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +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-08-08 17:03:28 +08:00
|
|
|
}
|
|
|
|
|
2022-09-05 10:27:33 +08:00
|
|
|
// unused in flutter
|
2022-09-01 17:36:37 +08:00
|
|
|
fn adapt_size(&self) {}
|
2022-08-08 17:03:28 +08:00
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
fn on_rgba(&self, data: &[u8]) {
|
|
|
|
if let Some(stream) = &*self.event_stream.read().unwrap() {
|
|
|
|
stream.add(EventToUI::Rgba(ZeroCopyBuffer(data.to_owned())));
|
2022-08-08 17:03:28 +08:00
|
|
|
}
|
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);
|
2022-12-31 21:41:16 +08:00
|
|
|
h.insert("cursor_embedded", if d.cursor_embedded { 1 } else { 0 });
|
2022-09-01 16:21:41 +08:00
|
|
|
displays.push(h);
|
2022-08-08 22:00:01 +08:00
|
|
|
}
|
2022-09-01 16:21:41 +08:00
|
|
|
let displays = serde_json::ser::to_string(&displays).unwrap_or("".to_owned());
|
2022-12-02 21:34:20 +08:00
|
|
|
let mut features: HashMap<&str, i32> = Default::default();
|
|
|
|
for ref f in pi.features.iter() {
|
|
|
|
features.insert("privacy_mode", if f.privacy_mode { 1 } else { 0 });
|
|
|
|
}
|
|
|
|
// compatible with 1.1.9
|
|
|
|
if get_version_number(&pi.version) < get_version_number("1.2.0") {
|
|
|
|
features.insert("privacy_mode", 0);
|
|
|
|
}
|
|
|
|
let features = serde_json::ser::to_string(&features).unwrap_or("".to_owned());
|
2022-08-31 20:46:30 +08:00
|
|
|
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()),
|
2022-08-31 20:46:30 +08:00
|
|
|
("displays", &displays),
|
2022-09-01 16:21:41 +08:00
|
|
|
("version", &pi.version),
|
2022-12-02 21:34:20 +08:00
|
|
|
("features", &features),
|
2022-09-01 16:21:41 +08:00
|
|
|
("current_display", &pi.current_display.to_string()),
|
2022-05-12 17:35:25 +08:00
|
|
|
],
|
2022-08-08 22:00:01 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-12-09 21:16:09 +08:00
|
|
|
fn on_connected(&self, _conn_type: ConnType) {}
|
|
|
|
|
2022-10-14 11:19:49 +08:00
|
|
|
fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str, retry: bool) {
|
2022-08-31 20:46:30 +08:00
|
|
|
let has_retry = if retry { "true" } else { "" };
|
|
|
|
self.push_event(
|
|
|
|
"msgbox",
|
2022-05-19 21:45:25 +08:00
|
|
|
vec![
|
2022-08-31 20:46:30 +08:00
|
|
|
("type", msgtype),
|
|
|
|
("title", title),
|
|
|
|
("text", text),
|
2022-10-14 11:19:49 +08:00
|
|
|
("link", link),
|
2022-08-31 20:46:30 +08:00
|
|
|
("hasRetry", has_retry),
|
2022-05-19 21:45:25 +08:00
|
|
|
],
|
2022-05-17 20:56:36 +08:00
|
|
|
);
|
2022-08-08 22:00:01 +08:00
|
|
|
}
|
|
|
|
|
2022-11-15 16:49:55 +08:00
|
|
|
fn cancel_msgbox(&self, tag: &str) {
|
|
|
|
self.push_event("cancel_msgbox", vec![("tag", tag)]);
|
|
|
|
}
|
|
|
|
|
2022-09-01 09:48:53 +08:00
|
|
|
fn new_message(&self, msg: String) {
|
|
|
|
self.push_event("chat_client_mode", vec![("text", &msg)]);
|
2022-08-08 22:00:01 +08:00
|
|
|
}
|
|
|
|
|
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-21 16:03:08 +08:00
|
|
|
("display", &display.display.to_string()),
|
2022-09-01 09:48:53 +08:00
|
|
|
("x", &display.x.to_string()),
|
|
|
|
("y", &display.y.to_string()),
|
|
|
|
("width", &display.width.to_string()),
|
|
|
|
("height", &display.height.to_string()),
|
2023-01-17 13:28:33 +08:00
|
|
|
(
|
|
|
|
"cursor_embedded",
|
|
|
|
&{
|
|
|
|
if display.cursor_embedded {
|
|
|
|
1
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.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
|
|
|
}
|
2023-01-17 13:28:33 +08:00
|
|
|
|
|
|
|
fn switch_back(&self, peer_id: &str) {
|
|
|
|
self.push_event("switch_back", [("peer_id", peer_id)].into());
|
|
|
|
}
|
2023-02-06 11:42:25 +08:00
|
|
|
|
|
|
|
fn on_voice_call_start(&self) {
|
|
|
|
self.push_event("on_voice_call_start", [].into());
|
|
|
|
}
|
|
|
|
|
2023-02-06 12:14:20 +08:00
|
|
|
fn on_voice_call_closed(&self, reason: &str) {
|
|
|
|
self.push_event("on_voice_call_closed", [("reason", reason)].into())
|
2023-02-06 11:42:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
fn on_voice_call_waiting(&self) {
|
|
|
|
self.push_event("on_voice_call_waiting", [].into());
|
|
|
|
}
|
|
|
|
|
|
|
|
fn on_voice_call_incoming(&self) {
|
|
|
|
self.push_event("on_voice_call_incoming", [].into());
|
|
|
|
}
|
2022-08-31 20:46:30 +08:00
|
|
|
}
|
2022-05-17 20:56:36 +08:00
|
|
|
|
2022-08-31 20:46:30 +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.
|
2023-01-17 13:28:33 +08:00
|
|
|
pub fn session_add(
|
|
|
|
id: &str,
|
|
|
|
is_file_transfer: bool,
|
|
|
|
is_port_forward: bool,
|
|
|
|
switch_uuid: &str,
|
|
|
|
) -> ResultType<()> {
|
2022-08-31 20:46:30 +08:00
|
|
|
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()
|
|
|
|
};
|
|
|
|
|
2022-09-01 17:36:37 +08:00
|
|
|
// TODO rdp
|
|
|
|
let conn_type = if is_file_transfer {
|
|
|
|
ConnType::FILE_TRANSFER
|
|
|
|
} else if is_port_forward {
|
|
|
|
ConnType::PORT_FORWARD
|
|
|
|
} else {
|
|
|
|
ConnType::DEFAULT_CONN
|
|
|
|
};
|
|
|
|
|
2023-01-17 13:28:33 +08:00
|
|
|
let switch_uuid = if switch_uuid.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(switch_uuid.to_string())
|
|
|
|
};
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
session
|
|
|
|
.lc
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
2023-01-17 13:28:33 +08:00
|
|
|
.initialize(session_id, conn_type, switch_uuid);
|
2022-09-01 17:36:37 +08:00
|
|
|
|
2022-09-05 10:27:33 +08:00
|
|
|
if let Some(same_id_session) = SESSIONS.write().unwrap().insert(id.to_owned(), session) {
|
2022-09-01 17:36:37 +08:00
|
|
|
same_id_session.close();
|
2022-05-17 20:56:36 +08:00
|
|
|
}
|
2022-07-11 18:23:58 +08:00
|
|
|
|
2022-08-31 20:46:30 +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 || {
|
|
|
|
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
|
2022-05-25 00:28:59 +08:00
|
|
|
#[cfg(not(any(target_os = "ios")))]
|
2022-05-12 17:35:25 +08:00
|
|
|
pub mod connection_manager {
|
2022-09-05 19:41:09 +08:00
|
|
|
use std::collections::HashMap;
|
2022-05-12 17:35:25 +08:00
|
|
|
|
2022-10-31 10:18:22 +08:00
|
|
|
#[cfg(any(target_os = "android"))]
|
2022-09-05 20:05:23 +08:00
|
|
|
use hbb_common::log;
|
2022-05-25 00:28:59 +08:00
|
|
|
#[cfg(any(target_os = "android"))]
|
2022-05-12 17:35:25 +08:00
|
|
|
use scrap::android::call_main_service_set_by_name;
|
2022-07-01 11:26:32 +08:00
|
|
|
|
2022-09-05 19:41:09 +08:00
|
|
|
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;
|
2022-05-17 19:59:37 +08:00
|
|
|
|
2022-09-05 19:41:09 +08:00
|
|
|
#[derive(Clone)]
|
|
|
|
struct FlutterHandler {}
|
2022-08-17 17:23:55 +08:00
|
|
|
|
2022-09-05 19:41:09 +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) =
|
2022-09-05 19:41:09 +08:00
|
|
|
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
|
|
|
}
|
2022-05-17 19:59:37 +08:00
|
|
|
|
2022-10-08 20:15:02 +08:00
|
|
|
fn remove_connection(&self, id: i32, close: bool) {
|
|
|
|
self.push_event(
|
|
|
|
"on_client_remove",
|
|
|
|
vec![("id", &id.to_string()), ("close", &close.to_string())],
|
|
|
|
);
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
|
2022-09-05 19:41:09 +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
|
|
|
}
|
2022-09-07 18:57:49 +08:00
|
|
|
|
2022-09-21 23:32:59 +08:00
|
|
|
fn change_theme(&self, dark: String) {
|
|
|
|
self.push_event("theme", vec![("dark", &dark)]);
|
2022-09-07 18:57:49 +08:00
|
|
|
}
|
2022-09-08 08:52:56 +08:00
|
|
|
|
|
|
|
fn change_language(&self) {
|
|
|
|
self.push_event("language", vec![]);
|
|
|
|
}
|
2022-11-10 10:27:13 +08:00
|
|
|
|
|
|
|
fn show_elevation(&self, show: bool) {
|
|
|
|
self.push_event("show_elevation", vec![("show", &show.to_string())]);
|
|
|
|
}
|
2023-02-06 12:53:57 +08:00
|
|
|
|
|
|
|
fn voice_call_started(&self, id: i32) {
|
|
|
|
self.push_event("voice_call_started", vec![("show", &id.to_string())]);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn voice_call_incoming(&self, id: i32) {
|
|
|
|
self.push_event("voice_call_incoming", vec![("id", &id.to_string())]);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn voice_call_closed(&self, id: i32, reason: &str) {
|
|
|
|
self.push_event("voice_call_closed", vec![("id", &id.to_string()), ("reason", &reason.to_string())]);
|
|
|
|
}
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
|
2022-09-05 19:41:09 +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
|
|
|
|
2023-02-07 15:39:46 +08:00
|
|
|
if let Some(s) = GLOBAL_EVENT_STREAM.read().unwrap().get(super::APP_TYPE_CM) {
|
2022-09-05 19:41:09 +08:00
|
|
|
s.add(serde_json::ser::to_string(&h).unwrap_or("".to_owned()));
|
|
|
|
};
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-05 19:41:09 +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
|
|
|
|
2022-09-05 19:41:09 +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
|
|
|
|
2022-09-05 19:41:09 +08:00
|
|
|
let cm = ConnectionManager {
|
|
|
|
ui_handler: FlutterHandler {},
|
|
|
|
};
|
|
|
|
std::thread::spawn(move || start_ipc(cm));
|
2022-05-12 17:35:25 +08:00
|
|
|
}
|
|
|
|
|
2022-09-05 19:41:09 +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>,
|
|
|
|
) {
|
2022-09-05 19:41:09 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2022-06-28 22:15:00 +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_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())
|
2022-09-21 16:03:08 +08:00
|
|
|
}
|
2022-10-26 19:42:14 +08:00
|
|
|
|
|
|
|
pub fn get_cur_session_id() -> String {
|
2022-10-27 10:56:14 +08:00
|
|
|
CUR_SESSION_ID.read().unwrap().clone()
|
2022-10-26 19:42:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_cur_session_id(id: String) {
|
|
|
|
if get_cur_session_id() != id {
|
|
|
|
*CUR_SESSION_ID.write().unwrap() = id;
|
|
|
|
}
|
|
|
|
}
|