From d4a712bb320e16d59f6c6ecebe118a3b0ba17633 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 8 Dec 2024 18:26:55 +0800 Subject: [PATCH] always block desktop settings page if video connection exists (#10224) 1. Always block desktop settings page if video connection exists, both mouse event and key event are blocked.. 2. Server control page always block key event. Signed-off-by: 21pages --- flutter/lib/common.dart | 7 +- flutter/lib/consts.dart | 2 +- .../lib/desktop/pages/connection_page.dart | 5 +- .../lib/desktop/pages/desktop_home_page.dart | 23 ++++++- .../desktop/pages/desktop_setting_page.dart | 65 +++++++++++++++++-- .../lib/desktop/pages/desktop_tab_page.dart | 17 +---- flutter/lib/desktop/pages/server_page.dart | 24 +++++-- .../lib/desktop/widgets/tabbar_widget.dart | 17 +---- flutter/lib/models/state_model.dart | 1 + src/ipc.rs | 2 + src/ui_interface.rs | 16 ++++- 11 files changed, 126 insertions(+), 53 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 0dd8849f3..208897ed0 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -2809,7 +2809,7 @@ Widget buildRemoteBlock( onExit: (event) => block.value = false, child: Stack(children: [ // scope block tab - FocusScope(child: child, canRequestFocus: !block.value), + preventMouseKeyBuilder(child: child, block: block.value), // mask block click, cm not block click and still use check_click_time to avoid block local click if (mask) Offstage( @@ -2821,6 +2821,11 @@ Widget buildRemoteBlock( )); } +Widget preventMouseKeyBuilder({required Widget child, required bool block}) { + return ExcludeFocus( + excluding: block, child: AbsorbPointer(child: child, absorbing: block)); +} + Widget unreadMessageCountBuilder(RxInt? count, {double? size, double? fontSize}) { return Obx(() => Offstage( diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index f6f9c4d34..95b207826 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -575,4 +575,4 @@ extension WindowsTargetExt on int { WindowsTarget get windowsVersion => getWindowsTarget(this); } -const kCheckSoftwareUpdateFinish = 'check_software_update_finish'; \ No newline at end of file +const kCheckSoftwareUpdateFinish = 'check_software_update_finish'; diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index e2681bb37..f2c712101 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -179,6 +179,9 @@ class _OnlineStatusWidgetState extends State { stateGlobal.svcStatus.value = SvcStatus.notReady; } _svcIsUsingPublicServer.value = await bind.mainIsUsingPublicServer(); + try { + stateGlobal.videoConnCount.value = status['video_conn_count'] as int; + } catch (_) {} } } @@ -359,7 +362,7 @@ class _ConnectionPageState extends State ); } String textToFind = textEditingValue.text.toLowerCase(); - _autocompleteOpts = peers + _autocompleteOpts = peers .where((peer) => peer.id.toLowerCase().contains(textToFind) || peer.username diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 754efbd5a..8061a4516 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -35,7 +35,7 @@ class DesktopHomePage extends StatefulWidget { const borderColor = Color(0xFF2F65BA); class _DesktopHomePageState extends State - with AutomaticKeepAliveClientMixin { + with AutomaticKeepAliveClientMixin, WidgetsBindingObserver { final _leftPaneScrollController = ScrollController(); @override @@ -51,6 +51,7 @@ class _DesktopHomePageState extends State bool isCardClosed = false; final RxBool _editHover = false.obs; + final RxBool _block = false.obs; final GlobalKey _childKey = GlobalKey(); @@ -58,14 +59,20 @@ class _DesktopHomePageState extends State Widget build(BuildContext context) { super.build(context); final isIncomingOnly = bind.isIncomingOnly(); - return Row( + return _buildBlock( + child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildLeftPane(context), if (!isIncomingOnly) const VerticalDivider(width: 1), if (!isIncomingOnly) Expanded(child: buildRightPane(context)), ], - ); + )); + } + + Widget _buildBlock({required Widget child}) { + return buildRemoteBlock( + block: _block, mask: true, use: canBeBlocked, child: child); } Widget buildLeftPane(BuildContext context) { @@ -805,6 +812,7 @@ class _DesktopHomePageState extends State _updateWindowSize(); }); } + WidgetsBinding.instance.addObserver(this); } _updateWindowSize() { @@ -830,9 +838,18 @@ class _DesktopHomePageState extends State platformFFI.unregisterEventHandler( kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish); } + WidgetsBinding.instance.removeObserver(this); super.dispose(); } + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + if (state == AppLifecycleState.resumed) { + shouldBeBlocked(_block, canBeBlocked); + } + } + Widget buildPluginEntry() { final entries = PluginUiManager.instance.entries.entries; return Offstage( diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 44cdbcbb8..56a99446c 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -107,13 +107,20 @@ class DesktopSettingPage extends StatefulWidget { } class _DesktopSettingPageState extends State - with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { + with + TickerProviderStateMixin, + AutomaticKeepAliveClientMixin, + WidgetsBindingObserver { late PageController controller; late Rx selectedTab; @override bool get wantKeepAlive => true; + final RxBool _block = false.obs; + final RxBool _canBeBlocked = false.obs; + Timer? _videoConnTimer; + _DesktopSettingPageState(SettingsTabKey initialTabkey) { var initialIndex = DesktopSettingPage.tabKeys.indexOf(initialTabkey); if (initialIndex == -1) { @@ -133,11 +140,34 @@ class _DesktopSettingPageState extends State }); } + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + if (state == AppLifecycleState.resumed) { + shouldBeBlocked(_block, canBeBlocked); + } + } + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + _videoConnTimer = + periodic_immediate(Duration(milliseconds: 1000), () async { + if (!mounted) { + return; + } + _canBeBlocked.value = await canBeBlocked(); + }); + } + @override void dispose() { super.dispose(); Get.delete(tag: _kSettingPageControllerTag); Get.delete(tag: _kSettingPageTabKeyTag); + WidgetsBinding.instance.removeObserver(this); + _videoConnTimer?.cancel(); } List<_TabInfo> _settingTabs() { @@ -207,12 +237,35 @@ class _DesktopSettingPageState extends State return children; } + Widget _buildBlock({required List children}) { + // check both mouseMoveTime and videoConnCount + return Obx(() { + final videoConnBlock = + _canBeBlocked.value && stateGlobal.videoConnCount > 0; + return Stack(children: [ + buildRemoteBlock( + block: _block, + mask: false, + use: canBeBlocked, + child: preventMouseKeyBuilder( + child: Row(children: children), + block: videoConnBlock, + ), + ), + if (videoConnBlock) + Container( + color: Colors.black.withOpacity(0.5), + ) + ]); + }); + } + @override Widget build(BuildContext context) { super.build(context); return Scaffold( backgroundColor: Theme.of(context).colorScheme.background, - body: Row( + body: _buildBlock( children: [ SizedBox( width: _kTabWidth, @@ -706,8 +759,8 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { locked = false; setState(() => {}); }), - AbsorbPointer( - absorbing: locked, + preventMouseKeyBuilder( + block: locked, child: Column(children: [ permissions(context), password(context), @@ -1374,8 +1427,8 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin { locked = false; setState(() => {}); }), - AbsorbPointer( - absorbing: locked, + preventMouseKeyBuilder( + block: locked, child: Column(children: [ network(context), ]), diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index 7319f7a3c..6440e55a1 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -37,13 +37,9 @@ class DesktopTabPage extends StatefulWidget { } } -class _DesktopTabPageState extends State - with WidgetsBindingObserver { +class _DesktopTabPageState extends State { final tabController = DesktopTabController(tabType: DesktopTabType.main); - final RxBool _block = false.obs; - // bool mouseIn = false; - _DesktopTabPageState() { RemoteCountState.init(); Get.put(tabController); @@ -69,19 +65,10 @@ class _DesktopTabPageState extends State } } - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - super.didChangeAppLifecycleState(state); - if (state == AppLifecycleState.resumed) { - shouldBeBlocked(_block, canBeBlocked); - } else if (state == AppLifecycleState.inactive) {} - } - @override void initState() { super.initState(); // HardwareKeyboard.instance.addHandler(_handleKeyEvent); - WidgetsBinding.instance.addObserver(this); } /* @@ -97,7 +84,6 @@ class _DesktopTabPageState extends State @override void dispose() { // HardwareKeyboard.instance.removeHandler(_handleKeyEvent); - WidgetsBinding.instance.removeObserver(this); Get.delete(); super.dispose(); @@ -119,7 +105,6 @@ class _DesktopTabPageState extends State isClose: false, ), ), - blockTab: _block, ))); return isMacOS || kUseCompatibleUiMode ? tabWidget diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index 53ba73c97..95d9f2c7c 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -110,7 +110,8 @@ class ConnectionManager extends StatefulWidget { class ConnectionManagerState extends State with WidgetsBindingObserver { - final RxBool _block = false.obs; + final RxBool _controlPageBlock = false.obs; + final RxBool _sidePageBlock = false.obs; ConnectionManagerState() { gFFI.serverModel.tabController.onSelected = (client_id_str) { @@ -139,7 +140,8 @@ class ConnectionManagerState extends State super.didChangeAppLifecycleState(state); if (state == AppLifecycleState.resumed) { if (!allowRemoteCMModification()) { - shouldBeBlocked(_block, null); + shouldBeBlocked(_controlPageBlock, null); + shouldBeBlocked(_sidePageBlock, null); } } } @@ -192,7 +194,6 @@ class ConnectionManagerState extends State selectedBorderColor: MyTheme.accent, maxLabelWidth: 100, tail: null, //buildScrollJumper(), - blockTab: allowRemoteCMModification() ? null : _block, tabBuilder: (key, icon, label, themeConf) { final client = serverModel.clients .firstWhereOrNull((client) => client.id.toString() == key); @@ -237,13 +238,20 @@ class ConnectionManagerState extends State ? buildSidePage() : buildRemoteBlock( child: buildSidePage(), - block: _block, + block: _sidePageBlock, mask: true), )), SizedBox( width: realClosedWidth, - child: - SizedBox(width: realClosedWidth, child: pageView)), + child: SizedBox( + width: realClosedWidth, + child: allowRemoteCMModification() + ? pageView + : buildRemoteBlock( + child: _buildKeyEventBlock(pageView), + block: _controlPageBlock, + mask: false, + ))), ]); return Container( color: Theme.of(context).scaffoldBackgroundColor, @@ -268,6 +276,10 @@ class ConnectionManagerState extends State } } + Widget _buildKeyEventBlock(Widget child) { + return ExcludeFocus(child: child, excluding: true); + } + Widget buildTitleBar() { return SizedBox( height: kDesktopRemoteTabBarHeight, diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 75ecacbfe..96ada22c9 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -246,7 +246,6 @@ class DesktopTab extends StatefulWidget { final Color? selectedTabBackgroundColor; final Color? unSelectedTabBackgroundColor; final Color? selectedBorderColor; - final RxBool? blockTab; final DesktopTabController controller; @@ -272,7 +271,6 @@ class DesktopTab extends StatefulWidget { this.selectedTabBackgroundColor, this.unSelectedTabBackgroundColor, this.selectedBorderColor, - this.blockTab, }) : super(key: key); static RxString tablabelGetter(String peerId) { @@ -311,7 +309,6 @@ class _DesktopTabState extends State Color? get unSelectedTabBackgroundColor => widget.unSelectedTabBackgroundColor; Color? get selectedBorderColor => widget.selectedBorderColor; - RxBool? get blockTab => widget.blockTab; DesktopTabController get controller => widget.controller; RxList get invisibleTabKeys => widget.invisibleTabKeys; Debouncer get _scrollDebounce => widget._scrollDebounce; @@ -533,21 +530,9 @@ class _DesktopTabState extends State ]); } - Widget _buildBlock({required Widget child}) { - if (blockTab != null) { - return buildRemoteBlock( - child: child, - block: blockTab!, - use: canBeBlocked, - mask: tabType == DesktopTabType.main); - } else { - return child; - } - } - List _tabWidgets = []; Widget _buildPageView() { - final child = _buildBlock( + final child = Container( child: Obx(() => PageView( controller: state.value.pageController, physics: NeverScrollableScrollPhysics(), diff --git a/flutter/lib/models/state_model.dart b/flutter/lib/models/state_model.dart index f09603649..2e1b516df 100644 --- a/flutter/lib/models/state_model.dart +++ b/flutter/lib/models/state_model.dart @@ -18,6 +18,7 @@ class StateGlobal { final RxDouble _windowBorderWidth = RxDouble(kWindowBorderWidth); final RxBool showRemoteToolBar = false.obs; final svcStatus = SvcStatus.notReady.obs; + final RxInt videoConnCount = 0.obs; final RxBool isFocused = false.obs; // for mobile and web bool isInMainPage = true; diff --git a/src/ipc.rs b/src/ipc.rs index 5126aaf4e..f1deb5ba8 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -266,6 +266,7 @@ pub enum Data { ControlledSessionCount(usize), CmErr(String), CheckHwcodec, + #[cfg(feature = "flutter")] VideoConnCount(Option), // Although the key is not neccessary, it is used to avoid hardcoding the key. WaylandScreencastRestoreToken((String, String)), @@ -455,6 +456,7 @@ async fn handle(data: Data, stream: &mut Connection) { log::info!("socks updated"); } }, + #[cfg(feature = "flutter")] Data::VideoConnCount(None) => { let n = crate::server::AUTHED_CONNS .lock() diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 5d7f9ee03..323b651fe 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -47,6 +47,8 @@ pub struct UiStatus { pub mouse_time: i64, #[cfg(not(feature = "flutter"))] pub id: String, + #[cfg(feature = "flutter")] + pub video_conn_count: usize, } #[derive(Debug, Clone, Serialize)] @@ -65,14 +67,14 @@ lazy_static::lazy_static! { mouse_time: 0, #[cfg(not(feature = "flutter"))] id: "".to_owned(), + #[cfg(feature = "flutter")] + video_conn_count: 0, })); static ref ASYNC_JOB_STATUS : Arc> = Default::default(); static ref ASYNC_HTTP_STATUS : Arc>> = Arc::new(Mutex::new(HashMap::new())); static ref TEMPORARY_PASSWD : Arc> = Arc::new(Mutex::new("".to_owned())); } -pub static VIDEO_CONN_COUNT: AtomicUsize = AtomicUsize::new(0); - #[cfg(not(any(target_os = "android", target_os = "ios")))] lazy_static::lazy_static! { static ref OPTION_SYNCED: Arc> = Default::default(); @@ -1144,6 +1146,8 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver { - VIDEO_CONN_COUNT.store(n, Ordering::Relaxed); + video_conn_count = n; } Ok(Some(ipc::Data::OnlineStatus(Some((mut x, _c))))) => { if x > 0 { @@ -1223,6 +1228,8 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver {} @@ -1236,6 +1243,7 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver