From a603e046e32f93867c13e29b158453c8c7fc84af Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 17 May 2023 23:19:20 +0800 Subject: [PATCH 01/11] refactor resolution, mid commit Signed-off-by: fufesou --- .../lib/desktop/widgets/remote_toolbar.dart | 40 +++++++-- libs/hbb_common/protos/message.proto | 4 + libs/hbb_common/src/config.rs | 9 ++ src/client.rs | 28 ++++++- src/core_main.rs | 4 +- src/platform/windows.rs | 4 + src/server/connection.rs | 70 +++++++--------- src/server/video_service.rs | 83 ++++++++++++++++++- src/ui_session_interface.rs | 9 +- 9 files changed, 196 insertions(+), 55 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index b10d3245f..2575ddadd 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -29,6 +29,10 @@ const _kKeyLegacyMode = 'legacy'; const _kKeyMapMode = 'map'; const _kKeyTranslateMode = 'translate'; +const _kResolutionOrigin = 'Origin'; +const _kResolutionCustom = 'Custom'; +const _kResolutionFitLocal = 'FitLocal'; + class MenubarState { final kStoreKey = 'remoteMenubarState'; late RxBool show; @@ -692,6 +696,8 @@ class _DisplayMenuState extends State<_DisplayMenu> { int get windowId => stateGlobal.windowId; Map get perms => widget.ffi.ffiModel.permissions; + RxBool _isOrignalResolution = true.obs; + RxBool _isFitLocalResolution = false.obs; PeerInfo get pi => widget.ffi.ffiModel.pi; FfiModel get ffiModel => widget.ffi.ffiModel; @@ -943,6 +949,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { final groupValue = "${display.width}x${display.height}"; onChanged(String? value) async { if (value == null) return; + final list = value.split('x'); if (list.length == 2) { final w = int.tryParse(list[0]); @@ -964,14 +971,37 @@ class _DisplayMenuState extends State<_DisplayMenu> { return _SubmenuButton( ffi: widget.ffi, - menuChildren: resolutions - .map((e) => RdoMenuButton( - value: '${e.width}x${e.height}', + menuChildren: [ + RdoMenuButton( + value: _kResolutionOrigin, groupValue: groupValue, onChanged: onChanged, ffi: widget.ffi, - child: Text('${e.width}x${e.height}'))) - .toList(), + child: Text('Origin'), + ), + RdoMenuButton( + value: _kResolutionFitLocal, + groupValue: groupValue, + onChanged: onChanged, + ffi: widget.ffi, + child: Text('Fit local'), + ), + // RdoMenuButton( + // value: _kResolutionCustom, + // groupValue: groupValue, + // onChanged: onChanged, + // ffi: widget.ffi, + // child: Text('Custom resolution'), + // ), + ] + + resolutions + .map((e) => RdoMenuButton( + value: '${e.width}x${e.height}', + groupValue: groupValue, + onChanged: onChanged, + ffi: widget.ffi, + child: Text('${e.width}x${e.height}'))) + .toList(), child: Text(translate("Resolution"))); } diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 0b0de28a1..1d8e76af4 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -41,6 +41,7 @@ message DisplayInfo { string name = 5; bool online = 6; bool cursor_embedded = 7; + Resolution original_resolution = 8; } message PortForward { @@ -444,6 +445,8 @@ message SwitchDisplay { int32 height = 5; bool cursor_embedded = 6; SupportedResolutions resolutions = 7; + // Do not care about the origin point for now. + Resolution original_resolution = 8; } message PermissionInfo { @@ -501,6 +504,7 @@ message OptionMessage { SupportedDecoding supported_decoding = 10; int32 custom_fps = 11; BoolOption disable_keyboard = 12; + Resolution custom_resolution = 13; } message TestDelay { diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 224709911..16d606c78 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -191,6 +191,12 @@ pub struct Config2 { pub options: HashMap, } +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] +pub struct Resolution { + pub w: i32, + pub h: i32, +} + #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] pub struct PeerConfig { #[serde(default, deserialize_with = "deserialize_vec_u8")] @@ -246,6 +252,9 @@ pub struct PeerConfig { #[serde(flatten)] pub view_only: ViewOnly, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub custom_resolution: Option, + // The other scalar value must before this #[serde(default, deserialize_with = "PeerConfig::deserialize_options")] pub options: HashMap, // not use delete to represent default values diff --git a/src/client.rs b/src/client.rs index 8ec021f55..a43dbcca1 100644 --- a/src/client.rs +++ b/src/client.rs @@ -31,9 +31,11 @@ use hbb_common::{ allow_err, anyhow::{anyhow, Context}, bail, - config::{Config, PeerConfig, PeerInfoSerde, CONNECT_TIMEOUT, READ_TIMEOUT, RELAY_PORT}, + config::{ + Config, PeerConfig, PeerInfoSerde, Resolution, CONNECT_TIMEOUT, READ_TIMEOUT, RELAY_PORT, + }, get_version_number, log, - message_proto::{option_message::BoolOption, *}, + message_proto::{option_message::BoolOption, Resolution as ProtoResolution, *}, protobuf::Message as _, rand, rendezvous_proto::*, @@ -1400,6 +1402,16 @@ impl LoginConfigHandler { msg.disable_clipboard = BoolOption::Yes.into(); n += 1; } + if let Some(r) = self.get_custom_resolution() { + if r.0 > 0 && r.1 > 0 { + msg.custom_resolution = Some(ProtoResolution { + width: r.0, + height: r.1, + ..Default::default() + }) + .into(); + } + } msg.supported_decoding = hbb_common::protobuf::MessageField::some(Decoder::supported_decodings(Some(&self.id))); n += 1; @@ -1571,6 +1583,18 @@ impl LoginConfigHandler { } } + #[inline] + pub fn get_custom_resolution(&self) -> Option<(i32, i32)> { + self.config.custom_resolution.as_ref().map(|r| (r.w, r.h)) + } + + #[inline] + pub fn set_custom_resolution(&mut self, wh: Option<(i32, i32)>) { + let mut config = self.load_config(); + config.custom_resolution = wh.map(|r| Resolution { w: r.0, h: r.1 }); + self.save_config(config); + } + /// Get user name. /// Return the name of the given peer. If the peer has no name, return the name in the config. /// diff --git a/src/core_main.rs b/src/core_main.rs index 4aad72b90..92b56da82 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -1,10 +1,10 @@ #[cfg(not(debug_assertions))] #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::platform::breakdown_callback; +use hbb_common::log; #[cfg(not(debug_assertions))] #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::platform::register_breakdown_handler; -use hbb_common::{allow_err, log}; /// shared by flutter and sciter main function /// @@ -270,7 +270,7 @@ fn init_plugins(args: &Vec) { crate::plugin::init(); } } else if "--service" == (&args[0] as &str) { - allow_err!(crate::plugin::remove_uninstalled()); + hbb_common::allow_err!(crate::plugin::remove_uninstalled()); } } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 147d42502..97a4d9c13 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1888,6 +1888,10 @@ pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType< if FALSE == EnumDisplaySettingsW(NULL as _, ENUM_CURRENT_SETTINGS, &mut dm) { bail!("EnumDisplaySettingsW failed, errno={}", GetLastError()); } + + // to-do: check if need change + println!("REMOVE ME ========================== dm ({},{}) to ({},{})", dm.dmPelsWidth, dm.dmPelsHeight, width, height); + let wname = wide_string(name); let len = if wname.len() <= dm.dmDeviceName.len() { wname.len() diff --git a/src/server/connection.rs b/src/server/connection.rs index e537f744d..d1da5c079 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -179,8 +179,6 @@ pub struct Connection { #[cfg(windows)] portable: PortableState, from_switch: bool, - #[cfg(not(any(target_os = "android", target_os = "ios")))] - origin_resolution: HashMap, voice_call_request_timestamp: Option, audio_input_device_before_voice_call: Option, options_in_login: Option, @@ -306,8 +304,6 @@ impl Connection { #[cfg(windows)] portable: Default::default(), from_switch: false, - #[cfg(not(any(target_os = "android", target_os = "ios")))] - origin_resolution: Default::default(), audio_sender: None, voice_call_request_timestamp: None, audio_input_device_before_voice_call: None, @@ -631,15 +627,18 @@ impl Connection { conn.post_conn_audit(json!({ "action": "close", })); - #[cfg(not(any(target_os = "android", target_os = "ios")))] - conn.reset_resolution(); - ALIVE_CONNS.lock().unwrap().retain(|&c| c != id); + let mut active_conns_lock = ALIVE_CONNS.lock().unwrap(); + active_conns_lock.retain(|&c| c != id); if let Some(s) = conn.server.upgrade() { let mut s = s.write().unwrap(); s.remove_connection(&conn.inner); #[cfg(not(any(target_os = "android", target_os = "ios")))] try_stop_record_cursor_pos(); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if active_conns_lock.is_empty() { + video_service::reset_resolutions(); + } log::info!("#{} connection loop exited", id); } @@ -1893,25 +1892,7 @@ impl Connection { } } #[cfg(not(any(target_os = "android", target_os = "ios")))] - Some(misc::Union::ChangeResolution(r)) => { - if self.keyboard { - if let Ok(name) = video_service::get_current_display_name() { - if let Ok(current) = crate::platform::current_resolution(&name) { - if let Err(e) = crate::platform::change_resolution( - &name, - r.width as _, - r.height as _, - ) { - log::error!("change resolution failed:{:?}", e); - } else { - if !self.origin_resolution.contains_key(&name) { - self.origin_resolution.insert(name, current); - } - } - } - } - } - } + Some(misc::Union::ChangeResolution(r)) => self.change_resolution(&r), #[cfg(all(feature = "flutter", feature = "plugin_framework"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] Some(misc::Union::PluginRequest(p)) => { @@ -1953,6 +1934,24 @@ impl Connection { true } + fn change_resolution(&mut self, r: &Resolution) { + if self.keyboard { + if let Ok(name) = video_service::get_current_display_name() { + if let Err(e) = + crate::platform::change_resolution(&name, r.width as _, r.height as _) + { + log::error!( + "Failed to change resolution '{}' to ({},{}):{:?}", + &name, + r.width, + r.height, + e + ); + } + } + } + } + pub async fn handle_voice_call(&mut self, accepted: bool) { if let Some(ts) = self.voice_call_request_timestamp.take() { let msg = new_voice_call_response(ts.get(), accepted); @@ -2147,6 +2146,11 @@ impl Connection { } } } + 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.keyboard { if let Ok(q) = o.block_input.enum_value() { match q { @@ -2262,20 +2266,6 @@ impl Connection { } } - #[cfg(not(any(target_os = "android", target_os = "ios")))] - fn reset_resolution(&self) { - self.origin_resolution - .iter() - .map(|(name, r)| { - if let Err(e) = - crate::platform::change_resolution(&name, r.width as _, r.height as _) - { - log::error!("change resolution failed:{:?}", e); - } - }) - .count(); - } - #[cfg(not(any(target_os = "android", target_os = "ios")))] fn release_pressed_modifiers(&mut self) { for modifier in self.pressed_modifiers.iter() { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index bee115fbb..00b75c8d2 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -62,8 +62,55 @@ lazy_static::lazy_static! { pub static ref IS_UAC_RUNNING: Arc> = Default::default(); pub static ref IS_FOREGROUND_WINDOW_ELEVATED: Arc> = Default::default(); pub static ref LAST_SYNC_DISPLAYS: Arc>> = Default::default(); + static ref ORIGINAL_RESOLUTIONS: Arc>> = Default::default(); } +#[inline] +pub fn set_original_resolution(display_name: &str, wh: (i32, i32)) -> (i32, i32) { + let mut original_resolutions = ORIGINAL_RESOLUTIONS.write().unwrap(); + match original_resolutions.get(display_name) { + Some(r) => r.clone(), + None => { + original_resolutions.insert(display_name.to_owned(), wh.clone()); + wh + } + } +} + +#[inline] +fn get_original_resolution(display_name: &str) -> Option<(i32, i32)> { + ORIGINAL_RESOLUTIONS + .read() + .unwrap() + .get(display_name) + .map(|r| r.clone()) +} + +#[inline] +fn get_or_set_original_resolution(display_name: &str, wh: (i32, i32)) -> (i32, i32) { + let r = get_original_resolution(display_name); + if let Some(r) = r { + return r; + } + set_original_resolution(display_name, wh) +} + +#[inline] +pub fn reset_resolutions() { + for (name, (w, h)) in ORIGINAL_RESOLUTIONS.read().unwrap().iter() { + if let Err(e) = crate::platform::change_resolution(name, *w as _, *h as _) { + log::error!( + "Failed to reset resolution of display '{}' to ({},{}): {}", + name, + w, + h, + e + ); + } + } +} + +#[inline] fn is_capturer_mag_supported() -> bool { #[cfg(windows)] return scrap::CapturerMag::is_supported(); @@ -71,22 +118,27 @@ fn is_capturer_mag_supported() -> bool { false } +#[inline] pub fn capture_cursor_embedded() -> bool { scrap::is_cursor_embedded() } +#[inline] pub fn notify_video_frame_fetched(conn_id: i32, frame_tm: Option) { FRAME_FETCHED_NOTIFIER.0.send((conn_id, frame_tm)).unwrap() } +#[inline] pub fn set_privacy_mode_conn_id(conn_id: i32) { *PRIVACY_MODE_CONN_ID.lock().unwrap() = conn_id } +#[inline] pub fn get_privacy_mode_conn_id() -> i32 { *PRIVACY_MODE_CONN_ID.lock().unwrap() } +#[inline] pub fn is_privacy_mode_supported() -> bool { #[cfg(windows)] return *IS_CAPTURER_MAGNIFIER_SUPPORTED @@ -491,6 +543,10 @@ fn run(sp: GenericService) -> ResultType<()> { if *SWITCH.lock().unwrap() { log::debug!("Broadcasting display switch"); let mut misc = Misc::new(); + let display_name = get_current_display_name(); + + // to-do: check if is virtual display + misc.set_switch_display(SwitchDisplay { display: c.current as _, x: c.origin.0 as _, @@ -500,12 +556,19 @@ fn run(sp: GenericService) -> ResultType<()> { cursor_embedded: capture_cursor_embedded(), #[cfg(not(any(target_os = "android", target_os = "ios")))] resolutions: Some(SupportedResolutions { - resolutions: get_current_display_name() - .map(|name| crate::platform::resolutions(&name)) + resolutions: display_name + .as_ref() + .map(|name| crate::platform::resolutions(name)) .unwrap_or(vec![]), ..SupportedResolutions::default() }) .into(), + original_resolution: Some(update_get_original_resolution( + &display_name.unwrap_or_default(), + c.width, + c.height, + )) + .into(), ..Default::default() }); let mut msg_out = Message::new(); @@ -820,6 +883,16 @@ pub fn handle_one_frame_encoded( Ok(send_conn_ids) } +#[inline] +fn update_get_original_resolution(display_name: &str, w: usize, h: usize) -> Resolution { + let wh = get_or_set_original_resolution(display_name, (w as _, h as _)); + Resolution { + width: wh.0, + height: wh.1, + ..Default::default() + } +} + pub(super) fn get_displays_2(all: &Vec) -> (usize, Vec) { let mut displays = Vec::new(); let mut primary = 0; @@ -835,6 +908,12 @@ pub(super) fn get_displays_2(all: &Vec) -> (usize, Vec) { name: d.name(), online: d.is_online(), cursor_embedded: false, + original_resolution: Some(update_get_original_resolution( + &d.name(), + d.width(), + d.height(), + )) + .into(), ..Default::default() }); } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index a804484a0..3d5442ae0 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -88,10 +88,7 @@ impl Session { } pub fn is_port_forward(&self) -> bool { - let conn_type = self.lc - .read() - .unwrap() - .conn_type; + let conn_type = self.lc.read().unwrap().conn_type; conn_type == ConnType::PORT_FORWARD || conn_type == ConnType::RDP } @@ -833,6 +830,10 @@ impl Session { } pub fn change_resolution(&self, width: i32, height: i32) { + self.lc + .write() + .unwrap() + .set_custom_resolution(Some((width, height))); let mut misc = Misc::new(); misc.set_change_resolution(Resolution { width, From 07500013ff67d035d31d3bef71194c5d5a6f5cea Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 18 May 2023 16:17:51 +0800 Subject: [PATCH 02/11] resolution, mid commit, to debug Signed-off-by: fufesou --- .../lib/desktop/pages/connection_page.dart | 2 - .../lib/desktop/widgets/remote_toolbar.dart | 456 +++++++++++++----- flutter/lib/models/model.dart | 49 +- src/client/io_loop.rs | 8 + src/flutter.rs | 10 + src/flutter_ffi.rs | 22 +- src/ui_session_interface.rs | 9 +- 7 files changed, 394 insertions(+), 162 deletions(-) diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index 3f6bb7d16..d709d3c52 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -19,8 +19,6 @@ import '../../common/widgets/peer_tab_page.dart'; import '../../models/platform_model.dart'; import '../widgets/button.dart'; -import 'package:flutter_hbb/common/widgets/dialog.dart'; - /// Connection page for connecting to a remote peer. class ConnectionPage extends StatefulWidget { const ConnectionPage({Key? key}) : super(key: key); diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 2575ddadd..da91e0dce 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -664,71 +664,24 @@ class _ControlMenu extends StatelessWidget { } } -class _DisplayMenu extends StatefulWidget { +class ScreenAdjustor { final String id; final FFI ffi; - final MenubarState state; - final Function(bool) setFullscreen; - final Widget pluginItem; - _DisplayMenu( - {Key? key, - required this.id, - required this.ffi, - required this.state, - required this.setFullscreen}) - : pluginItem = LocationItem.createLocationItem( - id, - ffi, - kLocationClientRemoteToolbarDisplay, - true, - ), - super(key: key); - - @override - State<_DisplayMenu> createState() => _DisplayMenuState(); -} - -class _DisplayMenuState extends State<_DisplayMenu> { + final VoidCallback cbExitFullscreen; window_size.Screen? _screen; + ScreenAdjustor({ + required this.id, + required this.ffi, + required this.cbExitFullscreen, + }); + bool get isFullscreen => stateGlobal.fullscreen; - int get windowId => stateGlobal.windowId; - Map get perms => widget.ffi.ffiModel.permissions; - RxBool _isOrignalResolution = true.obs; - RxBool _isFitLocalResolution = false.obs; - - PeerInfo get pi => widget.ffi.ffiModel.pi; - FfiModel get ffiModel => widget.ffi.ffiModel; - FFI get ffi => widget.ffi; - String get id => widget.id; - - @override - Widget build(BuildContext context) { - _updateScreen(); - return _IconSubmenuButton( - tooltip: 'Display Settings', - svg: "assets/display.svg", - ffi: widget.ffi, - color: _MenubarTheme.blueColor, - hoverColor: _MenubarTheme.hoverBlueColor, - menuChildren: [ - adjustWindow(), - viewStyle(), - scrollStyle(), - imageQuality(), - codec(), - resolutions(), - Divider(), - toggles(), - widget.pluginItem, - ]); - } - adjustWindow() { return futureBuilder( - future: _isWindowCanBeAdjusted(), + future: isWindowCanBeAdjusted(), hasData: (data) { final visible = data as bool; if (!visible) return Offstage(); @@ -736,18 +689,18 @@ class _DisplayMenuState extends State<_DisplayMenu> { children: [ MenuButton( child: Text(translate('Adjust Window')), - onPressed: _doAdjustWindow, - ffi: widget.ffi), + onPressed: doAdjustWindow, + ffi: ffi), Divider(), ], ); }); } - _doAdjustWindow() async { - await _updateScreen(); + doAdjustWindow() async { + await updateScreen(); if (_screen != null) { - widget.setFullscreen(false); + cbExitFullscreen(); double scale = _screen!.scaleFactor; final wndRect = await WindowController.fromWindowId(windowId).getFrame(); final mediaSize = MediaQueryData.fromWindow(ui.window).size; @@ -758,7 +711,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { double magicHeight = wndRect.bottom - wndRect.top - mediaSize.height * scale; - final canvasModel = widget.ffi.canvasModel; + final canvasModel = ffi.canvasModel; final width = (canvasModel.getDisplayWidth() * canvasModel.scale + CanvasModel.leftToEdge + CanvasModel.rightToEdge) * @@ -793,7 +746,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { } } - _updateScreen() async { + updateScreen() async { final v = await rustDeskWinManager.call( WindowType.Main, kWindowGetWindowInfo, ''); final String valueStr = v; @@ -813,8 +766,8 @@ class _DisplayMenuState extends State<_DisplayMenu> { } } - Future _isWindowCanBeAdjusted() async { - final viewStyle = await bind.sessionGetViewStyle(id: widget.id) ?? ''; + Future isWindowCanBeAdjusted() async { + final viewStyle = await bind.sessionGetViewStyle(id: id) ?? ''; if (viewStyle != kRemoteViewStyleOriginal) { return false; } @@ -833,7 +786,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { selfHeight = _screen!.frame.height; } - final canvasModel = widget.ffi.canvasModel; + final canvasModel = ffi.canvasModel; final displayWidth = canvasModel.getDisplayWidth(); final displayHeight = canvasModel.getDisplayHeight(); final requiredWidth = @@ -843,6 +796,77 @@ class _DisplayMenuState extends State<_DisplayMenu> { return selfWidth > (requiredWidth * scale) && selfHeight > (requiredHeight * scale); } +} + +class _DisplayMenu extends StatefulWidget { + final String id; + final FFI ffi; + final MenubarState state; + final Function(bool) setFullscreen; + final Widget pluginItem; + _DisplayMenu( + {Key? key, + required this.id, + required this.ffi, + required this.state, + required this.setFullscreen}) + : pluginItem = LocationItem.createLocationItem( + id, + ffi, + kLocationClientRemoteToolbarDisplay, + true, + ), + super(key: key); + + @override + State<_DisplayMenu> createState() => _DisplayMenuState(); +} + +class _DisplayMenuState extends State<_DisplayMenu> { + late final ScreenAdjustor _screenAdjustor = ScreenAdjustor( + id: widget.id, + ffi: widget.ffi, + cbExitFullscreen: () => widget.setFullscreen(false), + ); + + bool get isFullscreen => stateGlobal.fullscreen; + int get windowId => stateGlobal.windowId; + Map get perms => widget.ffi.ffiModel.permissions; + PeerInfo get pi => widget.ffi.ffiModel.pi; + FfiModel get ffiModel => widget.ffi.ffiModel; + FFI get ffi => widget.ffi; + String get id => widget.id; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + _screenAdjustor.updateScreen(); + return _IconSubmenuButton( + tooltip: 'Display Settings', + svg: "assets/display.svg", + ffi: widget.ffi, + color: _MenubarTheme.blueColor, + hoverColor: _MenubarTheme.hoverBlueColor, + menuChildren: [ + _screenAdjustor.adjustWindow(), + viewStyle(), + scrollStyle(), + imageQuality(), + codec(), + _ResolutionsMenu( + id: widget.id, + ffi: widget.ffi, + screenAdjustor: _screenAdjustor, + ), + Divider(), + toggles(), + widget.pluginItem, + ]); + } viewStyle() { return futureBuilder( @@ -941,70 +965,6 @@ class _DisplayMenuState extends State<_DisplayMenu> { }); } - resolutions() { - final resolutions = pi.resolutions; - final visible = ffiModel.keyboard && resolutions.length > 1; - if (!visible) return Offstage(); - final display = ffiModel.display; - final groupValue = "${display.width}x${display.height}"; - onChanged(String? value) async { - if (value == null) return; - - final list = value.split('x'); - if (list.length == 2) { - final w = int.tryParse(list[0]); - final h = int.tryParse(list[1]); - if (w != null && h != null) { - await bind.sessionChangeResolution( - id: widget.id, width: w, height: h); - Future.delayed(Duration(seconds: 3), () async { - final display = ffiModel.display; - if (w == display.width && h == display.height) { - if (await _isWindowCanBeAdjusted()) { - _doAdjustWindow(); - } - } - }); - } - } - } - - return _SubmenuButton( - ffi: widget.ffi, - menuChildren: [ - RdoMenuButton( - value: _kResolutionOrigin, - groupValue: groupValue, - onChanged: onChanged, - ffi: widget.ffi, - child: Text('Origin'), - ), - RdoMenuButton( - value: _kResolutionFitLocal, - groupValue: groupValue, - onChanged: onChanged, - ffi: widget.ffi, - child: Text('Fit local'), - ), - // RdoMenuButton( - // value: _kResolutionCustom, - // groupValue: groupValue, - // onChanged: onChanged, - // ffi: widget.ffi, - // child: Text('Custom resolution'), - // ), - ] + - resolutions - .map((e) => RdoMenuButton( - value: '${e.width}x${e.height}', - groupValue: groupValue, - onChanged: onChanged, - ffi: widget.ffi, - child: Text('${e.width}x${e.height}'))) - .toList(), - child: Text(translate("Resolution"))); - } - toggles() { return futureBuilder( future: toolbarDisplayToggle(context, id, ffi), @@ -1023,6 +983,242 @@ class _DisplayMenuState extends State<_DisplayMenu> { } } +class _ResolutionsMenu extends StatefulWidget { + final String id; + final FFI ffi; + final ScreenAdjustor screenAdjustor; + + _ResolutionsMenu({ + Key? key, + required this.id, + required this.ffi, + required this.screenAdjustor, + }) : super(key: key); + + @override + State<_ResolutionsMenu> createState() => _ResolutionsMenuState(); +} + +class _ResolutionsMenuState extends State<_ResolutionsMenu> { + String _groupValue = ''; + Resolution? _localResolution; + late final _customWidth = + TextEditingController(text: display.width.toString()); + late final _customHeight = + TextEditingController(text: display.height.toString()); + + PeerInfo get pi => widget.ffi.ffiModel.pi; + FfiModel get ffiModel => widget.ffi.ffiModel; + Display get display => ffiModel.display; + List get resolutions => pi.resolutions; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + final visible = ffiModel.keyboard && resolutions.length > 1; + if (!visible) return Offstage(); + _groupValue = "${display.width}x${display.height}"; + + _getLocalResolution(); + return _SubmenuButton( + ffi: widget.ffi, + menuChildren: [ + _OriginalResolutionMenuButton(), + _FitLocalResolutionMenuButton(), + _customResolutionMenuButton(), + ] + + _supportedResolutionMenuButtons(), + child: Text(translate("Resolution"))); + } + + _getLocalResolution() { + _localResolution = null; + final String currentDisplay = bind.mainGetCurrentDisplay(); + if (currentDisplay.isNotEmpty) { + try { + final display = json.decode(currentDisplay); + if (display['w'] != null && display['h'] != null) { + _localResolution = Resolution(display['w'], display['h']); + } + } catch (e) { + debugPrint('Failed to decode $currentDisplay, $e'); + } + } + } + + _onChanged(String? value) async { + if (value == null) return; + + int? w; + int? h; + if (value == _kResolutionOrigin) { + w = display.originalWidth; + h = display.originalHeight; + } else if (value == _kResolutionFitLocal) { + final resolution = _getBestFitResolution(); + if (resolution != null) { + w = resolution.width; + h = resolution.height; + } + } else if (value == _kResolutionCustom) { + debugPrint( + 'REMOVE ME ======================= ${_customWidth.value} ${_customHeight.value}'); + w = int.tryParse(_customWidth.value as String); + h = int.tryParse(_customHeight.value as String); + } else { + final list = value.split('x'); + if (list.length == 2) { + w = int.tryParse(list[0]); + h = int.tryParse(list[1]); + } + } + + if (w != null && h != null) { + await bind.sessionChangeResolution( + id: widget.id, + width: w, + height: h, + ); + Future.delayed(Duration(seconds: 3), () async { + final display = ffiModel.display; + if (w == display.width && h == display.height) { + if (await widget.screenAdjustor.isWindowCanBeAdjusted()) { + widget.screenAdjustor.doAdjustWindow(); + } + } + }); + } + } + + Widget _OriginalResolutionMenuButton() { + return Offstage( + offstage: display.isOriginalResolution, + child: RdoMenuButton( + value: _kResolutionOrigin, + groupValue: _groupValue, + onChanged: _onChanged, + ffi: widget.ffi, + child: Text( + '${translate('Original')} ${display.originalWidth}x${display.originalHeight}'), + ), + ); + } + + Widget _FitLocalResolutionMenuButton() { + return Offstage( + offstage: _isRemoteResolutionFitLocal(), + child: RdoMenuButton( + value: _kResolutionFitLocal, + groupValue: _groupValue, + onChanged: _onChanged, + ffi: widget.ffi, + child: Text( + '${translate('Fit Local')} ${display.originalWidth}x${display.originalHeight}'), + ), + ); + } + + List _supportedResolutionMenuButtons() => resolutions + .map((e) => RdoMenuButton( + value: '${e.width}x${e.height}', + groupValue: _groupValue, + onChanged: _onChanged, + ffi: widget.ffi, + child: Text('${e.width}x${e.height}'))) + .toList(); + + Widget _customResolutionMenuButton() { + return Offstage( + offstage: _isRemoteResolutionFitLocal(), + child: RdoMenuButton( + value: _kResolutionCustom, + groupValue: _groupValue, + onChanged: _onChanged, + ffi: widget.ffi, + child: _customResolutionWidget(), + ), + ); + } + + Widget _customResolutionWidget() { + return Column( + children: [ + Text(translate('Custom')), + SizedBox( + width: 5, + ), + _resolutionInput(_customWidth), + SizedBox( + width: 3, + ), + Text('x'), + SizedBox( + width: 3, + ), + _resolutionInput(_customHeight), + ], + ); + } + + TextField _resolutionInput(TextEditingController controller) { + return TextField( + decoration: InputDecoration( + border: InputBorder.none, + isDense: true, + ), + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(4), + FilteringTextInputFormatter.allow(RegExp(r'[0-9]')), + ], + controller: controller, + ); + } + + Resolution? _getBestFitResolution() { + if (_localResolution == null) { + return null; + } + + squareDistance(Resolution lhs, Resolution rhs) => + (lhs.width - rhs.width) * (lhs.width - rhs.width) + + (lhs.height - rhs.height) * (lhs.height - rhs.height); + + Resolution? res; + for (final r in resolutions) { + if (r.width <= _localResolution!.width && + r.height <= _localResolution!.height) { + if (res == null) { + res = r; + } else { + if (squareDistance(r, _localResolution!) < + squareDistance(res, _localResolution!)) { + res = r; + } + } + } + } + return res; + } + + bool _isRemoteResolutionFitLocal() { + if (_localResolution == null) { + return true; + } + final bestFitResolution = _getBestFitResolution(); + if (bestFitResolution == null) { + return true; + } + return bestFitResolution.width == display.width && + bestFitResolution.height == display.height; + } +} + class _KeyboardMenu extends StatelessWidget { final String id; final FFI ffi; diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index e94452bd7..a57a51752 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -295,11 +295,15 @@ class FfiModel with ChangeNotifier { handleSwitchDisplay(Map evt, String peerId) { _pi.currentDisplay = int.parse(evt['display']); var newDisplay = Display(); - newDisplay.x = double.parse(evt['x']); - newDisplay.y = double.parse(evt['y']); - newDisplay.width = int.parse(evt['width']); - newDisplay.height = int.parse(evt['height']); - newDisplay.cursorEmbedded = int.parse(evt['cursor_embedded']) == 1; + newDisplay.x = double.tryParse(evt['x']) ?? newDisplay.x; + newDisplay.y = double.tryParse(evt['y']) ?? newDisplay.y; + newDisplay.width = int.tryParse(evt['width']) ?? newDisplay.width; + newDisplay.height = int.tryParse(evt['height']) ?? newDisplay.height; + newDisplay.cursorEmbedded = int.tryParse(evt['cursor_embedded']) == 1; + newDisplay.originalWidth = + int.tryParse(evt['original_width']) ?? newDisplay.originalWidth; + newDisplay.originalHeight = + int.tryParse(evt['original_height']) ?? newDisplay.originalHeight; _updateCurDisplay(peerId, newDisplay); @@ -466,14 +470,7 @@ class FfiModel with ChangeNotifier { _pi.displays = []; List displays = json.decode(evt['displays']); for (int i = 0; i < displays.length; ++i) { - Map d0 = displays[i]; - var d = Display(); - d.x = d0['x'].toDouble(); - d.y = d0['y'].toDouble(); - d.width = d0['width']; - d.height = d0['height']; - d.cursorEmbedded = d0['cursor_embedded'] == 1; - _pi.displays.add(d); + _pi.displays.add(evtToDisplay(displays[i])); } stateGlobal.displaysCount.value = _pi.displays.length; if (_pi.currentDisplay < _pi.displays.length) { @@ -533,20 +530,25 @@ class FfiModel with ChangeNotifier { } } + Display evtToDisplay(Map evt) { + var d = Display(); + d.x = evt['x']?.toDouble() ?? d.x; + d.y = evt['y']?.toDouble() ?? d.y; + d.width = evt['width'] ?? d.width; + d.height = evt['height'] ?? d.height; + d.cursorEmbedded = evt['cursor_embedded'] == 1; + d.originalWidth = evt['original_width'] ?? d.originalWidth; + d.originalHeight = evt['original_height'] ?? d.originalHeight; + return d; + } + /// Handle the peer info synchronization event based on [evt]. handleSyncPeerInfo(Map evt, String peerId) async { if (evt['displays'] != null) { List displays = json.decode(evt['displays']); List newDisplays = []; for (int i = 0; i < displays.length; ++i) { - Map d0 = displays[i]; - var d = Display(); - d.x = d0['x'].toDouble(); - d.y = d0['y'].toDouble(); - d.width = d0['width']; - d.height = d0['height']; - d.cursorEmbedded = d0['cursor_embedded'] == 1; - newDisplays.add(d); + newDisplays.add(evtToDisplay(displays[i])); } _pi.displays = newDisplays; stateGlobal.displaysCount.value = _pi.displays.length; @@ -1718,6 +1720,8 @@ class Display { int width = 0; int height = 0; bool cursorEmbedded = false; + int originalWidth = 0; + int originalHeight = 0; Display() { width = (isDesktop || isWebDesktop) @@ -1740,6 +1744,9 @@ class Display { other.width == width && other.height == height && other.cursorEmbedded == cursorEmbedded; + + bool get isOriginalResolution => + width == originalWidth && height == originalHeight; } class Resolution { diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index d38e7f104..597c720ab 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1211,6 +1211,14 @@ 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.rs b/src/flutter.rs index f6461b744..1748d63df 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -286,6 +286,8 @@ impl FlutterHandler { h.insert("width", d.width); h.insert("height", d.height); h.insert("cursor_embedded", if d.cursor_embedded { 1 } else { 0 }); + h.insert("original_width", d.original_resolution.width); + h.insert("original_height", d.original_resolution.height); msg_vec.push(h); } serde_json::ser::to_string(&msg_vec).unwrap_or("".to_owned()) @@ -618,6 +620,14 @@ impl InvokeUiSession for FlutterHandler { .to_string(), ), ("resolutions", &resolutions), + ( + "original_width", + &display.original_resolution.width.to_string(), + ), + ( + "original_height", + &display.original_resolution.height.to_string(), + ), ], ); } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 9aadca1bc..d072c58a3 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -880,6 +880,18 @@ pub fn main_handle_relay_id(id: String) -> String { handle_relay_id(id) } +pub fn main_get_current_display() -> SyncReturn { + let display_info = match crate::video_service::get_current_display() { + Ok((_, _, display)) => serde_json::to_string(&HashMap::from([ + ("w", display.width()), + ("h", display.height()), + ])) + .unwrap_or_default(), + Err(..) => "".to_string(), + }; + SyncReturn(display_info) +} + pub fn session_add_port_forward( id: String, local_port: i32, @@ -1426,10 +1438,10 @@ pub fn plugin_event(_id: String, _peer: String, _event: Vec) { } } -pub fn plugin_register_event_stream(id: String, event2ui: StreamSink) { +pub fn plugin_register_event_stream(_id: String, _event2ui: StreamSink) { #[cfg(feature = "plugin_framework")] { - crate::plugin::native_handlers::session::session_register_event_stream(id, event2ui); + crate::plugin::native_handlers::session::session_register_event_stream(_id, _event2ui); } } @@ -1577,16 +1589,16 @@ pub fn plugin_list_reload() { } } -pub fn plugin_install(id: String, b: bool) { +pub fn plugin_install(_id: String, _b: bool) { #[cfg(feature = "plugin_framework")] #[cfg(not(any(target_os = "android", target_os = "ios")))] { - if b { + if _b { if let Err(e) = crate::plugin::install_plugin(&id) { log::error!("Failed to install plugin '{}': {}", id, e); } } else { - crate::plugin::uninstall_plugin(&id, true); + crate::plugin::uninstall_plugin(&_id, true); } } } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 3d5442ae0..8a43642d8 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -829,11 +829,12 @@ impl Session { } } + #[inline] + pub fn set_custom_resolution(&mut self, wh: Option<(i32, i32)>) { + self.lc.write().unwrap().set_custom_resolution(wh); + } + pub fn change_resolution(&self, width: i32, height: i32) { - self.lc - .write() - .unwrap() - .set_custom_resolution(Some((width, height))); let mut misc = Misc::new(); misc.set_change_resolution(Resolution { width, From c6ccee67aae888584b81d8928c53802915806139 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 18 May 2023 21:25:48 +0800 Subject: [PATCH 03/11] remember resolution, mid commit Signed-off-by: fufesou --- .../lib/desktop/widgets/remote_toolbar.dart | 57 +++++----- flutter/lib/models/model.dart | 21 +++- src/platform/windows.rs | 75 ++++++++----- src/server/video_service.rs | 105 ++++++++++++------ 4 files changed, 165 insertions(+), 93 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index da91e0dce..6d48777ed 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1019,17 +1019,22 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { @override Widget build(BuildContext context) { - final visible = ffiModel.keyboard && resolutions.length > 1; + final isVirtualDisplay = display.isVirtualDisplayResolution; + final visible = + ffiModel.keyboard && (isVirtualDisplay || resolutions.length > 1); if (!visible) return Offstage(); - _groupValue = "${display.width}x${display.height}"; - + _groupValue = '${display.width}x${display.height}'; _getLocalResolution(); + final showOriginalBtn = + display.isOriginalResolutionSet && !display.isOriginalResolution; + final showFitLocalBtn = !_isRemoteResolutionFitLocal(); + return _SubmenuButton( ffi: widget.ffi, menuChildren: [ - _OriginalResolutionMenuButton(), - _FitLocalResolutionMenuButton(), - _customResolutionMenuButton(), + _OriginalResolutionMenuButton(showOriginalBtn), + _FitLocalResolutionMenuButton(showFitLocalBtn), + _customResolutionMenuButton(isVirtualDisplay), ] + _supportedResolutionMenuButtons(), child: Text(translate("Resolution"))); @@ -1065,8 +1070,6 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { h = resolution.height; } } else if (value == _kResolutionCustom) { - debugPrint( - 'REMOVE ME ======================= ${_customWidth.value} ${_customHeight.value}'); w = int.tryParse(_customWidth.value as String); h = int.tryParse(_customHeight.value as String); } else { @@ -1094,9 +1097,9 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { } } - Widget _OriginalResolutionMenuButton() { + Widget _OriginalResolutionMenuButton(bool showOriginalBtn) { return Offstage( - offstage: display.isOriginalResolution, + offstage: !showOriginalBtn, child: RdoMenuButton( value: _kResolutionOrigin, groupValue: _groupValue, @@ -1108,16 +1111,16 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { ); } - Widget _FitLocalResolutionMenuButton() { + Widget _FitLocalResolutionMenuButton(bool showFitLocalBtn) { return Offstage( - offstage: _isRemoteResolutionFitLocal(), + offstage: !showFitLocalBtn, child: RdoMenuButton( value: _kResolutionFitLocal, groupValue: _groupValue, onChanged: _onChanged, ffi: widget.ffi, child: Text( - '${translate('Fit Local')} ${display.originalWidth}x${display.originalHeight}'), + '${translate('Fit Local')} ${_localResolution?.width ?? 0}x${_localResolution?.height ?? 0}'), ), ); } @@ -1131,9 +1134,9 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { child: Text('${e.width}x${e.height}'))) .toList(); - Widget _customResolutionMenuButton() { + Widget _customResolutionMenuButton(bool showCustomBtn) { return Offstage( - offstage: _isRemoteResolutionFitLocal(), + offstage: !showCustomBtn, child: RdoMenuButton( value: _kResolutionCustom, groupValue: _groupValue, @@ -1148,18 +1151,18 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { return Column( children: [ Text(translate('Custom')), - SizedBox( + Container( width: 5, ), - _resolutionInput(_customWidth), - SizedBox( + // _resolutionInput(_customWidth), + Container( width: 3, ), Text('x'), - SizedBox( + Container( width: 3, ), - _resolutionInput(_customHeight), + // _resolutionInput(_customHeight), ], ); } @@ -1185,21 +1188,21 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { return null; } + if (display.isVirtualDisplayResolution) { + return _localResolution!; + } + squareDistance(Resolution lhs, Resolution rhs) => (lhs.width - rhs.width) * (lhs.width - rhs.width) + (lhs.height - rhs.height) * (lhs.height - rhs.height); - Resolution? res; + Resolution res = Resolution(display.width, display.height); for (final r in resolutions) { if (r.width <= _localResolution!.width && r.height <= _localResolution!.height) { - if (res == null) { + if (squareDistance(r, _localResolution!) < + squareDistance(res, _localResolution!)) { res = r; - } else { - if (squareDistance(r, _localResolution!) < - squareDistance(res, _localResolution!)) { - res = r; - } } } } diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index a57a51752..294b348b9 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -301,9 +301,9 @@ class FfiModel with ChangeNotifier { newDisplay.height = int.tryParse(evt['height']) ?? newDisplay.height; newDisplay.cursorEmbedded = int.tryParse(evt['cursor_embedded']) == 1; newDisplay.originalWidth = - int.tryParse(evt['original_width']) ?? newDisplay.originalWidth; + int.tryParse(evt['original_width']) ?? kInvalidResolutionValue; newDisplay.originalHeight = - int.tryParse(evt['original_height']) ?? newDisplay.originalHeight; + int.tryParse(evt['original_height']) ?? kInvalidResolutionValue; _updateCurDisplay(peerId, newDisplay); @@ -537,8 +537,8 @@ class FfiModel with ChangeNotifier { d.width = evt['width'] ?? d.width; d.height = evt['height'] ?? d.height; d.cursorEmbedded = evt['cursor_embedded'] == 1; - d.originalWidth = evt['original_width'] ?? d.originalWidth; - d.originalHeight = evt['original_height'] ?? d.originalHeight; + d.originalWidth = evt['original_width'] ?? kInvalidResolutionValue; + d.originalHeight = evt['original_height'] ?? kInvalidResolutionValue; return d; } @@ -1714,14 +1714,17 @@ class FFI { } } +const kInvalidResolutionValue = -1; +const kVirtualDisplayResolutionValue = 0; + class Display { double x = 0; double y = 0; int width = 0; int height = 0; bool cursorEmbedded = false; - int originalWidth = 0; - int originalHeight = 0; + int originalWidth = kInvalidResolutionValue; + int originalHeight = kInvalidResolutionValue; Display() { width = (isDesktop || isWebDesktop) @@ -1745,6 +1748,12 @@ class Display { other.height == height && other.cursorEmbedded == cursorEmbedded; + bool get isOriginalResolutionSet => + originalWidth != kInvalidResolutionValue && + originalHeight != kInvalidResolutionValue; + bool get isVirtualDisplayResolution => + originalWidth == kVirtualDisplayResolutionValue && + originalHeight == kVirtualDisplayResolutionValue; bool get isOriginalResolution => width == originalWidth && height == originalHeight; } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 97a4d9c13..431bfd929 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -56,6 +56,10 @@ use windows_service::{ use winreg::enums::*; use winreg::RegKey; +// This string is defined here. +// https://github.com/fufesou/RustDeskIddDriver/blob/b370aad3f50028b039aad211df60c8051c4a64d6/RustDeskIddDriver/RustDeskIddDriver.inf#LL73C1-L73C40 +const IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device\0"; + pub fn get_cursor_pos() -> Option<(i32, i32)> { unsafe { #[allow(invalid_value)] @@ -1831,21 +1835,25 @@ pub fn set_path_permission(dir: &PathBuf, permission: &str) -> ResultType<()> { Ok(()) } +#[inline] +fn str_to_device_name(name: &str) -> [u16; 32] { + let mut device_name: Vec = wide_string(name); + if device_name.len() < 32 { + device_name.resize(32, 0); + } + let mut result = [0; 32]; + result.copy_from_slice(&device_name[..32]); + result +} + pub fn resolutions(name: &str) -> Vec { unsafe { let mut dm: DEVMODEW = std::mem::zeroed(); - let wname = wide_string(name); - let len = if wname.len() <= dm.dmDeviceName.len() { - wname.len() - } else { - dm.dmDeviceName.len() - }; - std::ptr::copy_nonoverlapping(wname.as_ptr(), dm.dmDeviceName.as_mut_ptr(), len); - dm.dmSize = std::mem::size_of::() as _; let mut v = vec![]; let mut num = 0; + let device_name = str_to_device_name(name); loop { - if EnumDisplaySettingsW(NULL as _, num, &mut dm) == 0 { + if EnumDisplaySettingsW(device_name.as_ptr(), num, &mut dm) == 0 { break; } let r = Resolution { @@ -1866,8 +1874,8 @@ pub fn current_resolution(name: &str) -> ResultType { unsafe { let mut dm: DEVMODEW = std::mem::zeroed(); dm.dmSize = std::mem::size_of::() as _; - let wname = wide_string(name); - if EnumDisplaySettingsW(wname.as_ptr(), ENUM_CURRENT_SETTINGS, &mut dm) == 0 { + let device_name = str_to_device_name(name); + if EnumDisplaySettingsW(device_name.as_ptr(), ENUM_CURRENT_SETTINGS, &mut dm) == 0 { bail!( "failed to get currrent resolution, errno={}", GetLastError() @@ -1882,29 +1890,46 @@ pub fn current_resolution(name: &str) -> ResultType { } } +pub fn is_virtual_display(name: &str) -> ResultType { + let device_name = str_to_device_name(name); + let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::zeroed() }; + dd.cb = std::mem::size_of::() as _; + let ok = unsafe { EnumDisplayDevicesW(device_name.as_ptr(), 0, &mut dd as _, 0) }; + if ok == FALSE { + bail!( + "enumerate display devices with device name '{}', errno {}", + name, + unsafe { GetLastError() } + ); + } + match std::string::String::from_utf16(&dd.DeviceString) { + Ok(s) => Ok(&s[..IDD_DEVICE_STRING.len()] == IDD_DEVICE_STRING), + Err(e) => bail!( + "convert the device string of '{}' to string: {}", + name, + e + ), + } +} + pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType<()> { + let device_name = str_to_device_name(name); unsafe { let mut dm: DEVMODEW = std::mem::zeroed(); - if FALSE == EnumDisplaySettingsW(NULL as _, ENUM_CURRENT_SETTINGS, &mut dm) { + if FALSE == EnumDisplaySettingsW(device_name.as_ptr() as _, ENUM_CURRENT_SETTINGS, &mut dm) + { bail!("EnumDisplaySettingsW failed, errno={}", GetLastError()); } - - // to-do: check if need change - println!("REMOVE ME ========================== dm ({},{}) to ({},{})", dm.dmPelsWidth, dm.dmPelsHeight, width, height); - - let wname = wide_string(name); - let len = if wname.len() <= dm.dmDeviceName.len() { - wname.len() - } else { - dm.dmDeviceName.len() - }; - std::ptr::copy_nonoverlapping(wname.as_ptr(), dm.dmDeviceName.as_mut_ptr(), len); - dm.dmSize = std::mem::size_of::() as _; + // dmPelsWidth and dmPelsHeight is the same to width and height + // Because this process is running in dpi awareness mode. + if dm.dmPelsWidth == width as u32 && dm.dmPelsHeight == height as u32 { + return Ok(()); + } dm.dmPelsWidth = width as _; dm.dmPelsHeight = height as _; dm.dmFields = DM_PELSHEIGHT | DM_PELSWIDTH; let res = ChangeDisplaySettingsExW( - wname.as_ptr(), + device_name.as_ptr(), &mut dm, NULL as _, CDS_UPDATEREGISTRY | CDS_GLOBAL | CDS_RESET, diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 00b75c8d2..5a63a60a6 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -25,9 +25,12 @@ use crate::virtual_display_manager; use crate::{platform::windows::is_process_consent_running, privacy_win_mag}; #[cfg(windows)] use hbb_common::get_version_number; -use hbb_common::tokio::sync::{ - mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, - Mutex as TokioMutex, +use hbb_common::{ + protobuf::MessageField, + tokio::sync::{ + mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, + Mutex as TokioMutex, + }, }; #[cfg(not(windows))] use scrap::Capturer; @@ -65,8 +68,9 @@ lazy_static::lazy_static! { static ref ORIGINAL_RESOLUTIONS: Arc>> = Default::default(); } +// Not virtual display #[inline] -pub fn set_original_resolution(display_name: &str, wh: (i32, i32)) -> (i32, i32) { +fn set_original_resolution_(display_name: &str, wh: (i32, i32)) -> (i32, i32) { let mut original_resolutions = ORIGINAL_RESOLUTIONS.write().unwrap(); match original_resolutions.get(display_name) { Some(r) => r.clone(), @@ -77,8 +81,9 @@ pub fn set_original_resolution(display_name: &str, wh: (i32, i32)) -> (i32, i32) } } +// Not virtual display #[inline] -fn get_original_resolution(display_name: &str) -> Option<(i32, i32)> { +fn get_original_resolution_(display_name: &str) -> Option<(i32, i32)> { ORIGINAL_RESOLUTIONS .read() .unwrap() @@ -86,13 +91,25 @@ fn get_original_resolution(display_name: &str) -> Option<(i32, i32)> { .map(|r| r.clone()) } +// Not virtual display #[inline] -fn get_or_set_original_resolution(display_name: &str, wh: (i32, i32)) -> (i32, i32) { - let r = get_original_resolution(display_name); +fn get_or_set_original_resolution_(display_name: &str, wh: (i32, i32)) -> (i32, i32) { + let r = get_original_resolution_(display_name); if let Some(r) = r { return r; } - set_original_resolution(display_name, wh) + set_original_resolution_(display_name, wh) +} + +// Not virtual display +#[inline] +fn update_get_original_resolution_(display_name: &str, w: usize, h: usize) -> Resolution { + let wh = get_or_set_original_resolution_(display_name, (w as _, h as _)); + Resolution { + width: wh.0, + height: wh.1, + ..Default::default() + } } #[inline] @@ -543,10 +560,13 @@ fn run(sp: GenericService) -> ResultType<()> { if *SWITCH.lock().unwrap() { log::debug!("Broadcasting display switch"); let mut misc = Misc::new(); - let display_name = get_current_display_name(); - - // to-do: check if is virtual display - + let display_name = get_current_display_name().unwrap_or_default(); + println!( + "REMOVE ME ============================ display_name: {:?}, is_virtual: {}", + display_name, + is_virtual_display(&display_name) + ); + let original_resolution = get_original_resolution(&display_name, c.width, c.height); misc.set_switch_display(SwitchDisplay { display: c.current as _, x: c.origin.0 as _, @@ -556,19 +576,15 @@ fn run(sp: GenericService) -> ResultType<()> { cursor_embedded: capture_cursor_embedded(), #[cfg(not(any(target_os = "android", target_os = "ios")))] resolutions: Some(SupportedResolutions { - resolutions: display_name - .as_ref() - .map(|name| crate::platform::resolutions(name)) - .unwrap_or(vec![]), + resolutions: if display_name.is_empty() { + vec![] + } else { + crate::platform::resolutions(&display_name) + }, ..SupportedResolutions::default() }) .into(), - original_resolution: Some(update_get_original_resolution( - &display_name.unwrap_or_default(), - c.width, - c.height, - )) - .into(), + original_resolution, ..Default::default() }); let mut msg_out = Message::new(); @@ -884,15 +900,37 @@ pub fn handle_one_frame_encoded( } #[inline] -fn update_get_original_resolution(display_name: &str, w: usize, h: usize) -> Resolution { - let wh = get_or_set_original_resolution(display_name, (w as _, h as _)); - Resolution { - width: wh.0, - height: wh.1, - ..Default::default() +fn get_original_resolution(display_name: &str, w: usize, h: usize) -> MessageField { + Some(if is_virtual_display(&display_name) { + Resolution { + width: 0, + height: 0, + ..Default::default() + } + } else { + update_get_original_resolution_(&display_name, w, h) + }) + .into() +} + +#[inline] +#[cfg(target_os = "windows")] +fn is_virtual_display(name: &str) -> bool { + match crate::platform::windows::is_virtual_display(&name) { + Ok(b) => b, + Err(e) => { + log::error!("Failed to check is virtual display for '{}': {}", &name, e); + false + } } } +#[inline] +#[cfg(not(target_os = "windows"))] +fn is_virtual_display(_name: &str) -> bool { + false +} + pub(super) fn get_displays_2(all: &Vec) -> (usize, Vec) { let mut displays = Vec::new(); let mut primary = 0; @@ -900,20 +938,17 @@ pub(super) fn get_displays_2(all: &Vec) -> (usize, Vec) { if d.is_primary() { primary = i; } + let display_name = d.name(); + let original_resolution = get_original_resolution(&display_name, d.width(), d.height()); displays.push(DisplayInfo { x: d.origin().0 as _, y: d.origin().1 as _, width: d.width() as _, height: d.height() as _, - name: d.name(), + name: display_name, online: d.is_online(), cursor_embedded: false, - original_resolution: Some(update_get_original_resolution( - &d.name(), - d.width(), - d.height(), - )) - .into(), + original_resolution, ..Default::default() }); } From b8ea705a2183d2d98b5e2c5f164f1d360c96b90f Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 18 May 2023 21:44:09 +0800 Subject: [PATCH 04/11] fix check virtual display on windows Signed-off-by: fufesou --- src/platform/windows.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 431bfd929..dd91a5c25 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1891,25 +1891,25 @@ pub fn current_resolution(name: &str) -> ResultType { } pub fn is_virtual_display(name: &str) -> ResultType { - let device_name = str_to_device_name(name); let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::zeroed() }; - dd.cb = std::mem::size_of::() as _; - let ok = unsafe { EnumDisplayDevicesW(device_name.as_ptr(), 0, &mut dd as _, 0) }; - if ok == FALSE { - bail!( - "enumerate display devices with device name '{}', errno {}", - name, - unsafe { GetLastError() } - ); - } - match std::string::String::from_utf16(&dd.DeviceString) { - Ok(s) => Ok(&s[..IDD_DEVICE_STRING.len()] == IDD_DEVICE_STRING), - Err(e) => bail!( - "convert the device string of '{}' to string: {}", - name, - e - ), + dd.cb = std::mem::size_of::() as DWORD; + let mut i_dev_num = 0; + loop { + let result = unsafe { EnumDisplayDevicesW(null_mut(), i_dev_num, &mut dd, 0) }; + if result == 0 { + break; + } + if let Ok(device_name) = String::from_utf16(&dd.DeviceName) { + if device_name == name { + return match std::string::String::from_utf16(&dd.DeviceString) { + Ok(s) => Ok(&s[..IDD_DEVICE_STRING.len()] == IDD_DEVICE_STRING), + Err(e) => bail!("convert the device string of '{}' to string: {}", name, e), + }; + } + } + i_dev_num += 1; } + bail!("No such display '{}'", name) } pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType<()> { From ac96ddaecb400778a55305e98d8ef34000fcac60 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 18 May 2023 21:57:18 +0800 Subject: [PATCH 05/11] windows, fix display name comparation Signed-off-by: fufesou --- src/platform/windows.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index dd91a5c25..46f92d511 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1890,6 +1890,17 @@ pub fn current_resolution(name: &str) -> ResultType { } } +#[inline] +fn is_device_name(device_name: &str, name: &str) -> bool { + if name.len() == device_name.len() { + name == device_name + } else if name.len() > device_name.len() { + false + } else { + &device_name[..name.len()] == name && device_name.as_bytes()[name.len() as usize] == 0 + } +} + pub fn is_virtual_display(name: &str) -> ResultType { let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::zeroed() }; dd.cb = std::mem::size_of::() as DWORD; @@ -1900,7 +1911,7 @@ pub fn is_virtual_display(name: &str) -> ResultType { break; } if let Ok(device_name) = String::from_utf16(&dd.DeviceName) { - if device_name == name { + if is_device_name(&device_name, name) { return match std::string::String::from_utf16(&dd.DeviceString) { Ok(s) => Ok(&s[..IDD_DEVICE_STRING.len()] == IDD_DEVICE_STRING), Err(e) => bail!("convert the device string of '{}' to string: {}", name, e), From c4cefdb54b6b3a8608150e5d59aea1c6b6adc681 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 18 May 2023 22:56:16 +0800 Subject: [PATCH 06/11] fix mismatch of current display index and current display name Signed-off-by: fufesou --- src/server/connection.rs | 2 ++ src/server/video_service.rs | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index d1da5c079..200a6dfb5 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1043,6 +1043,8 @@ impl Connection { }) .into(); #[cfg(not(any(target_os = "android", target_os = "ios")))] + video_service::try_reset_current_display(); + #[cfg(not(any(target_os = "android", target_os = "ios")))] { pi.resolutions = Some(SupportedResolutions { resolutions: video_service::get_current_display_name() diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 5a63a60a6..747fff73e 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -561,11 +561,6 @@ fn run(sp: GenericService) -> ResultType<()> { log::debug!("Broadcasting display switch"); let mut misc = Misc::new(); let display_name = get_current_display_name().unwrap_or_default(); - println!( - "REMOVE ME ============================ display_name: {:?}, is_virtual: {}", - display_name, - is_virtual_display(&display_name) - ); let original_resolution = get_original_resolution(&display_name, c.width, c.height); misc.set_switch_display(SwitchDisplay { display: c.current as _, @@ -967,6 +962,15 @@ pub fn is_inited_msg() -> Option { None } +// switch to primary display if long time (30 seconds) no users +#[inline] +pub fn try_reset_current_display() { + if LAST_ACTIVE.lock().unwrap().elapsed().as_secs() >= 30 { + *CURRENT_DISPLAY.lock().unwrap() = usize::MAX; + } + *LAST_ACTIVE.lock().unwrap() = time::Instant::now(); +} + pub async fn get_displays() -> ResultType<(usize, Vec)> { #[cfg(target_os = "linux")] { @@ -974,10 +978,6 @@ pub async fn get_displays() -> ResultType<(usize, Vec)> { return super::wayland::get_displays().await; } } - // switch to primary display if long time (30 seconds) no users - if LAST_ACTIVE.lock().unwrap().elapsed().as_secs() >= 30 { - *CURRENT_DISPLAY.lock().unwrap() = usize::MAX; - } Ok(get_displays_2(&try_get_displays()?)) } @@ -1071,6 +1071,8 @@ pub fn get_current_display() -> ResultType<(usize, usize, Display)> { get_current_display_2(try_get_displays()?) } +// `try_reset_current_display` is needed because `get_displays` may change the current display, +// which may cause the mismatch of current display and the current display name. pub fn get_current_display_name() -> ResultType { Ok(get_current_display_2(try_get_displays()?)?.2.name()) } From d339fd178bf66ed806fca792e705e8c4fb5ac809 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 18 May 2023 23:45:38 +0800 Subject: [PATCH 07/11] remove custom resolution Signed-off-by: fufesou --- .../lib/desktop/widgets/remote_toolbar.dart | 184 +++++++++--------- 1 file changed, 93 insertions(+), 91 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 6d48777ed..17b1ce540 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -29,10 +29,6 @@ const _kKeyLegacyMode = 'legacy'; const _kKeyMapMode = 'map'; const _kKeyTranslateMode = 'translate'; -const _kResolutionOrigin = 'Origin'; -const _kResolutionCustom = 'Custom'; -const _kResolutionFitLocal = 'FitLocal'; - class MenubarState { final kStoreKey = 'remoteMenubarState'; late RxBool show; @@ -1002,10 +998,6 @@ class _ResolutionsMenu extends StatefulWidget { class _ResolutionsMenuState extends State<_ResolutionsMenu> { String _groupValue = ''; Resolution? _localResolution; - late final _customWidth = - TextEditingController(text: display.width.toString()); - late final _customHeight = - TextEditingController(text: display.height.toString()); PeerInfo get pi => widget.ffi.ffiModel.pi; FfiModel get ffiModel => widget.ffi.ffiModel; @@ -1020,8 +1012,9 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { @override Widget build(BuildContext context) { final isVirtualDisplay = display.isVirtualDisplayResolution; - final visible = - ffiModel.keyboard && (isVirtualDisplay || resolutions.length > 1); + // final visible = + // ffiModel.keyboard && (isVirtualDisplay || resolutions.length > 1); + final visible = ffiModel.keyboard && resolutions.length > 1; if (!visible) return Offstage(); _groupValue = '${display.width}x${display.height}'; _getLocalResolution(); @@ -1034,12 +1027,22 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { menuChildren: [ _OriginalResolutionMenuButton(showOriginalBtn), _FitLocalResolutionMenuButton(showFitLocalBtn), - _customResolutionMenuButton(isVirtualDisplay), + // _customResolutionMenuButton(isVirtualDisplay), + _menuDivider(showOriginalBtn, showFitLocalBtn, isVirtualDisplay), ] + _supportedResolutionMenuButtons(), child: Text(translate("Resolution"))); } + _menuDivider( + bool showOriginalBtn, bool showFitLocalBtn, bool isVirtualDisplay) { + return Offstage( + // offstage: !(showOriginalBtn || showFitLocalBtn || isVirtualDisplay), + offstage: !(showOriginalBtn || showFitLocalBtn), + child: Divider(), + ); + } + _getLocalResolution() { _localResolution = null; final String currentDisplay = bind.mainGetCurrentDisplay(); @@ -1057,53 +1060,38 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { _onChanged(String? value) async { if (value == null) return; - - int? w; - int? h; - if (value == _kResolutionOrigin) { - w = display.originalWidth; - h = display.originalHeight; - } else if (value == _kResolutionFitLocal) { - final resolution = _getBestFitResolution(); - if (resolution != null) { - w = resolution.width; - h = resolution.height; - } - } else if (value == _kResolutionCustom) { - w = int.tryParse(_customWidth.value as String); - h = int.tryParse(_customHeight.value as String); - } else { - final list = value.split('x'); - if (list.length == 2) { - w = int.tryParse(list[0]); - h = int.tryParse(list[1]); + final list = value.split('x'); + if (list.length == 2) { + final w = int.tryParse(list[0]); + final h = int.tryParse(list[1]); + if (w != null && h != null) { + await _changeResolution(w, h); } } + } - if (w != null && h != null) { - await bind.sessionChangeResolution( - id: widget.id, - width: w, - height: h, - ); - Future.delayed(Duration(seconds: 3), () async { - final display = ffiModel.display; - if (w == display.width && h == display.height) { - if (await widget.screenAdjustor.isWindowCanBeAdjusted()) { - widget.screenAdjustor.doAdjustWindow(); - } + _changeResolution(int w, int h) async { + await bind.sessionChangeResolution( + id: widget.id, + width: w, + height: h, + ); + Future.delayed(Duration(seconds: 3), () async { + final display = ffiModel.display; + if (w == display.width && h == display.height) { + if (await widget.screenAdjustor.isWindowCanBeAdjusted()) { + widget.screenAdjustor.doAdjustWindow(); } - }); - } + } + }); } Widget _OriginalResolutionMenuButton(bool showOriginalBtn) { return Offstage( offstage: !showOriginalBtn, - child: RdoMenuButton( - value: _kResolutionOrigin, - groupValue: _groupValue, - onChanged: _onChanged, + child: MenuButton( + onPressed: () => + _changeResolution(display.originalWidth, display.originalHeight), ffi: widget.ffi, child: Text( '${translate('Original')} ${display.originalWidth}x${display.originalHeight}'), @@ -1114,10 +1102,13 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { Widget _FitLocalResolutionMenuButton(bool showFitLocalBtn) { return Offstage( offstage: !showFitLocalBtn, - child: RdoMenuButton( - value: _kResolutionFitLocal, - groupValue: _groupValue, - onChanged: _onChanged, + child: MenuButton( + onPressed: () { + final resolution = _getBestFitResolution(); + if (resolution != null) { + _changeResolution(resolution.width, resolution.height); + } + }, ffi: widget.ffi, child: Text( '${translate('Fit Local')} ${_localResolution?.width ?? 0}x${_localResolution?.height ?? 0}'), @@ -1137,50 +1128,61 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { Widget _customResolutionMenuButton(bool showCustomBtn) { return Offstage( offstage: !showCustomBtn, - child: RdoMenuButton( - value: _kResolutionCustom, - groupValue: _groupValue, - onChanged: _onChanged, + child: MenuButton( + onPressed: () => _customResolutionDialog(), ffi: widget.ffi, - child: _customResolutionWidget(), + child: + Text('${translate('Custom')} ${display.width}x${display.height}'), ), ); } - Widget _customResolutionWidget() { - return Column( - children: [ - Text(translate('Custom')), - Container( - width: 5, - ), - // _resolutionInput(_customWidth), - Container( - width: 3, - ), - Text('x'), - Container( - width: 3, - ), - // _resolutionInput(_customHeight), - ], - ); - } + _customResolutionDialog() async { + OverlayDialogManager dialogManager = widget.ffi.dialogManager; + dialogManager.dismissAll(); + dialogManager.show((setState, close, context) { + cancel() { + close(); + } - TextField _resolutionInput(TextEditingController controller) { - return TextField( - decoration: InputDecoration( - border: InputBorder.none, - isDense: true, - ), - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - LengthLimitingTextInputFormatter(4), - FilteringTextInputFormatter.allow(RegExp(r'[0-9]')), - ], - controller: controller, - ); + return CustomAlertDialog( + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.password_rounded, color: MyTheme.accent), + Text(translate('Custom width x height')).paddingOnly(left: 10), + ], + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Slider( + value: display.width.toDouble(), + min: 256, + max: 8192, + divisions: 1, + onChanged: (double value) {}, + ), + Slider( + value: display.height.toDouble(), + min: 256, + max: 8192, + divisions: 1, + onChanged: (double value) {}, + ), + ], + ), + actions: [ + dialogButton( + 'Cancel', + icon: Icon(Icons.close_rounded), + onPressed: cancel, + isOutline: true, + ), + ], + onCancel: cancel, + ); + }); } Resolution? _getBestFitResolution() { From 99ad6254c12d06a10410dc684baec29424e3a96e Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 18 May 2023 23:57:48 +0800 Subject: [PATCH 08/11] add translations Signed-off-by: fufesou --- .../lib/desktop/widgets/remote_toolbar.dart | 27 ++++++++++--------- res/lang.py | 2 +- src/lang/ca.rs | 3 +++ src/lang/cn.rs | 3 +++ src/lang/cs.rs | 3 +++ src/lang/da.rs | 3 +++ src/lang/de.rs | 3 +++ src/lang/el.rs | 3 +++ src/lang/en.rs | 3 +++ src/lang/eo.rs | 3 +++ src/lang/es.rs | 3 +++ src/lang/fa.rs | 3 +++ src/lang/fr.rs | 3 +++ src/lang/hu.rs | 3 +++ src/lang/id.rs | 3 +++ src/lang/it.rs | 3 +++ src/lang/ja.rs | 3 +++ src/lang/ko.rs | 3 +++ src/lang/kz.rs | 3 +++ src/lang/lt.rs | 3 +++ src/lang/nl.rs | 3 +++ src/lang/pl.rs | 3 +++ src/lang/pt_PT.rs | 3 +++ src/lang/ptbr.rs | 3 +++ src/lang/ro.rs | 3 +++ src/lang/ru.rs | 3 +++ src/lang/sk.rs | 3 +++ src/lang/sl.rs | 3 +++ src/lang/sq.rs | 3 +++ src/lang/sr.rs | 3 +++ src/lang/sv.rs | 3 +++ src/lang/template.rs | 3 +++ src/lang/th.rs | 3 +++ src/lang/tr.rs | 3 +++ src/lang/tw.rs | 3 +++ src/lang/ua.rs | 3 +++ src/lang/vn.rs | 3 +++ 37 files changed, 120 insertions(+), 14 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 17b1ce540..19dd79a2f 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1023,15 +1023,16 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { final showFitLocalBtn = !_isRemoteResolutionFitLocal(); return _SubmenuButton( - ffi: widget.ffi, - menuChildren: [ - _OriginalResolutionMenuButton(showOriginalBtn), - _FitLocalResolutionMenuButton(showFitLocalBtn), - // _customResolutionMenuButton(isVirtualDisplay), - _menuDivider(showOriginalBtn, showFitLocalBtn, isVirtualDisplay), - ] + - _supportedResolutionMenuButtons(), - child: Text(translate("Resolution"))); + ffi: widget.ffi, + menuChildren: [ + _OriginalResolutionMenuButton(showOriginalBtn), + _FitLocalResolutionMenuButton(showFitLocalBtn), + // _customResolutionMenuButton(isVirtualDisplay), + _menuDivider(showOriginalBtn, showFitLocalBtn, isVirtualDisplay), + ] + + _supportedResolutionMenuButtons(), + child: Text(translate("Resolution")), + ); } _menuDivider( @@ -1094,7 +1095,7 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { _changeResolution(display.originalWidth, display.originalHeight), ffi: widget.ffi, child: Text( - '${translate('Original')} ${display.originalWidth}x${display.originalHeight}'), + '${translate('resolution_original_tip')} ${display.originalWidth}x${display.originalHeight}'), ), ); } @@ -1111,7 +1112,7 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { }, ffi: widget.ffi, child: Text( - '${translate('Fit Local')} ${_localResolution?.width ?? 0}x${_localResolution?.height ?? 0}'), + '${translate('resolution_fit_local_tip')} ${_localResolution?.width ?? 0}x${_localResolution?.height ?? 0}'), ), ); } @@ -1131,8 +1132,8 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { child: MenuButton( onPressed: () => _customResolutionDialog(), ffi: widget.ffi, - child: - Text('${translate('Custom')} ${display.width}x${display.height}'), + child: Text( + '${translate('resolution_custom_tip')} ${display.width}x${display.height}'), ), ); } diff --git a/res/lang.py b/res/lang.py index 85fd08654..74492818a 100644 --- a/res/lang.py +++ b/res/lang.py @@ -36,7 +36,7 @@ def main(): def expand(): - for fn in glob.glob('./src/lang/*'): + for fn in glob.glob('./src/lang/*.rs'): lang = os.path.basename(fn)[:-3] if lang in ['en','template']: continue print(lang) diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 0d876d6f2..5ca5cb6f1 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 447460174..b325dee22 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", "启用"), ("Disable", "禁用"), ("Options", "选项"), + ("resolution_original_tip", "原始分辨率"), + ("resolution_fit_local_tip", "适应本地分辨率"), + ("resolution_custom_tip", "自定义分辨率"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index e38e7620f..0596c3590 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index e863f534f..3db456bfe 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 50f8f3e7e..199ff886c 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", "Aktivieren"), ("Disable", "Deaktivieren"), ("Options", "Einstellungen"), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 40c3d30d6..fafcf1698 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 93a56544a..8e86125c6 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -66,5 +66,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "Please install Xorg"), ("no_desktop_title_tip", "No desktop is available"), ("no_desktop_text_tip", "Please install GNOME desktop"), + ("resolution_original_tip", "Original resolution"), + ("resolution_fit_local_tip", "Fit local resolution"), + ("resolution_custom_tip", "Custom resolution"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 61e57d5f5..a18cc6ee0 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 6daf738ba..2b4b20115 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", "Habilitar"), ("Disable", "Inhabilitar"), ("Options", "Opciones"), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index eb35b0ff1..b44e796fd 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", "فعال کردن"), ("Disable", "غیر فعال کردن"), ("Options", "گزینه ها"), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 645a088bc..a1482a04b 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 3fca393b1..5828efb08 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index e746d509d..b90c96ec6 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index a0f2e9bab..e3c59bdae 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", "Abilita"), ("Disable", "Disabilita"), ("Options", "Opzioni"), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index cf49377ec..4e8267aea 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index eab5f189f..32c34d086 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index c0d90b433..e345c993c 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 953d0ad43..fe1336848 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 92f2c9557..bdcc4314c 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", "Activeer"), ("Disable", "Deactiveer"), ("Options", "Opties"), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 11bf58f19..9dffed6e9 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", "Włącz"), ("Disable", "Wyłącz"), ("Options", "Opcje"), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 07bb8a065..e28f8b1cc 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 5627a5f2a..e3fa71ad9 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", "Habilitar"), ("Disable", "Desabilitar"), ("Options", "Opções"), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 04d9f9d22..81665e73a 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index c2517df99..7d277450e 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", "Включить"), ("Disable", "Отключить"), ("Options", "Настройки"), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 0e2559193..cea678364 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index c6188aaf8..35edd0568 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index c55a4741e..c4e248ea2 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 3f37e23c7..5fc30af0b 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 5a76a1677..78331d448 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index f61635f9a..a20769abb 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index ab4ec4993..3ef4142c4 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 3fd57d1cd..7572cbee1 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index b9cdb60dd..f624b2006 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 68580c73b..2f98d9fe2 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 9e64ca784..6781ca2d5 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -506,5 +506,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable", ""), ("Disable", ""), ("Options", ""), + ("resolution_original_tip", ""), + ("resolution_fit_local_tip", ""), + ("resolution_custom_tip", ""), ].iter().cloned().collect(); } From 700a59ea72ffb73f14933a4c80781f02b9212746 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 18 May 2023 09:38:35 -0700 Subject: [PATCH 09/11] remove unused custom resolution ui Signed-off-by: fufesou --- .../lib/desktop/widgets/remote_toolbar.dart | 60 ------------------- 1 file changed, 60 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 19dd79a2f..bba146fa8 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1126,66 +1126,6 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { child: Text('${e.width}x${e.height}'))) .toList(); - Widget _customResolutionMenuButton(bool showCustomBtn) { - return Offstage( - offstage: !showCustomBtn, - child: MenuButton( - onPressed: () => _customResolutionDialog(), - ffi: widget.ffi, - child: Text( - '${translate('resolution_custom_tip')} ${display.width}x${display.height}'), - ), - ); - } - - _customResolutionDialog() async { - OverlayDialogManager dialogManager = widget.ffi.dialogManager; - dialogManager.dismissAll(); - dialogManager.show((setState, close, context) { - cancel() { - close(); - } - - return CustomAlertDialog( - title: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.password_rounded, color: MyTheme.accent), - Text(translate('Custom width x height')).paddingOnly(left: 10), - ], - ), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Slider( - value: display.width.toDouble(), - min: 256, - max: 8192, - divisions: 1, - onChanged: (double value) {}, - ), - Slider( - value: display.height.toDouble(), - min: 256, - max: 8192, - divisions: 1, - onChanged: (double value) {}, - ), - ], - ), - actions: [ - dialogButton( - 'Cancel', - icon: Icon(Icons.close_rounded), - onPressed: cancel, - isOutline: true, - ), - ], - onCancel: cancel, - ); - }); - } - Resolution? _getBestFitResolution() { if (_localResolution == null) { return null; From 8353afb02d79218f7445f1901126d00de81f6f7e Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 19 May 2023 09:51:01 +0800 Subject: [PATCH 10/11] fix build Signed-off-by: fufesou --- src/server.rs | 6 +++--- src/server/connection.rs | 2 ++ src/server/video_service.rs | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/server.rs b/src/server.rs index 66095fd9e..2b833576a 100644 --- a/src/server.rs +++ b/src/server.rs @@ -8,12 +8,10 @@ use std::{ use bytes::Bytes; pub use connection::*; -#[cfg(not(any(target_os = "android", target_os = "ios")))] -use hbb_common::config::Config2; use hbb_common::tcp::{self, new_listener}; use hbb_common::{ allow_err, - anyhow::{anyhow, Context}, + anyhow::Context, bail, config::{Config, CONNECT_TIMEOUT, RELAY_PORT}, log, @@ -25,6 +23,8 @@ use hbb_common::{ timeout, tokio, ResultType, Stream, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] +use hbb_common::{anyhow::anyhow, config::Config2}; +#[cfg(not(any(target_os = "android", target_os = "ios")))] use service::ServiceTmpl; use service::{GenericService, Service, Subscriber}; diff --git a/src/server/connection.rs b/src/server/connection.rs index 200a6dfb5..69cb56102 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1936,6 +1936,7 @@ impl Connection { true } + #[cfg(not(any(target_os = "android", target_os = "ios")))] fn change_resolution(&mut self, r: &Resolution) { if self.keyboard { if let Ok(name) = video_service::get_current_display_name() { @@ -2148,6 +2149,7 @@ impl Connection { } } } + #[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); diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 747fff73e..18b400fca 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -113,6 +113,7 @@ fn update_get_original_resolution_(display_name: &str, w: usize, h: usize) -> Re } #[inline] +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn reset_resolutions() { for (name, (w, h)) in ORIGINAL_RESOLUTIONS.read().unwrap().iter() { if let Err(e) = crate::platform::change_resolution(name, *w as _, *h as _) { From 8c0fa989b65ac21e78ff6b5406cabb1d497848e1 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 19 May 2023 11:10:24 +0800 Subject: [PATCH 11/11] fix macro_role: serde_field_string, add config tests Signed-off-by: fufesou --- libs/hbb_common/src/config.rs | 46 +++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 16d606c78..fff6fc5a1 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -105,12 +105,9 @@ macro_rules! serde_field_string { where D: de::Deserializer<'de>, { - let s: &str = de::Deserialize::deserialize(deserializer).unwrap_or_default(); - Ok(if s.is_empty() { - Self::$default_func() - } else { - s.to_owned() - }) + let s: String = + de::Deserialize::deserialize(deserializer).unwrap_or(Self::$default_func()); + Ok(s) } }; } @@ -252,7 +249,11 @@ pub struct PeerConfig { #[serde(flatten)] pub view_only: ViewOnly, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "deserialize_option_resolution" + )] pub custom_resolution: Option, // The other scalar value must before this @@ -1495,6 +1496,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); #[cfg(test)] mod tests { @@ -1543,4 +1545,34 @@ mod tests { }) ); } + + #[test] + fn test_peer_config_deserialize() { + let default_peer_config = toml::from_str::("").unwrap(); + // test custom_resolution + { + let wrong_type_str = r#" + view_style = "adaptive" + scroll_style = "scrollbar" + custom_resolution = true + "#; + let mut compare_config = default_peer_config.clone(); + compare_config.view_style = "adaptive".to_string(); + compare_config.scroll_style = "scrollbar".to_string(); + let cfg = toml::from_str::(wrong_type_str); + assert_eq!(cfg, Ok(compare_config), "Failed to test wrong_type_str"); + + let wrong_field_str = r#" + [custom_resolution] + w = 1920 + h = 1080 + hello = "world" + [ui_flutter] + "#; + let mut compare_config = default_peer_config.clone(); + compare_config.custom_resolution = Some(Resolution { w: 1920, h: 1080 }); + let cfg = toml::from_str::(wrong_field_str); + assert_eq!(cfg, Ok(compare_config), "Failed to test wrong_field_str"); + } + } }