From e670989e0ffca3d237633882872e98b9fb06450a Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 5 Jun 2023 18:01:43 +0800 Subject: [PATCH] restore custom resolution for each display Signed-off-by: fufesou --- .../lib/desktop/widgets/remote_toolbar.dart | 7 +- libs/hbb_common/src/config.rs | 8 +- src/client.rs | 30 ++++-- src/client/io_loop.rs | 10 +- src/flutter_ffi.rs | 4 +- src/server/connection.rs | 16 +++- src/ui_session_interface.rs | 93 +++++++++++++++++-- 7 files changed, 132 insertions(+), 36 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 1f4f74a33..d8871464d 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -995,7 +995,7 @@ class _ResolutionsMenu extends StatefulWidget { State<_ResolutionsMenu> createState() => _ResolutionsMenuState(); } -const double _kCustonResolutionEditingWidth = 42; +const double _kCustomResolutionEditingWidth = 42; const _kCustomResolutionValue = 'custom'; class _ResolutionsMenuState extends State<_ResolutionsMenu> { @@ -1102,6 +1102,7 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { _changeResolution(int w, int h) async { await bind.sessionChangeResolution( id: widget.id, + display: pi.currentDisplay, width: w, height: h, ); @@ -1157,12 +1158,12 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { children: [ Text('${translate('resolution_custom_tip')} '), SizedBox( - width: _kCustonResolutionEditingWidth, + width: _kCustomResolutionEditingWidth, child: _resolutionInput(_customWidth), ), Text(' x '), SizedBox( - width: _kCustonResolutionEditingWidth, + width: _kCustomResolutionEditingWidth, child: _resolutionInput(_customHeight), ), ], diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 8e0de8c0b..6a137f4ca 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -262,10 +262,10 @@ pub struct PeerConfig { #[serde( default, - skip_serializing_if = "Option::is_none", - deserialize_with = "deserialize_option_resolution" + deserialize_with = "deserialize_hashmap_resolutions", + skip_serializing_if = "HashMap::is_empty", )] - pub custom_resolution: Option, + pub custom_resolutions: HashMap, // The other scalar value must before this #[serde(default, deserialize_with = "PeerConfig::deserialize_options")] @@ -1509,7 +1509,7 @@ deserialize_default!(deserialize_option_string, Option); deserialize_default!(deserialize_hashmap_string_string, HashMap); deserialize_default!(deserialize_hashmap_string_bool, HashMap); deserialize_default!(deserialize_hashmap_string_configoidcprovider, HashMap); -deserialize_default!(deserialize_option_resolution, Option); +deserialize_default!(deserialize_hashmap_resolutions, HashMap); #[cfg(test)] mod tests { diff --git a/src/client.rs b/src/client.rs index a43dbcca1..9ab01682c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1353,7 +1353,9 @@ impl LoginConfigHandler { /// /// * `ignore_default` - If `true`, ignore the default value of the option. fn get_option_message(&self, ignore_default: bool) -> Option { - if self.conn_type.eq(&ConnType::FILE_TRANSFER) || self.conn_type.eq(&ConnType::PORT_FORWARD) || self.conn_type.eq(&ConnType::RDP) + if self.conn_type.eq(&ConnType::FILE_TRANSFER) + || self.conn_type.eq(&ConnType::PORT_FORWARD) + || self.conn_type.eq(&ConnType::RDP) { return None; } @@ -1402,7 +1404,7 @@ impl LoginConfigHandler { msg.disable_clipboard = BoolOption::Yes.into(); n += 1; } - if let Some(r) = self.get_custom_resolution() { + if let Some(r) = self.get_custom_resolution(0) { if r.0 > 0 && r.1 > 0 { msg.custom_resolution = Some(ProtoResolution { width: r.0, @@ -1424,7 +1426,9 @@ impl LoginConfigHandler { } pub fn get_option_message_after_login(&self) -> Option { - if self.conn_type.eq(&ConnType::FILE_TRANSFER) || self.conn_type.eq(&ConnType::PORT_FORWARD) || self.conn_type.eq(&ConnType::RDP) + if self.conn_type.eq(&ConnType::FILE_TRANSFER) + || self.conn_type.eq(&ConnType::PORT_FORWARD) + || self.conn_type.eq(&ConnType::RDP) { return None; } @@ -1584,14 +1588,26 @@ impl LoginConfigHandler { } #[inline] - pub fn get_custom_resolution(&self) -> Option<(i32, i32)> { - self.config.custom_resolution.as_ref().map(|r| (r.w, r.h)) + pub fn get_custom_resolution(&self, display: i32) -> Option<(i32, i32)> { + self.config + .custom_resolutions + .get(&display) + .map(|r| (r.w, r.h)) } #[inline] - pub fn set_custom_resolution(&mut self, wh: Option<(i32, i32)>) { + pub fn set_custom_resolution(&mut self, display: i32, wh: Option<(i32, i32)>) { let mut config = self.load_config(); - config.custom_resolution = wh.map(|r| Resolution { w: r.0, h: r.1 }); + match wh { + Some((w, h)) => { + config + .custom_resolutions + .insert(display, Resolution { w, h }); + } + None => { + config.custom_resolutions.remove(&display); + } + } self.save_config(config); } diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index f7f022b6e..906d06ec7 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1201,7 +1201,7 @@ impl Remote { } } Some(misc::Union::SwitchDisplay(s)) => { - self.handler.ui_handler.switch_display(&s); + self.handler.handle_peer_switch_display(&s); self.video_sender.send(MediaData::Reset).ok(); if s.width > 0 && s.height > 0 { self.handler.set_display( @@ -1212,14 +1212,6 @@ impl Remote { s.cursor_embedded, ); } - let custom_resolution = if s.width != s.original_resolution.width - || s.height != s.original_resolution.height - { - Some((s.width, s.height)) - } else { - None - }; - self.handler.set_custom_resolution(custom_resolution); } Some(misc::Union::CloseReason(c)) => { self.handler.msgbox("error", "Connection Error", &c, ""); diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index bf56c8d29..0e3aa59c3 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -535,9 +535,9 @@ pub fn session_switch_sides(id: String) { } } -pub fn session_change_resolution(id: String, width: i32, height: i32) { +pub fn session_change_resolution(id: String, display: i32, width: i32, height: i32) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.change_resolution(width, height); + session.change_resolution(display, width, height); } } diff --git a/src/server/connection.rs b/src/server/connection.rs index 8c211df39..3bf485e45 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1793,6 +1793,14 @@ impl Connection { Some(message::Union::Misc(misc)) => match misc.union { Some(misc::Union::SwitchDisplay(s)) => { video_service::switch_display(s.display).await; + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if s.width != 0 && s.height != 0 { + self.change_resolution(&Resolution { + width: s.width, + height: s.height, + ..Default::default() + }); + } } Some(misc::Union::ChatMessage(c)) => { self.send_to_cm(ipc::Data::ChatMessage { text: c.text }); @@ -2159,10 +2167,14 @@ impl Connection { } } } + // Custom resolution here is only valid in the process of establishing a connection. + // After the connection is established, the resolution is changed by another message. #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Some(custom_resolution) = o.custom_resolution.as_ref() { - if custom_resolution.width > 0 && custom_resolution.height > 0 { - self.change_resolution(&custom_resolution); + if Self::alive_conns().len() > 0 { + if custom_resolution.width > 0 && custom_resolution.height > 0 { + self.change_resolution(&custom_resolution); + } } } if self.keyboard { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index f6b19e8ea..3d658b99c 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -15,13 +15,21 @@ use bytes::Bytes; use rdev::{Event, EventType::*, KeyCode}; use uuid::Uuid; -use hbb_common::config::{Config, LocalConfig, PeerConfig}; #[cfg(not(feature = "flutter"))] use hbb_common::fs; -use hbb_common::rendezvous_proto::ConnType; -use hbb_common::tokio::{self, sync::mpsc}; -use hbb_common::{allow_err, message_proto::*}; -use hbb_common::{get_version_number, log, Stream}; +use hbb_common::{ + allow_err, + config::{Config, LocalConfig, PeerConfig}, + get_version_number, log, + message_proto::*, + rendezvous_proto::ConnType, + tokio::{ + self, + sync::mpsc, + time::{Duration as TokioDuration, Instant}, + }, + Stream, +}; use crate::client::io_loop::Remote; use crate::client::{ @@ -37,6 +45,8 @@ use crate::{client::Data, client::Interface}; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub static IS_IN: AtomicBool = AtomicBool::new(false); +const CHANGE_RESOLUTION_VALID_TIMEOUT_SECS: u64 = 15; + #[derive(Clone, Default)] pub struct Session { pub id: String, @@ -49,6 +59,7 @@ pub struct Session { pub server_keyboard_enabled: Arc>, pub server_file_transfer_enabled: Arc>, pub server_clipboard_enabled: Arc>, + pub last_change_display: Arc>, } #[derive(Clone)] @@ -59,6 +70,43 @@ pub struct SessionPermissionConfig { pub server_clipboard_enabled: Arc>, } +pub struct ChangeDisplayRecord { + time: Instant, + display: i32, + width: i32, + height: i32, +} + +impl Default for ChangeDisplayRecord { + fn default() -> Self { + Self { + time: Instant::now() + - TokioDuration::from_secs(CHANGE_RESOLUTION_VALID_TIMEOUT_SECS + 1), + display: 0, + width: 0, + height: 0, + } + } +} + +impl ChangeDisplayRecord { + fn new(display: i32, width: i32, height: i32) -> Self { + Self { + time: Instant::now(), + display, + width, + height, + } + } + + pub fn is_the_same_record(&self, display: i32, width: i32, height: i32) -> bool { + self.time.elapsed().as_secs() < CHANGE_RESOLUTION_VALID_TIMEOUT_SECS + && self.display == display + && self.width == width + && self.height == height + } +} + #[cfg(not(any(target_os = "android", target_os = "ios")))] impl SessionPermissionConfig { pub fn is_text_clipboard_required(&self) -> bool { @@ -485,9 +533,16 @@ impl Session { } pub fn switch_display(&self, display: i32) { + let (w, h) = match self.lc.read().unwrap().get_custom_resolution(display) { + Some((w, h)) => (w, h), + None => (0, 0), + }; + let mut misc = Misc::new(); misc.set_switch_display(SwitchDisplay { display, + width: w, + height: h, ..Default::default() }); let mut msg_out = Message::new(); @@ -831,12 +886,32 @@ impl Session { } } - #[inline] - pub fn set_custom_resolution(&mut self, wh: Option<(i32, i32)>) { - self.lc.write().unwrap().set_custom_resolution(wh); + pub fn handle_peer_switch_display(&self, display: &SwitchDisplay) { + self.ui_handler.switch_display(display); + + if self.last_change_display.lock().unwrap().is_the_same_record( + display.display, + display.width, + display.height, + ) { + let custom_resolution = if display.width != display.original_resolution.width + || display.height != display.original_resolution.height + { + Some((display.width, display.height)) + } else { + None + }; + self.lc + .write() + .unwrap() + .set_custom_resolution(display.display, custom_resolution); + } } - pub fn change_resolution(&self, width: i32, height: i32) { + pub fn change_resolution(&self, display: i32, width: i32, height: i32) { + *self.last_change_display.lock().unwrap() = + ChangeDisplayRecord::new(display, width, height); + let mut misc = Misc::new(); misc.set_change_resolution(Resolution { width,