From 1e9625045b222fd9d63013b37ceb88944ac5b6d9 Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 2 Feb 2023 20:05:57 +0900 Subject: [PATCH 01/20] fix chat text selectable --- flutter/lib/common/widgets/chat_page.dart | 25 +++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/flutter/lib/common/widgets/chat_page.dart b/flutter/lib/common/widgets/chat_page.dart index d1d96199a..62f81b797 100644 --- a/flutter/lib/common/widgets/chat_page.dart +++ b/flutter/lib/common/widgets/chat_page.dart @@ -95,10 +95,31 @@ class ChatPage extends StatelessWidget implements PageShape { color: Theme.of(context).colorScheme.primary)), messageOptions: MessageOptions( showOtherUsersAvatar: false, - showTime: true, - currentUserTextColor: Colors.white, textColor: Colors.white, maxWidth: constraints.maxWidth * 0.7, + messageTextBuilder: (message, _, __) { + final isOwnMessage = + message.user.id == currentUser.id; + return Column( + crossAxisAlignment: isOwnMessage + ? CrossAxisAlignment.end + : CrossAxisAlignment.start, + children: [ + Text(message.text, + style: TextStyle(color: Colors.white)), + Padding( + padding: const EdgeInsets.only(top: 5), + child: Text( + "${message.createdAt.hour}:${message.createdAt.minute}", + style: TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ); + }, messageDecorationBuilder: (_, __, ___) => defaultMessageDecoration( color: MyTheme.accent80, From c6269b54af37e60fb4a03e6f06623065ea998a0e Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 2 Feb 2023 21:39:25 +0900 Subject: [PATCH 02/20] add requestChatInputFocus() --- flutter/lib/models/chat_model.dart | 12 ++++++++++++ flutter/test/cm_test.dart | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/flutter/lib/models/chat_model.dart b/flutter/lib/models/chat_model.dart index 18a0be279..bab88a9dd 100644 --- a/flutter/lib/models/chat_model.dart +++ b/flutter/lib/models/chat_model.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:dash_chat_2/dash_chat_2.dart'; import 'package:draggable_float_widget/draggable_float_widget.dart'; import 'package:flutter/material.dart'; @@ -139,6 +141,7 @@ class ChatModel with ChangeNotifier { }); overlayState.insert(overlay); chatWindowOverlayEntry = overlay; + requestChatInputFocus(); } hideChatWindowOverlay() { @@ -188,6 +191,7 @@ class ChatModel with ChangeNotifier { await windowManager.setSizeAlignment( kConnectionManagerWindowSize, Alignment.topRight); } else { + requestChatInputFocus(); await windowManager.show(); await windowManager.setSizeAlignment(Size(600, 400), Alignment.topRight); _isShowCMChatPage = !_isShowCMChatPage; @@ -292,4 +296,12 @@ class ChatModel with ChangeNotifier { resetClientMode() { _messages[clientModeID]?.clear(); } + + void requestChatInputFocus() { + Timer(Duration(milliseconds: 100), () { + if (inputNode.hasListeners && inputNode.canRequestFocus) { + inputNode.requestFocus(); + } + }); + } } diff --git a/flutter/test/cm_test.dart b/flutter/test/cm_test.dart index 592a28fcf..2c037c7b0 100644 --- a/flutter/test/cm_test.dart +++ b/flutter/test/cm_test.dart @@ -16,7 +16,7 @@ final testClients = [ Client(3, false, false, "UserDDDDDDDDDDDd", "441123123", true, false, false) ]; -/// -t lib/cm_main.dart to test cm +/// flutter run -d {platform} -t lib/cm_test.dart to test cm void main(List args) async { isTest = true; WidgetsFlutterBinding.ensureInitialized(); From c306ec3ba76217f24d66384f31c1d8fecb388291 Mon Sep 17 00:00:00 2001 From: csf Date: Mon, 6 Feb 2023 09:54:21 +0900 Subject: [PATCH 03/20] opt chat window on its overlay, make window focusable as a desktop app --- flutter/lib/common/widgets/overlay.dart | 64 ++++++++++++++----------- flutter/lib/models/chat_model.dart | 31 ++++++++++-- 2 files changed, 61 insertions(+), 34 deletions(-) diff --git a/flutter/lib/common/widgets/overlay.dart b/flutter/lib/common/widgets/overlay.dart index 4b4172ffd..d84789d9c 100644 --- a/flutter/lib/common/widgets/overlay.dart +++ b/flutter/lib/common/widgets/overlay.dart @@ -1,6 +1,7 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; +import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart'; import 'package:provider/provider.dart'; import '../../consts.dart'; @@ -91,28 +92,31 @@ class DraggableChatWindow extends StatelessWidget { bottom: BorderSide( color: Theme.of(context).hintColor.withOpacity(0.4)))), height: 38, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8), - child: Row(children: [ - Icon(Icons.chat_bubble_outline, - size: 20, color: Theme.of(context).colorScheme.primary), - SizedBox(width: 6), - Text(translate("Chat")) - ])), - Padding( - padding: EdgeInsets.all(2), - child: ActionIcon( - message: 'Close', - icon: IconFont.close, - onTap: chatModel.hideChatWindowOverlay, - isClose: true, - boxSize: 32, - )) - ], - ), + child: Obx(() => Opacity( + opacity: chatModel.isWindowFocus.value ? 1.0 : 0.4, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 15, vertical: 8), + child: Row(children: [ + Icon(Icons.chat_bubble_outline, + size: 20, color: Theme.of(context).colorScheme.primary), + SizedBox(width: 6), + Text(translate("Chat")) + ])), + Padding( + padding: EdgeInsets.all(2), + child: ActionIcon( + message: 'Close', + icon: IconFont.close, + onTap: chatModel.hideChatWindowOverlay, + isClose: true, + boxSize: 32, + )) + ], + ))), ); } } @@ -304,15 +308,17 @@ class _DraggableState extends State { if (widget.checkKeyboard) { checkKeyboard(); } - if (widget.checkKeyboard) { + if (widget.checkScreenSize) { checkScreenSize(); } - return Positioned( - top: _position.dy, - left: _position.dx, - width: widget.width, - height: widget.height, - child: widget.builder(context, onPanUpdate)); + return Stack(children: [ + Positioned( + top: _position.dy, + left: _position.dx, + width: widget.width, + height: widget.height, + child: widget.builder(context, onPanUpdate)) + ]); } } diff --git a/flutter/lib/models/chat_model.dart b/flutter/lib/models/chat_model.dart index bab88a9dd..dd35bd22f 100644 --- a/flutter/lib/models/chat_model.dart +++ b/flutter/lib/models/chat_model.dart @@ -4,6 +4,8 @@ import 'package:dash_chat_2/dash_chat_2.dart'; import 'package:draggable_float_widget/draggable_float_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/models/platform_model.dart'; +import 'package:get/get_rx/src/rx_types/rx_types.dart'; +import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart'; import 'package:window_manager/window_manager.dart'; import '../consts.dart'; @@ -37,6 +39,8 @@ class ChatModel with ChangeNotifier { OverlayEntry? chatWindowOverlayEntry; bool isConnManager = false; + RxBool isWindowFocus = true.obs; + final ChatUser me = ChatUser( id: "", firstName: "Me", @@ -133,11 +137,28 @@ class ChatModel with ChangeNotifier { final overlayState = _getOverlayState(); if (overlayState == null) return; final overlay = OverlayEntry(builder: (context) { - return DraggableChatWindow( - position: const Offset(20, 80), - width: 250, - height: 350, - chatModel: this); + bool innerClicked = false; + return Listener( + onPointerDown: (_) { + if (!innerClicked) { + isWindowFocus.value = false; + } + innerClicked = false; + }, + child: Obx(() => Container( + color: isWindowFocus.value ? Colors.red.withOpacity(0.3) : null, + child: Listener( + onPointerDown: (_) { + innerClicked = true; + if (!isWindowFocus.value) { + isWindowFocus.value = true; + } + }, + child: DraggableChatWindow( + position: const Offset(20, 80), + width: 250, + height: 350, + chatModel: this))))); }); overlayState.insert(overlay); chatWindowOverlayEntry = overlay; From 893f18cdec1b4fedf72af67bbfb7fe03e047db11 Mon Sep 17 00:00:00 2001 From: csf Date: Tue, 7 Feb 2023 00:11:48 +0900 Subject: [PATCH 04/20] add PenetrableOverlayState, opt chat page over remote_page --- flutter/lib/common/widgets/overlay.dart | 106 ++++++++++++++---- flutter/lib/desktop/pages/remote_page.dart | 89 ++++++++------- .../lib/desktop/widgets/remote_menubar.dart | 13 ++- flutter/lib/models/chat_model.dart | 75 +++++-------- 4 files changed, 177 insertions(+), 106 deletions(-) diff --git a/flutter/lib/common/widgets/overlay.dart b/flutter/lib/common/widgets/overlay.dart index d84789d9c..3e248700f 100644 --- a/flutter/lib/common/widgets/overlay.dart +++ b/flutter/lib/common/widgets/overlay.dart @@ -1,7 +1,7 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; -import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart'; +import 'package:get/get.dart'; import 'package:provider/provider.dart'; import '../../consts.dart'; @@ -92,31 +92,30 @@ class DraggableChatWindow extends StatelessWidget { bottom: BorderSide( color: Theme.of(context).hintColor.withOpacity(0.4)))), height: 38, - child: Obx(() => Opacity( - opacity: chatModel.isWindowFocus.value ? 1.0 : 0.4, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 15, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8), + child: Obx(() => Opacity( + opacity: chatModel.isWindowFocus.value ? 1.0 : 0.4, child: Row(children: [ Icon(Icons.chat_bubble_outline, size: 20, color: Theme.of(context).colorScheme.primary), SizedBox(width: 6), Text(translate("Chat")) - ])), - Padding( - padding: EdgeInsets.all(2), - child: ActionIcon( - message: 'Close', - icon: IconFont.close, - onTap: chatModel.hideChatWindowOverlay, - isClose: true, - boxSize: 32, - )) - ], - ))), + ])))), + Padding( + padding: EdgeInsets.all(2), + child: ActionIcon( + message: 'Close', + icon: IconFont.close, + onTap: chatModel.hideChatWindowOverlay, + isClose: true, + boxSize: 32, + )) + ], + ), ); } } @@ -372,3 +371,68 @@ class QualityMonitor extends StatelessWidget { ) : const SizedBox.shrink())); } + +class PenetrableOverlayState { + final _middleBlocked = false.obs; + final _overlayKey = GlobalKey(); + + VoidCallback? onMiddleBlockedClick; // to-do use listener + + RxBool get middleBlocked => _middleBlocked; + GlobalKey get overlayKey => _overlayKey; + OverlayState? get overlayState => _overlayKey.currentState; + + OverlayState? getOverlayStateOrGlobal() { + if (overlayState == null) { + if (globalKey.currentState == null || + globalKey.currentState!.overlay == null) return null; + return globalKey.currentState!.overlay; + } else { + return overlayState; + } + } + + void addMiddleBlockedListener(void Function(bool) cb) { + _middleBlocked.listen(cb); + } + + void setMiddleBlocked(bool blocked) { + if (blocked != _middleBlocked.value) { + _middleBlocked.value = blocked; + } + } +} + +class PenetrableOverlay extends StatelessWidget { + final Widget underlying; + final List? upperLayer; + + final PenetrableOverlayState state; + + PenetrableOverlay( + {required this.underlying, required this.state, this.upperLayer}); + + @override + Widget build(BuildContext context) { + final initialEntries = [ + OverlayEntry(builder: (_) => underlying), + + /// middle layer + OverlayEntry( + builder: (context) => Obx(() => Listener( + onPointerDown: (_) { + state.onMiddleBlockedClick?.call(); + }, + child: Container( + color: state.middleBlocked.value + ? Colors.red.withOpacity(0.3) + : null)))), + ]; + + if (upperLayer != null) { + initialEntries.addAll(upperLayer!); + } + + return Overlay(key: state.overlayKey, initialEntries: initialEntries); + } +} diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 2e4668159..4bda68c2d 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -62,6 +62,8 @@ class _RemotePageState extends State late RxBool _remoteCursorMoved; late RxBool _keyboardEnabled; + final overlayState = PenetrableOverlayState(); + final FocusNode _rawKeyFocusNode = FocusNode(debugLabel: "rawkeyFocusNode"); Function(bool)? _onEnterOrLeaveImage4Menubar; @@ -133,6 +135,12 @@ class _RemotePageState extends State // }); // _isCustomCursorInited = true; // } + + _ffi.chatModel.setPenetrableOverlayState(overlayState); + // make remote page penetrable automatically, effective for chat over remote + overlayState.onMiddleBlockedClick = () { + overlayState.setMiddleBlocked(false); + }; } @override @@ -192,39 +200,47 @@ class _RemotePageState extends State Widget buildBody(BuildContext context) { return Scaffold( - backgroundColor: Theme.of(context).backgroundColor, - body: Overlay( - initialEntries: [ - OverlayEntry(builder: (context) { - _ffi.chatModel.setOverlayState(Overlay.of(context)); - _ffi.dialogManager.setOverlayState(Overlay.of(context)); - return Container( - color: Colors.black, - child: RawKeyFocusScope( - focusNode: _rawKeyFocusNode, - onFocusChange: (bool imageFocused) { - debugPrint( - "onFocusChange(window active:${!_isWindowBlur}) $imageFocused"); - // See [onWindowBlur]. - if (Platform.isWindows) { - if (_isWindowBlur) { - imageFocused = false; - Future.delayed(Duration.zero, () { - _rawKeyFocusNode.unfocus(); - }); - } - if (imageFocused) { - _ffi.inputModel.enterOrLeave(true); - } else { - _ffi.inputModel.enterOrLeave(false); - } - } - }, - inputModel: _ffi.inputModel, - child: getBodyForDesktop(context))); - }) - ], - )); + backgroundColor: Theme.of(context).backgroundColor, + body: PenetrableOverlay( + state: overlayState, + underlying: Container( + color: Colors.black, + child: RawKeyFocusScope( + focusNode: _rawKeyFocusNode, + onFocusChange: (bool imageFocused) { + debugPrint( + "onFocusChange(window active:${!_isWindowBlur}) $imageFocused"); + // See [onWindowBlur]. + if (Platform.isWindows) { + if (_isWindowBlur) { + imageFocused = false; + Future.delayed(Duration.zero, () { + _rawKeyFocusNode.unfocus(); + }); + } + if (imageFocused) { + _ffi.inputModel.enterOrLeave(true); + } else { + _ffi.inputModel.enterOrLeave(false); + } + } + }, + inputModel: _ffi.inputModel, + child: getBodyForDesktop(context))), + upperLayer: [ + OverlayEntry( + builder: (context) => RemoteMenubar( + id: widget.id, + ffi: _ffi, + state: widget.menubarState, + onEnterOrLeaveImageSetter: (func) => + _onEnterOrLeaveImage4Menubar = func, + onEnterOrLeaveImageCleaner: () => + _onEnterOrLeaveImage4Menubar = null, + )) + ], + ), + ); } @override @@ -345,13 +361,6 @@ class _RemotePageState extends State QualityMonitor(_ffi.qualityMonitorModel), null, null), ), ); - paints.add(RemoteMenubar( - id: widget.id, - ffi: _ffi, - state: widget.menubarState, - onEnterOrLeaveImageSetter: (func) => _onEnterOrLeaveImage4Menubar = func, - onEnterOrLeaveImageCleaner: () => _onEnterOrLeaveImage4Menubar = null, - )); return Stack( children: paints, ); diff --git a/flutter/lib/desktop/widgets/remote_menubar.dart b/flutter/lib/desktop/widgets/remote_menubar.dart index 64d289fcc..6ad030464 100644 --- a/flutter/lib/desktop/widgets/remote_menubar.dart +++ b/flutter/lib/desktop/widgets/remote_menubar.dart @@ -297,12 +297,23 @@ class _RemoteMenubarState extends State { ); } + final _chatButtonKey = GlobalKey(); Widget _buildChat(BuildContext context) { return IconButton( + key: _chatButtonKey, tooltip: translate('Chat'), onPressed: () { + RenderBox? renderBox = + _chatButtonKey.currentContext?.findRenderObject() as RenderBox?; + + Offset? initPos; + if (renderBox != null) { + final pos = renderBox.localToGlobal(Offset.zero); + initPos = Offset(pos.dx, pos.dy + _MenubarTheme.dividerHeight); + } + widget.ffi.chatModel.changeCurrentID(ChatModel.clientModeID); - widget.ffi.chatModel.toggleChatOverlay(); + widget.ffi.chatModel.toggleChatOverlay(chatInitPos: initPos); }, icon: const Icon( Icons.message, diff --git a/flutter/lib/models/chat_model.dart b/flutter/lib/models/chat_model.dart index dd35bd22f..b61ce79a7 100644 --- a/flutter/lib/models/chat_model.dart +++ b/flutter/lib/models/chat_model.dart @@ -5,7 +5,6 @@ import 'package:draggable_float_widget/draggable_float_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:get/get_rx/src/rx_types/rx_types.dart'; -import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart'; import 'package:window_manager/window_manager.dart'; import '../consts.dart'; @@ -30,16 +29,12 @@ class MessageBody { class ChatModel with ChangeNotifier { static final clientModeID = -1; - /// _overlayState: - /// Desktop: store session overlay by using [setOverlayState]. - /// Mobile: always null, use global overlay. - /// see [_getOverlayState] in [showChatIconOverlay] or [showChatWindowOverlay] - OverlayState? _overlayState; OverlayEntry? chatIconOverlayEntry; OverlayEntry? chatWindowOverlayEntry; bool isConnManager = false; RxBool isWindowFocus = true.obs; + PenetrableOverlayState? pOverlayState; final ChatUser me = ChatUser( id: "", @@ -58,6 +53,19 @@ class ChatModel with ChangeNotifier { bool get isShowCMChatPage => _isShowCMChatPage; + void setPenetrableOverlayState(PenetrableOverlayState state) { + pOverlayState = state; + + pOverlayState!.addMiddleBlockedListener((v) { + if (!v) { + isWindowFocus.value = false; + if (isWindowFocus.value) { + isWindowFocus.toggle(); + } + } + }); + } + final WeakReference parent; ChatModel(this.parent); @@ -74,20 +82,6 @@ class ChatModel with ChangeNotifier { } } - setOverlayState(OverlayState? os) { - _overlayState = os; - } - - OverlayState? _getOverlayState() { - if (_overlayState == null) { - if (globalKey.currentState == null || - globalKey.currentState!.overlay == null) return null; - return globalKey.currentState!.overlay; - } else { - return _overlayState; - } - } - showChatIconOverlay({Offset offset = const Offset(200, 50)}) { if (chatIconOverlayEntry != null) { chatIconOverlayEntry!.remove(); @@ -100,7 +94,7 @@ class ChatModel with ChangeNotifier { } } - final overlayState = _getOverlayState(); + final overlayState = pOverlayState?.getOverlayStateOrGlobal(); if (overlayState == null) return; final overlay = OverlayEntry(builder: (context) { @@ -132,33 +126,26 @@ class ChatModel with ChangeNotifier { } } - showChatWindowOverlay() { + showChatWindowOverlay({Offset? chatInitPos}) { if (chatWindowOverlayEntry != null) return; - final overlayState = _getOverlayState(); + isWindowFocus.value = true; + pOverlayState?.setMiddleBlocked(true); + + final overlayState = pOverlayState?.getOverlayStateOrGlobal(); if (overlayState == null) return; final overlay = OverlayEntry(builder: (context) { - bool innerClicked = false; return Listener( onPointerDown: (_) { - if (!innerClicked) { - isWindowFocus.value = false; + if (!isWindowFocus.value) { + isWindowFocus.value = true; + pOverlayState?.setMiddleBlocked(true); } - innerClicked = false; }, - child: Obx(() => Container( - color: isWindowFocus.value ? Colors.red.withOpacity(0.3) : null, - child: Listener( - onPointerDown: (_) { - innerClicked = true; - if (!isWindowFocus.value) { - isWindowFocus.value = true; - } - }, - child: DraggableChatWindow( - position: const Offset(20, 80), - width: 250, - height: 350, - chatModel: this))))); + child: DraggableChatWindow( + position: chatInitPos ?? Offset(20, 80), + width: 250, + height: 350, + chatModel: this)); }); overlayState.insert(overlay); chatWindowOverlayEntry = overlay; @@ -167,6 +154,7 @@ class ChatModel with ChangeNotifier { hideChatWindowOverlay() { if (chatWindowOverlayEntry != null) { + pOverlayState?.setMiddleBlocked(false); chatWindowOverlayEntry!.remove(); chatWindowOverlayEntry = null; return; @@ -176,13 +164,13 @@ class ChatModel with ChangeNotifier { _isChatOverlayHide() => ((!isDesktop && chatIconOverlayEntry == null) || chatWindowOverlayEntry == null); - toggleChatOverlay() { + toggleChatOverlay({Offset? chatInitPos}) { if (_isChatOverlayHide()) { gFFI.invokeMethod("enable_soft_keyboard", true); if (!isDesktop) { showChatIconOverlay(); } - showChatWindowOverlay(); + showChatWindowOverlay(chatInitPos: chatInitPos); } else { hideChatIconOverlay(); hideChatWindowOverlay(); @@ -310,7 +298,6 @@ class ChatModel with ChangeNotifier { close() { hideChatIconOverlay(); hideChatWindowOverlay(); - _overlayState = null; notifyListeners(); } From 948f9f28dbbd2846e8026f595021d7fb2a7c0b73 Mon Sep 17 00:00:00 2001 From: NicKoehler <53040044+NicKoehler@users.noreply.github.com> Date: Wed, 8 Feb 2023 10:15:08 +0100 Subject: [PATCH 05/20] Update it.rs --- src/lang/it.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 9730bbc2d..a4ea58304 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -446,8 +446,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("FPS", "FPS"), ("Auto", "Auto"), ("Other Default Options", "Altre Opzioni Predefinite"), - ("Voice call", ""), - ("Text chat", ""), - ("Stop voice call", ""), + ("Voice call", "Chiamata vocale"), + ("Text chat", "Chat testuale"), + ("Stop voice call", "Interrompi la chiamata vocale"), ].iter().cloned().collect(); } From 7c13be587638c61ffee6fe09a82c2e114ffd2531 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 8 Feb 2023 17:26:44 +0800 Subject: [PATCH 06/20] update issue template and clippy for hbb_common --- .github/ISSUE_TEMPLATE/bug_report.yaml | 15 ++++-- libs/hbb_common/build.rs | 4 +- libs/hbb_common/src/bytes_codec.rs | 40 ++++++++-------- libs/hbb_common/src/config.rs | 6 +-- libs/hbb_common/src/lib.rs | 60 +++++++++++------------- libs/hbb_common/src/password_security.rs | 48 +++++++++---------- libs/hbb_common/src/platform/linux.rs | 2 +- libs/hbb_common/src/protos/mod.rs | 2 +- libs/hbb_common/src/socket_client.rs | 22 ++++----- libs/hbb_common/src/tcp.rs | 2 +- 10 files changed, 103 insertions(+), 98 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 16509a3be..c2d92097c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -30,13 +30,22 @@ body: description: A clear and concise description of what you expected to happen validations: required: true + - type: input + id: os + attributes: + label: Operating system(s) on local side and remote side + description: What operating system(s) do you see this bug on? local side -> remote side. + placeholder: | + Windows 10 -> osx + validations: + required: true - type: input id: version attributes: - label: Operating System(s) and RustDesk Version(s) on local side and remote side - description: What Operatiing System(s) and version(s) of RustDesk do you see this bug on? local side / remote side. + label: RustDesk Version(s) on local side and remote side + description: What RustDesk version(s) do you see this bug on? local side -> remote side. placeholder: | - Windows 10, 1.1.9 / osx 13.1, 1.1.8 + 1.1.9 -> 1.1.8 validations: required: true - type: textarea diff --git a/libs/hbb_common/build.rs b/libs/hbb_common/build.rs index fe0d31076..5ebc3a287 100644 --- a/libs/hbb_common/build.rs +++ b/libs/hbb_common/build.rs @@ -2,11 +2,11 @@ fn main() { let out_dir = format!("{}/protos", std::env::var("OUT_DIR").unwrap()); std::fs::create_dir_all(&out_dir).unwrap(); - + protobuf_codegen::Codegen::new() .pure() .out_dir(out_dir) - .inputs(&["protos/rendezvous.proto", "protos/message.proto"]) + .inputs(["protos/rendezvous.proto", "protos/message.proto"]) .include("protos") .customize(protobuf_codegen::Customize::default().tokio_bytes(true)) .run() diff --git a/libs/hbb_common/src/bytes_codec.rs b/libs/hbb_common/src/bytes_codec.rs index 699aa9bff..bfc798715 100644 --- a/libs/hbb_common/src/bytes_codec.rs +++ b/libs/hbb_common/src/bytes_codec.rs @@ -143,32 +143,32 @@ mod tests { let mut buf = BytesMut::new(); let mut bytes: Vec = Vec::new(); bytes.resize(0x3F, 1); - assert!(!codec.encode(bytes.into(), &mut buf).is_err()); + assert!(codec.encode(bytes.into(), &mut buf).is_ok()); let buf_saved = buf.clone(); assert_eq!(buf.len(), 0x3F + 1); if let Ok(Some(res)) = codec.decode(&mut buf) { assert_eq!(res.len(), 0x3F); assert_eq!(res[0], 1); } else { - assert!(false); + panic!(); } let mut codec2 = BytesCodec::new(); let mut buf2 = BytesMut::new(); if let Ok(None) = codec2.decode(&mut buf2) { } else { - assert!(false); + panic!(); } buf2.extend(&buf_saved[0..1]); if let Ok(None) = codec2.decode(&mut buf2) { } else { - assert!(false); + panic!(); } buf2.extend(&buf_saved[1..]); if let Ok(Some(res)) = codec2.decode(&mut buf2) { assert_eq!(res.len(), 0x3F); assert_eq!(res[0], 1); } else { - assert!(false); + panic!(); } } @@ -177,21 +177,21 @@ mod tests { let mut codec = BytesCodec::new(); let mut buf = BytesMut::new(); let mut bytes: Vec = Vec::new(); - assert!(!codec.encode("".into(), &mut buf).is_err()); + assert!(codec.encode("".into(), &mut buf).is_ok()); assert_eq!(buf.len(), 1); bytes.resize(0x3F + 1, 2); - assert!(!codec.encode(bytes.into(), &mut buf).is_err()); + assert!(codec.encode(bytes.into(), &mut buf).is_ok()); assert_eq!(buf.len(), 0x3F + 2 + 2); if let Ok(Some(res)) = codec.decode(&mut buf) { assert_eq!(res.len(), 0); } else { - assert!(false); + panic!(); } if let Ok(Some(res)) = codec.decode(&mut buf) { assert_eq!(res.len(), 0x3F + 1); assert_eq!(res[0], 2); } else { - assert!(false); + panic!(); } } @@ -201,13 +201,13 @@ mod tests { let mut buf = BytesMut::new(); let mut bytes: Vec = Vec::new(); bytes.resize(0x3F - 1, 3); - assert!(!codec.encode(bytes.into(), &mut buf).is_err()); + assert!(codec.encode(bytes.into(), &mut buf).is_ok()); assert_eq!(buf.len(), 0x3F + 1 - 1); if let Ok(Some(res)) = codec.decode(&mut buf) { assert_eq!(res.len(), 0x3F - 1); assert_eq!(res[0], 3); } else { - assert!(false); + panic!(); } } #[test] @@ -216,13 +216,13 @@ mod tests { let mut buf = BytesMut::new(); let mut bytes: Vec = Vec::new(); bytes.resize(0x3FFF, 4); - assert!(!codec.encode(bytes.into(), &mut buf).is_err()); + assert!(codec.encode(bytes.into(), &mut buf).is_ok()); assert_eq!(buf.len(), 0x3FFF + 2); if let Ok(Some(res)) = codec.decode(&mut buf) { assert_eq!(res.len(), 0x3FFF); assert_eq!(res[0], 4); } else { - assert!(false); + panic!(); } } @@ -232,13 +232,13 @@ mod tests { let mut buf = BytesMut::new(); let mut bytes: Vec = Vec::new(); bytes.resize(0x3FFFFF, 5); - assert!(!codec.encode(bytes.into(), &mut buf).is_err()); + assert!(codec.encode(bytes.into(), &mut buf).is_ok()); assert_eq!(buf.len(), 0x3FFFFF + 3); if let Ok(Some(res)) = codec.decode(&mut buf) { assert_eq!(res.len(), 0x3FFFFF); assert_eq!(res[0], 5); } else { - assert!(false); + panic!(); } } @@ -248,33 +248,33 @@ mod tests { let mut buf = BytesMut::new(); let mut bytes: Vec = Vec::new(); bytes.resize(0x3FFFFF + 1, 6); - assert!(!codec.encode(bytes.into(), &mut buf).is_err()); + assert!(codec.encode(bytes.into(), &mut buf).is_ok()); let buf_saved = buf.clone(); assert_eq!(buf.len(), 0x3FFFFF + 4 + 1); if let Ok(Some(res)) = codec.decode(&mut buf) { assert_eq!(res.len(), 0x3FFFFF + 1); assert_eq!(res[0], 6); } else { - assert!(false); + panic!(); } let mut codec2 = BytesCodec::new(); let mut buf2 = BytesMut::new(); buf2.extend(&buf_saved[0..1]); if let Ok(None) = codec2.decode(&mut buf2) { } else { - assert!(false); + panic!(); } buf2.extend(&buf_saved[1..6]); if let Ok(None) = codec2.decode(&mut buf2) { } else { - assert!(false); + panic!(); } buf2.extend(&buf_saved[6..]); if let Ok(Some(res)) = codec2.decode(&mut buf2) { assert_eq!(res.len(), 0x3FFFFF + 1); assert_eq!(res[0], 6); } else { - assert!(false); + panic!(); } } } diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 71dd9a5c6..1e4d80c9f 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -288,7 +288,7 @@ fn patch(path: PathBuf) -> PathBuf { .trim() .to_owned(); if user != "root" { - return format!("/home/{}", user).into(); + return format!("/home/{user}").into(); } } } @@ -525,7 +525,7 @@ impl Config { let mut path: PathBuf = format!("/tmp/{}", *APP_NAME.read().unwrap()).into(); fs::create_dir(&path).ok(); fs::set_permissions(&path, fs::Permissions::from_mode(0o0777)).ok(); - path.push(format!("ipc{}", postfix)); + path.push(format!("ipc{postfix}")); path.to_str().unwrap_or("").to_owned() } } @@ -562,7 +562,7 @@ impl Config { .unwrap_or_default(); } if !rendezvous_server.contains(':') { - rendezvous_server = format!("{}:{}", rendezvous_server, RENDEZVOUS_PORT); + rendezvous_server = format!("{rendezvous_server}:{RENDEZVOUS_PORT}"); } rendezvous_server } diff --git a/libs/hbb_common/src/lib.rs b/libs/hbb_common/src/lib.rs index c9f9e90d7..1c49adfb7 100644 --- a/libs/hbb_common/src/lib.rs +++ b/libs/hbb_common/src/lib.rs @@ -211,11 +211,7 @@ pub fn gen_version() { // generate build date let build_date = format!("{}", chrono::Local::now().format("%Y-%m-%d %H:%M")); file.write_all( - format!( - "#[allow(dead_code)]\npub const BUILD_DATE: &str = \"{}\";", - build_date - ) - .as_bytes(), + format!("#[allow(dead_code)]\npub const BUILD_DATE: &str = \"{build_date}\";\n").as_bytes(), ) .ok(); file.sync_all().ok(); @@ -342,39 +338,39 @@ mod test { #[test] fn test_ipv6() { - assert_eq!(is_ipv6_str("1:2:3"), true); - assert_eq!(is_ipv6_str("[ab:2:3]:12"), true); - assert_eq!(is_ipv6_str("[ABEF:2a:3]:12"), true); - assert_eq!(is_ipv6_str("[ABEG:2a:3]:12"), false); - assert_eq!(is_ipv6_str("1[ab:2:3]:12"), false); - assert_eq!(is_ipv6_str("1.1.1.1"), false); - assert_eq!(is_ip_str("1.1.1.1"), true); - assert_eq!(is_ipv6_str("1:2:"), false); - assert_eq!(is_ipv6_str("1:2::0"), true); - assert_eq!(is_ipv6_str("[1:2::0]:1"), true); - assert_eq!(is_ipv6_str("[1:2::0]:"), false); - assert_eq!(is_ipv6_str("1:2::0]:1"), false); + assert!(is_ipv6_str("1:2:3")); + assert!(is_ipv6_str("[ab:2:3]:12")); + assert!(is_ipv6_str("[ABEF:2a:3]:12")); + assert!(!is_ipv6_str("[ABEG:2a:3]:12")); + assert!(!is_ipv6_str("1[ab:2:3]:12")); + assert!(!is_ipv6_str("1.1.1.1")); + assert!(is_ip_str("1.1.1.1")); + assert!(!is_ipv6_str("1:2:")); + assert!(is_ipv6_str("1:2::0")); + assert!(is_ipv6_str("[1:2::0]:1")); + assert!(!is_ipv6_str("[1:2::0]:")); + assert!(!is_ipv6_str("1:2::0]:1")); } #[test] fn test_hostname_port() { - assert_eq!(is_domain_port_str("a:12"), false); - assert_eq!(is_domain_port_str("a.b.c:12"), false); - assert_eq!(is_domain_port_str("test.com:12"), true); - assert_eq!(is_domain_port_str("test-UPPER.com:12"), true); - assert_eq!(is_domain_port_str("some-other.domain.com:12"), true); - assert_eq!(is_domain_port_str("under_score:12"), false); - assert_eq!(is_domain_port_str("a@bc:12"), false); - assert_eq!(is_domain_port_str("1.1.1.1:12"), false); - assert_eq!(is_domain_port_str("1.2.3:12"), false); - assert_eq!(is_domain_port_str("1.2.3.45:12"), false); - assert_eq!(is_domain_port_str("a.b.c:123456"), false); - assert_eq!(is_domain_port_str("---:12"), false); - assert_eq!(is_domain_port_str(".:12"), false); + assert!(!is_domain_port_str("a:12")); + assert!(!is_domain_port_str("a.b.c:12")); + assert!(is_domain_port_str("test.com:12")); + assert!(is_domain_port_str("test-UPPER.com:12")); + assert!(is_domain_port_str("some-other.domain.com:12")); + assert!(!is_domain_port_str("under_score:12")); + assert!(!is_domain_port_str("a@bc:12")); + assert!(!is_domain_port_str("1.1.1.1:12")); + assert!(!is_domain_port_str("1.2.3:12")); + assert!(!is_domain_port_str("1.2.3.45:12")); + assert!(!is_domain_port_str("a.b.c:123456")); + assert!(!is_domain_port_str("---:12")); + assert!(!is_domain_port_str(".:12")); // todo: should we also check for these edge cases? // out-of-range port - assert_eq!(is_domain_port_str("test.com:0"), true); - assert_eq!(is_domain_port_str("test.com:98989"), true); + assert!(is_domain_port_str("test.com:0")); + assert!(is_domain_port_str("test.com:98989")); } #[test] diff --git a/libs/hbb_common/src/password_security.rs b/libs/hbb_common/src/password_security.rs index 0b66107fc..ddfe28baa 100644 --- a/libs/hbb_common/src/password_security.rs +++ b/libs/hbb_common/src/password_security.rs @@ -192,51 +192,51 @@ mod test { let data = "Hello World"; let encrypted = encrypt_str_or_original(data, version); let (decrypted, succ, store) = decrypt_str_or_original(&encrypted, version); - println!("data: {}", data); - println!("encrypted: {}", encrypted); - println!("decrypted: {}", decrypted); + println!("data: {data}"); + println!("encrypted: {encrypted}"); + println!("decrypted: {decrypted}"); assert_eq!(data, decrypted); assert_eq!(version, &encrypted[..2]); - assert_eq!(succ, true); - assert_eq!(store, false); + assert!(succ); + assert!(!store); let (_, _, store) = decrypt_str_or_original(&encrypted, "99"); - assert_eq!(store, true); - assert_eq!(decrypt_str_or_original(&decrypted, version).1, false); + assert!(store); + assert!(!decrypt_str_or_original(&decrypted, version).1); assert_eq!(encrypt_str_or_original(&encrypted, version), encrypted); println!("test vec"); let data: Vec = vec![1, 2, 3, 4, 5, 6]; let encrypted = encrypt_vec_or_original(&data, version); let (decrypted, succ, store) = decrypt_vec_or_original(&encrypted, version); - println!("data: {:?}", data); - println!("encrypted: {:?}", encrypted); - println!("decrypted: {:?}", decrypted); + println!("data: {data:?}"); + println!("encrypted: {encrypted:?}"); + println!("decrypted: {decrypted:?}"); assert_eq!(data, decrypted); assert_eq!(version.as_bytes(), &encrypted[..2]); - assert_eq!(store, false); - assert_eq!(succ, true); + assert!(!store); + assert!(succ); let (_, _, store) = decrypt_vec_or_original(&encrypted, "99"); - assert_eq!(store, true); - assert_eq!(decrypt_vec_or_original(&decrypted, version).1, false); + assert!(store); + assert!(!decrypt_vec_or_original(&decrypted, version).1); assert_eq!(encrypt_vec_or_original(&encrypted, version), encrypted); println!("test original"); let data = version.to_string() + "Hello World"; let (decrypted, succ, store) = decrypt_str_or_original(&data, version); assert_eq!(data, decrypted); - assert_eq!(store, true); - assert_eq!(succ, false); + assert!(store); + assert!(!succ); let verbytes = version.as_bytes(); - let data: Vec = vec![verbytes[0] as u8, verbytes[1] as u8, 1, 2, 3, 4, 5, 6]; + let data: Vec = vec![verbytes[0], verbytes[1], 1, 2, 3, 4, 5, 6]; let (decrypted, succ, store) = decrypt_vec_or_original(&data, version); assert_eq!(data, decrypted); - assert_eq!(store, true); - assert_eq!(succ, false); + assert!(store); + assert!(!succ); let (_, succ, store) = decrypt_str_or_original("", version); - assert_eq!(store, false); - assert_eq!(succ, false); - let (_, succ, store) = decrypt_vec_or_original(&vec![], version); - assert_eq!(store, false); - assert_eq!(succ, false); + assert!(!store); + assert!(!succ); + let (_, succ, store) = decrypt_vec_or_original(&[], version); + assert!(!store); + assert!(!succ); } } diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 716025dc7..7c107d11c 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -60,7 +60,7 @@ fn get_display_server_of_session(session: &str) -> String { .replace("TTY=", "") .trim_end() .into(); - if let Ok(xorg_results) = run_cmds(format!("ps -e | grep \"{}.\\\\+Xorg\"", tty)) + if let Ok(xorg_results) = run_cmds(format!("ps -e | grep \"{tty}.\\\\+Xorg\"")) // And check if Xorg is running on that tty { if xorg_results.trim_end() != "" { diff --git a/libs/hbb_common/src/protos/mod.rs b/libs/hbb_common/src/protos/mod.rs index c001c58fb..57d9b68fe 100644 --- a/libs/hbb_common/src/protos/mod.rs +++ b/libs/hbb_common/src/protos/mod.rs @@ -1 +1 @@ -include!(concat!(env!("OUT_DIR"), "/protos/mod.rs")); \ No newline at end of file +include!(concat!(env!("OUT_DIR"), "/protos/mod.rs")); diff --git a/libs/hbb_common/src/socket_client.rs b/libs/hbb_common/src/socket_client.rs index a034b4e12..2d9b5a984 100644 --- a/libs/hbb_common/src/socket_client.rs +++ b/libs/hbb_common/src/socket_client.rs @@ -13,22 +13,22 @@ use tokio_socks::{IntoTargetAddr, TargetAddr}; pub fn check_port(host: T, port: i32) -> String { let host = host.to_string(); if crate::is_ipv6_str(&host) { - if host.starts_with("[") { + if host.starts_with('[') { return host; } - return format!("[{}]:{}", host, port); + return format!("[{host}]:{port}"); } - if !host.contains(":") { - return format!("{}:{}", host, port); + if !host.contains(':') { + return format!("{host}:{port}"); } - return host; + host } #[inline] pub fn increase_port(host: T, offset: i32) -> String { let host = host.to_string(); if crate::is_ipv6_str(&host) { - if host.starts_with("[") { + if host.starts_with('[') { let tmp: Vec<&str> = host.split("]:").collect(); if tmp.len() == 2 { let port: i32 = tmp[1].parse().unwrap_or(0); @@ -37,8 +37,8 @@ pub fn increase_port(host: T, offset: i32) -> String { } } } - } else if host.contains(":") { - let tmp: Vec<&str> = host.split(":").collect(); + } else if host.contains(':') { + let tmp: Vec<&str> = host.split(':').collect(); if tmp.len() == 2 { let port: i32 = tmp[1].parse().unwrap_or(0); if port > 0 { @@ -46,7 +46,7 @@ pub fn increase_port(host: T, offset: i32) -> String { } } } - return host; + host } pub fn test_if_valid_server(host: &str) -> String { @@ -148,7 +148,7 @@ pub async fn query_nip_io(addr: &SocketAddr) -> ResultType { pub fn ipv4_to_ipv6(addr: String, ipv4: bool) -> String { if !ipv4 && crate::is_ipv4_str(&addr) { if let Some(ip) = addr.split(':').next() { - return addr.replace(ip, &format!("{}.nip.io", ip)); + return addr.replace(ip, &format!("{ip}.nip.io")); } } addr @@ -163,7 +163,7 @@ async fn test_target(target: &str) -> ResultType { tokio::net::lookup_host(target) .await? .next() - .context(format!("Failed to look up host for {}", target)) + .context(format!("Failed to look up host for {target}")) } #[inline] diff --git a/libs/hbb_common/src/tcp.rs b/libs/hbb_common/src/tcp.rs index a7ac4eb3a..f574e8309 100644 --- a/libs/hbb_common/src/tcp.rs +++ b/libs/hbb_common/src/tcp.rs @@ -100,7 +100,7 @@ impl FramedStream { } } } - bail!(format!("Failed to connect to {}", remote_addr)); + bail!(format!("Failed to connect to {remote_addr}")); } pub async fn connect<'a, 't, P, T>( From 4134b77680126f408e5ce88f7eb4c3ad5711b749 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 8 Feb 2023 19:17:59 +0800 Subject: [PATCH 07/20] improve ffi enum data size, fix compile warning on mac --- src/client/io_loop.rs | 2 +- src/common.rs | 7 +---- src/core_main.rs | 4 +-- src/ipc.rs | 28 +++++++++++++++---- src/keyboard.rs | 6 ++-- src/server.rs | 62 +++++++++++++++++++++--------------------- src/ui/macos.rs | 9 ++---- src/ui_cm_interface.rs | 4 ++- 8 files changed, 65 insertions(+), 57 deletions(-) diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index f5792bce3..5186aff4d 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -25,7 +25,7 @@ use hbb_common::{allow_err, get_time, message_proto::*, sleep}; use hbb_common::{fs, log, Stream}; use crate::client::{ - new_voice_call_request, Client, CodecFormat, LoginConfigHandler, MediaData, MediaSender, + new_voice_call_request, Client, CodecFormat, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, SERVER_CLIPBOARD_ENABLED, SERVER_FILE_TRANSFER_ENABLED, SERVER_KEYBOARD_ENABLED, }; diff --git a/src/common.rs b/src/common.rs index 2142d973d..79a4664db 100644 --- a/src/common.rs +++ b/src/common.rs @@ -30,7 +30,7 @@ use hbb_common::{ // #[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))] use hbb_common::{config::RENDEZVOUS_PORT, futures::future::join_all}; -use crate::ui_interface::{set_option, get_option}; +use crate::ui_interface::{get_option, set_option}; pub type NotifyMessageBox = fn(String, String, String, String) -> dyn Future; @@ -762,8 +762,3 @@ pub fn make_fd_to_json(id: i32, path: String, entries: &Vec) -> Strin fd_json.insert("entries".into(), json!(entries_out)); serde_json::to_string(&fd_json).unwrap_or("".into()) } - -#[cfg(test)] -mod test_common { - -} diff --git a/src/core_main.rs b/src/core_main.rs index 03d057eff..0af7026e9 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -1,6 +1,4 @@ -use std::future::Future; - -use hbb_common::{log, ResultType}; +use hbb_common::log; /// shared by flutter and sciter main function /// diff --git a/src/ipc.rs b/src/ipc.rs index 0ede560fc..699b0bcd7 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -16,10 +16,10 @@ use hbb_common::{ config::{self, Config, Config2}, futures::StreamExt as _, futures_util::sink::SinkExt, - log, password_security as password, ResultType, timeout, - tokio, + log, password_security as password, timeout, tokio, tokio::io::{AsyncRead, AsyncWrite}, tokio_util::codec::Framed, + ResultType, }; use crate::rendezvous_mediator::RendezvousMediator; @@ -190,7 +190,7 @@ pub enum Data { Socks(Option), FS(FS), Test, - SyncConfig(Option<(Config, Config2)>), + SyncConfig(Option>), #[cfg(not(any(target_os = "android", target_os = "ios")))] ClipboardFile(ClipboardFile), ClipboardFileEnabled(bool), @@ -419,7 +419,8 @@ async fn handle(data: Data, stream: &mut Connection) { let t = Config::get_nat_type(); allow_err!(stream.send(&Data::NatType(Some(t))).await); } - Data::SyncConfig(Some((config, config2))) => { + Data::SyncConfig(Some(configs)) => { + let (config, config2) = *configs; let _chk = CheckIfRestart::new(); Config::set(config); Config2::set(config2); @@ -428,7 +429,9 @@ async fn handle(data: Data, stream: &mut Connection) { Data::SyncConfig(None) => { allow_err!( stream - .send(&Data::SyncConfig(Some((Config::get(), Config2::get())))) + .send(&Data::SyncConfig(Some( + (Config::get(), Config2::get()).into() + ))) .await ); } @@ -840,6 +843,19 @@ pub async fn test_rendezvous_server() -> ResultType<()> { #[tokio::main(flavor = "current_thread")] pub async fn send_url_scheme(url: String) -> ResultType<()> { - connect(1_000, "_url").await?.send(&Data::UrlLink(url)).await?; + connect(1_000, "_url") + .await? + .send(&Data::UrlLink(url)) + .await?; Ok(()) } + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn verify_ffi_enum_data_size() { + println!("{}", std::mem::size_of::()); + assert!(std::mem::size_of::() < 96); + } +} diff --git a/src/keyboard.rs b/src/keyboard.rs index 91480ba30..17c52abf7 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -212,7 +212,7 @@ pub fn start_grab_loop() { } let mut _keyboard_mode = KeyboardMode::Map; - let scan_code = event.scan_code; + let _scan_code = event.scan_code; let res = if KEYBOARD_HOOKED.load(Ordering::SeqCst) { _keyboard_mode = client::process_event(&event, None); if is_press { @@ -225,7 +225,7 @@ pub fn start_grab_loop() { }; #[cfg(target_os = "windows")] - match scan_code { + match _scan_code { 0x1D | 0x021D => rdev::set_modifier(Key::ControlLeft, is_press), 0xE01D => rdev::set_modifier(Key::ControlRight, is_press), 0x2A => rdev::set_modifier(Key::ShiftLeft, is_press), @@ -241,7 +241,7 @@ pub fn start_grab_loop() { #[cfg(target_os = "windows")] unsafe { // AltGr - if scan_code == 0x021D { + if _scan_code == 0x021D { IS_0X021D_DOWN = is_press; } } diff --git a/src/server.rs b/src/server.rs index 616d92375..7807c4fac 100644 --- a/src/server.rs +++ b/src/server.rs @@ -8,6 +8,9 @@ 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::new_listener; use hbb_common::{ allow_err, anyhow::{anyhow, Context}, @@ -17,18 +20,15 @@ use hbb_common::{ message_proto::*, protobuf::{Enum, Message as _}, rendezvous_proto::*, - ResultType, socket_client, - sodiumoxide::crypto::{box_, secretbox, sign}, Stream, timeout, tokio, + sodiumoxide::crypto::{box_, secretbox, sign}, + timeout, tokio, ResultType, Stream, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] -use hbb_common::config::Config2; -use hbb_common::tcp::new_listener; -use service::{GenericService, Service, Subscriber}; -#[cfg(not(any(target_os = "android", target_os = "ios")))] use service::ServiceTmpl; +use service::{GenericService, Service, Subscriber}; -use crate::ipc::{connect, Data}; +use crate::ipc::Data; pub mod audio_service; cfg_if::cfg_if! { @@ -65,7 +65,7 @@ type ConnMap = HashMap; lazy_static::lazy_static! { pub static ref CHILD_PROCESS: Childs = Default::default(); pub static ref CONN_COUNT: Arc> = Default::default(); - // A client server used to provide local services(audio, video, clipboard, etc.) + // A client server used to provide local services(audio, video, clipboard, etc.) // for all initiative connections. // // [Note] @@ -420,7 +420,8 @@ pub async fn start_server(is_server: bool) { if conn.send(&Data::SyncConfig(None)).await.is_ok() { if let Ok(Some(data)) = conn.next_timeout(1000).await { match data { - Data::SyncConfig(Some((config, config2))) => { + Data::SyncConfig(Some(configs)) => { + let (config, config2) = *configs; if Config::set(config) { log::info!("config synced"); } @@ -450,28 +451,26 @@ pub async fn start_ipc_url_server() { while let Some(Ok(conn)) = incoming.next().await { let mut conn = crate::ipc::Connection::new(conn); match conn.next_timeout(1000).await { - Ok(Some(data)) => { - match data { - Data::UrlLink(url) => { - #[cfg(feature = "flutter")] - { - if let Some(stream) = crate::flutter::GLOBAL_EVENT_STREAM.read().unwrap().get( - crate::flutter::APP_TYPE_MAIN - ) { - let mut m = HashMap::new(); - m.insert("name", "on_url_scheme_received"); - m.insert("url", url.as_str()); - stream.add(serde_json::to_string(&m).unwrap()); - } else { - log::warn!("No main window app found!"); - } - } - } - _ => { - log::warn!("An unexpected data was sent to the ipc url server.") + Ok(Some(data)) => match data { + #[cfg(feature = "flutter")] + Data::UrlLink(url) => { + if let Some(stream) = crate::flutter::GLOBAL_EVENT_STREAM + .read() + .unwrap() + .get(crate::flutter::APP_TYPE_MAIN) + { + let mut m = HashMap::new(); + m.insert("name", "on_url_scheme_received"); + m.insert("url", url.as_str()); + stream.add(serde_json::to_string(&m).unwrap()); + } else { + log::warn!("No main window app found!"); } } - } + _ => { + log::warn!("An unexpected data was sent to the ipc url server.") + } + }, Err(err) => { log::error!("{}", err); } @@ -509,7 +508,8 @@ async fn sync_and_watch_config_dir() { if conn.send(&Data::SyncConfig(None)).await.is_ok() { if let Ok(Some(data)) = conn.next_timeout(1000).await { match data { - Data::SyncConfig(Some((config, config2))) => { + Data::SyncConfig(Some(configs)) => { + let (config, config2) = *configs; let _chk = crate::ipc::CheckIfRestart::new(); if cfg0.0 != config { cfg0.0 = config.clone(); @@ -534,7 +534,7 @@ async fn sync_and_watch_config_dir() { let cfg = (Config::get(), Config2::get()); if cfg != cfg0 { log::info!("config updated, sync to root"); - match conn.send(&Data::SyncConfig(Some(cfg.clone()))).await { + match conn.send(&Data::SyncConfig(Some(cfg.clone().into()))).await { Err(e) => { log::error!("sync config to root failed: {}", e); break; diff --git a/src/ui/macos.rs b/src/ui/macos.rs index 98e355dc1..f34b7c2c1 100644 --- a/src/ui/macos.rs +++ b/src/ui/macos.rs @@ -14,12 +14,9 @@ use objc::{ sel, sel_impl, }; use objc::runtime::Class; -use objc_id::WeakId; use sciter::{Host, make_args}; -use hbb_common::{log, tokio}; - -use crate::ui_cm_interface::start_ipc; +use hbb_common::log; static APP_HANDLER_IVAR: &str = "GoDeskAppHandler"; @@ -141,7 +138,7 @@ extern "C" fn application_should_handle_open_untitled_file( if !LAUNCHED { return YES; } - hbb_common::log::debug!("icon clicked on finder"); + log::debug!("icon clicked on finder"); if std::env::args().nth(1) == Some("--server".to_owned()) { crate::platform::macos::check_main_window(); } @@ -267,4 +264,4 @@ pub fn make_tray() { set_delegate(None); } crate::tray::make_tray(); -} \ No newline at end of file +} diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index ccddab0ee..de33b0169 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -845,6 +845,7 @@ pub fn elevate_portable(_id: i32) { } } +#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] #[inline] pub fn handle_incoming_voice_call(id: i32, accept: bool) { if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) { @@ -852,9 +853,10 @@ pub fn handle_incoming_voice_call(id: i32, accept: bool) { }; } +#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] #[inline] pub fn close_voice_call(id: i32) { if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) { allow_err!(client.tx.send(Data::CloseVoiceCall("".to_owned()))); }; -} \ No newline at end of file +} From 1588e44d61b255ed3eb99529e90dd7e18e4779d9 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 8 Feb 2023 19:17:43 +0800 Subject: [PATCH 08/20] win, translate mode, fix dead key Signed-off-by: fufesou --- Cargo.lock | 2 +- src/keyboard.rs | 21 +++++++++++++-------- src/ui_session_interface.rs | 6 +++--- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c53c573f2..83f623ca7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4405,7 +4405,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev#4d8231f05e14c5a04cd7d2c1288e87ad52d39e4c" +source = "git+https://github.com/fufesou/rdev#cedc4e62744566775026af4b434ef799804c1130" dependencies = [ "cocoa", "core-foundation 0.9.3", diff --git a/src/keyboard.rs b/src/keyboard.rs index 17c52abf7..5b9920714 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -193,8 +193,8 @@ pub mod client { #[cfg(windows)] pub fn update_grab_get_key_name() { match get_keyboard_mode_enum() { - KeyboardMode::Map => rdev::set_get_key_name(false), - KeyboardMode::Translate => rdev::set_get_key_name(true), + KeyboardMode::Map => rdev::set_get_key_unicode(false), + KeyboardMode::Translate => rdev::set_get_key_unicode(true), _ => {} }; } @@ -256,6 +256,7 @@ pub fn start_grab_loop() { if let Err(error) = rdev::grab(func) { log::error!("rdev Error: {:?}", error) } + rdev::set_event_popup(false); }); #[cfg(target_os = "linux")] @@ -757,12 +758,10 @@ pub fn map_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Option) { match &event.unicode { Some(unicode_info) => { - if !unicode_info.is_dead { - for code in &unicode_info.unicode { - let mut evt = key_event.clone(); - evt.set_unicode(*code as _); - events.push(evt); - } + for code in &unicode_info.unicode { + let mut evt = key_event.clone(); + evt.set_unicode(*code as _); + events.push(evt); } } None => {} @@ -816,6 +815,12 @@ pub fn translate_virtual_keycode(event: &Event, mut key_event: KeyEvent) -> Opti pub fn translate_keyboard_mode(event: &Event, key_event: KeyEvent) -> Vec { let mut events: Vec = Vec::new(); + if let Some(unicode_info) = &event.unicode { + if unicode_info.is_dead { + return events; + } + } + #[cfg(target_os = "windows")] unsafe { if event.scan_code == 0x021D { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index dc0e365ab..87ea8e9eb 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -368,8 +368,8 @@ impl Session { #[cfg(target_os = "windows")] { match &self.lc.read().unwrap().keyboard_mode as _ { - "legacy" => rdev::set_get_key_name(true), - "translate" => rdev::set_get_key_name(true), + "legacy" => rdev::set_get_key_unicode(true), + "translate" => rdev::set_get_key_unicode(true), _ => {} } } @@ -381,7 +381,7 @@ impl Session { pub fn leave(&self) { #[cfg(target_os = "windows")] { - rdev::set_get_key_name(false); + rdev::set_get_key_unicode(false); } IS_IN.store(false, Ordering::SeqCst); keyboard::client::change_grab_status(GrabState::Wait); From c049e728fd3f49db3b99338c377131294ce90473 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 8 Feb 2023 19:25:25 +0800 Subject: [PATCH 09/20] suppress warns Signed-off-by: fufesou --- src/flutter_ffi.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 2e6c450c1..bb1b8b8b9 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -928,7 +928,7 @@ pub fn main_start_dbus_server() { { use crate::dbus::start_dbus_server; // spawn new thread to start dbus server - std::thread::spawn(|| { + thread::spawn(|| { let _ = start_dbus_server(); }); } @@ -1275,7 +1275,7 @@ pub fn main_is_login_wayland() -> SyncReturn { pub fn main_start_pa() { #[cfg(target_os = "linux")] - std::thread::spawn(crate::ipc::start_pa); + thread::spawn(crate::ipc::start_pa); } pub fn main_hide_docker() -> SyncReturn { @@ -1302,9 +1302,9 @@ pub fn main_start_ipc_url_server() { /// /// * macOS only #[allow(unused_variables)] -pub fn send_url_scheme(url: String) { +pub fn send_url_scheme(_url: String) { #[cfg(target_os = "macos")] - thread::spawn(move || crate::ui::macos::handle_url_scheme(url)); + thread::spawn(move || crate::ui::macos::handle_url_scheme(_url)); } #[cfg(target_os = "android")] @@ -1324,7 +1324,7 @@ pub mod server_side { _class: JClass, ) { log::debug!("startServer from java"); - std::thread::spawn(move || start_server(true)); + thread::spawn(move || start_server(true)); } #[no_mangle] From 3a0137a3f71bef19fa3a0e440f3025c860cfcaf3 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 8 Feb 2023 19:45:15 +0800 Subject: [PATCH 10/20] suppress warns Signed-off-by: fufesou --- src/flutter_ffi.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index bb1b8b8b9..ec4a90973 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1,6 +1,9 @@ -use std::{collections::HashMap, ffi::{CStr, CString}, os::raw::c_char, thread}; +use std::{collections::HashMap, ffi::{CStr, CString}, os::raw::c_char}; use std::str::FromStr; +#[cfg(any(target_os = "linux", target_os = "macos"))] +use std::thread; + use flutter_rust_bridge::{StreamSink, SyncReturn, ZeroCopyBuffer}; use serde_json::json; From 2feed1cdaf26c0f1a0f4f07819bfcb86b0e3934e Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 8 Feb 2023 20:00:16 +0800 Subject: [PATCH 11/20] though this change exe name to rustdesk, but it also change the name used in the other place --- flutter/macos/Runner.xcodeproj/project.pbxproj | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/flutter/macos/Runner.xcodeproj/project.pbxproj b/flutter/macos/Runner.xcodeproj/project.pbxproj index 18166c8ff..066560203 100644 --- a/flutter/macos/Runner.xcodeproj/project.pbxproj +++ b/flutter/macos/Runner.xcodeproj/project.pbxproj @@ -64,7 +64,7 @@ 295AD07E63F13855C270A0E0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* rustdesk.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = rustdesk.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* RustDesk.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RustDesk.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -127,7 +127,7 @@ 33CC10EE2044A3C60003C045 /* Products */ = { isa = PBXGroup; children = ( - 33CC10ED2044A3C60003C045 /* rustdesk.app */, + 33CC10ED2044A3C60003C045 /* RustDesk.app */, ); name = Products; sourceTree = ""; @@ -212,7 +212,7 @@ ); name = Runner; productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* rustdesk.app */; + productReference = 33CC10ED2044A3C60003C045 /* RustDesk.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -462,7 +462,6 @@ ); MACOSX_DEPLOYMENT_TARGET = 10.14; PRODUCT_BUNDLE_IDENTIFIER = com.carriez.rustdesk; - PRODUCT_NAME = rustdesk; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -608,7 +607,6 @@ ); MACOSX_DEPLOYMENT_TARGET = 10.14; PRODUCT_BUNDLE_IDENTIFIER = com.carriez.rustdesk; - PRODUCT_NAME = rustdesk; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; "SWIFT_OBJC_BRIDGING_HEADER[arch=*]" = Runner/bridge_generated.h; @@ -646,7 +644,6 @@ /dev/null, ); PRODUCT_BUNDLE_IDENTIFIER = com.carriez.rustdesk; - PRODUCT_NAME = rustdesk; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; "SWIFT_OBJC_BRIDGING_HEADER[arch=*]" = Runner/bridge_generated.h; From 80da209be8226a0af25b64511e6c35f36cfb8829 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 8 Feb 2023 20:09:07 +0800 Subject: [PATCH 12/20] change executable name from RustDesk to rustdesk in mac deployment --- .github/workflows/flutter-nightly.yml | 1 - build.py | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index 5ca284cee..f03cd0be8 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -242,7 +242,6 @@ jobs: security unlock-keychain -p ${{ secrets.MACOS_P12_PASSWORD }} rustdesk.keychain # start sign the rustdesk.app and dmg rm rustdesk-${{ env.VERSION }}.dmg || true - mv ./flutter/build/macos/Build/Products/Release/rustdesk.app ./flutter/build/macos/Build/Products/Release/RustDesk.app codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep ./flutter/build/macos/Build/Products/Release/RustDesk.app -v create-dmg --icon "RustDesk.app" 200 190 --hide-extension "RustDesk.app" --window-size 800 400 --app-drop-link 600 185 rustdesk-${{ env.VERSION }}.dmg ./flutter/build/macos/Build/Products/Release/RustDesk.app codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep rustdesk-${{ env.VERSION }}.dmg -v diff --git a/build.py b/build.py index 6b107ff4b..dce434720 100755 --- a/build.py +++ b/build.py @@ -322,8 +322,9 @@ def build_flutter_dmg(version, features): os.system('sed -i "" "s/char \*\*rustdesk_core_main(int \*args_len);//" flutter/macos/Runner/bridge_generated.h') os.chdir('flutter') os.system('flutter build macos --release') + os.system('mv ./build/macos/Build/Products/Release/RustDesk.app/Contents/MacOS/RustDesk ./build/macos/Build/Products/Release/RustDesk.app/Contents/MacOS/rustdesk') os.system( - "create-dmg rustdesk.dmg ./build/macos/Build/Products/Release/rustdesk.app") + "create-dmg rustdesk.dmg ./build/macos/Build/Products/Release/RustDesk.app") os.rename("rustdesk.dmg", f"../rustdesk-{version}.dmg") os.chdir("..") From 3ae53a5d577b944baffb33da4ef9c959fefa1c72 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 8 Feb 2023 20:41:19 +0800 Subject: [PATCH 13/20] fix build Signed-off-by: fufesou --- src/keyboard.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/keyboard.rs b/src/keyboard.rs index 5b9920714..28e151580 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -256,6 +256,7 @@ pub fn start_grab_loop() { if let Err(error) = rdev::grab(func) { log::error!("rdev Error: {:?}", error) } + #[cfg(target_os = "windows")] rdev::set_event_popup(false); }); From 0dba0130893b54951fe3df3b9ce4997037a0a7ca Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 8 Feb 2023 21:54:48 +0900 Subject: [PATCH 14/20] remove unused Overlay in desktop_tab_page.dart and server_page.dart --- .../lib/desktop/pages/desktop_tab_page.dart | 28 +++++++--------- flutter/lib/desktop/pages/server_page.dart | 33 ++++++++----------- 2 files changed, 24 insertions(+), 37 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index c1965921c..35d5a61ef 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -64,23 +64,17 @@ class _DesktopTabPageState extends State { @override Widget build(BuildContext context) { final tabWidget = Container( - child: Overlay(initialEntries: [ - OverlayEntry(builder: (context) { - gFFI.dialogManager.setOverlayState(Overlay.of(context)); - return Scaffold( - backgroundColor: Theme.of(context).backgroundColor, - body: DesktopTab( - controller: tabController, - tail: ActionIcon( - message: 'Settings', - icon: IconFont.menu, - onTap: DesktopTabPage.onAddSetting, - isClose: false, - ), - )); - }) - ]), - ); + child: Scaffold( + backgroundColor: Theme.of(context).backgroundColor, + body: DesktopTab( + controller: tabController, + tail: ActionIcon( + message: 'Settings', + icon: IconFont.menu, + onTap: DesktopTabPage.onAddSetting, + isClose: false, + ), + ))); return Platform.isMacOS ? tabWidget : Obx( diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index 521413647..b4d7f4fac 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -68,26 +68,19 @@ class _DesktopServerPageState extends State ], child: Consumer( builder: (context, serverModel, child) => Container( - decoration: BoxDecoration( - border: - Border.all(color: MyTheme.color(context).border!)), - child: Overlay(initialEntries: [ - OverlayEntry(builder: (context) { - gFFI.dialogManager.setOverlayState(Overlay.of(context)); - return Scaffold( - backgroundColor: Theme.of(context).backgroundColor, - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded(child: ConnectionManager()), - ], - ), - ), - ); - }) - ]), - ))); + decoration: BoxDecoration( + border: Border.all(color: MyTheme.color(context).border!)), + child: Scaffold( + backgroundColor: Theme.of(context).backgroundColor, + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded(child: ConnectionManager()), + ], + ), + ), + )))); } @override From 3d5aca18d690235ec1fb361b8526a25af7d46672 Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 8 Feb 2023 22:01:15 +0900 Subject: [PATCH 15/20] refactor OverlayKeyState for OverlayDialogManager and ChatModel --- flutter/lib/common.dart | 30 +++++++++++-------- flutter/lib/common/widgets/overlay.dart | 24 ++++----------- .../lib/desktop/pages/file_manager_page.dart | 7 +++-- flutter/lib/desktop/pages/remote_page.dart | 16 ++++++---- flutter/lib/models/chat_model.dart | 18 +++++------ 5 files changed, 47 insertions(+), 48 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index a2623ff15..04e29eaa0 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -369,20 +369,25 @@ class Dialog { } } +class OverlayKeyState { + final _overlayKey = GlobalKey(); + + /// use global overlay by default + OverlayState? get state => + _overlayKey.currentState ?? globalKey.currentState?.overlay; + + GlobalKey? get key => _overlayKey; +} + class OverlayDialogManager { - OverlayState? _overlayState; final Map _dialogs = {}; + var _overlayKeyState = OverlayKeyState(); int _tagCount = 0; OverlayEntry? _mobileActionsOverlayEntry; - /// By default OverlayDialogManager use global overlay - OverlayDialogManager() { - _overlayState = globalKey.currentState?.overlay; - } - - void setOverlayState(OverlayState? overlayState) { - _overlayState = overlayState; + void setOverlayState(OverlayKeyState overlayKeyState) { + _overlayKeyState = overlayKeyState; } void dismissAll() { @@ -406,7 +411,7 @@ class OverlayDialogManager { bool useAnimation = true, bool forceGlobal = false}) { final overlayState = - forceGlobal ? globalKey.currentState?.overlay : _overlayState; + forceGlobal ? globalKey.currentState?.overlay : _overlayKeyState.state; if (overlayState == null) { return Future.error( @@ -510,7 +515,8 @@ class OverlayDialogManager { void showMobileActionsOverlay({FFI? ffi}) { if (_mobileActionsOverlayEntry != null) return; - if (_overlayState == null) return; + final overlayState = _overlayKeyState.state; + if (overlayState == null) return; // compute overlay position final screenW = MediaQuery.of(globalKey.currentContext!).size.width; @@ -536,7 +542,7 @@ class OverlayDialogManager { onHidePressed: () => hideMobileActionsOverlay(), ); }); - _overlayState!.insert(overlay); + overlayState.insert(overlay); _mobileActionsOverlayEntry = overlay; } @@ -1701,4 +1707,4 @@ Future updateSystemWindowTheme() async { : SystemWindowTheme.dark); } } -} \ No newline at end of file +} diff --git a/flutter/lib/common/widgets/overlay.dart b/flutter/lib/common/widgets/overlay.dart index 3e248700f..32dced02a 100644 --- a/flutter/lib/common/widgets/overlay.dart +++ b/flutter/lib/common/widgets/overlay.dart @@ -372,25 +372,12 @@ class QualityMonitor extends StatelessWidget { : const SizedBox.shrink())); } -class PenetrableOverlayState { +class BlockableOverlayState extends OverlayKeyState { final _middleBlocked = false.obs; - final _overlayKey = GlobalKey(); VoidCallback? onMiddleBlockedClick; // to-do use listener RxBool get middleBlocked => _middleBlocked; - GlobalKey get overlayKey => _overlayKey; - OverlayState? get overlayState => _overlayKey.currentState; - - OverlayState? getOverlayStateOrGlobal() { - if (overlayState == null) { - if (globalKey.currentState == null || - globalKey.currentState!.overlay == null) return null; - return globalKey.currentState!.overlay; - } else { - return overlayState; - } - } void addMiddleBlockedListener(void Function(bool) cb) { _middleBlocked.listen(cb); @@ -403,13 +390,13 @@ class PenetrableOverlayState { } } -class PenetrableOverlay extends StatelessWidget { +class BlockableOverlay extends StatelessWidget { final Widget underlying; final List? upperLayer; - final PenetrableOverlayState state; + final BlockableOverlayState state; - PenetrableOverlay( + BlockableOverlay( {required this.underlying, required this.state, this.upperLayer}); @override @@ -433,6 +420,7 @@ class PenetrableOverlay extends StatelessWidget { initialEntries.addAll(upperLayer!); } - return Overlay(key: state.overlayKey, initialEntries: initialEntries); + /// set key + return Overlay(key: state.key, initialEntries: initialEntries); } } diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index b6a9e5fed..9955c2768 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -80,6 +80,7 @@ class _FileManagerPageState extends State Entry? _lastClickEntry; final _dropMaskVisible = false.obs; // TODO impl drop mask + final _overlayKeyState = OverlayKeyState(); ScrollController getBreadCrumbScrollController(bool isLocal) { return isLocal ? _breadCrumbScrollerLocal : _breadCrumbScrollerRemote; @@ -115,6 +116,7 @@ class _FileManagerPageState extends State // register location listener _locationNodeLocal.addListener(onLocalLocationFocusChanged); _locationNodeRemote.addListener(onRemoteLocationFocusChanged); + _ffi.dialogManager.setOverlayState(_overlayKeyState); } @override @@ -137,9 +139,8 @@ class _FileManagerPageState extends State @override Widget build(BuildContext context) { super.build(context); - return Overlay(initialEntries: [ - OverlayEntry(builder: (context) { - _ffi.dialogManager.setOverlayState(Overlay.of(context)); + return Overlay(key: _overlayKeyState.key, initialEntries: [ + OverlayEntry(builder: (_) { return ChangeNotifierProvider.value( value: _ffi.fileModel, child: Consumer(builder: (context, model, child) { diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 4bda68c2d..c444d1f5f 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -62,7 +62,7 @@ class _RemotePageState extends State late RxBool _remoteCursorMoved; late RxBool _keyboardEnabled; - final overlayState = PenetrableOverlayState(); + final _blockableOverlayState = BlockableOverlayState(); final FocusNode _rawKeyFocusNode = FocusNode(debugLabel: "rawkeyFocusNode"); @@ -136,10 +136,11 @@ class _RemotePageState extends State // _isCustomCursorInited = true; // } - _ffi.chatModel.setPenetrableOverlayState(overlayState); + _ffi.dialogManager.setOverlayState(_blockableOverlayState); + _ffi.chatModel.setOverlayState(_blockableOverlayState); // make remote page penetrable automatically, effective for chat over remote - overlayState.onMiddleBlockedClick = () { - overlayState.setMiddleBlocked(false); + _blockableOverlayState.onMiddleBlockedClick = () { + _blockableOverlayState.setMiddleBlocked(false); }; } @@ -201,8 +202,11 @@ class _RemotePageState extends State Widget buildBody(BuildContext context) { return Scaffold( backgroundColor: Theme.of(context).backgroundColor, - body: PenetrableOverlay( - state: overlayState, + + /// the Overlay key will be set with _blockableOverlayState in BlockableOverlay + /// see override build() in [BlockableOverlay] + body: BlockableOverlay( + state: _blockableOverlayState, underlying: Container( color: Colors.black, child: RawKeyFocusScope( diff --git a/flutter/lib/models/chat_model.dart b/flutter/lib/models/chat_model.dart index b61ce79a7..8320d08dd 100644 --- a/flutter/lib/models/chat_model.dart +++ b/flutter/lib/models/chat_model.dart @@ -34,7 +34,7 @@ class ChatModel with ChangeNotifier { bool isConnManager = false; RxBool isWindowFocus = true.obs; - PenetrableOverlayState? pOverlayState; + BlockableOverlayState? _blockableOverlayState; final ChatUser me = ChatUser( id: "", @@ -53,10 +53,10 @@ class ChatModel with ChangeNotifier { bool get isShowCMChatPage => _isShowCMChatPage; - void setPenetrableOverlayState(PenetrableOverlayState state) { - pOverlayState = state; + void setOverlayState(BlockableOverlayState blockableOverlayState) { + _blockableOverlayState = blockableOverlayState; - pOverlayState!.addMiddleBlockedListener((v) { + _blockableOverlayState!.addMiddleBlockedListener((v) { if (!v) { isWindowFocus.value = false; if (isWindowFocus.value) { @@ -94,7 +94,7 @@ class ChatModel with ChangeNotifier { } } - final overlayState = pOverlayState?.getOverlayStateOrGlobal(); + final overlayState = _blockableOverlayState?.state; if (overlayState == null) return; final overlay = OverlayEntry(builder: (context) { @@ -129,16 +129,16 @@ class ChatModel with ChangeNotifier { showChatWindowOverlay({Offset? chatInitPos}) { if (chatWindowOverlayEntry != null) return; isWindowFocus.value = true; - pOverlayState?.setMiddleBlocked(true); + _blockableOverlayState?.setMiddleBlocked(true); - final overlayState = pOverlayState?.getOverlayStateOrGlobal(); + final overlayState = _blockableOverlayState?.state; if (overlayState == null) return; final overlay = OverlayEntry(builder: (context) { return Listener( onPointerDown: (_) { if (!isWindowFocus.value) { isWindowFocus.value = true; - pOverlayState?.setMiddleBlocked(true); + _blockableOverlayState?.setMiddleBlocked(true); } }, child: DraggableChatWindow( @@ -154,7 +154,7 @@ class ChatModel with ChangeNotifier { hideChatWindowOverlay() { if (chatWindowOverlayEntry != null) { - pOverlayState?.setMiddleBlocked(false); + _blockableOverlayState?.setMiddleBlocked(false); chatWindowOverlayEntry!.remove(); chatWindowOverlayEntry = null; return; From ac1ae9fc3bbfb7c7cd343222f59618957637093c Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 8 Feb 2023 10:11:53 +0900 Subject: [PATCH 16/20] workaround: PageView reload --- .../lib/desktop/widgets/tabbar_widget.dart | 33 +++++++++++++++---- flutter/lib/models/model.dart | 4 +-- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 598b2cc4c..ddc51eddb 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -327,14 +327,32 @@ class DesktopTab extends StatelessWidget { )); } + List _tabWidgets = []; Widget _buildPageView() { return _buildBlock( child: Obx(() => PageView( controller: state.value.pageController, physics: NeverScrollableScrollPhysics(), - children: state.value.tabs - .map((tab) => tab.page) - .toList(growable: false)))); + children: () { + /// to-do refactor, separate connection state and UI state for remote session. + /// [workaround] PageView children need an immutable list, after it has been passed into PageView + final tabLen = state.value.tabs.length; + if (tabLen == _tabWidgets.length) { + return _tabWidgets; + } else if (_tabWidgets.isNotEmpty && + tabLen == _tabWidgets.length + 1) { + /// On add. Use the previous list(pointer) to prevent item's state init twice. + /// *[_tabWidgets.isNotEmpty] means TabsWindow(remote_tab_page or file_manager_tab_page) opened before, but was hidden. In this case, we have to reload, otherwise the child can't be built. + _tabWidgets.add(state.value.tabs.last.page); + return _tabWidgets; + } else { + /// On remove or change. Use new list(pointer) to reload list children so that items loading order is normal. + /// the Widgets in list must enable [AutomaticKeepAliveClientMixin] + final newList = state.value.tabs.map((v) => v.page).toList(); + _tabWidgets = newList; + return newList; + } + }()))); } /// Check whether to show ListView @@ -765,7 +783,8 @@ class _ListView extends StatelessWidget { tabBuilder: tabBuilder, tabMenuBuilder: tabMenuBuilder, maxLabelWidth: maxLabelWidth, - selectedTabBackgroundColor: selectedTabBackgroundColor ?? MyTheme.tabbar(context).selectedTabBackgroundColor, + selectedTabBackgroundColor: selectedTabBackgroundColor ?? + MyTheme.tabbar(context).selectedTabBackgroundColor, unSelectedTabBackgroundColor: unSelectedTabBackgroundColor, ); }).toList())); @@ -1119,7 +1138,8 @@ class TabbarTheme extends ThemeExtension { dividerColor: dividerColor ?? this.dividerColor, hoverColor: hoverColor ?? this.hoverColor, closeHoverColor: closeHoverColor ?? this.closeHoverColor, - selectedTabBackgroundColor: selectedTabBackgroundColor ?? this.selectedTabBackgroundColor, + selectedTabBackgroundColor: + selectedTabBackgroundColor ?? this.selectedTabBackgroundColor, ); } @@ -1145,7 +1165,8 @@ class TabbarTheme extends ThemeExtension { dividerColor: Color.lerp(dividerColor, other.dividerColor, t), hoverColor: Color.lerp(hoverColor, other.hoverColor, t), closeHoverColor: Color.lerp(closeHoverColor, other.closeHoverColor, t), - selectedTabBackgroundColor: Color.lerp(selectedTabBackgroundColor, other.selectedTabBackgroundColor, t), + selectedTabBackgroundColor: Color.lerp( + selectedTabBackgroundColor, other.selectedTabBackgroundColor, t), ); } diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 1eac1be39..5e4693ccc 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -17,7 +17,6 @@ import 'package:flutter_hbb/models/server_model.dart'; import 'package:flutter_hbb/models/user_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; import 'package:flutter_hbb/common/shared_state.dart'; -import 'package:flutter_hbb/utils/multi_window_manager.dart'; import 'package:tuple/tuple.dart'; import 'package:image/image.dart' as img2; import 'package:flutter_custom_cursor/cursor_manager.dart'; @@ -25,7 +24,6 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import '../common.dart'; -import '../common/shared_state.dart'; import '../utils/image.dart' as img; import '../mobile/widgets/dialog.dart'; import 'input_model.dart'; @@ -1348,13 +1346,13 @@ class FFI { canvasModel.y, canvasModel.scale, ffiModel.pi.currentDisplay); } bind.sessionClose(id: id); - id = ''; imageModel.update(null); cursorModel.clear(); ffiModel.clear(); canvasModel.clear(); inputModel.resetModifiers(); debugPrint('model $id closed'); + id = ''; } void setMethodCallHandler(FMethod callback) { From 552e45b320a6e1361580764332f8401801e7c160 Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 8 Feb 2023 22:05:11 +0900 Subject: [PATCH 17/20] BlockableOverlay blocked layer transparent color --- flutter/lib/common/widgets/overlay.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/flutter/lib/common/widgets/overlay.dart b/flutter/lib/common/widgets/overlay.dart index 32dced02a..ba7b8a059 100644 --- a/flutter/lib/common/widgets/overlay.dart +++ b/flutter/lib/common/widgets/overlay.dart @@ -411,9 +411,8 @@ class BlockableOverlay extends StatelessWidget { state.onMiddleBlockedClick?.call(); }, child: Container( - color: state.middleBlocked.value - ? Colors.red.withOpacity(0.3) - : null)))), + color: + state.middleBlocked.value ? Colors.transparent : null)))), ]; if (upperLayer != null) { From 38d26ec47b2d44e351661b71451afcc6ebfaa275 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 8 Feb 2023 21:50:18 +0800 Subject: [PATCH 18/20] fix altgr Signed-off-by: fufesou --- src/keyboard.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 28e151580..105b84400 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -840,6 +840,13 @@ pub fn translate_keyboard_mode(event: &Event, key_event: KeyEvent) -> Vec Vec Date: Wed, 8 Feb 2023 22:06:18 +0800 Subject: [PATCH 19/20] fix CI --- src/flutter_ffi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index ec4a90973..ad0d119d7 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, ffi::{CStr, CString}, os::raw::c_char}; use std::str::FromStr; -#[cfg(any(target_os = "linux", target_os = "macos"))] +#[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))] use std::thread; use flutter_rust_bridge::{StreamSink, SyncReturn, ZeroCopyBuffer}; From 974fa86b8abb2fc90f43a069bc22ad71429d53c6 Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Wed, 8 Feb 2023 22:47:41 +0100 Subject: [PATCH 20/20] Update de.rs --- src/lang/de.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index 44bbafdac..1743505cc 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -392,7 +392,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("or", "oder"), ("Continue with", "Fortfahren mit"), ("Elevate", "Erheben"), - ("Zoom cursor", "Cursor zoomen"), + ("Zoom cursor", "Cursor vergrößern"), ("Accept sessions via password", "Sitzung mit Passwort bestätigen"), ("Accept sessions via click", "Sitzung mit einem Klick bestätigen"), ("Accept sessions via both", "Sitzung mit Klick und Passwort bestätigen"), @@ -414,8 +414,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Select local keyboard type", "Lokalen Tastaturtyp auswählen"), ("software_render_tip", "Wenn Sie eine Nvidia-Grafikkarte haben und sich das entfernte Fenster sofort nach dem Herstellen der Verbindung schließt, kann es helfen, den Nouveau-Treiber zu installieren und Software-Rendering zu verwenden. Ein Neustart der Software ist erforderlich."), ("Always use software rendering", "Software-Rendering immer verwenden"), - ("config_input", "Um den entfernten Desktop mit der Tastatur steuern zu können, müssen Sie RustDesk \"Input Monitoring\"-Rechte erteilen."), - ("config_microphone", ""), + ("config_input", "Um den entfernten Desktop mit der Tastatur steuern zu können, müssen Sie RustDesk die Berechtigung \"Input Monitoring\" erteilen."), + ("config_microphone", "Um aus der Ferne sprechen zu können, müssen Sie RustDesk die Berechtigung \"Audio aufzeichnen\" erteilen."), ("request_elevation_tip", "Sie können auch erhöhte Rechte anfordern, wenn sich jemand auf der Gegenseite befindet."), ("Wait", "Warten"), ("Elevation Error", "Berechtigungsfehler"), @@ -445,9 +445,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Bitrate", "Bitrate"), ("FPS", "fps"), ("Auto", "Automatisch"), - ("Other Default Options", "Weitere Standardoptionen"), - ("Voice call", ""), - ("Text chat", ""), - ("Stop voice call", ""), + ("Other Default Options", "Weitere Standardeinstellungen"), + ("Voice call", "Sprachanruf"), + ("Text chat", "Text-Chat"), + ("Stop voice call", "Sprachanruf beenden"), ].iter().cloned().collect(); }