mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge remote-tracking branch 'github/master' into sigma
This commit is contained in:
commit
e3963adf49
15
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
15
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -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
|
||||||
|
|||||||
1
.github/workflows/flutter-nightly.yml
vendored
1
.github/workflows/flutter-nightly.yml
vendored
@ -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
2
Cargo.lock
generated
@ -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",
|
||||||
|
|||||||
3
build.py
3
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.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("..")
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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() != "" {
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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>(
|
||||||
|
|||||||
@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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 {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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
|
||||||
///
|
///
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
28
src/ipc.rs
28
src/ipc.rs
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user