Merge remote-tracking branch 'github/master' into sigma

This commit is contained in:
sjpark 2023-02-09 11:53:04 +09:00
commit e3963adf49
38 changed files with 498 additions and 339 deletions

View File

@ -30,13 +30,22 @@ body:
description: A clear and concise description of what you expected to happen description: A clear and concise description of what you expected to happen
validations: validations:
required: true 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 - type: input
id: version id: version
attributes: attributes:
label: Operating System(s) and RustDesk Version(s) on local side and remote side label: 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. description: What RustDesk version(s) do you see this bug on? local side -> remote side.
placeholder: | placeholder: |
Windows 10, 1.1.9 / osx 13.1, 1.1.8 1.1.9 -> 1.1.8
validations: validations:
required: true required: true
- type: textarea - type: textarea

View File

@ -242,7 +242,6 @@ jobs:
security unlock-keychain -p ${{ secrets.MACOS_P12_PASSWORD }} rustdesk.keychain security unlock-keychain -p ${{ secrets.MACOS_P12_PASSWORD }} rustdesk.keychain
# start sign the rustdesk.app and dmg # start sign the rustdesk.app and dmg
rm rustdesk-${{ env.VERSION }}.dmg || true 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 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 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 codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep rustdesk-${{ env.VERSION }}.dmg -v

2
Cargo.lock generated
View File

@ -4405,7 +4405,7 @@ dependencies = [
[[package]] [[package]]
name = "rdev" name = "rdev"
version = "0.5.0-2" version = "0.5.0-2"
source = "git+https://github.com/fufesou/rdev#4d8231f05e14c5a04cd7d2c1288e87ad52d39e4c" source = "git+https://github.com/fufesou/rdev#cedc4e62744566775026af4b434ef799804c1130"
dependencies = [ dependencies = [
"cocoa", "cocoa",
"core-foundation 0.9.3", "core-foundation 0.9.3",

View File

@ -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.system('sed -i "" "s/char \*\*rustdesk_core_main(int \*args_len);//" flutter/macos/Runner/bridge_generated.h')
os.chdir('flutter') os.chdir('flutter')
os.system('flutter build macos --release') 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( 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.rename("rustdesk.dmg", f"../rustdesk-{version}.dmg")
os.chdir("..") os.chdir("..")

View File

@ -367,20 +367,25 @@ class Dialog<T> {
} }
} }
class OverlayKeyState {
final _overlayKey = GlobalKey<OverlayState>();
/// use global overlay by default
OverlayState? get state =>
_overlayKey.currentState ?? globalKey.currentState?.overlay;
GlobalKey<OverlayState>? get key => _overlayKey;
}
class OverlayDialogManager { class OverlayDialogManager {
OverlayState? _overlayState;
final Map<String, Dialog> _dialogs = {}; final Map<String, Dialog> _dialogs = {};
var _overlayKeyState = OverlayKeyState();
int _tagCount = 0; int _tagCount = 0;
OverlayEntry? _mobileActionsOverlayEntry; OverlayEntry? _mobileActionsOverlayEntry;
/// By default OverlayDialogManager use global overlay void setOverlayState(OverlayKeyState overlayKeyState) {
OverlayDialogManager() { _overlayKeyState = overlayKeyState;
_overlayState = globalKey.currentState?.overlay;
}
void setOverlayState(OverlayState? overlayState) {
_overlayState = overlayState;
} }
void dismissAll() { void dismissAll() {
@ -404,7 +409,7 @@ class OverlayDialogManager {
bool useAnimation = true, bool useAnimation = true,
bool forceGlobal = false}) { bool forceGlobal = false}) {
final overlayState = final overlayState =
forceGlobal ? globalKey.currentState?.overlay : _overlayState; forceGlobal ? globalKey.currentState?.overlay : _overlayKeyState.state;
if (overlayState == null) { if (overlayState == null) {
return Future.error( return Future.error(
@ -508,7 +513,8 @@ class OverlayDialogManager {
void showMobileActionsOverlay({FFI? ffi}) { void showMobileActionsOverlay({FFI? ffi}) {
if (_mobileActionsOverlayEntry != null) return; if (_mobileActionsOverlayEntry != null) return;
if (_overlayState == null) return; final overlayState = _overlayKeyState.state;
if (overlayState == null) return;
// compute overlay position // compute overlay position
final screenW = MediaQuery.of(globalKey.currentContext!).size.width; final screenW = MediaQuery.of(globalKey.currentContext!).size.width;
@ -534,7 +540,7 @@ class OverlayDialogManager {
onHidePressed: () => hideMobileActionsOverlay(), onHidePressed: () => hideMobileActionsOverlay(),
); );
}); });
_overlayState!.insert(overlay); overlayState.insert(overlay);
_mobileActionsOverlayEntry = overlay; _mobileActionsOverlayEntry = overlay;
} }

View File

@ -95,10 +95,31 @@ class ChatPage extends StatelessWidget implements PageShape {
color: Theme.of(context).colorScheme.primary)), color: Theme.of(context).colorScheme.primary)),
messageOptions: MessageOptions( messageOptions: MessageOptions(
showOtherUsersAvatar: false, showOtherUsersAvatar: false,
showTime: true,
currentUserTextColor: Colors.white,
textColor: Colors.white, textColor: Colors.white,
maxWidth: constraints.maxWidth * 0.7, maxWidth: constraints.maxWidth * 0.7,
messageTextBuilder: (message, _, __) {
final isOwnMessage =
message.user.id == currentUser.id;
return Column(
crossAxisAlignment: isOwnMessage
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: <Widget>[
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: (_, __, ___) => messageDecorationBuilder: (_, __, ___) =>
defaultMessageDecoration( defaultMessageDecoration(
color: MyTheme.accent80, color: MyTheme.accent80,

View File

@ -1,6 +1,7 @@
import 'package:auto_size_text/auto_size_text.dart'; import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../consts.dart'; import '../../consts.dart';
@ -96,12 +97,14 @@ class DraggableChatWindow extends StatelessWidget {
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8), padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8),
child: Row(children: [ child: Obx(() => Opacity(
Icon(Icons.chat_bubble_outline, opacity: chatModel.isWindowFocus.value ? 1.0 : 0.4,
size: 20, color: Theme.of(context).colorScheme.primary), child: Row(children: [
SizedBox(width: 6), Icon(Icons.chat_bubble_outline,
Text(translate("Chat")) size: 20, color: Theme.of(context).colorScheme.primary),
])), SizedBox(width: 6),
Text(translate("Chat"))
])))),
Padding( Padding(
padding: EdgeInsets.all(2), padding: EdgeInsets.all(2),
child: ActionIcon( child: ActionIcon(
@ -304,15 +307,17 @@ class _DraggableState extends State<Draggable> {
if (widget.checkKeyboard) { if (widget.checkKeyboard) {
checkKeyboard(); checkKeyboard();
} }
if (widget.checkKeyboard) { if (widget.checkScreenSize) {
checkScreenSize(); checkScreenSize();
} }
return Positioned( return Stack(children: [
top: _position.dy, Positioned(
left: _position.dx, top: _position.dy,
width: widget.width, left: _position.dx,
height: widget.height, width: widget.width,
child: widget.builder(context, onPanUpdate)); height: widget.height,
child: widget.builder(context, onPanUpdate))
]);
} }
} }
@ -366,3 +371,55 @@ class QualityMonitor extends StatelessWidget {
) )
: const SizedBox.shrink())); : const SizedBox.shrink()));
} }
class BlockableOverlayState extends OverlayKeyState {
final _middleBlocked = false.obs;
VoidCallback? onMiddleBlockedClick; // to-do use listener
RxBool get middleBlocked => _middleBlocked;
void addMiddleBlockedListener(void Function(bool) cb) {
_middleBlocked.listen(cb);
}
void setMiddleBlocked(bool blocked) {
if (blocked != _middleBlocked.value) {
_middleBlocked.value = blocked;
}
}
}
class BlockableOverlay extends StatelessWidget {
final Widget underlying;
final List<OverlayEntry>? upperLayer;
final BlockableOverlayState state;
BlockableOverlay(
{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.transparent : null)))),
];
if (upperLayer != null) {
initialEntries.addAll(upperLayer!);
}
/// set key
return Overlay(key: state.key, initialEntries: initialEntries);
}
}

View File

@ -64,23 +64,17 @@ class _DesktopTabPageState extends State<DesktopTabPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final tabWidget = Container( final tabWidget = Container(
child: Overlay(initialEntries: [ child: Scaffold(
OverlayEntry(builder: (context) { backgroundColor: Theme.of(context).backgroundColor,
gFFI.dialogManager.setOverlayState(Overlay.of(context)); body: DesktopTab(
return Scaffold( controller: tabController,
backgroundColor: Theme.of(context).backgroundColor, tail: ActionIcon(
body: DesktopTab( message: 'Settings',
controller: tabController, icon: IconFont.menu,
tail: ActionIcon( onTap: DesktopTabPage.onAddSetting,
message: 'Settings', isClose: false,
icon: IconFont.menu, ),
onTap: DesktopTabPage.onAddSetting, )));
isClose: false,
),
));
})
]),
);
return Platform.isMacOS return Platform.isMacOS
? tabWidget ? tabWidget
: Obx( : Obx(

View File

@ -80,6 +80,7 @@ class _FileManagerPageState extends State<FileManagerPage>
Entry? _lastClickEntry; Entry? _lastClickEntry;
final _dropMaskVisible = false.obs; // TODO impl drop mask final _dropMaskVisible = false.obs; // TODO impl drop mask
final _overlayKeyState = OverlayKeyState();
ScrollController getBreadCrumbScrollController(bool isLocal) { ScrollController getBreadCrumbScrollController(bool isLocal) {
return isLocal ? _breadCrumbScrollerLocal : _breadCrumbScrollerRemote; return isLocal ? _breadCrumbScrollerLocal : _breadCrumbScrollerRemote;
@ -115,6 +116,7 @@ class _FileManagerPageState extends State<FileManagerPage>
// register location listener // register location listener
_locationNodeLocal.addListener(onLocalLocationFocusChanged); _locationNodeLocal.addListener(onLocalLocationFocusChanged);
_locationNodeRemote.addListener(onRemoteLocationFocusChanged); _locationNodeRemote.addListener(onRemoteLocationFocusChanged);
_ffi.dialogManager.setOverlayState(_overlayKeyState);
} }
@override @override
@ -137,9 +139,8 @@ class _FileManagerPageState extends State<FileManagerPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);
return Overlay(initialEntries: [ return Overlay(key: _overlayKeyState.key, initialEntries: [
OverlayEntry(builder: (context) { OverlayEntry(builder: (_) {
_ffi.dialogManager.setOverlayState(Overlay.of(context));
return ChangeNotifierProvider.value( return ChangeNotifierProvider.value(
value: _ffi.fileModel, value: _ffi.fileModel,
child: Consumer<FileModel>(builder: (context, model, child) { child: Consumer<FileModel>(builder: (context, model, child) {

View File

@ -61,6 +61,8 @@ class _RemotePageState extends State<RemotePage>
late RxBool _remoteCursorMoved; late RxBool _remoteCursorMoved;
late RxBool _keyboardEnabled; late RxBool _keyboardEnabled;
final _blockableOverlayState = BlockableOverlayState();
final FocusNode _rawKeyFocusNode = FocusNode(debugLabel: "rawkeyFocusNode"); final FocusNode _rawKeyFocusNode = FocusNode(debugLabel: "rawkeyFocusNode");
Function(bool)? _onEnterOrLeaveImage4Menubar; Function(bool)? _onEnterOrLeaveImage4Menubar;
@ -132,6 +134,13 @@ class _RemotePageState extends State<RemotePage>
// }); // });
// _isCustomCursorInited = true; // _isCustomCursorInited = true;
// } // }
_ffi.dialogManager.setOverlayState(_blockableOverlayState);
_ffi.chatModel.setOverlayState(_blockableOverlayState);
// make remote page penetrable automatically, effective for chat over remote
_blockableOverlayState.onMiddleBlockedClick = () {
_blockableOverlayState.setMiddleBlocked(false);
};
} }
@override @override
@ -191,39 +200,50 @@ class _RemotePageState extends State<RemotePage>
Widget buildBody(BuildContext context) { Widget buildBody(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: Theme.of(context).backgroundColor, backgroundColor: Theme.of(context).backgroundColor,
body: Overlay(
initialEntries: [ /// the Overlay key will be set with _blockableOverlayState in BlockableOverlay
OverlayEntry(builder: (context) { /// see override build() in [BlockableOverlay]
_ffi.chatModel.setOverlayState(Overlay.of(context)); body: BlockableOverlay(
_ffi.dialogManager.setOverlayState(Overlay.of(context)); state: _blockableOverlayState,
return Container( underlying: Container(
color: Colors.black, color: Colors.black,
child: RawKeyFocusScope( child: RawKeyFocusScope(
focusNode: _rawKeyFocusNode, focusNode: _rawKeyFocusNode,
onFocusChange: (bool imageFocused) { onFocusChange: (bool imageFocused) {
debugPrint( debugPrint(
"onFocusChange(window active:${!_isWindowBlur}) $imageFocused"); "onFocusChange(window active:${!_isWindowBlur}) $imageFocused");
// See [onWindowBlur]. // See [onWindowBlur].
if (Platform.isWindows) { if (Platform.isWindows) {
if (_isWindowBlur) { if (_isWindowBlur) {
imageFocused = false; imageFocused = false;
Future.delayed(Duration.zero, () { Future.delayed(Duration.zero, () {
_rawKeyFocusNode.unfocus(); _rawKeyFocusNode.unfocus();
}); });
} }
if (imageFocused) { if (imageFocused) {
_ffi.inputModel.enterOrLeave(true); _ffi.inputModel.enterOrLeave(true);
} else { } else {
_ffi.inputModel.enterOrLeave(false); _ffi.inputModel.enterOrLeave(false);
} }
} }
}, },
inputModel: _ffi.inputModel, inputModel: _ffi.inputModel,
child: getBodyForDesktop(context))); child: getBodyForDesktop(context))),
}) upperLayer: [
], OverlayEntry(
)); builder: (context) => RemoteMenubar(
id: widget.id,
ffi: _ffi,
state: widget.menubarState,
onEnterOrLeaveImageSetter: (func) =>
_onEnterOrLeaveImage4Menubar = func,
onEnterOrLeaveImageCleaner: () =>
_onEnterOrLeaveImage4Menubar = null,
))
],
),
);
} }
@override @override
@ -344,13 +364,6 @@ class _RemotePageState extends State<RemotePage>
QualityMonitor(_ffi.qualityMonitorModel), null, null), 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( return Stack(
children: paints, children: paints,
); );

View File

@ -68,26 +68,19 @@ class _DesktopServerPageState extends State<DesktopServerPage>
], ],
child: Consumer<ServerModel>( child: Consumer<ServerModel>(
builder: (context, serverModel, child) => Container( builder: (context, serverModel, child) => Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: border: Border.all(color: MyTheme.color(context).border!)),
Border.all(color: MyTheme.color(context).border!)), child: Scaffold(
child: Overlay(initialEntries: [ backgroundColor: Theme.of(context).backgroundColor,
OverlayEntry(builder: (context) { body: Center(
gFFI.dialogManager.setOverlayState(Overlay.of(context)); child: Column(
return Scaffold( mainAxisAlignment: MainAxisAlignment.start,
backgroundColor: Theme.of(context).backgroundColor, children: [
body: Center( Expanded(child: ConnectionManager()),
child: Column( ],
mainAxisAlignment: MainAxisAlignment.start, ),
children: [ ),
Expanded(child: ConnectionManager()), ))));
],
),
),
);
})
]),
)));
} }
@override @override

View File

@ -688,9 +688,11 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
); );
} }
final _chatButtonKey = GlobalKey();
Widget _buildChat(BuildContext context) { Widget _buildChat(BuildContext context) {
FfiModel ffiModel = Provider.of<FfiModel>(context); FfiModel ffiModel = Provider.of<FfiModel>(context);
return mod_menu.PopupMenuButton( return mod_menu.PopupMenuButton(
key: _chatButtonKey,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
icon: SvgPicture.asset( icon: SvgPicture.asset(
"assets/chat.svg", "assets/chat.svg",
@ -779,8 +781,17 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
style: style, style: style,
), ),
proc: () { proc: () {
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.changeCurrentID(ChatModel.clientModeID);
widget.ffi.chatModel.toggleChatOverlay(); widget.ffi.chatModel.toggleChatOverlay(chatInitPos: initPos);
}, },
padding: padding, padding: padding,
dismissOnClicked: true, dismissOnClicked: true,

View File

@ -327,14 +327,32 @@ class DesktopTab extends StatelessWidget {
)); ));
} }
List<Widget> _tabWidgets = [];
Widget _buildPageView() { Widget _buildPageView() {
return _buildBlock( return _buildBlock(
child: Obx(() => PageView( child: Obx(() => PageView(
controller: state.value.pageController, controller: state.value.pageController,
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
children: state.value.tabs children: () {
.map((tab) => tab.page) /// to-do refactor, separate connection state and UI state for remote session.
.toList(growable: false)))); /// [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 /// Check whether to show ListView
@ -767,7 +785,8 @@ class _ListView extends StatelessWidget {
tabBuilder: tabBuilder, tabBuilder: tabBuilder,
tabMenuBuilder: tabMenuBuilder, tabMenuBuilder: tabMenuBuilder,
maxLabelWidth: maxLabelWidth, maxLabelWidth: maxLabelWidth,
selectedTabBackgroundColor: selectedTabBackgroundColor ?? MyTheme.tabbar(context).selectedTabBackgroundColor, selectedTabBackgroundColor: selectedTabBackgroundColor ??
MyTheme.tabbar(context).selectedTabBackgroundColor,
unSelectedTabBackgroundColor: unSelectedTabBackgroundColor, unSelectedTabBackgroundColor: unSelectedTabBackgroundColor,
); );
}).toList())); }).toList()));
@ -1121,7 +1140,8 @@ class TabbarTheme extends ThemeExtension<TabbarTheme> {
dividerColor: dividerColor ?? this.dividerColor, dividerColor: dividerColor ?? this.dividerColor,
hoverColor: hoverColor ?? this.hoverColor, hoverColor: hoverColor ?? this.hoverColor,
closeHoverColor: closeHoverColor ?? this.closeHoverColor, closeHoverColor: closeHoverColor ?? this.closeHoverColor,
selectedTabBackgroundColor: selectedTabBackgroundColor ?? this.selectedTabBackgroundColor, selectedTabBackgroundColor:
selectedTabBackgroundColor ?? this.selectedTabBackgroundColor,
); );
} }
@ -1147,7 +1167,8 @@ class TabbarTheme extends ThemeExtension<TabbarTheme> {
dividerColor: Color.lerp(dividerColor, other.dividerColor, t), dividerColor: Color.lerp(dividerColor, other.dividerColor, t),
hoverColor: Color.lerp(hoverColor, other.hoverColor, t), hoverColor: Color.lerp(hoverColor, other.hoverColor, t),
closeHoverColor: Color.lerp(closeHoverColor, other.closeHoverColor, t), closeHoverColor: Color.lerp(closeHoverColor, other.closeHoverColor, t),
selectedTabBackgroundColor: Color.lerp(selectedTabBackgroundColor, other.selectedTabBackgroundColor, t), selectedTabBackgroundColor: Color.lerp(
selectedTabBackgroundColor, other.selectedTabBackgroundColor, t),
); );
} }

View File

@ -1,7 +1,10 @@
import 'dart:async';
import 'package:dash_chat_2/dash_chat_2.dart'; import 'package:dash_chat_2/dash_chat_2.dart';
import 'package:draggable_float_widget/draggable_float_widget.dart'; import 'package:draggable_float_widget/draggable_float_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
@ -27,16 +30,13 @@ class MessageBody {
class ChatModel with ChangeNotifier { class ChatModel with ChangeNotifier {
static final clientModeID = -1; 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? chatIconOverlayEntry;
OverlayEntry? chatWindowOverlayEntry; OverlayEntry? chatWindowOverlayEntry;
bool isConnManager = false; bool isConnManager = false;
RxBool isWindowFocus = true.obs;
BlockableOverlayState? _blockableOverlayState;
final Rx<VoiceCallStatus> _voiceCallStatus = Rx(VoiceCallStatus.notStarted); final Rx<VoiceCallStatus> _voiceCallStatus = Rx(VoiceCallStatus.notStarted);
Rx<VoiceCallStatus> get voiceCallStatus => _voiceCallStatus; Rx<VoiceCallStatus> get voiceCallStatus => _voiceCallStatus;
@ -58,6 +58,19 @@ class ChatModel with ChangeNotifier {
bool get isShowCMChatPage => _isShowCMChatPage; bool get isShowCMChatPage => _isShowCMChatPage;
void setOverlayState(BlockableOverlayState blockableOverlayState) {
_blockableOverlayState = blockableOverlayState;
_blockableOverlayState!.addMiddleBlockedListener((v) {
if (!v) {
isWindowFocus.value = false;
if (isWindowFocus.value) {
isWindowFocus.toggle();
}
}
});
}
final WeakReference<FFI> parent; final WeakReference<FFI> parent;
ChatModel(this.parent); ChatModel(this.parent);
@ -74,20 +87,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)}) { showChatIconOverlay({Offset offset = const Offset(200, 50)}) {
if (chatIconOverlayEntry != null) { if (chatIconOverlayEntry != null) {
chatIconOverlayEntry!.remove(); chatIconOverlayEntry!.remove();
@ -100,7 +99,7 @@ class ChatModel with ChangeNotifier {
} }
} }
final overlayState = _getOverlayState(); final overlayState = _blockableOverlayState?.state;
if (overlayState == null) return; if (overlayState == null) return;
final overlay = OverlayEntry(builder: (context) { final overlay = OverlayEntry(builder: (context) {
@ -132,23 +131,35 @@ class ChatModel with ChangeNotifier {
} }
} }
showChatWindowOverlay() { showChatWindowOverlay({Offset? chatInitPos}) {
if (chatWindowOverlayEntry != null) return; if (chatWindowOverlayEntry != null) return;
final overlayState = _getOverlayState(); isWindowFocus.value = true;
_blockableOverlayState?.setMiddleBlocked(true);
final overlayState = _blockableOverlayState?.state;
if (overlayState == null) return; if (overlayState == null) return;
final overlay = OverlayEntry(builder: (context) { final overlay = OverlayEntry(builder: (context) {
return DraggableChatWindow( return Listener(
position: const Offset(20, 80), onPointerDown: (_) {
width: 250, if (!isWindowFocus.value) {
height: 350, isWindowFocus.value = true;
chatModel: this); _blockableOverlayState?.setMiddleBlocked(true);
}
},
child: DraggableChatWindow(
position: chatInitPos ?? Offset(20, 80),
width: 250,
height: 350,
chatModel: this));
}); });
overlayState.insert(overlay); overlayState.insert(overlay);
chatWindowOverlayEntry = overlay; chatWindowOverlayEntry = overlay;
requestChatInputFocus();
} }
hideChatWindowOverlay() { hideChatWindowOverlay() {
if (chatWindowOverlayEntry != null) { if (chatWindowOverlayEntry != null) {
_blockableOverlayState?.setMiddleBlocked(false);
chatWindowOverlayEntry!.remove(); chatWindowOverlayEntry!.remove();
chatWindowOverlayEntry = null; chatWindowOverlayEntry = null;
return; return;
@ -158,13 +169,13 @@ class ChatModel with ChangeNotifier {
_isChatOverlayHide() => ((!isDesktop && chatIconOverlayEntry == null) || _isChatOverlayHide() => ((!isDesktop && chatIconOverlayEntry == null) ||
chatWindowOverlayEntry == null); chatWindowOverlayEntry == null);
toggleChatOverlay() { toggleChatOverlay({Offset? chatInitPos}) {
if (_isChatOverlayHide()) { if (_isChatOverlayHide()) {
gFFI.invokeMethod("enable_soft_keyboard", true); gFFI.invokeMethod("enable_soft_keyboard", true);
if (!isDesktop) { if (!isDesktop) {
showChatIconOverlay(); showChatIconOverlay();
} }
showChatWindowOverlay(); showChatWindowOverlay(chatInitPos: chatInitPos);
} else { } else {
hideChatIconOverlay(); hideChatIconOverlay();
hideChatWindowOverlay(); hideChatWindowOverlay();
@ -194,6 +205,7 @@ class ChatModel with ChangeNotifier {
await windowManager.setSizeAlignment( await windowManager.setSizeAlignment(
kConnectionManagerWindowSize, Alignment.topRight); kConnectionManagerWindowSize, Alignment.topRight);
} else { } else {
requestChatInputFocus();
await windowManager.show(); await windowManager.show();
await windowManager.setSizeAlignment(Size(600, 400), Alignment.topRight); await windowManager.setSizeAlignment(Size(600, 400), Alignment.topRight);
_isShowCMChatPage = !_isShowCMChatPage; _isShowCMChatPage = !_isShowCMChatPage;
@ -291,7 +303,6 @@ class ChatModel with ChangeNotifier {
close() { close() {
hideChatIconOverlay(); hideChatIconOverlay();
hideChatWindowOverlay(); hideChatWindowOverlay();
_overlayState = null;
notifyListeners(); notifyListeners();
} }
@ -299,6 +310,14 @@ class ChatModel with ChangeNotifier {
_messages[clientModeID]?.clear(); _messages[clientModeID]?.clear();
} }
void requestChatInputFocus() {
Timer(Duration(milliseconds: 100), () {
if (inputNode.hasListeners && inputNode.canRequestFocus) {
inputNode.requestFocus();
}
});
}
void onVoiceCallWaiting() { void onVoiceCallWaiting() {
_voiceCallStatus.value = VoiceCallStatus.waitingForResponse; _voiceCallStatus.value = VoiceCallStatus.waitingForResponse;
} }

View File

@ -18,7 +18,6 @@ import 'package:flutter_hbb/models/server_model.dart';
import 'package:flutter_hbb/models/user_model.dart'; import 'package:flutter_hbb/models/user_model.dart';
import 'package:flutter_hbb/models/state_model.dart'; import 'package:flutter_hbb/models/state_model.dart';
import 'package:flutter_hbb/common/shared_state.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:tuple/tuple.dart';
import 'package:image/image.dart' as img2; import 'package:image/image.dart' as img2;
import 'package:flutter_custom_cursor/cursor_manager.dart'; import 'package:flutter_custom_cursor/cursor_manager.dart';
@ -26,7 +25,6 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../common.dart'; import '../common.dart';
import '../common/shared_state.dart';
import '../utils/image.dart' as img; import '../utils/image.dart' as img;
import '../mobile/widgets/dialog.dart'; import '../mobile/widgets/dialog.dart';
import 'input_model.dart'; import 'input_model.dart';
@ -1393,13 +1391,13 @@ class FFI {
canvasModel.y, canvasModel.scale, ffiModel.pi.currentDisplay); canvasModel.y, canvasModel.scale, ffiModel.pi.currentDisplay);
} }
bind.sessionClose(id: id); bind.sessionClose(id: id);
id = '';
imageModel.update(null); imageModel.update(null);
cursorModel.clear(); cursorModel.clear();
ffiModel.clear(); ffiModel.clear();
canvasModel.clear(); canvasModel.clear();
inputModel.resetModifiers(); inputModel.resetModifiers();
debugPrint('model $id closed'); debugPrint('model $id closed');
id = '';
} }
void setMethodCallHandler(FMethod callback) { void setMethodCallHandler(FMethod callback) {

View File

@ -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 = "<group>"; }; 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 = "<group>"; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
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 = "<group>"; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
@ -127,7 +127,7 @@
33CC10EE2044A3C60003C045 /* Products */ = { 33CC10EE2044A3C60003C045 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
33CC10ED2044A3C60003C045 /* rustdesk.app */, 33CC10ED2044A3C60003C045 /* RustDesk.app */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@ -212,7 +212,7 @@
); );
name = Runner; name = Runner;
productName = Runner; productName = Runner;
productReference = 33CC10ED2044A3C60003C045 /* rustdesk.app */; productReference = 33CC10ED2044A3C60003C045 /* RustDesk.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
}; };
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
@ -462,7 +462,6 @@
); );
MACOSX_DEPLOYMENT_TARGET = 10.14; MACOSX_DEPLOYMENT_TARGET = 10.14;
PRODUCT_BUNDLE_IDENTIFIER = com.carriez.rustdesk; PRODUCT_BUNDLE_IDENTIFIER = com.carriez.rustdesk;
PRODUCT_NAME = rustdesk;
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@ -608,7 +607,6 @@
); );
MACOSX_DEPLOYMENT_TARGET = 10.14; MACOSX_DEPLOYMENT_TARGET = 10.14;
PRODUCT_BUNDLE_IDENTIFIER = com.carriez.rustdesk; PRODUCT_BUNDLE_IDENTIFIER = com.carriez.rustdesk;
PRODUCT_NAME = rustdesk;
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
"SWIFT_OBJC_BRIDGING_HEADER[arch=*]" = Runner/bridge_generated.h; "SWIFT_OBJC_BRIDGING_HEADER[arch=*]" = Runner/bridge_generated.h;
@ -646,7 +644,6 @@
/dev/null, /dev/null,
); );
PRODUCT_BUNDLE_IDENTIFIER = com.carriez.rustdesk; PRODUCT_BUNDLE_IDENTIFIER = com.carriez.rustdesk;
PRODUCT_NAME = rustdesk;
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
"SWIFT_OBJC_BRIDGING_HEADER[arch=*]" = Runner/bridge_generated.h; "SWIFT_OBJC_BRIDGING_HEADER[arch=*]" = Runner/bridge_generated.h;

View File

@ -16,7 +16,7 @@ final testClients = [
Client(3, false, false, "UserDDDDDDDDDDDd", "441123123", true, false, false) 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<String> args) async { void main(List<String> args) async {
isTest = true; isTest = true;
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();

View File

@ -6,7 +6,7 @@ fn main() {
protobuf_codegen::Codegen::new() protobuf_codegen::Codegen::new()
.pure() .pure()
.out_dir(out_dir) .out_dir(out_dir)
.inputs(&["protos/rendezvous.proto", "protos/message.proto"]) .inputs(["protos/rendezvous.proto", "protos/message.proto"])
.include("protos") .include("protos")
.customize(protobuf_codegen::Customize::default().tokio_bytes(true)) .customize(protobuf_codegen::Customize::default().tokio_bytes(true))
.run() .run()

View File

@ -143,32 +143,32 @@ mod tests {
let mut buf = BytesMut::new(); let mut buf = BytesMut::new();
let mut bytes: Vec<u8> = Vec::new(); let mut bytes: Vec<u8> = Vec::new();
bytes.resize(0x3F, 1); 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(); let buf_saved = buf.clone();
assert_eq!(buf.len(), 0x3F + 1); assert_eq!(buf.len(), 0x3F + 1);
if let Ok(Some(res)) = codec.decode(&mut buf) { if let Ok(Some(res)) = codec.decode(&mut buf) {
assert_eq!(res.len(), 0x3F); assert_eq!(res.len(), 0x3F);
assert_eq!(res[0], 1); assert_eq!(res[0], 1);
} else { } else {
assert!(false); panic!();
} }
let mut codec2 = BytesCodec::new(); let mut codec2 = BytesCodec::new();
let mut buf2 = BytesMut::new(); let mut buf2 = BytesMut::new();
if let Ok(None) = codec2.decode(&mut buf2) { if let Ok(None) = codec2.decode(&mut buf2) {
} else { } else {
assert!(false); panic!();
} }
buf2.extend(&buf_saved[0..1]); buf2.extend(&buf_saved[0..1]);
if let Ok(None) = codec2.decode(&mut buf2) { if let Ok(None) = codec2.decode(&mut buf2) {
} else { } else {
assert!(false); panic!();
} }
buf2.extend(&buf_saved[1..]); buf2.extend(&buf_saved[1..]);
if let Ok(Some(res)) = codec2.decode(&mut buf2) { if let Ok(Some(res)) = codec2.decode(&mut buf2) {
assert_eq!(res.len(), 0x3F); assert_eq!(res.len(), 0x3F);
assert_eq!(res[0], 1); assert_eq!(res[0], 1);
} else { } else {
assert!(false); panic!();
} }
} }
@ -177,21 +177,21 @@ mod tests {
let mut codec = BytesCodec::new(); let mut codec = BytesCodec::new();
let mut buf = BytesMut::new(); let mut buf = BytesMut::new();
let mut bytes: Vec<u8> = Vec::new(); let mut bytes: Vec<u8> = Vec::new();
assert!(!codec.encode("".into(), &mut buf).is_err()); assert!(codec.encode("".into(), &mut buf).is_ok());
assert_eq!(buf.len(), 1); assert_eq!(buf.len(), 1);
bytes.resize(0x3F + 1, 2); 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); assert_eq!(buf.len(), 0x3F + 2 + 2);
if let Ok(Some(res)) = codec.decode(&mut buf) { if let Ok(Some(res)) = codec.decode(&mut buf) {
assert_eq!(res.len(), 0); assert_eq!(res.len(), 0);
} else { } else {
assert!(false); panic!();
} }
if let Ok(Some(res)) = codec.decode(&mut buf) { if let Ok(Some(res)) = codec.decode(&mut buf) {
assert_eq!(res.len(), 0x3F + 1); assert_eq!(res.len(), 0x3F + 1);
assert_eq!(res[0], 2); assert_eq!(res[0], 2);
} else { } else {
assert!(false); panic!();
} }
} }
@ -201,13 +201,13 @@ mod tests {
let mut buf = BytesMut::new(); let mut buf = BytesMut::new();
let mut bytes: Vec<u8> = Vec::new(); let mut bytes: Vec<u8> = Vec::new();
bytes.resize(0x3F - 1, 3); 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); assert_eq!(buf.len(), 0x3F + 1 - 1);
if let Ok(Some(res)) = codec.decode(&mut buf) { if let Ok(Some(res)) = codec.decode(&mut buf) {
assert_eq!(res.len(), 0x3F - 1); assert_eq!(res.len(), 0x3F - 1);
assert_eq!(res[0], 3); assert_eq!(res[0], 3);
} else { } else {
assert!(false); panic!();
} }
} }
#[test] #[test]
@ -216,13 +216,13 @@ mod tests {
let mut buf = BytesMut::new(); let mut buf = BytesMut::new();
let mut bytes: Vec<u8> = Vec::new(); let mut bytes: Vec<u8> = Vec::new();
bytes.resize(0x3FFF, 4); 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); assert_eq!(buf.len(), 0x3FFF + 2);
if let Ok(Some(res)) = codec.decode(&mut buf) { if let Ok(Some(res)) = codec.decode(&mut buf) {
assert_eq!(res.len(), 0x3FFF); assert_eq!(res.len(), 0x3FFF);
assert_eq!(res[0], 4); assert_eq!(res[0], 4);
} else { } else {
assert!(false); panic!();
} }
} }
@ -232,13 +232,13 @@ mod tests {
let mut buf = BytesMut::new(); let mut buf = BytesMut::new();
let mut bytes: Vec<u8> = Vec::new(); let mut bytes: Vec<u8> = Vec::new();
bytes.resize(0x3FFFFF, 5); 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); assert_eq!(buf.len(), 0x3FFFFF + 3);
if let Ok(Some(res)) = codec.decode(&mut buf) { if let Ok(Some(res)) = codec.decode(&mut buf) {
assert_eq!(res.len(), 0x3FFFFF); assert_eq!(res.len(), 0x3FFFFF);
assert_eq!(res[0], 5); assert_eq!(res[0], 5);
} else { } else {
assert!(false); panic!();
} }
} }
@ -248,33 +248,33 @@ mod tests {
let mut buf = BytesMut::new(); let mut buf = BytesMut::new();
let mut bytes: Vec<u8> = Vec::new(); let mut bytes: Vec<u8> = Vec::new();
bytes.resize(0x3FFFFF + 1, 6); 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(); let buf_saved = buf.clone();
assert_eq!(buf.len(), 0x3FFFFF + 4 + 1); assert_eq!(buf.len(), 0x3FFFFF + 4 + 1);
if let Ok(Some(res)) = codec.decode(&mut buf) { if let Ok(Some(res)) = codec.decode(&mut buf) {
assert_eq!(res.len(), 0x3FFFFF + 1); assert_eq!(res.len(), 0x3FFFFF + 1);
assert_eq!(res[0], 6); assert_eq!(res[0], 6);
} else { } else {
assert!(false); panic!();
} }
let mut codec2 = BytesCodec::new(); let mut codec2 = BytesCodec::new();
let mut buf2 = BytesMut::new(); let mut buf2 = BytesMut::new();
buf2.extend(&buf_saved[0..1]); buf2.extend(&buf_saved[0..1]);
if let Ok(None) = codec2.decode(&mut buf2) { if let Ok(None) = codec2.decode(&mut buf2) {
} else { } else {
assert!(false); panic!();
} }
buf2.extend(&buf_saved[1..6]); buf2.extend(&buf_saved[1..6]);
if let Ok(None) = codec2.decode(&mut buf2) { if let Ok(None) = codec2.decode(&mut buf2) {
} else { } else {
assert!(false); panic!();
} }
buf2.extend(&buf_saved[6..]); buf2.extend(&buf_saved[6..]);
if let Ok(Some(res)) = codec2.decode(&mut buf2) { if let Ok(Some(res)) = codec2.decode(&mut buf2) {
assert_eq!(res.len(), 0x3FFFFF + 1); assert_eq!(res.len(), 0x3FFFFF + 1);
assert_eq!(res[0], 6); assert_eq!(res[0], 6);
} else { } else {
assert!(false); panic!();
} }
} }
} }

View File

@ -288,7 +288,7 @@ fn patch(path: PathBuf) -> PathBuf {
.trim() .trim()
.to_owned(); .to_owned();
if user != "root" { 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(); let mut path: PathBuf = format!("/tmp/{}", *APP_NAME.read().unwrap()).into();
fs::create_dir(&path).ok(); fs::create_dir(&path).ok();
fs::set_permissions(&path, fs::Permissions::from_mode(0o0777)).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() path.to_str().unwrap_or("").to_owned()
} }
} }
@ -562,7 +562,7 @@ impl Config {
.unwrap_or_default(); .unwrap_or_default();
} }
if !rendezvous_server.contains(':') { if !rendezvous_server.contains(':') {
rendezvous_server = format!("{}:{}", rendezvous_server, RENDEZVOUS_PORT); rendezvous_server = format!("{rendezvous_server}:{RENDEZVOUS_PORT}");
} }
rendezvous_server rendezvous_server
} }

View File

@ -211,11 +211,7 @@ pub fn gen_version() {
// generate build date // generate build date
let build_date = format!("{}", chrono::Local::now().format("%Y-%m-%d %H:%M")); let build_date = format!("{}", chrono::Local::now().format("%Y-%m-%d %H:%M"));
file.write_all( file.write_all(
format!( format!("#[allow(dead_code)]\npub const BUILD_DATE: &str = \"{build_date}\";\n").as_bytes(),
"#[allow(dead_code)]\npub const BUILD_DATE: &str = \"{}\";",
build_date
)
.as_bytes(),
) )
.ok(); .ok();
file.sync_all().ok(); file.sync_all().ok();
@ -342,39 +338,39 @@ mod test {
#[test] #[test]
fn test_ipv6() { fn test_ipv6() {
assert_eq!(is_ipv6_str("1:2:3"), true); assert!(is_ipv6_str("1:2:3"));
assert_eq!(is_ipv6_str("[ab:2:3]:12"), true); assert!(is_ipv6_str("[ab:2:3]:12"));
assert_eq!(is_ipv6_str("[ABEF:2a:3]:12"), true); assert!(is_ipv6_str("[ABEF:2a:3]:12"));
assert_eq!(is_ipv6_str("[ABEG:2a:3]:12"), false); assert!(!is_ipv6_str("[ABEG:2a:3]:12"));
assert_eq!(is_ipv6_str("1[ab:2:3]:12"), false); assert!(!is_ipv6_str("1[ab:2:3]:12"));
assert_eq!(is_ipv6_str("1.1.1.1"), false); assert!(!is_ipv6_str("1.1.1.1"));
assert_eq!(is_ip_str("1.1.1.1"), true); assert!(is_ip_str("1.1.1.1"));
assert_eq!(is_ipv6_str("1:2:"), false); assert!(!is_ipv6_str("1:2:"));
assert_eq!(is_ipv6_str("1:2::0"), true); assert!(is_ipv6_str("1:2::0"));
assert_eq!(is_ipv6_str("[1:2::0]:1"), true); assert!(is_ipv6_str("[1:2::0]:1"));
assert_eq!(is_ipv6_str("[1:2::0]:"), false); assert!(!is_ipv6_str("[1:2::0]:"));
assert_eq!(is_ipv6_str("1:2::0]:1"), false); assert!(!is_ipv6_str("1:2::0]:1"));
} }
#[test] #[test]
fn test_hostname_port() { fn test_hostname_port() {
assert_eq!(is_domain_port_str("a:12"), false); assert!(!is_domain_port_str("a:12"));
assert_eq!(is_domain_port_str("a.b.c:12"), false); assert!(!is_domain_port_str("a.b.c:12"));
assert_eq!(is_domain_port_str("test.com:12"), true); assert!(is_domain_port_str("test.com:12"));
assert_eq!(is_domain_port_str("test-UPPER.com:12"), true); assert!(is_domain_port_str("test-UPPER.com:12"));
assert_eq!(is_domain_port_str("some-other.domain.com:12"), true); assert!(is_domain_port_str("some-other.domain.com:12"));
assert_eq!(is_domain_port_str("under_score:12"), false); assert!(!is_domain_port_str("under_score:12"));
assert_eq!(is_domain_port_str("a@bc:12"), false); assert!(!is_domain_port_str("a@bc:12"));
assert_eq!(is_domain_port_str("1.1.1.1:12"), false); assert!(!is_domain_port_str("1.1.1.1:12"));
assert_eq!(is_domain_port_str("1.2.3:12"), false); assert!(!is_domain_port_str("1.2.3:12"));
assert_eq!(is_domain_port_str("1.2.3.45:12"), false); assert!(!is_domain_port_str("1.2.3.45:12"));
assert_eq!(is_domain_port_str("a.b.c:123456"), false); assert!(!is_domain_port_str("a.b.c:123456"));
assert_eq!(is_domain_port_str("---:12"), false); assert!(!is_domain_port_str("---:12"));
assert_eq!(is_domain_port_str(".:12"), false); assert!(!is_domain_port_str(".:12"));
// todo: should we also check for these edge cases? // todo: should we also check for these edge cases?
// out-of-range port // out-of-range port
assert_eq!(is_domain_port_str("test.com:0"), true); assert!(is_domain_port_str("test.com:0"));
assert_eq!(is_domain_port_str("test.com:98989"), true); assert!(is_domain_port_str("test.com:98989"));
} }
#[test] #[test]

View File

@ -192,51 +192,51 @@ mod test {
let data = "Hello World"; let data = "Hello World";
let encrypted = encrypt_str_or_original(data, version); let encrypted = encrypt_str_or_original(data, version);
let (decrypted, succ, store) = decrypt_str_or_original(&encrypted, version); let (decrypted, succ, store) = decrypt_str_or_original(&encrypted, version);
println!("data: {}", data); println!("data: {data}");
println!("encrypted: {}", encrypted); println!("encrypted: {encrypted}");
println!("decrypted: {}", decrypted); println!("decrypted: {decrypted}");
assert_eq!(data, decrypted); assert_eq!(data, decrypted);
assert_eq!(version, &encrypted[..2]); assert_eq!(version, &encrypted[..2]);
assert_eq!(succ, true); assert!(succ);
assert_eq!(store, false); assert!(!store);
let (_, _, store) = decrypt_str_or_original(&encrypted, "99"); let (_, _, store) = decrypt_str_or_original(&encrypted, "99");
assert_eq!(store, true); assert!(store);
assert_eq!(decrypt_str_or_original(&decrypted, version).1, false); assert!(!decrypt_str_or_original(&decrypted, version).1);
assert_eq!(encrypt_str_or_original(&encrypted, version), encrypted); assert_eq!(encrypt_str_or_original(&encrypted, version), encrypted);
println!("test vec"); println!("test vec");
let data: Vec<u8> = vec![1, 2, 3, 4, 5, 6]; let data: Vec<u8> = vec![1, 2, 3, 4, 5, 6];
let encrypted = encrypt_vec_or_original(&data, version); let encrypted = encrypt_vec_or_original(&data, version);
let (decrypted, succ, store) = decrypt_vec_or_original(&encrypted, version); let (decrypted, succ, store) = decrypt_vec_or_original(&encrypted, version);
println!("data: {:?}", data); println!("data: {data:?}");
println!("encrypted: {:?}", encrypted); println!("encrypted: {encrypted:?}");
println!("decrypted: {:?}", decrypted); println!("decrypted: {decrypted:?}");
assert_eq!(data, decrypted); assert_eq!(data, decrypted);
assert_eq!(version.as_bytes(), &encrypted[..2]); assert_eq!(version.as_bytes(), &encrypted[..2]);
assert_eq!(store, false); assert!(!store);
assert_eq!(succ, true); assert!(succ);
let (_, _, store) = decrypt_vec_or_original(&encrypted, "99"); let (_, _, store) = decrypt_vec_or_original(&encrypted, "99");
assert_eq!(store, true); assert!(store);
assert_eq!(decrypt_vec_or_original(&decrypted, version).1, false); assert!(!decrypt_vec_or_original(&decrypted, version).1);
assert_eq!(encrypt_vec_or_original(&encrypted, version), encrypted); assert_eq!(encrypt_vec_or_original(&encrypted, version), encrypted);
println!("test original"); println!("test original");
let data = version.to_string() + "Hello World"; let data = version.to_string() + "Hello World";
let (decrypted, succ, store) = decrypt_str_or_original(&data, version); let (decrypted, succ, store) = decrypt_str_or_original(&data, version);
assert_eq!(data, decrypted); assert_eq!(data, decrypted);
assert_eq!(store, true); assert!(store);
assert_eq!(succ, false); assert!(!succ);
let verbytes = version.as_bytes(); let verbytes = version.as_bytes();
let data: Vec<u8> = vec![verbytes[0] as u8, verbytes[1] as u8, 1, 2, 3, 4, 5, 6]; let data: Vec<u8> = vec![verbytes[0], verbytes[1], 1, 2, 3, 4, 5, 6];
let (decrypted, succ, store) = decrypt_vec_or_original(&data, version); let (decrypted, succ, store) = decrypt_vec_or_original(&data, version);
assert_eq!(data, decrypted); assert_eq!(data, decrypted);
assert_eq!(store, true); assert!(store);
assert_eq!(succ, false); assert!(!succ);
let (_, succ, store) = decrypt_str_or_original("", version); let (_, succ, store) = decrypt_str_or_original("", version);
assert_eq!(store, false); assert!(!store);
assert_eq!(succ, false); assert!(!succ);
let (_, succ, store) = decrypt_vec_or_original(&vec![], version); let (_, succ, store) = decrypt_vec_or_original(&[], version);
assert_eq!(store, false); assert!(!store);
assert_eq!(succ, false); assert!(!succ);
} }
} }

View File

@ -60,7 +60,7 @@ fn get_display_server_of_session(session: &str) -> String {
.replace("TTY=", "") .replace("TTY=", "")
.trim_end() .trim_end()
.into(); .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 // And check if Xorg is running on that tty
{ {
if xorg_results.trim_end() != "" { if xorg_results.trim_end() != "" {

View File

@ -13,22 +13,22 @@ use tokio_socks::{IntoTargetAddr, TargetAddr};
pub fn check_port<T: std::string::ToString>(host: T, port: i32) -> String { pub fn check_port<T: std::string::ToString>(host: T, port: i32) -> String {
let host = host.to_string(); let host = host.to_string();
if crate::is_ipv6_str(&host) { if crate::is_ipv6_str(&host) {
if host.starts_with("[") { if host.starts_with('[') {
return host; return host;
} }
return format!("[{}]:{}", host, port); return format!("[{host}]:{port}");
} }
if !host.contains(":") { if !host.contains(':') {
return format!("{}:{}", host, port); return format!("{host}:{port}");
} }
return host; host
} }
#[inline] #[inline]
pub fn increase_port<T: std::string::ToString>(host: T, offset: i32) -> String { pub fn increase_port<T: std::string::ToString>(host: T, offset: i32) -> String {
let host = host.to_string(); let host = host.to_string();
if crate::is_ipv6_str(&host) { if crate::is_ipv6_str(&host) {
if host.starts_with("[") { if host.starts_with('[') {
let tmp: Vec<&str> = host.split("]:").collect(); let tmp: Vec<&str> = host.split("]:").collect();
if tmp.len() == 2 { if tmp.len() == 2 {
let port: i32 = tmp[1].parse().unwrap_or(0); let port: i32 = tmp[1].parse().unwrap_or(0);
@ -37,8 +37,8 @@ pub fn increase_port<T: std::string::ToString>(host: T, offset: i32) -> String {
} }
} }
} }
} else if host.contains(":") { } else if host.contains(':') {
let tmp: Vec<&str> = host.split(":").collect(); let tmp: Vec<&str> = host.split(':').collect();
if tmp.len() == 2 { if tmp.len() == 2 {
let port: i32 = tmp[1].parse().unwrap_or(0); let port: i32 = tmp[1].parse().unwrap_or(0);
if port > 0 { if port > 0 {
@ -46,7 +46,7 @@ pub fn increase_port<T: std::string::ToString>(host: T, offset: i32) -> String {
} }
} }
} }
return host; host
} }
pub fn test_if_valid_server(host: &str) -> String { pub fn test_if_valid_server(host: &str) -> String {
@ -148,7 +148,7 @@ pub async fn query_nip_io(addr: &SocketAddr) -> ResultType<SocketAddr> {
pub fn ipv4_to_ipv6(addr: String, ipv4: bool) -> String { pub fn ipv4_to_ipv6(addr: String, ipv4: bool) -> String {
if !ipv4 && crate::is_ipv4_str(&addr) { if !ipv4 && crate::is_ipv4_str(&addr) {
if let Some(ip) = addr.split(':').next() { if let Some(ip) = addr.split(':').next() {
return addr.replace(ip, &format!("{}.nip.io", ip)); return addr.replace(ip, &format!("{ip}.nip.io"));
} }
} }
addr addr
@ -163,7 +163,7 @@ async fn test_target(target: &str) -> ResultType<SocketAddr> {
tokio::net::lookup_host(target) tokio::net::lookup_host(target)
.await? .await?
.next() .next()
.context(format!("Failed to look up host for {}", target)) .context(format!("Failed to look up host for {target}"))
} }
#[inline] #[inline]

View File

@ -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>( pub async fn connect<'a, 't, P, T>(

View File

@ -25,7 +25,7 @@ use hbb_common::{allow_err, get_time, message_proto::*, sleep};
use hbb_common::{fs, log, Stream}; use hbb_common::{fs, log, Stream};
use crate::client::{ 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, QualityStatus, MILLI1, SEC30, SERVER_CLIPBOARD_ENABLED, SERVER_FILE_TRANSFER_ENABLED,
SERVER_KEYBOARD_ENABLED, SERVER_KEYBOARD_ENABLED,
}; };

View File

@ -30,7 +30,7 @@ use hbb_common::{
// #[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))] // #[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))]
use hbb_common::{config::RENDEZVOUS_PORT, futures::future::join_all}; 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<Output = ()>; pub type NotifyMessageBox = fn(String, String, String, String) -> dyn Future<Output = ()>;
@ -762,8 +762,3 @@ pub fn make_fd_to_json(id: i32, path: String, entries: &Vec<FileEntry>) -> Strin
fd_json.insert("entries".into(), json!(entries_out)); fd_json.insert("entries".into(), json!(entries_out));
serde_json::to_string(&fd_json).unwrap_or("".into()) serde_json::to_string(&fd_json).unwrap_or("".into())
} }
#[cfg(test)]
mod test_common {
}

View File

@ -1,6 +1,4 @@
use std::future::Future; use hbb_common::log;
use hbb_common::{log, ResultType};
/// shared by flutter and sciter main function /// shared by flutter and sciter main function
/// ///

View File

@ -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; use std::str::FromStr;
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))]
use std::thread;
use flutter_rust_bridge::{StreamSink, SyncReturn, ZeroCopyBuffer}; use flutter_rust_bridge::{StreamSink, SyncReturn, ZeroCopyBuffer};
use serde_json::json; use serde_json::json;
@ -928,7 +931,7 @@ pub fn main_start_dbus_server() {
{ {
use crate::dbus::start_dbus_server; use crate::dbus::start_dbus_server;
// spawn new thread to start dbus server // spawn new thread to start dbus server
std::thread::spawn(|| { thread::spawn(|| {
let _ = start_dbus_server(); let _ = start_dbus_server();
}); });
} }
@ -1275,7 +1278,7 @@ pub fn main_is_login_wayland() -> SyncReturn<bool> {
pub fn main_start_pa() { pub fn main_start_pa() {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
std::thread::spawn(crate::ipc::start_pa); thread::spawn(crate::ipc::start_pa);
} }
pub fn main_hide_docker() -> SyncReturn<bool> { pub fn main_hide_docker() -> SyncReturn<bool> {
@ -1302,9 +1305,9 @@ pub fn main_start_ipc_url_server() {
/// ///
/// * macOS only /// * macOS only
#[allow(unused_variables)] #[allow(unused_variables)]
pub fn send_url_scheme(url: String) { pub fn send_url_scheme(_url: String) {
#[cfg(target_os = "macos")] #[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")] #[cfg(target_os = "android")]
@ -1324,7 +1327,7 @@ pub mod server_side {
_class: JClass, _class: JClass,
) { ) {
log::debug!("startServer from java"); log::debug!("startServer from java");
std::thread::spawn(move || start_server(true)); thread::spawn(move || start_server(true));
} }
#[no_mangle] #[no_mangle]

View File

@ -16,10 +16,10 @@ use hbb_common::{
config::{self, Config, Config2}, config::{self, Config, Config2},
futures::StreamExt as _, futures::StreamExt as _,
futures_util::sink::SinkExt, futures_util::sink::SinkExt,
log, password_security as password, ResultType, timeout, log, password_security as password, timeout, tokio,
tokio,
tokio::io::{AsyncRead, AsyncWrite}, tokio::io::{AsyncRead, AsyncWrite},
tokio_util::codec::Framed, tokio_util::codec::Framed,
ResultType,
}; };
use crate::rendezvous_mediator::RendezvousMediator; use crate::rendezvous_mediator::RendezvousMediator;
@ -190,7 +190,7 @@ pub enum Data {
Socks(Option<config::Socks5Server>), Socks(Option<config::Socks5Server>),
FS(FS), FS(FS),
Test, Test,
SyncConfig(Option<(Config, Config2)>), SyncConfig(Option<Box<(Config, Config2)>>),
#[cfg(not(any(target_os = "android", target_os = "ios")))] #[cfg(not(any(target_os = "android", target_os = "ios")))]
ClipboardFile(ClipboardFile), ClipboardFile(ClipboardFile),
ClipboardFileEnabled(bool), ClipboardFileEnabled(bool),
@ -419,7 +419,8 @@ async fn handle(data: Data, stream: &mut Connection) {
let t = Config::get_nat_type(); let t = Config::get_nat_type();
allow_err!(stream.send(&Data::NatType(Some(t))).await); 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(); let _chk = CheckIfRestart::new();
Config::set(config); Config::set(config);
Config2::set(config2); Config2::set(config2);
@ -428,7 +429,9 @@ async fn handle(data: Data, stream: &mut Connection) {
Data::SyncConfig(None) => { Data::SyncConfig(None) => {
allow_err!( allow_err!(
stream stream
.send(&Data::SyncConfig(Some((Config::get(), Config2::get())))) .send(&Data::SyncConfig(Some(
(Config::get(), Config2::get()).into()
)))
.await .await
); );
} }
@ -840,6 +843,19 @@ pub async fn test_rendezvous_server() -> ResultType<()> {
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
pub async fn send_url_scheme(url: String) -> ResultType<()> { 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(()) Ok(())
} }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn verify_ffi_enum_data_size() {
println!("{}", std::mem::size_of::<Data>());
assert!(std::mem::size_of::<Data>() < 96);
}
}

View File

@ -193,8 +193,8 @@ pub mod client {
#[cfg(windows)] #[cfg(windows)]
pub fn update_grab_get_key_name() { pub fn update_grab_get_key_name() {
match get_keyboard_mode_enum() { match get_keyboard_mode_enum() {
KeyboardMode::Map => rdev::set_get_key_name(false), KeyboardMode::Map => rdev::set_get_key_unicode(false),
KeyboardMode::Translate => rdev::set_get_key_name(true), KeyboardMode::Translate => rdev::set_get_key_unicode(true),
_ => {} _ => {}
}; };
} }
@ -259,7 +259,7 @@ pub fn start_grab_loop() {
let mut _keyboard_mode = KeyboardMode::Map; 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) { let res = if KEYBOARD_HOOKED.load(Ordering::SeqCst) {
_keyboard_mode = client::process_event(&event, None); _keyboard_mode = client::process_event(&event, None);
if is_press { if is_press {
@ -272,7 +272,7 @@ pub fn start_grab_loop() {
}; };
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
match scan_code { match _scan_code {
0x1D | 0x021D => rdev::set_modifier(Key::ControlLeft, is_press), 0x1D | 0x021D => rdev::set_modifier(Key::ControlLeft, is_press),
0xE01D => rdev::set_modifier(Key::ControlRight, is_press), 0xE01D => rdev::set_modifier(Key::ControlRight, is_press),
0x2A => rdev::set_modifier(Key::ShiftLeft, is_press), 0x2A => rdev::set_modifier(Key::ShiftLeft, is_press),
@ -288,7 +288,7 @@ pub fn start_grab_loop() {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
unsafe { unsafe {
// AltGr // AltGr
if scan_code == 0x021D { if _scan_code == 0x021D {
IS_0X021D_DOWN = is_press; IS_0X021D_DOWN = is_press;
} }
} }
@ -303,6 +303,8 @@ pub fn start_grab_loop() {
if let Err(error) = rdev::grab(func) { if let Err(error) = rdev::grab(func) {
log::error!("rdev Error: {:?}", error) log::error!("rdev Error: {:?}", error)
} }
#[cfg(target_os = "windows")]
rdev::set_event_popup(false);
}); });
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -804,12 +806,10 @@ pub fn map_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEv
fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec<KeyEvent>) { fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec<KeyEvent>) {
match &event.unicode { match &event.unicode {
Some(unicode_info) => { Some(unicode_info) => {
if !unicode_info.is_dead { for code in &unicode_info.unicode {
for code in &unicode_info.unicode { let mut evt = key_event.clone();
let mut evt = key_event.clone(); evt.set_unicode(*code as _);
evt.set_unicode(*code as _); events.push(evt);
events.push(evt);
}
} }
} }
None => {} None => {}
@ -863,6 +863,12 @@ pub fn translate_virtual_keycode(event: &Event, mut key_event: KeyEvent) -> Opti
pub fn translate_keyboard_mode(event: &Event, key_event: KeyEvent) -> Vec<KeyEvent> { pub fn translate_keyboard_mode(event: &Event, key_event: KeyEvent) -> Vec<KeyEvent> {
let mut events: Vec<KeyEvent> = Vec::new(); let mut events: Vec<KeyEvent> = Vec::new();
if let Some(unicode_info) = &event.unicode {
if unicode_info.is_dead {
return events;
}
}
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
unsafe { unsafe {
if event.scan_code == 0x021D { if event.scan_code == 0x021D {
@ -881,6 +887,13 @@ pub fn translate_keyboard_mode(event: &Event, key_event: KeyEvent) -> Vec<KeyEve
try_fill_unicode(event, &key_event, &mut events); try_fill_unicode(event, &key_event, &mut events);
} }
#[cfg(target_os = "windows")]
unsafe {
if IS_0X021D_DOWN {
return events;
}
}
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
try_fill_unicode(event, &key_event, &mut events); try_fill_unicode(event, &key_event, &mut events);
@ -888,7 +901,6 @@ pub fn translate_keyboard_mode(event: &Event, key_event: KeyEvent) -> Vec<KeyEve
if let Some(evt) = translate_virtual_keycode(event, key_event) { if let Some(evt) = translate_virtual_keycode(event, key_event) {
events.push(evt); events.push(evt);
} }
return events;
} }
events events
} }

View File

@ -392,7 +392,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("or", "oder"), ("or", "oder"),
("Continue with", "Fortfahren mit"), ("Continue with", "Fortfahren mit"),
("Elevate", "Erheben"), ("Elevate", "Erheben"),
("Zoom cursor", "Cursor zoomen"), ("Zoom cursor", "Cursor vergrößern"),
("Accept sessions via password", "Sitzung mit Passwort bestätigen"), ("Accept sessions via password", "Sitzung mit Passwort bestätigen"),
("Accept sessions via click", "Sitzung mit einem Klick bestätigen"), ("Accept sessions via click", "Sitzung mit einem Klick bestätigen"),
("Accept sessions via both", "Sitzung mit Klick und Passwort 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"), ("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."), ("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"), ("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_input", "Um den entfernten Desktop mit der Tastatur steuern zu können, müssen Sie RustDesk die Berechtigung \"Input Monitoring\" erteilen."),
("config_microphone", ""), ("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."), ("request_elevation_tip", "Sie können auch erhöhte Rechte anfordern, wenn sich jemand auf der Gegenseite befindet."),
("Wait", "Warten"), ("Wait", "Warten"),
("Elevation Error", "Berechtigungsfehler"), ("Elevation Error", "Berechtigungsfehler"),
@ -445,9 +445,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Bitrate", "Bitrate"), ("Bitrate", "Bitrate"),
("FPS", "fps"), ("FPS", "fps"),
("Auto", "Automatisch"), ("Auto", "Automatisch"),
("Other Default Options", "Weitere Standardoptionen"), ("Other Default Options", "Weitere Standardeinstellungen"),
("Voice call", ""), ("Voice call", "Sprachanruf"),
("Text chat", ""), ("Text chat", "Text-Chat"),
("Stop voice call", ""), ("Stop voice call", "Sprachanruf beenden"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -446,8 +446,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("FPS", "FPS"), ("FPS", "FPS"),
("Auto", "Auto"), ("Auto", "Auto"),
("Other Default Options", "Altre Opzioni Predefinite"), ("Other Default Options", "Altre Opzioni Predefinite"),
("Voice call", ""), ("Voice call", "Chiamata vocale"),
("Text chat", ""), ("Text chat", "Chat testuale"),
("Stop voice call", ""), ("Stop voice call", "Interrompi la chiamata vocale"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -8,6 +8,9 @@ use std::{
use bytes::Bytes; use bytes::Bytes;
pub use connection::*; 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::{ use hbb_common::{
allow_err, allow_err,
anyhow::{anyhow, Context}, anyhow::{anyhow, Context},
@ -17,18 +20,15 @@ use hbb_common::{
message_proto::*, message_proto::*,
protobuf::{Enum, Message as _}, protobuf::{Enum, Message as _},
rendezvous_proto::*, rendezvous_proto::*,
ResultType,
socket_client, 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")))] #[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::ServiceTmpl;
use service::{GenericService, Service, Subscriber};
use crate::ipc::{connect, Data}; use crate::ipc::Data;
pub mod audio_service; pub mod audio_service;
cfg_if::cfg_if! { cfg_if::cfg_if! {
@ -420,7 +420,8 @@ pub async fn start_server(is_server: bool) {
if conn.send(&Data::SyncConfig(None)).await.is_ok() { if conn.send(&Data::SyncConfig(None)).await.is_ok() {
if let Ok(Some(data)) = conn.next_timeout(1000).await { if let Ok(Some(data)) = conn.next_timeout(1000).await {
match data { match data {
Data::SyncConfig(Some((config, config2))) => { Data::SyncConfig(Some(configs)) => {
let (config, config2) = *configs;
if Config::set(config) { if Config::set(config) {
log::info!("config synced"); log::info!("config synced");
} }
@ -450,28 +451,26 @@ pub async fn start_ipc_url_server() {
while let Some(Ok(conn)) = incoming.next().await { while let Some(Ok(conn)) = incoming.next().await {
let mut conn = crate::ipc::Connection::new(conn); let mut conn = crate::ipc::Connection::new(conn);
match conn.next_timeout(1000).await { match conn.next_timeout(1000).await {
Ok(Some(data)) => { Ok(Some(data)) => match data {
match data { #[cfg(feature = "flutter")]
Data::UrlLink(url) => { Data::UrlLink(url) => {
#[cfg(feature = "flutter")] if let Some(stream) = crate::flutter::GLOBAL_EVENT_STREAM
{ .read()
if let Some(stream) = crate::flutter::GLOBAL_EVENT_STREAM.read().unwrap().get( .unwrap()
crate::flutter::APP_TYPE_MAIN .get(crate::flutter::APP_TYPE_MAIN)
) { {
let mut m = HashMap::new(); let mut m = HashMap::new();
m.insert("name", "on_url_scheme_received"); m.insert("name", "on_url_scheme_received");
m.insert("url", url.as_str()); m.insert("url", url.as_str());
stream.add(serde_json::to_string(&m).unwrap()); stream.add(serde_json::to_string(&m).unwrap());
} else { } else {
log::warn!("No main window app found!"); log::warn!("No main window app found!");
}
}
}
_ => {
log::warn!("An unexpected data was sent to the ipc url server.")
} }
} }
} _ => {
log::warn!("An unexpected data was sent to the ipc url server.")
}
},
Err(err) => { Err(err) => {
log::error!("{}", 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 conn.send(&Data::SyncConfig(None)).await.is_ok() {
if let Ok(Some(data)) = conn.next_timeout(1000).await { if let Ok(Some(data)) = conn.next_timeout(1000).await {
match data { match data {
Data::SyncConfig(Some((config, config2))) => { Data::SyncConfig(Some(configs)) => {
let (config, config2) = *configs;
let _chk = crate::ipc::CheckIfRestart::new(); let _chk = crate::ipc::CheckIfRestart::new();
if cfg0.0 != config { if cfg0.0 != config {
cfg0.0 = config.clone(); cfg0.0 = config.clone();
@ -534,7 +534,7 @@ async fn sync_and_watch_config_dir() {
let cfg = (Config::get(), Config2::get()); let cfg = (Config::get(), Config2::get());
if cfg != cfg0 { if cfg != cfg0 {
log::info!("config updated, sync to root"); 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) => { Err(e) => {
log::error!("sync config to root failed: {}", e); log::error!("sync config to root failed: {}", e);
break; break;

View File

@ -14,12 +14,9 @@ use objc::{
sel, sel_impl, sel, sel_impl,
}; };
use objc::runtime::Class; use objc::runtime::Class;
use objc_id::WeakId;
use sciter::{Host, make_args}; use sciter::{Host, make_args};
use hbb_common::{log, tokio}; use hbb_common::log;
use crate::ui_cm_interface::start_ipc;
static APP_HANDLER_IVAR: &str = "GoDeskAppHandler"; static APP_HANDLER_IVAR: &str = "GoDeskAppHandler";
@ -141,7 +138,7 @@ extern "C" fn application_should_handle_open_untitled_file(
if !LAUNCHED { if !LAUNCHED {
return YES; 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()) { if std::env::args().nth(1) == Some("--server".to_owned()) {
crate::platform::macos::check_main_window(); crate::platform::macos::check_main_window();
} }

View File

@ -845,6 +845,7 @@ pub fn elevate_portable(_id: i32) {
} }
} }
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
#[inline] #[inline]
pub fn handle_incoming_voice_call(id: i32, accept: bool) { pub fn handle_incoming_voice_call(id: i32, accept: bool) {
if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) { if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) {
@ -852,6 +853,7 @@ pub fn handle_incoming_voice_call(id: i32, accept: bool) {
}; };
} }
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
#[inline] #[inline]
pub fn close_voice_call(id: i32) { pub fn close_voice_call(id: i32) {
if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) { if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) {

View File

@ -369,8 +369,8 @@ impl<T: InvokeUiSession> Session<T> {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
match &self.lc.read().unwrap().keyboard_mode as _ { match &self.lc.read().unwrap().keyboard_mode as _ {
"legacy" => rdev::set_get_key_name(true), "legacy" => rdev::set_get_key_unicode(true),
"translate" => rdev::set_get_key_name(true), "translate" => rdev::set_get_key_unicode(true),
_ => {} _ => {}
} }
} }
@ -382,7 +382,7 @@ impl<T: InvokeUiSession> Session<T> {
pub fn leave(&self) { pub fn leave(&self) {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
rdev::set_get_key_name(false); rdev::set_get_key_unicode(false);
} }
IS_IN.store(false, Ordering::SeqCst); IS_IN.store(false, Ordering::SeqCst);
keyboard::client::change_grab_status(GrabState::Wait); keyboard::client::change_grab_status(GrabState::Wait);