Fix. Multi sub windows, sync peer options (#7247)

* Fix. Multi window, sync peer options

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Remove unused `use`

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou 2024-02-23 22:49:53 +08:00 committed by GitHub
parent cce1ce0ee0
commit 2459bcd206
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 136 additions and 57 deletions

View File

@ -221,7 +221,7 @@ List<(String, String)> otherDefaultSettings() {
('Privacy mode', 'privacy_mode'),
if (isMobile) ('Touch mode', 'touch-mode'),
('True color (4:4:4)', 'i444'),
('Reverse mouse wheel', 'reverse_mouse_wheel'),
('Reverse mouse wheel', kKeyReverseMouseWheel),
('swap-left-right-mouse', 'swap-left-right-mouse'),
if (isDesktop && useTextureRender)
(

View File

@ -655,7 +655,7 @@ List<TToggleMenu> toolbarKeyboardToggles(FFI ffi) {
var optionValue =
bind.sessionGetReverseMouseWheelSync(sessionId: sessionId) ?? '';
if (optionValue == '') {
optionValue = bind.mainGetUserDefaultOption(key: 'reverse_mouse_wheel');
optionValue = bind.mainGetUserDefaultOption(key: kKeyReverseMouseWheel);
}
onChanged(bool? value) async {
if (value == null) return;

View File

@ -23,7 +23,8 @@ const String kPlatformAdditionsHeadless = "headless";
const String kPlatformAdditionsIsInstalled = "is_installed";
const String kPlatformAdditionsVirtualDisplays = "virtual_displays";
const String kPlatformAdditionsHasFileClipboard = "has_file_clipboard";
const String kPlatformAdditionsSupportedPrivacyModeImpl = "supported_privacy_mode_impl";
const String kPlatformAdditionsSupportedPrivacyModeImpl =
"supported_privacy_mode_impl";
const String kPeerPlatformWindows = "Windows";
const String kPeerPlatformLinux = "Linux";
@ -68,6 +69,7 @@ const String kOptionOpenNewConnInTabs = "enable-open-new-connections-in-tabs";
const String kOptionOpenInTabs = "allow-open-in-tabs";
const String kOptionOpenInWindows = "allow-open-in-windows";
const String kOptionForceAlwaysRelay = "force-always-relay";
const String kOptionViewOnly = "view-only";
const String kUniLinksPrefix = "rustdesk://";
const String kUrlActionClose = "close";
@ -86,6 +88,7 @@ const String kKeyShowDisplaysAsIndividualWindows =
const String kKeyUseAllMyDisplaysForTheRemoteSession =
'use_all_my_displays_for_the_remote_session';
const String kKeyShowMonitorsToolbar = 'show_monitors_toolbar';
const String kKeyReverseMouseWheel = "reverse_mouse_wheel";
// the executable name of the portable version
const String kEnvPortableExecutable = "RUSTDESK_APPNAME";

View File

@ -1732,7 +1732,7 @@ class _KeyboardMenu extends StatelessWidget {
? (value) async {
if (value == null) return;
await bind.sessionToggleOption(
sessionId: ffi.sessionId, value: 'view-only');
sessionId: ffi.sessionId, value: kOptionViewOnly);
ffiModel.setViewOnly(id, value);
}
: null,

View File

@ -357,12 +357,26 @@ class FfiModel with ChangeNotifier {
if (isDesktop) {
gFFI.cmFileModel.onFileTransferLog(evt);
}
} else if (name == 'sync_peer_option') {
_handleSyncPeerOption(evt, peerId);
} else {
debugPrint('Unknown event name: $name');
}
};
}
_handleSyncPeerOption(Map<String, dynamic> evt, String peer) {
final k = evt['k'];
final v = evt['v'];
if (k == kOptionViewOnly) {
setViewOnly(peer, v as bool);
} else if (k == 'keyboard_mode') {
parent.target?.inputModel.updateKeyboardMode();
} else if (k == 'input_source') {
stateGlobal.getInputSource(force: true);
}
}
onUrlSchemeReceived(Map<String, dynamic> evt) {
final url = evt['url'].toString().trim();
if (url.startsWith(kUniLinksPrefix) && handleUriLink(uriString: url)) {
@ -728,7 +742,7 @@ class FfiModel with ChangeNotifier {
setViewOnly(
peerId,
bind.sessionGetToggleOptionSync(
sessionId: sessionId, arg: 'view-only'));
sessionId: sessionId, arg: kOptionViewOnly));
}
if (connType == ConnType.defaultConn) {
final platformAdditions = evt['platform_additions'];

View File

@ -13,6 +13,7 @@ use hbb_common::{
anyhow::anyhow, bail, config::LocalConfig, get_version_number, log, message_proto::*,
rendezvous_proto::ConnType, ResultType,
};
use serde::Serialize;
use serde_json::json;
#[cfg(any(feature = "flutter_texture_render", feature = "gpucodec"))]
@ -471,12 +472,19 @@ impl FlutterHandler {
///
/// * `name` - The name of the event.
/// * `event` - Fields of the event content.
pub fn push_event(&self, name: &str, event: Vec<(&str, &str)>) {
let mut h: HashMap<&str, &str> = event.iter().cloned().collect();
pub fn push_event<V>(&self, name: &str, event: &[(&str, V)], excludes: &[&SessionID])
where
V: Sized + Serialize + Clone,
{
let mut h: HashMap<&str, serde_json::Value> =
event.iter().map(|(k, v)| (*k, json!(*v))).collect();
debug_assert!(h.get("name").is_none());
h.insert("name", name);
h.insert("name", json!(name));
let out = serde_json::ser::to_string(&h).unwrap_or("".to_owned());
for (_, session) in self.session_handlers.read().unwrap().iter() {
for (sid, session) in self.session_handlers.read().unwrap().iter() {
if excludes.contains(&sid) {
continue;
}
if let Some(stream) = &session.event_stream {
stream.add(EventToUI::Event(out.clone()));
}
@ -539,7 +547,7 @@ impl InvokeUiSession for FlutterHandler {
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()),
@ -550,17 +558,19 @@ impl InvokeUiSession for FlutterHandler {
&serde_json::ser::to_string(&colors).unwrap_or("".to_owned()),
),
],
&[],
);
}
fn set_cursor_id(&self, id: String) {
self.push_event("cursor_id", vec![("id", &id.to_string())]);
self.push_event("cursor_id", &[("id", &id.to_string())], &[]);
}
fn set_cursor_position(&self, cp: CursorPosition) {
self.push_event(
"cursor_position",
vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())],
&[("x", &cp.x.to_string()), ("y", &cp.y.to_string())],
&[],
);
}
@ -568,11 +578,11 @@ impl InvokeUiSession for FlutterHandler {
fn set_display(&self, _x: i32, _y: i32, _w: i32, _h: i32, _cursor_embedded: bool) {}
fn update_privacy_mode(&self) {
self.push_event("update_privacy_mode", [].into());
self.push_event::<&str>("update_privacy_mode", &[], &[]);
}
fn set_permission(&self, name: &str, value: bool) {
self.push_event("permission", vec![(name, &value.to_string())]);
self.push_event("permission", &[(name, &value.to_string())], &[]);
}
// unused in flutter
@ -582,7 +592,7 @@ impl InvokeUiSession for FlutterHandler {
const NULL: String = String::new();
self.push_event(
"update_quality_status",
vec![
&[
("speed", &status.speed.map_or(NULL, |it| it)),
(
"fps",
@ -599,38 +609,42 @@ impl InvokeUiSession for FlutterHandler {
),
("chroma", &status.chroma.map_or(NULL, |it| it.to_string())),
],
&[],
);
}
fn set_connection_type(&self, is_secured: bool, direct: bool) {
self.push_event(
"connection_ready",
vec![
&[
("secure", &is_secured.to_string()),
("direct", &direct.to_string()),
],
&[],
);
}
fn set_fingerprint(&self, fingerprint: String) {
self.push_event("fingerprint", vec![("fingerprint", &fingerprint)]);
self.push_event("fingerprint", &[("fingerprint", &fingerprint)], &[]);
}
fn job_error(&self, id: i32, err: String, file_num: i32) {
self.push_event(
"job_error",
vec![
&[
("id", &id.to_string()),
("err", &err),
("file_num", &file_num.to_string()),
],
&[],
);
}
fn job_done(&self, id: i32, file_num: i32) {
self.push_event(
"job_done",
vec![("id", &id.to_string()), ("file_num", &file_num.to_string())],
&[("id", &id.to_string()), ("file_num", &file_num.to_string())],
&[],
);
}
@ -638,7 +652,7 @@ impl InvokeUiSession for FlutterHandler {
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)]);
self.push_event("load_last_job", &[("value", job_json)], &[]);
}
fn update_folder_files(
@ -653,15 +667,17 @@ impl InvokeUiSession for FlutterHandler {
if only_count {
self.push_event(
"update_folder_files",
vec![("info", &make_fd_flutter(id, entries, only_count))],
&[("info", &make_fd_flutter(id, entries, only_count))],
&[],
);
} else {
self.push_event(
"file_dir",
vec![
("value", &crate::common::make_fd_to_json(id, path, entries)),
&[
("is_local", "false"),
("value", &crate::common::make_fd_to_json(id, path, entries)),
],
&[],
);
}
}
@ -682,25 +698,27 @@ impl InvokeUiSession for FlutterHandler {
) {
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()),
("is_identical", &is_identical.to_string()),
],
&[],
);
}
fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64) {
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()),
],
&[],
);
}
@ -796,7 +814,7 @@ impl InvokeUiSession for FlutterHandler {
}
self.push_event(
"peer_info",
vec![
&[
("username", &pi.username),
("hostname", &pi.hostname),
("platform", &pi.platform),
@ -808,6 +826,7 @@ impl InvokeUiSession for FlutterHandler {
("resolutions", &resolutions),
("platform_additions", &pi.platform_additions),
],
&[],
);
}
@ -815,14 +834,16 @@ impl InvokeUiSession for FlutterHandler {
self.peer_info.write().unwrap().displays = displays.clone();
self.push_event(
"sync_peer_info",
vec![("displays", &Self::make_displays_msg(displays))],
&[("displays", &Self::make_displays_msg(displays))],
&[],
);
}
fn set_platform_additions(&self, data: &str) {
self.push_event(
"sync_platform_additions",
vec![("platform_additions", &data)],
&[("platform_additions", &data)],
&[],
)
}
@ -837,10 +858,11 @@ impl InvokeUiSession for FlutterHandler {
}
self.push_event(
"set_multiple_windows_session",
vec![(
&[(
"windows_sessions",
&serde_json::ser::to_string(&msg_vec).unwrap_or("".to_owned()),
)],
&[],
);
}
@ -850,29 +872,30 @@ impl InvokeUiSession for FlutterHandler {
let has_retry = if retry { "true" } else { "" };
self.push_event(
"msgbox",
vec![
&[
("type", msgtype),
("title", title),
("text", text),
("link", link),
("hasRetry", has_retry),
],
&[],
);
}
fn cancel_msgbox(&self, tag: &str) {
self.push_event("cancel_msgbox", vec![("tag", tag)]);
self.push_event("cancel_msgbox", &[("tag", tag)], &[]);
}
fn new_message(&self, msg: String) {
self.push_event("chat_client_mode", vec![("text", &msg)]);
self.push_event("chat_client_mode", &[("text", &msg)], &[]);
}
fn switch_display(&self, display: &SwitchDisplay) {
let resolutions = serialize_resolutions(&display.resolutions.resolutions);
self.push_event(
"switch_display",
vec![
&[
("display", &display.display.to_string()),
("x", &display.x.to_string()),
("y", &display.y.to_string()),
@ -899,46 +922,49 @@ impl InvokeUiSession for FlutterHandler {
&display.original_resolution.height.to_string(),
),
],
&[],
);
}
fn update_block_input_state(&self, on: bool) {
self.push_event(
"update_block_input_state",
[("input_state", if on { "on" } else { "off" })].into(),
&[("input_state", if on { "on" } else { "off" })],
&[],
);
}
#[cfg(any(target_os = "android", target_os = "ios"))]
fn clipboard(&self, content: String) {
self.push_event("clipboard", vec![("content", &content)]);
self.push_event("clipboard", &[("content", &content)], &[]);
}
fn switch_back(&self, peer_id: &str) {
self.push_event("switch_back", [("peer_id", peer_id)].into());
self.push_event("switch_back", &[("peer_id", peer_id)], &[]);
}
fn portable_service_running(&self, running: bool) {
self.push_event(
"portable_service_running",
[("running", running.to_string().as_str())].into(),
&[("running", running.to_string().as_str())],
&[],
);
}
fn on_voice_call_started(&self) {
self.push_event("on_voice_call_started", [].into());
self.push_event::<&str>("on_voice_call_started", &[], &[]);
}
fn on_voice_call_closed(&self, reason: &str) {
let _res = self.push_event("on_voice_call_closed", [("reason", reason)].into());
let _res = self.push_event("on_voice_call_closed", &[("reason", reason)], &[]);
}
fn on_voice_call_waiting(&self) {
self.push_event("on_voice_call_waiting", [].into());
self.push_event::<&str>("on_voice_call_waiting", &[], &[]);
}
fn on_voice_call_incoming(&self) {
self.push_event("on_voice_call_incoming", [].into());
self.push_event::<&str>("on_voice_call_incoming", &[], &[]);
}
#[inline]
@ -1129,6 +1155,7 @@ pub mod connection_manager {
use hbb_common::log;
#[cfg(any(target_os = "android"))]
use scrap::android::call_main_service_set_by_name;
use serde_json::json;
use crate::ui_cm_interface::InvokeUiCM;
@ -1149,50 +1176,54 @@ pub mod connection_manager {
log::debug!("call_service_set_by_name fail,{}", e);
}
// send to UI, refresh widget
self.push_event("add_connection", vec![("client", &client_json)]);
self.push_event("add_connection", &[("client", &client_json)]);
}
fn remove_connection(&self, id: i32, close: bool) {
self.push_event(
"on_client_remove",
vec![("id", &id.to_string()), ("close", &close.to_string())],
&[("id", &id.to_string()), ("close", &close.to_string())],
);
}
fn new_message(&self, id: i32, text: String) {
self.push_event(
"chat_server_mode",
vec![("id", &id.to_string()), ("text", &text)],
&[("id", &id.to_string()), ("text", &text)],
);
}
fn change_theme(&self, dark: String) {
self.push_event("theme", vec![("dark", &dark)]);
self.push_event("theme", &[("dark", &dark)]);
}
fn change_language(&self) {
self.push_event("language", vec![]);
self.push_event::<&str>("language", &[]);
}
fn show_elevation(&self, show: bool) {
self.push_event("show_elevation", vec![("show", &show.to_string())]);
self.push_event("show_elevation", &[("show", &show.to_string())]);
}
fn update_voice_call_state(&self, client: &crate::ui_cm_interface::Client) {
let client_json = serde_json::to_string(&client).unwrap_or("".into());
self.push_event("update_voice_call_state", vec![("client", &client_json)]);
self.push_event("update_voice_call_state", &[("client", &client_json)]);
}
fn file_transfer_log(&self, action: &str, log: &str) {
self.push_event("cm_file_transfer_log", vec![(action, log)]);
self.push_event("cm_file_transfer_log", &[(action, log)]);
}
}
impl FlutterHandler {
fn push_event(&self, name: &str, event: Vec<(&str, &str)>) {
let mut h: HashMap<&str, &str> = event.iter().cloned().collect();
fn push_event<V>(&self, name: &str, event: &[(&str, V)])
where
V: Sized + serde::Serialize + Clone,
{
let mut h: HashMap<&str, serde_json::Value> =
event.iter().map(|(k, v)| (*k, json!(*v))).collect();
debug_assert!(h.get("name").is_none());
h.insert("name", name);
h.insert("name", json!(name));
if let Some(s) = GLOBAL_EVENT_STREAM.read().unwrap().get(super::APP_TYPE_CM) {
s.add(serde_json::ser::to_string(&h).unwrap_or("".to_owned()));
@ -1442,7 +1473,7 @@ pub fn get_adapter_luid() -> Option<i64> {
#[inline]
pub fn push_session_event(session_id: &SessionID, name: &str, event: Vec<(&str, &str)>) {
if let Some(s) = sessions::get_session_by_session_id(session_id) {
s.push_event(name, event);
s.push_event(name, &event, &[]);
}
}
@ -1580,6 +1611,29 @@ pub fn get_cur_session() -> Option<FlutterSession> {
sessions::get_session_by_session_id(&*CUR_SESSION_ID.read().unwrap())
}
#[inline]
pub fn try_sync_peer_option(
session: &FlutterSession,
cur_id: &SessionID,
key: &str,
_value: Option<serde_json::Value>,
) {
let mut event = Vec::new();
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if key == "view-only" {
event = vec![
("k", json!(key.to_string())),
("v", json!(session.lc.read().unwrap().view_only.v)),
];
}
if ["keyboard_mode", "input_source"].contains(&key) {
event = vec![("k", json!(key.to_string())), ("v", json!(""))];
}
if !event.is_empty() {
session.push_event("sync_peer_option", &event, &[cur_id]);
}
}
// sessions mod is used to avoid the big lock of sessions' map.
pub mod sessions {
#[cfg(feature = "flutter_texture_render")]

View File

@ -1,8 +1,9 @@
use crate::{
client::file_trait::FileManager,
common::is_keyboard_mode_supported,
common::make_fd_to_json,
flutter::{self, session_add, session_add_existed, session_start_, sessions},
common::{is_keyboard_mode_supported, make_fd_to_json},
flutter::{
self, session_add, session_add_existed, session_start_, sessions, try_sync_peer_option,
},
input::*,
ui_interface::{self, *},
};
@ -228,6 +229,7 @@ pub fn session_toggle_option(session_id: SessionID, value: String) {
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
log::warn!("toggle option {}", &value);
session.toggle_option(value.clone());
try_sync_peer_option(&session, &session_id, &value, None);
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if sessions::get_session_by_session_id(&session_id).is_some() && value == "disable-clipboard" {
@ -340,6 +342,7 @@ pub fn session_set_keyboard_mode(session_id: SessionID, value: String) {
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
session.save_keyboard_mode(value.clone());
_mode_updated = true;
try_sync_peer_option(&session, &session_id, "keyboard_mode", None);
}
#[cfg(windows)]
if _mode_updated {
@ -884,7 +887,12 @@ pub fn main_get_input_source() -> SyncReturn<String> {
pub fn main_set_input_source(session_id: SessionID, value: String) {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
change_input_source(session_id, value);
{
change_input_source(session_id, value);
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
try_sync_peer_option(&session, &session_id, "input_source", None);
}
}
}
pub fn main_get_my_id() -> String {