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 <sunboeasy@gmail.com>
This commit is contained in:
parent
1c17fddf51
commit
d4a712bb32
@ -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(
|
||||
|
@ -179,6 +179,9 @@ class _OnlineStatusWidgetState extends State<OnlineStatusWidget> {
|
||||
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<ConnectionPage>
|
||||
);
|
||||
}
|
||||
String textToFind = textEditingValue.text.toLowerCase();
|
||||
_autocompleteOpts = peers
|
||||
_autocompleteOpts = peers
|
||||
.where((peer) =>
|
||||
peer.id.toLowerCase().contains(textToFind) ||
|
||||
peer.username
|
||||
|
@ -35,7 +35,7 @@ class DesktopHomePage extends StatefulWidget {
|
||||
const borderColor = Color(0xFF2F65BA);
|
||||
|
||||
class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
with AutomaticKeepAliveClientMixin, WidgetsBindingObserver {
|
||||
final _leftPaneScrollController = ScrollController();
|
||||
|
||||
@override
|
||||
@ -51,6 +51,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
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<DesktopHomePage>
|
||||
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<DesktopHomePage>
|
||||
_updateWindowSize();
|
||||
});
|
||||
}
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
_updateWindowSize() {
|
||||
@ -830,9 +838,18 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
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(
|
||||
|
@ -107,13 +107,20 @@ class DesktopSettingPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _DesktopSettingPageState extends State<DesktopSettingPage>
|
||||
with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {
|
||||
with
|
||||
TickerProviderStateMixin,
|
||||
AutomaticKeepAliveClientMixin,
|
||||
WidgetsBindingObserver {
|
||||
late PageController controller;
|
||||
late Rx<SettingsTabKey> 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<DesktopSettingPage>
|
||||
});
|
||||
}
|
||||
|
||||
@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<PageController>(tag: _kSettingPageControllerTag);
|
||||
Get.delete<RxInt>(tag: _kSettingPageTabKeyTag);
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_videoConnTimer?.cancel();
|
||||
}
|
||||
|
||||
List<_TabInfo> _settingTabs() {
|
||||
@ -207,12 +237,35 @@ class _DesktopSettingPageState extends State<DesktopSettingPage>
|
||||
return children;
|
||||
}
|
||||
|
||||
Widget _buildBlock({required List<Widget> 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: <Widget>[
|
||||
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),
|
||||
]),
|
||||
|
@ -37,13 +37,9 @@ class DesktopTabPage extends StatefulWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _DesktopTabPageState extends State<DesktopTabPage>
|
||||
with WidgetsBindingObserver {
|
||||
class _DesktopTabPageState extends State<DesktopTabPage> {
|
||||
final tabController = DesktopTabController(tabType: DesktopTabType.main);
|
||||
|
||||
final RxBool _block = false.obs;
|
||||
// bool mouseIn = false;
|
||||
|
||||
_DesktopTabPageState() {
|
||||
RemoteCountState.init();
|
||||
Get.put<DesktopTabController>(tabController);
|
||||
@ -69,19 +65,10 @@ class _DesktopTabPageState extends State<DesktopTabPage>
|
||||
}
|
||||
}
|
||||
|
||||
@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<DesktopTabPage>
|
||||
@override
|
||||
void dispose() {
|
||||
// HardwareKeyboard.instance.removeHandler(_handleKeyEvent);
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
Get.delete<DesktopTabController>();
|
||||
|
||||
super.dispose();
|
||||
@ -119,7 +105,6 @@ class _DesktopTabPageState extends State<DesktopTabPage>
|
||||
isClose: false,
|
||||
),
|
||||
),
|
||||
blockTab: _block,
|
||||
)));
|
||||
return isMacOS || kUseCompatibleUiMode
|
||||
? tabWidget
|
||||
|
@ -110,7 +110,8 @@ class ConnectionManager extends StatefulWidget {
|
||||
|
||||
class ConnectionManagerState extends State<ConnectionManager>
|
||||
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<ConnectionManager>
|
||||
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<ConnectionManager>
|
||||
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<ConnectionManager>
|
||||
? 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<ConnectionManager>
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildKeyEventBlock(Widget child) {
|
||||
return ExcludeFocus(child: child, excluding: true);
|
||||
}
|
||||
|
||||
Widget buildTitleBar() {
|
||||
return SizedBox(
|
||||
height: kDesktopRemoteTabBarHeight,
|
||||
|
@ -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<DesktopTab>
|
||||
Color? get unSelectedTabBackgroundColor =>
|
||||
widget.unSelectedTabBackgroundColor;
|
||||
Color? get selectedBorderColor => widget.selectedBorderColor;
|
||||
RxBool? get blockTab => widget.blockTab;
|
||||
DesktopTabController get controller => widget.controller;
|
||||
RxList<String> get invisibleTabKeys => widget.invisibleTabKeys;
|
||||
Debouncer get _scrollDebounce => widget._scrollDebounce;
|
||||
@ -533,21 +530,9 @@ class _DesktopTabState extends State<DesktopTab>
|
||||
]);
|
||||
}
|
||||
|
||||
Widget _buildBlock({required Widget child}) {
|
||||
if (blockTab != null) {
|
||||
return buildRemoteBlock(
|
||||
child: child,
|
||||
block: blockTab!,
|
||||
use: canBeBlocked,
|
||||
mask: tabType == DesktopTabType.main);
|
||||
} else {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> _tabWidgets = [];
|
||||
Widget _buildPageView() {
|
||||
final child = _buildBlock(
|
||||
final child = Container(
|
||||
child: Obx(() => PageView(
|
||||
controller: state.value.pageController,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
|
@ -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;
|
||||
|
@ -266,6 +266,7 @@ pub enum Data {
|
||||
ControlledSessionCount(usize),
|
||||
CmErr(String),
|
||||
CheckHwcodec,
|
||||
#[cfg(feature = "flutter")]
|
||||
VideoConnCount(Option<usize>),
|
||||
// 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()
|
||||
|
@ -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<Mutex<String>> = Default::default();
|
||||
static ref ASYNC_HTTP_STATUS : Arc<Mutex<HashMap<String, String>>> = Arc::new(Mutex::new(HashMap::new()));
|
||||
static ref TEMPORARY_PASSWD : Arc<Mutex<String>> = 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<Mutex<bool>> = Default::default();
|
||||
@ -1144,6 +1146,8 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
|
||||
let mut key_confirmed = false;
|
||||
let mut rx = rx;
|
||||
let mut mouse_time = 0;
|
||||
#[cfg(feature = "flutter")]
|
||||
let mut video_conn_count = 0;
|
||||
#[cfg(not(feature = "flutter"))]
|
||||
let mut id = "".to_owned();
|
||||
#[cfg(any(
|
||||
@ -1204,8 +1208,9 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
|
||||
*TEMPORARY_PASSWD.lock().unwrap() = value;
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "flutter")]
|
||||
Ok(Some(ipc::Data::VideoConnCount(Some(n)))) => {
|
||||
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<ipc:
|
||||
mouse_time,
|
||||
#[cfg(not(feature = "flutter"))]
|
||||
id: id.clone(),
|
||||
#[cfg(feature = "flutter")]
|
||||
video_conn_count,
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
@ -1236,6 +1243,7 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
|
||||
c.send(&ipc::Data::Options(None)).await.ok();
|
||||
c.send(&ipc::Data::Config(("id".to_owned(), None))).await.ok();
|
||||
c.send(&ipc::Data::Config(("temporary-password".to_owned(), None))).await.ok();
|
||||
#[cfg(feature = "flutter")]
|
||||
c.send(&ipc::Data::VideoConnCount(None)).await.ok();
|
||||
}
|
||||
}
|
||||
@ -1256,6 +1264,8 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
|
||||
mouse_time,
|
||||
#[cfg(not(feature = "flutter"))]
|
||||
id: id.clone(),
|
||||
#[cfg(feature = "flutter")]
|
||||
video_conn_count,
|
||||
};
|
||||
sleep(1.).await;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user