Merge branch 'rustdesk:master' into master
This commit is contained in:
commit
3ec327c2fa
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2559,7 +2559,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "impersonate_system"
|
name = "impersonate_system"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/21pages/impersonate-system#af4a82050580217a434c2024e181a98de24823ec"
|
source = "git+https://github.com/21pages/impersonate-system#c48f37a8fd17413b2a4ba655c3873bdc5c8d25aa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
2
build.py
2
build.py
@ -17,7 +17,7 @@ flutter_win_target_dir = 'flutter/build/windows/runner/Release/'
|
|||||||
|
|
||||||
|
|
||||||
def get_version():
|
def get_version():
|
||||||
with open("Cargo.toml") as fh:
|
with open("Cargo.toml", encoding="utf-8") as fh:
|
||||||
for line in fh:
|
for line in fh:
|
||||||
if line.startswith("version"):
|
if line.startswith("version"):
|
||||||
return line.replace("version", "").replace("=", "").replace('"', '').strip()
|
return line.replace("version", "").replace("=", "").replace('"', '').strip()
|
||||||
|
@ -2,7 +2,6 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:back_button_interceptor/back_button_interceptor.dart';
|
import 'package:back_button_interceptor/back_button_interceptor.dart';
|
||||||
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
||||||
@ -1210,8 +1209,11 @@ Future<void> initUniLinks() async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamSubscription listenUniLinks() {
|
StreamSubscription? listenUniLinks() {
|
||||||
if (Platform.isWindows || Platform.isMacOS) {
|
if (!(Platform.isWindows || Platform.isMacOS)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
final sub = uriLinkStream.listen((Uri? uri) {
|
final sub = uriLinkStream.listen((Uri? uri) {
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
callUniLinksUriHandler(uri);
|
callUniLinksUriHandler(uri);
|
||||||
@ -1222,11 +1224,6 @@ StreamSubscription listenUniLinks() {
|
|||||||
print("uni links error: $err");
|
print("uni links error: $err");
|
||||||
});
|
});
|
||||||
return sub;
|
return sub;
|
||||||
} else {
|
|
||||||
// return empty stream subscription for uniform logic
|
|
||||||
final stream = Stream.empty();
|
|
||||||
return stream.listen((event) {/*ignore*/});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkArguments() {
|
void checkArguments() {
|
||||||
|
@ -41,6 +41,9 @@ const Duration kTabTransitionDuration = Duration.zero;
|
|||||||
const double kEmptyMarginTop = 50;
|
const double kEmptyMarginTop = 50;
|
||||||
const double kDesktopIconButtonSplashRadius = 20;
|
const double kDesktopIconButtonSplashRadius = 20;
|
||||||
|
|
||||||
|
/// [kMinCursorSize] indicates min cursor (w, h)
|
||||||
|
const int kMinCursorSize = 24;
|
||||||
|
|
||||||
/// [kDefaultScrollAmountMultiplier] indicates how many rows can be scrolled after a minimum scroll action of mouse
|
/// [kDefaultScrollAmountMultiplier] indicates how many rows can be scrolled after a minimum scroll action of mouse
|
||||||
const kDefaultScrollAmountMultiplier = 5.0;
|
const kDefaultScrollAmountMultiplier = 5.0;
|
||||||
const kDefaultScrollDuration = Duration(milliseconds: 50);
|
const kDefaultScrollDuration = Duration(milliseconds: 50);
|
||||||
|
@ -412,15 +412,6 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
Timer(const Duration(seconds: 1), () async {
|
|
||||||
final installed = bind.mainIsInstalled();
|
|
||||||
final root = await bind.mainIsRoot();
|
|
||||||
final release = await bind.mainIsRelease();
|
|
||||||
if (Platform.isWindows && release && !installed && !root) {
|
|
||||||
msgBox('custom-elevation-nocancel', 'Prompt', 'elevation_prompt', '',
|
|
||||||
gFFI.dialogManager);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Timer(const Duration(seconds: 5), () async {
|
Timer(const Duration(seconds: 5), () async {
|
||||||
updateUrl = await bind.mainGetSoftwareUpdateUrl();
|
updateUrl = await bind.mainGetSoftwareUpdateUrl();
|
||||||
if (updateUrl.isNotEmpty) setState(() {});
|
if (updateUrl.isNotEmpty) setState(() {});
|
||||||
|
@ -5,7 +5,6 @@ import 'package:file_picker/file_picker.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
import 'package:flutter_hbb/consts.dart';
|
|
||||||
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
|
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
|
||||||
import 'package:flutter_hbb/models/platform_model.dart';
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
|
@ -6,6 +6,7 @@ import 'package:flutter_hbb/consts.dart';
|
|||||||
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart';
|
import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||||
|
import 'package:flutter_hbb/models/state_model.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';
|
||||||
|
|
||||||
@ -62,8 +63,6 @@ class _DesktopTabPageState extends State<DesktopTabPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
RxBool fullscreen = false.obs;
|
|
||||||
Get.put(fullscreen, tag: 'fullscreen');
|
|
||||||
final tabWidget = Container(
|
final tabWidget = Container(
|
||||||
child: Overlay(initialEntries: [
|
child: Overlay(initialEntries: [
|
||||||
OverlayEntry(builder: (context) {
|
OverlayEntry(builder: (context) {
|
||||||
@ -84,9 +83,11 @@ class _DesktopTabPageState extends State<DesktopTabPage> {
|
|||||||
);
|
);
|
||||||
return Platform.isMacOS
|
return Platform.isMacOS
|
||||||
? tabWidget
|
? tabWidget
|
||||||
: Obx(() => DragToResizeArea(
|
: Obx(
|
||||||
resizeEdgeSize:
|
() => DragToResizeArea(
|
||||||
fullscreen.value ? kFullScreenEdgeSize : kWindowEdgeSize,
|
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
||||||
child: tabWidget));
|
child: tabWidget,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import 'package:desktop_multi_window/desktop_multi_window.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:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/file_manager_page.dart';
|
import 'package:flutter_hbb/desktop/pages/file_manager_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
@ -47,7 +48,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
|||||||
|
|
||||||
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
||||||
print(
|
print(
|
||||||
"[FileTransfer] call ${call.method} with args ${call.arguments} from window ${fromWindowId} to ${windowId()}");
|
"[FileTransfer] call ${call.method} with args ${call.arguments} from window $fromWindowId to ${windowId()}");
|
||||||
// for simplify, just replace connectionId
|
// for simplify, just replace connectionId
|
||||||
if (call.method == "new_file_transfer") {
|
if (call.method == "new_file_transfer") {
|
||||||
final args = jsonDecode(call.arguments);
|
final args = jsonDecode(call.arguments);
|
||||||
@ -87,9 +88,9 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
|||||||
return Platform.isMacOS
|
return Platform.isMacOS
|
||||||
? tabWidget
|
? tabWidget
|
||||||
: SubWindowDragToResizeArea(
|
: SubWindowDragToResizeArea(
|
||||||
resizeEdgeSize: kWindowEdgeSize,
|
|
||||||
windowId: windowId(),
|
|
||||||
child: tabWidget,
|
child: tabWidget,
|
||||||
|
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
||||||
|
windowId: stateGlobal.windowId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import 'package:desktop_multi_window/desktop_multi_window.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:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/port_forward_page.dart';
|
import 'package:flutter_hbb/desktop/pages/port_forward_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
@ -50,7 +51,7 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
|
|||||||
|
|
||||||
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
||||||
debugPrint(
|
debugPrint(
|
||||||
"call ${call.method} with args ${call.arguments} from window ${fromWindowId}");
|
"call ${call.method} with args ${call.arguments} from window $fromWindowId");
|
||||||
// for simplify, just replace connectionId
|
// for simplify, just replace connectionId
|
||||||
if (call.method == "new_port_forward") {
|
if (call.method == "new_port_forward") {
|
||||||
final args = jsonDecode(call.arguments);
|
final args = jsonDecode(call.arguments);
|
||||||
@ -98,9 +99,9 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
|
|||||||
return Platform.isMacOS
|
return Platform.isMacOS
|
||||||
? tabWidget
|
? tabWidget
|
||||||
: SubWindowDragToResizeArea(
|
: SubWindowDragToResizeArea(
|
||||||
resizeEdgeSize: kWindowEdgeSize,
|
|
||||||
windowId: windowId(),
|
|
||||||
child: tabWidget,
|
child: tabWidget,
|
||||||
|
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
||||||
|
windowId: stateGlobal.windowId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,15 +28,9 @@ class RemotePage extends StatefulWidget {
|
|||||||
const RemotePage({
|
const RemotePage({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.windowId,
|
|
||||||
required this.tabBarHeight,
|
|
||||||
required this.windowBorderWidth,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
final int windowId;
|
|
||||||
final double tabBarHeight;
|
|
||||||
final double windowBorderWidth;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RemotePage> createState() => _RemotePageState();
|
State<RemotePage> createState() => _RemotePageState();
|
||||||
@ -58,11 +52,6 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
|
|
||||||
late FFI _ffi;
|
late FFI _ffi;
|
||||||
|
|
||||||
void _updateTabBarHeight() {
|
|
||||||
_ffi.canvasModel.tabBarHeight = widget.tabBarHeight;
|
|
||||||
_ffi.canvasModel.windowBorderWidth = widget.windowBorderWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _initStates(String id) {
|
void _initStates(String id) {
|
||||||
PrivacyModeState.init(id);
|
PrivacyModeState.init(id);
|
||||||
BlockInputState.init(id);
|
BlockInputState.init(id);
|
||||||
@ -91,7 +80,6 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
|
|
||||||
_ffi = FFI();
|
_ffi = FFI();
|
||||||
|
|
||||||
_updateTabBarHeight();
|
|
||||||
Get.put(_ffi, tag: widget.id);
|
Get.put(_ffi, tag: widget.id);
|
||||||
_ffi.start(widget.id);
|
_ffi.start(widget.id);
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
@ -164,7 +152,6 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
_updateTabBarHeight();
|
|
||||||
return WillPopScope(
|
return WillPopScope(
|
||||||
onWillPop: () async {
|
onWillPop: () async {
|
||||||
clientClose(_ffi.dialogManager);
|
clientClose(_ffi.dialogManager);
|
||||||
@ -241,7 +228,6 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
paints.add(QualityMonitor(_ffi.qualityMonitorModel));
|
paints.add(QualityMonitor(_ffi.qualityMonitorModel));
|
||||||
paints.add(RemoteMenubar(
|
paints.add(RemoteMenubar(
|
||||||
id: widget.id,
|
id: widget.id,
|
||||||
windowId: widget.windowId,
|
|
||||||
ffi: _ffi,
|
ffi: _ffi,
|
||||||
onEnterOrLeaveImageSetter: (func) => _onEnterOrLeaveImage4Menubar = func,
|
onEnterOrLeaveImageSetter: (func) => _onEnterOrLeaveImage4Menubar = func,
|
||||||
onEnterOrLeaveImageCleaner: () => _onEnterOrLeaveImage4Menubar = null,
|
onEnterOrLeaveImageCleaner: () => _onEnterOrLeaveImage4Menubar = null,
|
||||||
@ -284,7 +270,7 @@ class ImagePaint extends StatelessWidget {
|
|||||||
? keyboardEnabled.isTrue
|
? keyboardEnabled.isTrue
|
||||||
? (remoteCursorMoved.isTrue
|
? (remoteCursorMoved.isTrue
|
||||||
? SystemMouseCursors.none
|
? SystemMouseCursors.none
|
||||||
: _buildCustomCursorLinux(context, s))
|
: _buildCustomCursor(context, s))
|
||||||
: _buildDisabledCursor(context, s)
|
: _buildDisabledCursor(context, s)
|
||||||
: MouseCursor.defer,
|
: MouseCursor.defer,
|
||||||
onHover: (evt) {},
|
onHover: (evt) {},
|
||||||
@ -333,41 +319,43 @@ class ImagePaint extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseCursor _buildCustomCursorLinux(BuildContext context, double scale) {
|
MouseCursor _buildCustomCursor(BuildContext context, double scale) {
|
||||||
final cursor = Provider.of<CursorModel>(context);
|
final cursor = Provider.of<CursorModel>(context);
|
||||||
final cacheLinux = cursor.cacheLinux;
|
final cache = cursor.cache ?? cursor.defaultCache;
|
||||||
if (cacheLinux == null) {
|
if (cache == null) {
|
||||||
return MouseCursor.defer;
|
return MouseCursor.defer;
|
||||||
} else {
|
} else {
|
||||||
final key = cacheLinux.key(scale);
|
final key = cache.updateGetKey(scale);
|
||||||
cursor.addKeyLinux(key);
|
cursor.addKey(key);
|
||||||
return FlutterCustomMemoryImageCursor(
|
return FlutterCustomMemoryImageCursor(
|
||||||
pixbuf: cacheLinux.data,
|
pixbuf: cache.data,
|
||||||
key: key,
|
key: key,
|
||||||
hotx: cacheLinux.hotx,
|
// hotx: cache.hotx,
|
||||||
hoty: cacheLinux.hoty,
|
// hoty: cache.hoty,
|
||||||
imageWidth: (cacheLinux.width * scale).toInt(),
|
hotx: 0,
|
||||||
imageHeight: (cacheLinux.height * scale).toInt(),
|
hoty: 0,
|
||||||
|
imageWidth: (cache.width * cache.scale).toInt(),
|
||||||
|
imageHeight: (cache.height * cache.scale).toInt(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseCursor _buildDisabledCursor(BuildContext context, double scale) {
|
MouseCursor _buildDisabledCursor(BuildContext context, double scale) {
|
||||||
final cursor = Provider.of<CursorModel>(context);
|
final cursor = Provider.of<CursorModel>(context);
|
||||||
final cacheLinux = cursor.cacheLinux;
|
final cache = cursor.cache;
|
||||||
if (cacheLinux == null) {
|
if (cache == null) {
|
||||||
return MouseCursor.defer;
|
return MouseCursor.defer;
|
||||||
} else {
|
} else {
|
||||||
if (cursor.cachedForbidmemoryCursorData == null) {
|
if (cursor.cachedForbidmemoryCursorData == null) {
|
||||||
cursor.updateForbiddenCursorBuffer();
|
cursor.updateForbiddenCursorBuffer();
|
||||||
}
|
}
|
||||||
final key = 'disabled_cursor_key';
|
final key = 'disabled_cursor_key';
|
||||||
cursor.addKeyLinux(key);
|
cursor.addKey(key);
|
||||||
return FlutterCustomMemoryImageCursor(
|
return FlutterCustomMemoryImageCursor(
|
||||||
pixbuf: cursor.cachedForbidmemoryCursorData,
|
pixbuf: cursor.cachedForbidmemoryCursorData,
|
||||||
key: key,
|
key: key,
|
||||||
hotx: cacheLinux.hotx,
|
hotx: 0,
|
||||||
hoty: cacheLinux.hoty,
|
hoty: 0,
|
||||||
imageWidth: 32,
|
imageWidth: 32,
|
||||||
imageHeight: 32,
|
imageHeight: 32,
|
||||||
);
|
);
|
||||||
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
import 'package:flutter_hbb/common/shared_state.dart';
|
import 'package:flutter_hbb/common/shared_state.dart';
|
||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/remote_page.dart';
|
import 'package:flutter_hbb/desktop/pages/remote_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
@ -34,7 +35,6 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
|
|
||||||
_ConnectionTabPageState(Map<String, dynamic> params) {
|
_ConnectionTabPageState(Map<String, dynamic> params) {
|
||||||
RemoteCountState.init();
|
RemoteCountState.init();
|
||||||
final RxBool fullscreen = Get.find(tag: 'fullscreen');
|
|
||||||
final peerId = params['id'];
|
final peerId = params['id'];
|
||||||
if (peerId != null) {
|
if (peerId != null) {
|
||||||
ConnectionTypeState.init(peerId);
|
ConnectionTypeState.init(peerId);
|
||||||
@ -44,14 +44,11 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
selectedIcon: selectedIcon,
|
selectedIcon: selectedIcon,
|
||||||
unselectedIcon: unselectedIcon,
|
unselectedIcon: unselectedIcon,
|
||||||
onTabCloseButton: () => tabController.closeBy(peerId),
|
onTabCloseButton: () => tabController.closeBy(peerId),
|
||||||
page: Obx(() => RemotePage(
|
page: RemotePage(
|
||||||
key: ValueKey(peerId),
|
key: ValueKey(peerId),
|
||||||
id: peerId,
|
id: peerId,
|
||||||
windowId: windowId(),
|
),
|
||||||
tabBarHeight:
|
));
|
||||||
fullscreen.isTrue ? 0 : kDesktopRemoteTabBarHeight,
|
|
||||||
windowBorderWidth: fullscreen.isTrue ? 0 : kWindowBorderWidth,
|
|
||||||
))));
|
|
||||||
_update_remote_count();
|
_update_remote_count();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,7 +63,6 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
print(
|
print(
|
||||||
"call ${call.method} with args ${call.arguments} from window $fromWindowId");
|
"call ${call.method} with args ${call.arguments} from window $fromWindowId");
|
||||||
|
|
||||||
final RxBool fullscreen = Get.find(tag: 'fullscreen');
|
|
||||||
// for simplify, just replace connectionId
|
// for simplify, just replace connectionId
|
||||||
if (call.method == "new_remote_desktop") {
|
if (call.method == "new_remote_desktop") {
|
||||||
final args = jsonDecode(call.arguments);
|
final args = jsonDecode(call.arguments);
|
||||||
@ -80,14 +76,8 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
selectedIcon: selectedIcon,
|
selectedIcon: selectedIcon,
|
||||||
unselectedIcon: unselectedIcon,
|
unselectedIcon: unselectedIcon,
|
||||||
onTabCloseButton: () => tabController.closeBy(id),
|
onTabCloseButton: () => tabController.closeBy(id),
|
||||||
page: Obx(() => RemotePage(
|
page: RemotePage(key: ValueKey(id), id: id),
|
||||||
key: ValueKey(id),
|
));
|
||||||
id: id,
|
|
||||||
windowId: windowId(),
|
|
||||||
tabBarHeight:
|
|
||||||
fullscreen.isTrue ? 0 : kDesktopRemoteTabBarHeight,
|
|
||||||
windowBorderWidth: fullscreen.isTrue ? 0 : kWindowBorderWidth,
|
|
||||||
))));
|
|
||||||
} else if (call.method == "onDestroy") {
|
} else if (call.method == "onDestroy") {
|
||||||
tabController.clear();
|
tabController.clear();
|
||||||
} else if (call.method == kWindowActionRebuild) {
|
} else if (call.method == kWindowActionRebuild) {
|
||||||
@ -102,7 +92,6 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final RxBool fullscreen = Get.find(tag: 'fullscreen');
|
|
||||||
final tabWidget = Container(
|
final tabWidget = Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
@ -110,16 +99,11 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
width: kWindowBorderWidth)),
|
width: kWindowBorderWidth)),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Theme.of(context).backgroundColor,
|
backgroundColor: Theme.of(context).backgroundColor,
|
||||||
body: Obx(() => DesktopTab(
|
body: DesktopTab(
|
||||||
controller: tabController,
|
controller: tabController,
|
||||||
showTabBar: fullscreen.isFalse,
|
|
||||||
onWindowCloseButton: handleWindowCloseButton,
|
onWindowCloseButton: handleWindowCloseButton,
|
||||||
tail: const AddButton().paddingOnly(left: 10),
|
tail: const AddButton().paddingOnly(left: 10),
|
||||||
pageViewBuilder: (pageView) {
|
pageViewBuilder: (pageView) => pageView,
|
||||||
WindowController.fromWindowId(windowId())
|
|
||||||
.setFullscreen(fullscreen.isTrue);
|
|
||||||
return pageView;
|
|
||||||
},
|
|
||||||
tabBuilder: (key, icon, label, themeConf) => Obx(() {
|
tabBuilder: (key, icon, label, themeConf) => Obx(() {
|
||||||
final connectionType = ConnectionTypeState.find(key);
|
final connectionType = ConnectionTypeState.find(key);
|
||||||
if (!connectionType.isValid()) {
|
if (!connectionType.isValid()) {
|
||||||
@ -156,15 +140,15 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
))),
|
)),
|
||||||
);
|
);
|
||||||
return Platform.isMacOS
|
return Platform.isMacOS
|
||||||
? tabWidget
|
? tabWidget
|
||||||
: Obx(() => SubWindowDragToResizeArea(
|
: SubWindowDragToResizeArea(
|
||||||
resizeEdgeSize:
|
child: tabWidget,
|
||||||
fullscreen.value ? kFullScreenEdgeSize : kWindowEdgeSize,
|
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
||||||
windowId: windowId(),
|
windowId: stateGlobal.windowId,
|
||||||
child: tabWidget));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onRemoveId(String id) {
|
void onRemoveId(String id) {
|
||||||
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/remote_tab_page.dart';
|
import 'package:flutter_hbb/desktop/pages/remote_tab_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/refresh_wrapper.dart';
|
import 'package:flutter_hbb/desktop/widgets/refresh_wrapper.dart';
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
/// multi-tab desktop remote screen
|
/// multi-tab desktop remote screen
|
||||||
@ -13,8 +12,6 @@ class DesktopRemoteScreen extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
RxBool fullscreen = false.obs;
|
|
||||||
Get.put(fullscreen, tag: 'fullscreen');
|
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
ChangeNotifierProvider.value(value: gFFI.ffiModel),
|
ChangeNotifierProvider.value(value: gFFI.ffiModel),
|
||||||
|
@ -6,6 +6,7 @@ import 'dart:ui' as ui;
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hbb/models/chat_model.dart';
|
import 'package:flutter_hbb/models/chat_model.dart';
|
||||||
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:rxdart/rxdart.dart' as rxdart;
|
import 'package:rxdart/rxdart.dart' as rxdart;
|
||||||
@ -29,7 +30,6 @@ class _MenubarTheme {
|
|||||||
|
|
||||||
class RemoteMenubar extends StatefulWidget {
|
class RemoteMenubar extends StatefulWidget {
|
||||||
final String id;
|
final String id;
|
||||||
final int windowId;
|
|
||||||
final FFI ffi;
|
final FFI ffi;
|
||||||
final Function(Function(bool)) onEnterOrLeaveImageSetter;
|
final Function(Function(bool)) onEnterOrLeaveImageSetter;
|
||||||
final Function() onEnterOrLeaveImageCleaner;
|
final Function() onEnterOrLeaveImageCleaner;
|
||||||
@ -37,7 +37,6 @@ class RemoteMenubar extends StatefulWidget {
|
|||||||
const RemoteMenubar({
|
const RemoteMenubar({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.windowId,
|
|
||||||
required this.ffi,
|
required this.ffi,
|
||||||
required this.onEnterOrLeaveImageSetter,
|
required this.onEnterOrLeaveImageSetter,
|
||||||
required this.onEnterOrLeaveImageCleaner,
|
required this.onEnterOrLeaveImageCleaner,
|
||||||
@ -55,9 +54,12 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
bool _isCursorOverImage = false;
|
bool _isCursorOverImage = false;
|
||||||
window_size.Screen? _screen;
|
window_size.Screen? _screen;
|
||||||
|
|
||||||
bool get isFullscreen => Get.find<RxBool>(tag: 'fullscreen').isTrue;
|
int get windowId => stateGlobal.windowId;
|
||||||
|
|
||||||
|
bool get isFullscreen => stateGlobal.fullscreen;
|
||||||
void _setFullscreen(bool v) {
|
void _setFullscreen(bool v) {
|
||||||
Get.find<RxBool>(tag: 'fullscreen').value = v;
|
stateGlobal.setFullscreen(v);
|
||||||
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -111,13 +113,19 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
},
|
},
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_show.value = !_show.value;
|
_show.value = !_show.value;
|
||||||
|
_hideColor.value = Colors.white24;
|
||||||
if (_show.isTrue) {
|
if (_show.isTrue) {
|
||||||
_updateScreen();
|
_updateScreen();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Obx(() => Container(
|
child: Obx(() => Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: _hideColor.value,
|
color: _hideColor.value,
|
||||||
).marginOnly(bottom: 8.0))))));
|
border: Border.all(color: MyTheme.border),
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(5.0)),
|
||||||
|
),
|
||||||
|
).marginOnly(bottom: 8.0)),
|
||||||
|
))));
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateScreen() async {
|
_updateScreen() async {
|
||||||
@ -170,7 +178,11 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
textStyle: TextStyle(color: _MenubarTheme.commonColor)),
|
textStyle: TextStyle(color: _MenubarTheme.commonColor)),
|
||||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
Container(
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
|
border: Border.all(color: MyTheme.border),
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: menubarItems,
|
children: menubarItems,
|
||||||
@ -203,7 +215,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
_setFullscreen(!isFullscreen);
|
_setFullscreen(!isFullscreen);
|
||||||
},
|
},
|
||||||
icon: Obx(() => isFullscreen
|
icon: isFullscreen
|
||||||
? const Icon(
|
? const Icon(
|
||||||
Icons.fullscreen_exit,
|
Icons.fullscreen_exit,
|
||||||
color: _MenubarTheme.commonColor,
|
color: _MenubarTheme.commonColor,
|
||||||
@ -211,7 +223,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
: const Icon(
|
: const Icon(
|
||||||
Icons.fullscreen,
|
Icons.fullscreen,
|
||||||
color: _MenubarTheme.commonColor,
|
color: _MenubarTheme.commonColor,
|
||||||
)),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -910,8 +922,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
_setFullscreen(false);
|
_setFullscreen(false);
|
||||||
double scale = _screen!.scaleFactor;
|
double scale = _screen!.scaleFactor;
|
||||||
final wndRect =
|
final wndRect =
|
||||||
await WindowController.fromWindowId(widget.windowId)
|
await WindowController.fromWindowId(windowId).getFrame();
|
||||||
.getFrame();
|
|
||||||
final mediaSize = MediaQueryData.fromWindow(ui.window).size;
|
final mediaSize = MediaQueryData.fromWindow(ui.window).size;
|
||||||
// On windows, wndRect is equal to GetWindowRect and mediaSize is equal to GetClientRect.
|
// On windows, wndRect is equal to GetWindowRect and mediaSize is equal to GetClientRect.
|
||||||
// https://stackoverflow.com/a/7561083
|
// https://stackoverflow.com/a/7561083
|
||||||
@ -934,8 +945,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
double top = wndRect.top + (wndRect.height - height) / 2;
|
double top = wndRect.top + (wndRect.height - height) / 2;
|
||||||
|
|
||||||
Rect frameRect = _screen!.frame;
|
Rect frameRect = _screen!.frame;
|
||||||
final RxBool fullscreen = Get.find(tag: 'fullscreen');
|
if (!isFullscreen) {
|
||||||
if (fullscreen.isFalse) {
|
|
||||||
frameRect = _screen!.visibleFrame;
|
frameRect = _screen!.visibleFrame;
|
||||||
}
|
}
|
||||||
if (left < frameRect.left) {
|
if (left < frameRect.left) {
|
||||||
@ -950,7 +960,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
if ((top + height) > frameRect.bottom) {
|
if ((top + height) > frameRect.bottom) {
|
||||||
top = frameRect.bottom - height;
|
top = frameRect.bottom - height;
|
||||||
}
|
}
|
||||||
await WindowController.fromWindowId(widget.windowId)
|
await WindowController.fromWindowId(windowId)
|
||||||
.setFrame(Rect.fromLTWH(left, top, width, height));
|
.setFrame(Rect.fromLTWH(left, top, width, height));
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
@ -9,6 +9,7 @@ import 'package:flutter_hbb/common.dart';
|
|||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/main.dart';
|
import 'package:flutter_hbb/main.dart';
|
||||||
import 'package:flutter_hbb/models/platform_model.dart';
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:get/get_rx/src/rx_workers/utils/debouncer.dart';
|
import 'package:get/get_rx/src/rx_workers/utils/debouncer.dart';
|
||||||
import 'package:scroll_pos/scroll_pos.dart';
|
import 'package:scroll_pos/scroll_pos.dart';
|
||||||
@ -176,10 +177,10 @@ typedef TabBuilder = Widget Function(
|
|||||||
typedef LabelGetter = Rx<String> Function(String key);
|
typedef LabelGetter = Rx<String> Function(String key);
|
||||||
|
|
||||||
/// [_lastClickTime], help to handle double click
|
/// [_lastClickTime], help to handle double click
|
||||||
int _lastClickTime = DateTime.now().millisecondsSinceEpoch;
|
int _lastClickTime =
|
||||||
|
DateTime.now().millisecondsSinceEpoch - kDesktopDoubleClickTimeMilli - 1000;
|
||||||
|
|
||||||
class DesktopTab extends StatelessWidget {
|
class DesktopTab extends StatelessWidget {
|
||||||
final bool showTabBar;
|
|
||||||
final bool showLogo;
|
final bool showLogo;
|
||||||
final bool showTitle;
|
final bool showTitle;
|
||||||
final bool showMinimize;
|
final bool showMinimize;
|
||||||
@ -206,7 +207,6 @@ class DesktopTab extends StatelessWidget {
|
|||||||
DesktopTab({
|
DesktopTab({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
this.showTabBar = true,
|
|
||||||
this.showLogo = true,
|
this.showLogo = true,
|
||||||
this.showTitle = true,
|
this.showTitle = true,
|
||||||
this.showMinimize = true,
|
this.showMinimize = true,
|
||||||
@ -229,8 +229,8 @@ class DesktopTab extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(children: [
|
return Column(children: [
|
||||||
Offstage(
|
Obx(() => Offstage(
|
||||||
offstage: !showTabBar,
|
offstage: !stateGlobal.showTabBar.isTrue,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: _kTabBarHeight,
|
height: _kTabBarHeight,
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -245,7 +245,7 @@ class DesktopTab extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)),
|
))),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: pageViewBuilder != null
|
child: pageViewBuilder != null
|
||||||
? pageViewBuilder!(_buildPageView())
|
? pageViewBuilder!(_buildPageView())
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
|
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/server_page.dart';
|
import 'package:flutter_hbb/desktop/pages/server_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/install_page.dart';
|
import 'package:flutter_hbb/desktop/pages/install_page.dart';
|
||||||
@ -15,7 +15,6 @@ import 'package:flutter_localizations/flutter_localizations.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:uni_links_desktop/uni_links_desktop.dart';
|
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
// import 'package:window_manager/window_manager.dart';
|
// import 'package:window_manager/window_manager.dart';
|
||||||
@ -41,11 +40,14 @@ Future<void> main(List<String> args) async {
|
|||||||
// main window
|
// main window
|
||||||
if (args.isNotEmpty && args.first == 'multi_window') {
|
if (args.isNotEmpty && args.first == 'multi_window') {
|
||||||
windowId = int.parse(args[1]);
|
windowId = int.parse(args[1]);
|
||||||
|
stateGlobal.setWindowId(windowId!);
|
||||||
WindowController.fromWindowId(windowId!).showTitleBar(false);
|
WindowController.fromWindowId(windowId!).showTitleBar(false);
|
||||||
final argument = args[2].isEmpty
|
final argument = args[2].isEmpty
|
||||||
? <String, dynamic>{}
|
? <String, dynamic>{}
|
||||||
: jsonDecode(args[2]) as Map<String, dynamic>;
|
: jsonDecode(args[2]) as Map<String, dynamic>;
|
||||||
int type = argument['type'] ?? -1;
|
int type = argument['type'] ?? -1;
|
||||||
|
// to-do: No need to parse window id ?
|
||||||
|
// Because stateGlobal.windowId is a global value.
|
||||||
argument['windowId'] = windowId;
|
argument['windowId'] = windowId;
|
||||||
WindowType wType = type.windowType;
|
WindowType wType = type.windowType;
|
||||||
switch (wType) {
|
switch (wType) {
|
||||||
|
@ -10,6 +10,7 @@ import '../../models/model.dart';
|
|||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import '../common.dart';
|
import '../common.dart';
|
||||||
import '../consts.dart';
|
import '../consts.dart';
|
||||||
|
import './state_model.dart';
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
/// Mouse button enum.
|
/// Mouse button enum.
|
||||||
@ -321,9 +322,7 @@ class InputModel {
|
|||||||
double x = evt['x'];
|
double x = evt['x'];
|
||||||
double y = max(0.0, evt['y']);
|
double y = max(0.0, evt['y']);
|
||||||
if (isDesktop) {
|
if (isDesktop) {
|
||||||
final RxBool fullscreen = Get.find(tag: 'fullscreen');
|
y = y - stateGlobal.tabBarHeight;
|
||||||
final tabBarHeight = fullscreen.isTrue ? 0 : kDesktopRemoteTabBarHeight;
|
|
||||||
y = y - tabBarHeight;
|
|
||||||
}
|
}
|
||||||
final canvasModel = parent.target!.canvasModel;
|
final canvasModel = parent.target!.canvasModel;
|
||||||
final ffiModel = parent.target!.ffiModel;
|
final ffiModel = parent.target!.ffiModel;
|
||||||
|
@ -14,9 +14,11 @@ import 'package:flutter_hbb/models/chat_model.dart';
|
|||||||
import 'package:flutter_hbb/models/file_model.dart';
|
import 'package:flutter_hbb/models/file_model.dart';
|
||||||
import 'package:flutter_hbb/models/server_model.dart';
|
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/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
import 'package:image/image.dart' as img2;
|
||||||
import 'package:flutter_custom_cursor/flutter_custom_cursor.dart';
|
import 'package:flutter_custom_cursor/flutter_custom_cursor.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
@ -482,9 +484,9 @@ class CanvasModel with ChangeNotifier {
|
|||||||
// image scale
|
// image scale
|
||||||
double _scale = 1.0;
|
double _scale = 1.0;
|
||||||
// the tabbar over the image
|
// the tabbar over the image
|
||||||
double tabBarHeight = 0.0;
|
// double tabBarHeight = 0.0;
|
||||||
// the window border's width
|
// the window border's width
|
||||||
double windowBorderWidth = 0.0;
|
// double windowBorderWidth = 0.0;
|
||||||
// remote id
|
// remote id
|
||||||
String id = '';
|
String id = '';
|
||||||
// scroll offset x percent
|
// scroll offset x percent
|
||||||
@ -570,6 +572,9 @@ class CanvasModel with ChangeNotifier {
|
|||||||
return parent.target?.ffiModel.display.height ?? defaultHeight;
|
return parent.target?.ffiModel.display.height ?? defaultHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double get windowBorderWidth => stateGlobal.windowBorderWidth;
|
||||||
|
double get tabBarHeight => stateGlobal.tabBarHeight;
|
||||||
|
|
||||||
Size get size {
|
Size get size {
|
||||||
final size = MediaQueryData.fromWindow(ui.window).size;
|
final size = MediaQueryData.fromWindow(ui.window).size;
|
||||||
return Size(size.width - windowBorderWidth * 2,
|
return Size(size.width - windowBorderWidth * 2,
|
||||||
@ -665,15 +670,19 @@ class CanvasModel with ChangeNotifier {
|
|||||||
class CursorData {
|
class CursorData {
|
||||||
final String peerId;
|
final String peerId;
|
||||||
final int id;
|
final int id;
|
||||||
final Uint8List? data;
|
final img2.Image? image;
|
||||||
final double hotx;
|
double scale;
|
||||||
final double hoty;
|
Uint8List? data;
|
||||||
|
double hotx;
|
||||||
|
double hoty;
|
||||||
final int width;
|
final int width;
|
||||||
final int height;
|
final int height;
|
||||||
|
|
||||||
CursorData({
|
CursorData({
|
||||||
required this.peerId,
|
required this.peerId,
|
||||||
required this.id,
|
required this.id,
|
||||||
|
required this.image,
|
||||||
|
required this.scale,
|
||||||
required this.data,
|
required this.data,
|
||||||
required this.hotx,
|
required this.hotx,
|
||||||
required this.hoty,
|
required this.hoty,
|
||||||
@ -683,16 +692,46 @@ class CursorData {
|
|||||||
|
|
||||||
int _doubleToInt(double v) => (v * 10e6).round().toInt();
|
int _doubleToInt(double v) => (v * 10e6).round().toInt();
|
||||||
|
|
||||||
String key(double scale) =>
|
double _checkUpdateScale(double scale) {
|
||||||
'${peerId}_${id}_${_doubleToInt(width * scale)}_${_doubleToInt(height * scale)}';
|
// Update data if scale changed.
|
||||||
|
if (Platform.isWindows) {
|
||||||
|
final tgtWidth = (width * scale).toInt();
|
||||||
|
final tgtHeight = (width * scale).toInt();
|
||||||
|
if (tgtWidth < kMinCursorSize || tgtHeight < kMinCursorSize) {
|
||||||
|
double sw = kMinCursorSize.toDouble() / width;
|
||||||
|
double sh = kMinCursorSize.toDouble() / height;
|
||||||
|
scale = sw < sh ? sh : sw;
|
||||||
|
}
|
||||||
|
if (_doubleToInt(this.scale) != _doubleToInt(scale)) {
|
||||||
|
data = img2
|
||||||
|
.copyResize(
|
||||||
|
image!,
|
||||||
|
width: (width * scale).toInt(),
|
||||||
|
height: (height * scale).toInt(),
|
||||||
|
)
|
||||||
|
.getBytes(format: img2.Format.bgra);
|
||||||
|
hotx = (width * scale) / 2;
|
||||||
|
hoty = (height * scale) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.scale = scale;
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
String updateGetKey(double scale) {
|
||||||
|
scale = _checkUpdateScale(scale);
|
||||||
|
return '${peerId}_${id}_${_doubleToInt(width * scale)}_${_doubleToInt(height * scale)}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CursorModel with ChangeNotifier {
|
class CursorModel with ChangeNotifier {
|
||||||
ui.Image? _image;
|
ui.Image? _image;
|
||||||
final _images = <int, Tuple3<ui.Image, double, double>>{};
|
final _images = <int, Tuple3<ui.Image, double, double>>{};
|
||||||
CursorData? _cacheLinux;
|
CursorData? _cache;
|
||||||
final _cacheMapLinux = <int, CursorData>{};
|
final _defaultCacheId = -1;
|
||||||
final _cacheKeysLinux = <String>{};
|
CursorData? _defaultCache;
|
||||||
|
final _cacheMap = <int, CursorData>{};
|
||||||
|
final _cacheKeys = <String>{};
|
||||||
double _x = -10000;
|
double _x = -10000;
|
||||||
double _y = -10000;
|
double _y = -10000;
|
||||||
double _hotx = 0;
|
double _hotx = 0;
|
||||||
@ -703,7 +742,8 @@ class CursorModel with ChangeNotifier {
|
|||||||
WeakReference<FFI> parent;
|
WeakReference<FFI> parent;
|
||||||
|
|
||||||
ui.Image? get image => _image;
|
ui.Image? get image => _image;
|
||||||
CursorData? get cacheLinux => _cacheLinux;
|
CursorData? get cache => _cache;
|
||||||
|
CursorData? get defaultCache => _getDefaultCache();
|
||||||
|
|
||||||
double get x => _x - _displayOriginX;
|
double get x => _x - _displayOriginX;
|
||||||
|
|
||||||
@ -717,8 +757,31 @@ class CursorModel with ChangeNotifier {
|
|||||||
|
|
||||||
CursorModel(this.parent);
|
CursorModel(this.parent);
|
||||||
|
|
||||||
Set<String> get cachedKeysLinux => _cacheKeysLinux;
|
Set<String> get cachedKeys => _cacheKeys;
|
||||||
addKeyLinux(String key) => _cacheKeysLinux.add(key);
|
addKey(String key) => _cacheKeys.add(key);
|
||||||
|
|
||||||
|
CursorData? _getDefaultCache() {
|
||||||
|
if (_defaultCache == null) {
|
||||||
|
if (Platform.isWindows) {
|
||||||
|
Uint8List data = defaultCursorImage!.getBytes(format: img2.Format.bgra);
|
||||||
|
_hotx = defaultCursorImage!.width / 2;
|
||||||
|
_hoty = defaultCursorImage!.height / 2;
|
||||||
|
|
||||||
|
_defaultCache = CursorData(
|
||||||
|
peerId: id,
|
||||||
|
id: _defaultCacheId,
|
||||||
|
image: defaultCursorImage?.clone(),
|
||||||
|
scale: 1.0,
|
||||||
|
data: data,
|
||||||
|
hotx: _hotx,
|
||||||
|
hoty: _hoty,
|
||||||
|
width: defaultCursorImage!.width,
|
||||||
|
height: defaultCursorImage!.height,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _defaultCache;
|
||||||
|
}
|
||||||
|
|
||||||
// remote physical display coordinate
|
// remote physical display coordinate
|
||||||
Rect getVisibleRect() {
|
Rect getVisibleRect() {
|
||||||
@ -863,27 +926,48 @@ class CursorModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_updateCacheLinux(ui.Image image, int id, int w, int h) async {
|
_updateCacheLinux(ui.Image image, int id, int w, int h) async {
|
||||||
ByteData? data;
|
Uint8List? data;
|
||||||
|
img2.Image? image2;
|
||||||
if (Platform.isWindows) {
|
if (Platform.isWindows) {
|
||||||
data = await image.toByteData(format: ui.ImageByteFormat.rawRgba);
|
ByteData? data2 =
|
||||||
|
await image.toByteData(format: ui.ImageByteFormat.rawRgba);
|
||||||
|
if (data2 != null) {
|
||||||
|
data = data2.buffer.asUint8List();
|
||||||
|
image2 = img2.Image.fromBytes(w, h, data);
|
||||||
} else {
|
} else {
|
||||||
data = await image.toByteData(format: ui.ImageByteFormat.png);
|
data = defaultCursorImage?.getBytes(format: img2.Format.bgra);
|
||||||
|
image2 = defaultCursorImage?.clone();
|
||||||
|
_hotx = defaultCursorImage!.width / 2;
|
||||||
|
_hoty = defaultCursorImage!.height / 2;
|
||||||
}
|
}
|
||||||
_cacheLinux = CursorData(
|
} else {
|
||||||
|
ByteData? data2 = await image.toByteData(format: ui.ImageByteFormat.png);
|
||||||
|
if (data2 != null) {
|
||||||
|
data = data2.buffer.asUint8List();
|
||||||
|
} else {
|
||||||
|
data = Uint8List.fromList(img2.encodePng(defaultCursorImage!));
|
||||||
|
_hotx = defaultCursorImage!.width / 2;
|
||||||
|
_hoty = defaultCursorImage!.height / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_cache = CursorData(
|
||||||
peerId: this.id,
|
peerId: this.id,
|
||||||
data: data?.buffer.asUint8List(),
|
|
||||||
id: id,
|
id: id,
|
||||||
|
image: image2,
|
||||||
|
scale: 1.0,
|
||||||
|
data: data,
|
||||||
hotx: _hotx,
|
hotx: _hotx,
|
||||||
hoty: _hoty,
|
hoty: _hoty,
|
||||||
width: w,
|
width: w,
|
||||||
height: h,
|
height: h,
|
||||||
);
|
);
|
||||||
_cacheMapLinux[id] = _cacheLinux!;
|
_cacheMap[id] = _cache!;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCursorId(Map<String, dynamic> evt) async {
|
updateCursorId(Map<String, dynamic> evt) async {
|
||||||
final id = int.parse(evt['id']);
|
final id = int.parse(evt['id']);
|
||||||
_cacheLinux = _cacheMapLinux[id];
|
_cache = _cacheMap[id];
|
||||||
final tmp = _images[id];
|
final tmp = _images[id];
|
||||||
if (tmp != null) {
|
if (tmp != null) {
|
||||||
_image = tmp.item1;
|
_image = tmp.item1;
|
||||||
@ -931,15 +1015,15 @@ class CursorModel with ChangeNotifier {
|
|||||||
_image = null;
|
_image = null;
|
||||||
_images.clear();
|
_images.clear();
|
||||||
|
|
||||||
_clearCacheLinux();
|
_clearCache();
|
||||||
_cacheLinux = null;
|
_cache = null;
|
||||||
_cacheMapLinux.clear();
|
_cacheMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
_clearCacheLinux() {
|
_clearCache() {
|
||||||
final cachedKeys = {...cachedKeysLinux};
|
final keys = {...cachedKeys};
|
||||||
for (var key in cachedKeys) {
|
for (var k in keys) {
|
||||||
customCursorController.freeCache(key);
|
customCursorController.freeCache(k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -948,6 +1032,9 @@ class CursorModel with ChangeNotifier {
|
|||||||
cachedForbidmemoryCursorData ??= base64Decode(
|
cachedForbidmemoryCursorData ??= base64Decode(
|
||||||
'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAkZQTFRFAAAA2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4GWAwCAAAAAAAA2B4GAAAAMTExAAAAAAAA2B4G2B4G2B4GAAAAmZmZkZGRAQEBAAAA2B4G2B4G2B4G////oKCgAwMDag8D2B4G2B4G2B4Gra2tBgYGbg8D2B4G2B4Gubm5CQkJTwsCVgwC2B4GxcXFDg4OAAAAAAAA2B4G2B4Gz8/PFBQUAAAAAAAA2B4G2B4G2B4G2B4G2B4G2B4G2B4GDgIA2NjYGxsbAAAAAAAA2B4GFwMB4eHhIyMjAAAAAAAA2B4G6OjoLCwsAAAAAAAA2B4G2B4G2B4G2B4G2B4GCQEA4ODgv7+/iYmJY2NjAgICAAAA9PT0Ojo6AAAAAAAAAAAA+/v7SkpKhYWFr6+vAAAAAAAA8/PzOTk5ERER9fX1KCgoAAAAgYGBKioqAAAAAAAApqamlpaWAAAAAAAAAAAAAAAAAAAAAAAALi4u/v7+GRkZAAAAAAAAAAAAAAAAAAAAfn5+AAAAAAAAV1dXkJCQAAAAAAAAAQEBAAAAAAAAAAAA7Hz6BAAAAMJ0Uk5TAAIWEwEynNz6//fVkCAatP2fDUHs6cDD8d0mPfT5fiEskiIR584A0gejr3AZ+P4plfALf5ZiTL85a4ziD6697fzN3UYE4v/4TwrNHuT///tdRKZh///+1U/ZBv///yjb///eAVL//50Cocv//6oFBbPvpGZCbfT//7cIhv///8INM///zBEcWYSZmO7//////1P////ts/////8vBv//////gv//R/z///QQz9sevP///2waXhNO/+fc//8mev/5gAe2r90MAAAByUlEQVR4nGNggANGJmYWBpyAlY2dg5OTi5uHF6s0H78AJxRwCAphyguLgKRExcQlQLSkFLq8tAwnp6ycPNABjAqKQKNElVDllVU4OVVhVquJA81Q10BRoAkUUYbJa4Edoo0sr6PLqaePLG/AyWlohKTAmJPTBFnelAFoixmSAnNOTgsUeQZLTk4rJAXWnJw2EHlbiDyDPCenHZICe04HFrh+RydnBgYWPU5uJAWinJwucPNd3dw9GDw5Ob2QFHBzcnrD7ffx9fMPCOTkDEINhmC4+3x8Q0LDwlEDIoKTMzIKKg9SEBIdE8sZh6SAJZ6Tkx0qD1YQkpCYlIwclCng0AXLQxSEpKalZyCryATKZwkhKQjJzsnNQ1KQXwBUUVhUXBJYWgZREFJeUVmFpMKlWg+anmqgCkJq6+obkG1pLEBTENLU3NKKrIKhrb2js8u4G6Kgpze0r3/CRAZMAHbkpJDJU6ZMmTqtFbuC6TNmhsyaMnsOFlmwgrnzpsxfELJwEXZ5Bp/FS3yWLlsesmLlKuwKVk9Ys5Zh3foN0zduwq5g85atDAzbpqSGbN9RhV0FGOzctWH3lD14FOzdt3H/gQw8Cg4u2gQPAwBYDXXdIH+wqAAAAABJRU5ErkJggg==');
|
'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAkZQTFRFAAAA2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4GWAwCAAAAAAAA2B4GAAAAMTExAAAAAAAA2B4G2B4G2B4GAAAAmZmZkZGRAQEBAAAA2B4G2B4G2B4G////oKCgAwMDag8D2B4G2B4G2B4Gra2tBgYGbg8D2B4G2B4Gubm5CQkJTwsCVgwC2B4GxcXFDg4OAAAAAAAA2B4G2B4Gz8/PFBQUAAAAAAAA2B4G2B4G2B4G2B4G2B4G2B4G2B4GDgIA2NjYGxsbAAAAAAAA2B4GFwMB4eHhIyMjAAAAAAAA2B4G6OjoLCwsAAAAAAAA2B4G2B4G2B4G2B4G2B4GCQEA4ODgv7+/iYmJY2NjAgICAAAA9PT0Ojo6AAAAAAAAAAAA+/v7SkpKhYWFr6+vAAAAAAAA8/PzOTk5ERER9fX1KCgoAAAAgYGBKioqAAAAAAAApqamlpaWAAAAAAAAAAAAAAAAAAAAAAAALi4u/v7+GRkZAAAAAAAAAAAAAAAAAAAAfn5+AAAAAAAAV1dXkJCQAAAAAAAAAQEBAAAAAAAAAAAA7Hz6BAAAAMJ0Uk5TAAIWEwEynNz6//fVkCAatP2fDUHs6cDD8d0mPfT5fiEskiIR584A0gejr3AZ+P4plfALf5ZiTL85a4ziD6697fzN3UYE4v/4TwrNHuT///tdRKZh///+1U/ZBv///yjb///eAVL//50Cocv//6oFBbPvpGZCbfT//7cIhv///8INM///zBEcWYSZmO7//////1P////ts/////8vBv//////gv//R/z///QQz9sevP///2waXhNO/+fc//8mev/5gAe2r90MAAAByUlEQVR4nGNggANGJmYWBpyAlY2dg5OTi5uHF6s0H78AJxRwCAphyguLgKRExcQlQLSkFLq8tAwnp6ycPNABjAqKQKNElVDllVU4OVVhVquJA81Q10BRoAkUUYbJa4Edoo0sr6PLqaePLG/AyWlohKTAmJPTBFnelAFoixmSAnNOTgsUeQZLTk4rJAXWnJw2EHlbiDyDPCenHZICe04HFrh+RydnBgYWPU5uJAWinJwucPNd3dw9GDw5Ob2QFHBzcnrD7ffx9fMPCOTkDEINhmC4+3x8Q0LDwlEDIoKTMzIKKg9SEBIdE8sZh6SAJZ6Tkx0qD1YQkpCYlIwclCng0AXLQxSEpKalZyCryATKZwkhKQjJzsnNQ1KQXwBUUVhUXBJYWgZREFJeUVmFpMKlWg+anmqgCkJq6+obkG1pLEBTENLU3NKKrIKhrb2js8u4G6Kgpze0r3/CRAZMAHbkpJDJU6ZMmTqtFbuC6TNmhsyaMnsOFlmwgrnzpsxfELJwEXZ5Bp/FS3yWLlsesmLlKuwKVk9Ys5Zh3foN0zduwq5g85atDAzbpqSGbN9RhV0FGOzctWH3lD14FOzdt3H/gQw8Cg4u2gQPAwBYDXXdIH+wqAAAAABJRU5ErkJggg==');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img2.Image? defaultCursorImage = img2.decodePng(base64Decode(
|
||||||
|
'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAFmSURBVFiF7dWxSlxREMbx34QFDRowYBchZSxSCWlMCOwD5FGEFHap06UI7KPsAyyEEIQFqxRaCqYTsqCJFsKkuAeRXb17wrqV918dztw55zszc2fo6Oh47MR/e3zO1/iAHWmznHKGQwx9ip/LEbCfazbsoY8j/JLOhcC6sCW9wsjEwJf483AC9nPNc1+lFRwI13d+l3rYFS799rFGxJMqARv2pBXh+72XQ7gWvklPS7TmMl9Ak/M+DqrENvxAv/guKKApuKPWl0/TROK4+LbSqzhuB+OZ3fRSeFPWY+Fkyn56Y29hfgTSpnQ+s98cvorVey66uPlNFxKwZOYLCGfCs5n9NMYVrsp6mvXSoFqpqYFDvMBkStgJJe93dZOwVXxbqUnBENulydSReqUrDhcX0PT2EXarBYS3GNXMhboinBgIl9K71kg0L3+PvyYGdVpruT2MwrF0iotiXfIwus0Dj+OOjo6Of+e7ab74RkpgAAAAAElFTkSuQmCC'));
|
||||||
}
|
}
|
||||||
|
|
||||||
class QualityMonitorData {
|
class QualityMonitorData {
|
||||||
|
35
flutter/lib/models/state_model.dart
Normal file
35
flutter/lib/models/state_model.dart
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import '../consts.dart';
|
||||||
|
|
||||||
|
class StateGlobal {
|
||||||
|
int _windowId = -1;
|
||||||
|
bool _fullscreen = false;
|
||||||
|
final RxBool _showTabBar = true.obs;
|
||||||
|
final RxDouble _resizeEdgeSize = 8.0.obs;
|
||||||
|
|
||||||
|
int get windowId => _windowId;
|
||||||
|
bool get fullscreen => _fullscreen;
|
||||||
|
double get tabBarHeight => fullscreen ? 0 : kDesktopRemoteTabBarHeight;
|
||||||
|
double get windowBorderWidth => fullscreen ? 0 : kWindowBorderWidth;
|
||||||
|
RxBool get showTabBar => _showTabBar;
|
||||||
|
RxDouble get resizeEdgeSize => _resizeEdgeSize;
|
||||||
|
|
||||||
|
setWindowId(int id) => _windowId = id;
|
||||||
|
setFullscreen(bool v) {
|
||||||
|
if (_fullscreen != v) {
|
||||||
|
_fullscreen = v;
|
||||||
|
_showTabBar.value = !_fullscreen;
|
||||||
|
_resizeEdgeSize.value =
|
||||||
|
fullscreen ? kFullScreenEdgeSize : kWindowEdgeSize;
|
||||||
|
WindowController.fromWindowId(windowId).setFullscreen(_fullscreen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateGlobal._();
|
||||||
|
|
||||||
|
static final StateGlobal instance = StateGlobal._();
|
||||||
|
}
|
||||||
|
|
||||||
|
final stateGlobal = StateGlobal.instance;
|
@ -77,7 +77,9 @@ class UserModel {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
final m = jsonDecode(userInfo);
|
final m = jsonDecode(userInfo);
|
||||||
|
if (m != null) {
|
||||||
userName.value = m['name'] ?? '';
|
userName.value = m['name'] ?? '';
|
||||||
|
}
|
||||||
return userName.value;
|
return userName.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ class RustDeskMultiWindowManager {
|
|||||||
int? wId = findWindowByType(type);
|
int? wId = findWindowByType(type);
|
||||||
if (wId != null) {
|
if (wId != null) {
|
||||||
debugPrint("closing multi window: ${type.toString()}");
|
debugPrint("closing multi window: ${type.toString()}");
|
||||||
saveWindowPosition(type, windowId: wId);
|
await saveWindowPosition(type, windowId: wId);
|
||||||
try {
|
try {
|
||||||
final ids = await DesktopMultiWindow.getAllSubWindowIds();
|
final ids = await DesktopMultiWindow.getAllSubWindowIds();
|
||||||
if (!ids.contains(wId)) {
|
if (!ids.contains(wId)) {
|
||||||
|
@ -64,7 +64,7 @@ dependencies:
|
|||||||
desktop_multi_window:
|
desktop_multi_window:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Kingtous/rustdesk_desktop_multi_window
|
url: https://github.com/Kingtous/rustdesk_desktop_multi_window
|
||||||
ref: bf278fc8a8ff787e46fa3ab97674373bfaa20f23
|
ref: 39a8a5b8aed059a89a1694ed2dffe69e31da2ac1
|
||||||
freezed_annotation: ^2.0.3
|
freezed_annotation: ^2.0.3
|
||||||
tray_manager:
|
tray_manager:
|
||||||
git:
|
git:
|
||||||
@ -96,7 +96,10 @@ dependencies:
|
|||||||
# url: https://github.com/Kingtous/flutter_improved_scrolling
|
# url: https://github.com/Kingtous/flutter_improved_scrolling
|
||||||
# ref: 62f09545149f320616467c306c8c5f71714a18e6
|
# ref: 62f09545149f320616467c306c8c5f71714a18e6
|
||||||
uni_links: ^0.5.1
|
uni_links: ^0.5.1
|
||||||
uni_links_desktop: ^0.1.3
|
uni_links_desktop:
|
||||||
|
git:
|
||||||
|
url: https://github.com/fufesou/uni_links_desktop.git
|
||||||
|
ref: 5be5113d59c753989dbf1106241379e3fd4c9b18
|
||||||
path: ^1.8.1
|
path: ^1.8.1
|
||||||
auto_size_text: ^3.0.0
|
auto_size_text: ^3.0.0
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include <flutter/dart_project.h>
|
#include <flutter/dart_project.h>
|
||||||
#include <flutter/flutter_view_controller.h>
|
#include <flutter/flutter_view_controller.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <tchar.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "flutter_window.h"
|
#include "flutter_window.h"
|
||||||
@ -54,7 +55,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
|||||||
// only do uni links when dispatch a rustdesk links
|
// only do uni links when dispatch a rustdesk links
|
||||||
auto prefix = std::string(uniLinksPrefix);
|
auto prefix = std::string(uniLinksPrefix);
|
||||||
if (!command_line_arguments.empty() && command_line_arguments.front().compare(0, prefix.size(), prefix.c_str()) == 0) {
|
if (!command_line_arguments.empty() && command_line_arguments.front().compare(0, prefix.size(), prefix.c_str()) == 0) {
|
||||||
HWND hwnd = ::FindWindow(L"FLUTTER_RUNNER_WIN32_WINDOW", L"rustdesk");
|
HWND hwnd = ::FindWindow(_T("FLUTTER_RUNNER_WIN32_WINDOW"), _T("RustDesk"));
|
||||||
if (hwnd != NULL) {
|
if (hwnd != NULL) {
|
||||||
DispatchToUniLinksDesktop(hwnd);
|
DispatchToUniLinksDesktop(hwnd);
|
||||||
|
|
||||||
|
@ -135,14 +135,14 @@ typedef struct _FORMAT_IDS FORMAT_IDS;
|
|||||||
|
|
||||||
#define TAG "windows"
|
#define TAG "windows"
|
||||||
|
|
||||||
// #ifdef WITH_DEBUG_CLIPRDR
|
#ifdef WITH_DEBUG_CLIPRDR
|
||||||
#define DEBUG_CLIPRDR(fmt, ...) fprintf(stderr, "DEBUG %s[%d] %s() " fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__);fflush(stderr)
|
#define DEBUG_CLIPRDR(fmt, ...) fprintf(stderr, "DEBUG %s[%d] %s() " fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__);fflush(stderr)
|
||||||
// #else
|
#else
|
||||||
// #define DEBUG_CLIPRDR(fmt, ...) \
|
#define DEBUG_CLIPRDR(fmt, ...) \
|
||||||
// do \
|
do \
|
||||||
// { \
|
{ \
|
||||||
// } while (0)
|
} while (0)
|
||||||
// #endif
|
#endif
|
||||||
|
|
||||||
typedef BOOL(WINAPI *fnAddClipboardFormatListener)(HWND hwnd);
|
typedef BOOL(WINAPI *fnAddClipboardFormatListener)(HWND hwnd);
|
||||||
typedef BOOL(WINAPI *fnRemoveClipboardFormatListener)(HWND hwnd);
|
typedef BOOL(WINAPI *fnRemoveClipboardFormatListener)(HWND hwnd);
|
||||||
|
@ -21,19 +21,21 @@ static mut LAYOUT: HKL = std::ptr::null_mut();
|
|||||||
pub const ENIGO_INPUT_EXTRA_VALUE: ULONG_PTR = 100;
|
pub const ENIGO_INPUT_EXTRA_VALUE: ULONG_PTR = 100;
|
||||||
|
|
||||||
fn mouse_event(flags: u32, data: u32, dx: i32, dy: i32) -> DWORD {
|
fn mouse_event(flags: u32, data: u32, dx: i32, dy: i32) -> DWORD {
|
||||||
let mut input = INPUT {
|
let mut input: INPUT = unsafe { std::mem::MaybeUninit::zeroed().assume_init() };
|
||||||
type_: INPUT_MOUSE,
|
input.type_ = INPUT_MOUSE;
|
||||||
u: unsafe {
|
unsafe {
|
||||||
transmute(MOUSEINPUT {
|
let dst_ptr = (&mut input.u as *mut _) as *mut u8;
|
||||||
|
let m = MOUSEINPUT {
|
||||||
dx,
|
dx,
|
||||||
dy,
|
dy,
|
||||||
mouseData: data,
|
mouseData: data,
|
||||||
dwFlags: flags,
|
dwFlags: flags,
|
||||||
time: 0,
|
time: 0,
|
||||||
dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
|
dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
|
||||||
})
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
let src_ptr = (&m as *const _) as *const u8;
|
||||||
|
std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, size_of::<MOUSEINPUT>());
|
||||||
|
}
|
||||||
unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) }
|
unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,18 +52,20 @@ fn keybd_event(flags: u32, vk: u16, scan: u16) -> DWORD {
|
|||||||
scan = MapVirtualKeyExW(vk as _, 0, LAYOUT) as _;
|
scan = MapVirtualKeyExW(vk as _, 0, LAYOUT) as _;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut input = INPUT {
|
let mut input: INPUT = unsafe { std::mem::MaybeUninit::zeroed().assume_init() };
|
||||||
type_: INPUT_KEYBOARD,
|
input.type_ = INPUT_KEYBOARD;
|
||||||
u: unsafe {
|
unsafe {
|
||||||
transmute_copy(&KEYBDINPUT {
|
let dst_ptr = (&mut input.u as *mut _) as *mut u8;
|
||||||
|
let k = KEYBDINPUT {
|
||||||
wVk: vk,
|
wVk: vk,
|
||||||
wScan: scan,
|
wScan: scan,
|
||||||
dwFlags: flags,
|
dwFlags: flags,
|
||||||
time: 0,
|
time: 0,
|
||||||
dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
|
dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
|
||||||
})
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
let src_ptr = (&k as *const _) as *const u8;
|
||||||
|
std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, size_of::<KEYBDINPUT>());
|
||||||
|
}
|
||||||
unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) }
|
unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -795,7 +795,7 @@ const PEERS: &str = "peers";
|
|||||||
|
|
||||||
impl PeerConfig {
|
impl PeerConfig {
|
||||||
pub fn load(id: &str) -> PeerConfig {
|
pub fn load(id: &str) -> PeerConfig {
|
||||||
let _ = CONFIG.read().unwrap(); // for lock
|
let _lock = CONFIG.read().unwrap();
|
||||||
match confy::load_path(&Self::path(id)) {
|
match confy::load_path(&Self::path(id)) {
|
||||||
Ok(config) => {
|
Ok(config) => {
|
||||||
let mut config: PeerConfig = config;
|
let mut config: PeerConfig = config;
|
||||||
@ -827,7 +827,7 @@ impl PeerConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn store(&self, id: &str) {
|
pub fn store(&self, id: &str) {
|
||||||
let _ = CONFIG.read().unwrap(); // for lock
|
let _lock = CONFIG.read().unwrap();
|
||||||
let mut config = self.clone();
|
let mut config = self.clone();
|
||||||
config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
|
config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
|
||||||
config
|
config
|
||||||
@ -999,7 +999,7 @@ pub struct LanPeers {
|
|||||||
|
|
||||||
impl LanPeers {
|
impl LanPeers {
|
||||||
pub fn load() -> LanPeers {
|
pub fn load() -> LanPeers {
|
||||||
let _ = CONFIG.read().unwrap(); // for lock
|
let _lock = CONFIG.read().unwrap();
|
||||||
match confy::load_path(&Config::file_("_lan_peers")) {
|
match confy::load_path(&Config::file_("_lan_peers")) {
|
||||||
Ok(peers) => peers,
|
Ok(peers) => peers,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -7,7 +7,7 @@ arch=('x86_64')
|
|||||||
url=""
|
url=""
|
||||||
license=('AGPL-3.0')
|
license=('AGPL-3.0')
|
||||||
groups=()
|
groups=()
|
||||||
depends=('gtk3' 'xdotool' 'libxcb' 'libxfixes' 'alsa-lib' 'pipewire' 'ttf-arphic-uming' 'curl' 'libappindicator-gtk3' 'libva' 'libvdpau' 'libayatana-appindicator')
|
depends=('gtk3' 'xdotool' 'libxcb' 'libxfixes' 'alsa-lib' 'pipewire' 'curl' 'libva' 'libvdpau' 'libayatana-appindicator')
|
||||||
makedepends=()
|
makedepends=()
|
||||||
checkdepends=()
|
checkdepends=()
|
||||||
optdepends=()
|
optdepends=()
|
||||||
|
@ -3,7 +3,7 @@ Version: 1.1.9
|
|||||||
Release: 0
|
Release: 0
|
||||||
Summary: RPM package
|
Summary: RPM package
|
||||||
License: GPL-3.0
|
License: GPL-3.0
|
||||||
Requires: gtk3 libxcb1 xdotool libXfixes3 pulseaudio-utils alsa-utils arphic-uming-fonts curl libXtst6
|
Requires: gtk3 libxcb1 xdotool libXfixes3 pipewire alsa-utils curl libXtst6 libayatana-appindicator3-1 libvdpau1 libva2
|
||||||
|
|
||||||
%description
|
%description
|
||||||
The best open-source remote desktop client software, written in Rust.
|
The best open-source remote desktop client software, written in Rust.
|
||||||
|
@ -3,7 +3,7 @@ Version: 1.1.9
|
|||||||
Release: 0
|
Release: 0
|
||||||
Summary: RPM package
|
Summary: RPM package
|
||||||
License: GPL-3.0
|
License: GPL-3.0
|
||||||
Requires: gtk3 libxcb libxdo libXfixes pulseaudio-libs alsa-lib cjkuni-uming-fonts curl
|
Requires: gtk3 libxcb libxdo libXfixes pipewire alsa-lib curl libayatana-appindicator3-1 libvdpau1 libva2
|
||||||
|
|
||||||
%description
|
%description
|
||||||
The best open-source remote desktop client software, written in Rust.
|
The best open-source remote desktop client software, written in Rust.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::client::{
|
use crate::client::{
|
||||||
Client, CodecFormat, FileManager, MediaData, MediaSender, QualityStatus, MILLI1, SEC30,
|
Client, CodecFormat, MediaData, MediaSender, QualityStatus, MILLI1, SEC30,
|
||||||
SERVER_CLIPBOARD_ENABLED, SERVER_FILE_TRANSFER_ENABLED, SERVER_KEYBOARD_ENABLED,
|
SERVER_CLIPBOARD_ENABLED, SERVER_FILE_TRANSFER_ENABLED, SERVER_KEYBOARD_ENABLED,
|
||||||
};
|
};
|
||||||
use crate::common;
|
use crate::common;
|
||||||
@ -15,7 +15,7 @@ use crate::{client::Data, client::Interface};
|
|||||||
use hbb_common::config::{PeerConfig, TransferSerde};
|
use hbb_common::config::{PeerConfig, TransferSerde};
|
||||||
use hbb_common::fs::{
|
use hbb_common::fs::{
|
||||||
can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult,
|
can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult,
|
||||||
RemoveJobMeta, TransferJobMeta,
|
RemoveJobMeta,
|
||||||
};
|
};
|
||||||
use hbb_common::message_proto::permission_info::Permission;
|
use hbb_common::message_proto::permission_info::Permission;
|
||||||
use hbb_common::protobuf::Message as _;
|
use hbb_common::protobuf::Message as _;
|
||||||
@ -23,6 +23,7 @@ use hbb_common::rendezvous_proto::ConnType;
|
|||||||
use hbb_common::tokio::{
|
use hbb_common::tokio::{
|
||||||
self,
|
self,
|
||||||
sync::mpsc,
|
sync::mpsc,
|
||||||
|
sync::Mutex as TokioMutex,
|
||||||
time::{self, Duration, Instant, Interval},
|
time::{self, Duration, Instant, Interval},
|
||||||
};
|
};
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
@ -113,15 +114,23 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
// just build for now
|
// just build for now
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let (_tx_holder, mut rx_clip_client) = mpsc::unbounded_channel::<i32>();
|
let (_tx_holder, mut rx_clip_client) = mpsc::unbounded_channel::<i32>();
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let (client_conn_id, rx_clip_client1) =
|
let (_tx_holder, rx) = mpsc::unbounded_channel();
|
||||||
clipboard::get_rx_cliprdr_client(&self.handler.id);
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let mut rx_clip_client = rx_clip_client1.lock().await;
|
let mut rx_clip_client_lock = Arc::new(TokioMutex::new(rx));
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
self.client_conn_id = client_conn_id;
|
let is_conn_not_default = self.handler.is_file_transfer()
|
||||||
|
|| self.handler.is_port_forward()
|
||||||
|
|| self.handler.is_rdp();
|
||||||
|
if !is_conn_not_default {
|
||||||
|
(self.client_conn_id, rx_clip_client_lock) =
|
||||||
|
clipboard::get_rx_cliprdr_client(&self.handler.id);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
let mut rx_clip_client = rx_clip_client_lock.lock().await;
|
||||||
|
|
||||||
let mut status_timer = time::interval(Duration::new(1, 0));
|
let mut status_timer = time::interval(Duration::new(1, 0));
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
|
||||||
future::Future,
|
future::Future,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
@ -27,6 +26,9 @@ pub type NotifyMessageBox = fn(String, String, String, String) -> dyn Future<Out
|
|||||||
pub const CLIPBOARD_NAME: &'static str = "clipboard";
|
pub const CLIPBOARD_NAME: &'static str = "clipboard";
|
||||||
pub const CLIPBOARD_INTERVAL: u64 = 333;
|
pub const CLIPBOARD_INTERVAL: u64 = 333;
|
||||||
|
|
||||||
|
// the executable name of the portable version
|
||||||
|
pub const PORTABLE_APPNAME_RUNTIME_ENV_KEY: &str = "RUSTDESK_APPNAME";
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref CONTENT: Arc<Mutex<String>> = Default::default();
|
pub static ref CONTENT: Arc<Mutex<String>> = Default::default();
|
||||||
pub static ref SOFTWARE_UPDATE_URL: Arc<Mutex<String>> = Default::default();
|
pub static ref SOFTWARE_UPDATE_URL: Arc<Mutex<String>> = Default::default();
|
||||||
|
@ -85,11 +85,6 @@ pub fn core_main() -> Option<Vec<String>> {
|
|||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
if !crate::platform::is_installed() && args.is_empty() {
|
|
||||||
crate::platform::elevate_or_run_as_system(is_setup, _is_elevate, _is_run_as_system);
|
|
||||||
}
|
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
std::thread::spawn(move || crate::start_server(false));
|
std::thread::spawn(move || crate::start_server(false));
|
||||||
} else {
|
} else {
|
||||||
|
@ -418,6 +418,7 @@ pub fn session_start_(id: &str, event_stream: StreamSink<EventToUI>) -> ResultTy
|
|||||||
pub mod connection_manager {
|
pub mod connection_manager {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android"))]
|
||||||
use hbb_common::log;
|
use hbb_common::log;
|
||||||
#[cfg(any(target_os = "android"))]
|
#[cfg(any(target_os = "android"))]
|
||||||
use scrap::android::call_main_service_set_by_name;
|
use scrap::android::call_main_service_set_by_name;
|
||||||
|
@ -7,13 +7,14 @@ use std::{
|
|||||||
use flutter_rust_bridge::{StreamSink, SyncReturn, ZeroCopyBuffer};
|
use flutter_rust_bridge::{StreamSink, SyncReturn, ZeroCopyBuffer};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
|
use hbb_common::ResultType;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
config::{self, LocalConfig, PeerConfig, ONLINE},
|
config::{self, LocalConfig, PeerConfig, ONLINE},
|
||||||
fs, log,
|
fs, log,
|
||||||
};
|
};
|
||||||
use hbb_common::{message_proto::Hash, ResultType};
|
|
||||||
|
|
||||||
use crate::flutter::{self, SESSIONS};
|
use crate::flutter::{self, SESSIONS};
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
use crate::start_server;
|
use crate::start_server;
|
||||||
use crate::ui_interface::{self, *};
|
use crate::ui_interface::{self, *};
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -362,7 +363,7 @@ pub fn session_create_dir(id: String, act_id: i32, path: String, is_remote: bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn session_read_local_dir_sync(id: String, path: String, show_hidden: bool) -> String {
|
pub fn session_read_local_dir_sync(_id: String, path: String, show_hidden: bool) -> String {
|
||||||
if let Ok(fd) = fs::read_dir(&fs::get_path(&path), show_hidden) {
|
if let Ok(fd) = fs::read_dir(&fs::get_path(&path), show_hidden) {
|
||||||
return make_fd_to_json(fd.id, path, &fd.entries);
|
return make_fd_to_json(fd.id, path, &fd.entries);
|
||||||
}
|
}
|
||||||
|
@ -34,5 +34,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("uac_warning", "Temporarily denied access due to elevation request, please wait for the remote user to accept the UAC dialog. To avoid this problem, it is recommended to install the software on the remote device or run it with administrator privileges."),
|
("uac_warning", "Temporarily denied access due to elevation request, please wait for the remote user to accept the UAC dialog. To avoid this problem, it is recommended to install the software on the remote device or run it with administrator privileges."),
|
||||||
("elevated_foreground_window_warning", "Temporarily unable to use the mouse and keyboard, because the current window of the remote desktop requires higher privilege to operate, you can request the remote user to minimize the current window. To avoid this problem, it is recommended to install the software on the remote device or run it with administrator privileges."),
|
("elevated_foreground_window_warning", "Temporarily unable to use the mouse and keyboard, because the current window of the remote desktop requires higher privilege to operate, you can request the remote user to minimize the current window. To avoid this problem, it is recommended to install the software on the remote device or run it with administrator privileges."),
|
||||||
("JumpLink", "View"),
|
("JumpLink", "View"),
|
||||||
|
("Stop service", "Stop Service"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::{CursorData, ResultType};
|
use super::{CursorData, ResultType};
|
||||||
|
use crate::common::PORTABLE_APPNAME_RUNTIME_ENV_KEY;
|
||||||
use crate::ipc;
|
use crate::ipc;
|
||||||
use crate::license::*;
|
use crate::license::*;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
@ -21,7 +22,10 @@ use winapi::{
|
|||||||
errhandlingapi::GetLastError,
|
errhandlingapi::GetLastError,
|
||||||
handleapi::CloseHandle,
|
handleapi::CloseHandle,
|
||||||
minwinbase::STILL_ACTIVE,
|
minwinbase::STILL_ACTIVE,
|
||||||
processthreadsapi::{GetCurrentProcess, GetExitCodeProcess, OpenProcess, OpenProcessToken},
|
processthreadsapi::{
|
||||||
|
GetCurrentProcess, GetCurrentProcessId, GetExitCodeProcess, OpenProcess,
|
||||||
|
OpenProcessToken,
|
||||||
|
},
|
||||||
securitybaseapi::GetTokenInformation,
|
securitybaseapi::GetTokenInformation,
|
||||||
shellapi::ShellExecuteA,
|
shellapi::ShellExecuteA,
|
||||||
winbase::*,
|
winbase::*,
|
||||||
@ -878,23 +882,35 @@ fn get_install_info_with_subkey(subkey: String) -> (String, String, String, Stri
|
|||||||
(subkey, path, start_menu, exe)
|
(subkey, path, start_menu, exe)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_exe_cmd(src_exe: &str, _exe: &str, _path: &str) -> String {
|
pub fn copy_exe_cmd(src_exe: &str, _exe: &str, path: &str) -> String {
|
||||||
#[cfg(feature = "flutter")]
|
#[cfg(feature = "flutter")]
|
||||||
return format!(
|
let main_exe = format!(
|
||||||
"XCOPY \"{}\" \"{}\" /Y /E /H /C /I /K /R /Z",
|
"XCOPY \"{}\" \"{}\" /Y /E /H /C /I /K /R /Z",
|
||||||
PathBuf::from(src_exe)
|
PathBuf::from(src_exe)
|
||||||
.parent()
|
.parent()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
_path
|
path
|
||||||
);
|
);
|
||||||
#[cfg(not(feature = "flutter"))]
|
#[cfg(not(feature = "flutter"))]
|
||||||
return format!(
|
let main_exe = format!(
|
||||||
"copy /Y \"{src_exe}\" \"{exe}\"",
|
"copy /Y \"{src_exe}\" \"{exe}\"",
|
||||||
src_exe = src_exe,
|
src_exe = src_exe,
|
||||||
exe = _exe
|
exe = _exe
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return format!(
|
||||||
|
"
|
||||||
|
{main_exe}
|
||||||
|
copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\"
|
||||||
|
\"{src_exe}\" --extract \"{path}\"
|
||||||
|
",
|
||||||
|
main_exe = main_exe,
|
||||||
|
path = path,
|
||||||
|
ORIGIN_PROCESS_EXE = crate::ui::win_privacy::ORIGIN_PROCESS_EXE,
|
||||||
|
broker_exe = crate::ui::win_privacy::INJECTED_PROCESS_EXE,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_me() -> ResultType<()> {
|
pub fn update_me() -> ResultType<()> {
|
||||||
@ -905,18 +921,16 @@ pub fn update_me() -> ResultType<()> {
|
|||||||
chcp 65001
|
chcp 65001
|
||||||
sc stop {app_name}
|
sc stop {app_name}
|
||||||
taskkill /F /IM {broker_exe}
|
taskkill /F /IM {broker_exe}
|
||||||
taskkill /F /IM {app_name}.exe
|
taskkill /F /IM {app_name}.exe /FI \"PID ne {cur_pid}\"
|
||||||
{copy_exe}
|
{copy_exe}
|
||||||
\"{src_exe}\" --extract \"{path}\"
|
|
||||||
sc start {app_name}
|
sc start {app_name}
|
||||||
{lic}
|
{lic}
|
||||||
",
|
",
|
||||||
src_exe = src_exe,
|
|
||||||
copy_exe = copy_exe_cmd(&src_exe, &exe, &path),
|
copy_exe = copy_exe_cmd(&src_exe, &exe, &path),
|
||||||
broker_exe = crate::ui::win_privacy::INJECTED_PROCESS_EXE,
|
broker_exe = crate::ui::win_privacy::INJECTED_PROCESS_EXE,
|
||||||
path = path,
|
|
||||||
app_name = crate::get_app_name(),
|
app_name = crate::get_app_name(),
|
||||||
lic = register_licence(),
|
lic = register_licence(),
|
||||||
|
cur_pid = get_current_pid(),
|
||||||
);
|
);
|
||||||
std::thread::sleep(std::time::Duration::from_millis(1000));
|
std::thread::sleep(std::time::Duration::from_millis(1000));
|
||||||
run_cmds(cmds, false, "update")?;
|
run_cmds(cmds, false, "update")?;
|
||||||
@ -1087,8 +1101,6 @@ if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name}
|
|||||||
chcp 65001
|
chcp 65001
|
||||||
md \"{path}\"
|
md \"{path}\"
|
||||||
{copy_exe}
|
{copy_exe}
|
||||||
copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\"
|
|
||||||
\"{src_exe}\" --extract \"{path}\"
|
|
||||||
reg add {subkey} /f
|
reg add {subkey} /f
|
||||||
reg add {subkey} /f /v DisplayIcon /t REG_SZ /d \"{exe}\"
|
reg add {subkey} /f /v DisplayIcon /t REG_SZ /d \"{exe}\"
|
||||||
reg add {subkey} /f /v DisplayName /t REG_SZ /d \"{app_name}\"
|
reg add {subkey} /f /v DisplayName /t REG_SZ /d \"{app_name}\"
|
||||||
@ -1119,10 +1131,7 @@ sc delete {app_name}
|
|||||||
",
|
",
|
||||||
uninstall_str=uninstall_str,
|
uninstall_str=uninstall_str,
|
||||||
path=path,
|
path=path,
|
||||||
src_exe=src_exe,
|
|
||||||
exe=exe,
|
exe=exe,
|
||||||
ORIGIN_PROCESS_EXE = crate::ui::win_privacy::ORIGIN_PROCESS_EXE,
|
|
||||||
broker_exe=crate::ui::win_privacy::INJECTED_PROCESS_EXE,
|
|
||||||
subkey=subkey,
|
subkey=subkey,
|
||||||
app_name=crate::get_app_name(),
|
app_name=crate::get_app_name(),
|
||||||
version=crate::VERSION,
|
version=crate::VERSION,
|
||||||
@ -1178,13 +1187,14 @@ fn get_before_uninstall() -> String {
|
|||||||
sc stop {app_name}
|
sc stop {app_name}
|
||||||
sc delete {app_name}
|
sc delete {app_name}
|
||||||
taskkill /F /IM {broker_exe}
|
taskkill /F /IM {broker_exe}
|
||||||
taskkill /F /IM {app_name}.exe
|
taskkill /F /IM {app_name}.exe /FI \"PID ne {cur_pid}\"
|
||||||
reg delete HKEY_CLASSES_ROOT\\.{ext} /f
|
reg delete HKEY_CLASSES_ROOT\\.{ext} /f
|
||||||
netsh advfirewall firewall delete rule name=\"{app_name} Service\"
|
netsh advfirewall firewall delete rule name=\"{app_name} Service\"
|
||||||
",
|
",
|
||||||
app_name = app_name,
|
app_name = app_name,
|
||||||
broker_exe = crate::ui::win_privacy::INJECTED_PROCESS_EXE,
|
broker_exe = crate::ui::win_privacy::INJECTED_PROCESS_EXE,
|
||||||
ext = ext
|
ext = ext,
|
||||||
|
cur_pid = get_current_pid(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1324,7 +1334,12 @@ fn get_reg_of(subkey: &str, name: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_license_from_exe_name() -> ResultType<License> {
|
fn get_license_from_exe_name() -> ResultType<License> {
|
||||||
let exe = std::env::current_exe()?.to_str().unwrap_or("").to_owned();
|
let mut exe = std::env::current_exe()?.to_str().unwrap_or("").to_owned();
|
||||||
|
// if defined portable appname entry, replace original executable name with it.
|
||||||
|
if let Ok(portable_exe) = std::env::var(PORTABLE_APPNAME_RUNTIME_ENV_KEY) {
|
||||||
|
exe = portable_exe;
|
||||||
|
log::debug!("update portable executable name to {}", exe);
|
||||||
|
}
|
||||||
get_license_from_string(&exe)
|
get_license_from_string(&exe)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1613,3 +1628,7 @@ pub fn is_foreground_window_elevated() -> ResultType<bool> {
|
|||||||
is_elevated(Some(process_id))
|
is_elevated(Some(process_id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_current_pid() -> u32 {
|
||||||
|
unsafe { GetCurrentProcessId() }
|
||||||
|
}
|
||||||
|
@ -725,7 +725,7 @@ fn legacy_keyboard_mode(evt: &KeyEvent) {
|
|||||||
// disable numlock if press home etc when numlock is on,
|
// disable numlock if press home etc when numlock is on,
|
||||||
// because we will get numpad value (7,8,9 etc) if not
|
// because we will get numpad value (7,8,9 etc) if not
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let mut disable_numlock = false;
|
let mut _disable_numlock = false;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
en.reset_flag();
|
en.reset_flag();
|
||||||
// When long-pressed the command key, then press and release
|
// When long-pressed the command key, then press and release
|
||||||
@ -775,8 +775,8 @@ fn legacy_keyboard_mode(evt: &KeyEvent) {
|
|||||||
if let Some(key) = KEY_MAP.get(&ck.value()) {
|
if let Some(key) = KEY_MAP.get(&ck.value()) {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
if let Some(_) = NUMPAD_KEY_MAP.get(&ck.value()) {
|
if let Some(_) = NUMPAD_KEY_MAP.get(&ck.value()) {
|
||||||
disable_numlock = en.get_key_state(Key::NumLock);
|
_disable_numlock = en.get_key_state(Key::NumLock);
|
||||||
if disable_numlock {
|
if _disable_numlock {
|
||||||
en.key_down(Key::NumLock).ok();
|
en.key_down(Key::NumLock).ok();
|
||||||
en.key_up(Key::NumLock);
|
en.key_up(Key::NumLock);
|
||||||
}
|
}
|
||||||
|
@ -468,6 +468,7 @@ fn run(sp: GenericService) -> ResultType<()> {
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
start_uac_elevation_check();
|
start_uac_elevation_check();
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
let mut would_block_count = 0u32;
|
let mut would_block_count = 0u32;
|
||||||
|
|
||||||
while sp.ok() {
|
while sp.ok() {
|
||||||
@ -570,9 +571,9 @@ fn run(sp: GenericService) -> ResultType<()> {
|
|||||||
try_gdi += 1;
|
try_gdi += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
would_block_count += 1;
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
|
would_block_count += 1;
|
||||||
if !scrap::is_x11() {
|
if !scrap::is_x11() {
|
||||||
if would_block_count >= 100 {
|
if would_block_count >= 100 {
|
||||||
// For now, the user should choose and agree screen sharing agiain.
|
// For now, the user should choose and agree screen sharing agiain.
|
||||||
@ -600,9 +601,12 @@ fn run(sp: GenericService) -> ResultType<()> {
|
|||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
would_block_count = 0;
|
would_block_count = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut fetched_conn_ids = HashSet::new();
|
let mut fetched_conn_ids = HashSet::new();
|
||||||
let timeout_millis = 3_000u64;
|
let timeout_millis = 3_000u64;
|
||||||
|
@ -17,18 +17,18 @@ pub fn set_wayland_scrap_map_err() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn map_err_scrap(err: String) -> io::Error {
|
fn map_err_scrap(err: String) -> io::Error {
|
||||||
// REMOVE ME ===================================== uncomment to handle error
|
// to-do: Remove this the following log
|
||||||
// // to-do: Handle error better, do not restart server
|
|
||||||
// if err.starts_with("Did not receive a reply") {
|
|
||||||
// log::error!("Fatal pipewire error, {}", &err);
|
|
||||||
// std::process::exit(-1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
log::error!(
|
log::error!(
|
||||||
"REMOVE ME ===================================== wayland scrap error {}",
|
"REMOVE ME ===================================== wayland scrap error {}",
|
||||||
&err
|
&err
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// to-do: Handle error better, do not restart server
|
||||||
|
if err.starts_with("Did not receive a reply") {
|
||||||
|
log::error!("Fatal pipewire error, {}", &err);
|
||||||
|
std::process::exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
if DISTRO.name.to_uppercase() == "Ubuntu".to_uppercase() {
|
if DISTRO.name.to_uppercase() == "Ubuntu".to_uppercase() {
|
||||||
if DISTRO.version_id < "21".to_owned() {
|
if DISTRO.version_id < "21".to_owned() {
|
||||||
io::Error::new(io::ErrorKind::Other, SCRAP_UBUNTU_HIGHER_REQUIRED)
|
io::Error::new(io::ErrorKind::Other, SCRAP_UBUNTU_HIGHER_REQUIRED)
|
||||||
|
@ -15,7 +15,7 @@ use hbb_common::{
|
|||||||
protobuf::Message as _,
|
protobuf::Message as _,
|
||||||
rendezvous_proto::*,
|
rendezvous_proto::*,
|
||||||
tcp::FramedStream,
|
tcp::FramedStream,
|
||||||
tokio::{self, sync::mpsc, time},
|
tokio::{self, sync::mpsc},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::common::get_app_name;
|
use crate::common::get_app_name;
|
||||||
|
@ -1242,9 +1242,3 @@ function refreshCurrentUser() {
|
|||||||
function getHttpHeaders() {
|
function getHttpHeaders() {
|
||||||
return "Authorization: Bearer " + handler.get_local_option("access_token");
|
return "Authorization: Bearer " + handler.get_local_option("access_token");
|
||||||
}
|
}
|
||||||
|
|
||||||
$(body).timer(1000, function check_elevation(){
|
|
||||||
if (is_win && handler.is_release() && !handler.is_installed() && !handler.is_root()) {
|
|
||||||
msgbox("custom-elevation-nocancel", "Prompt", "elevation_prompt");
|
|
||||||
}
|
|
||||||
});
|
|
@ -18,8 +18,6 @@ use hbb_common::{
|
|||||||
allow_err, fs::TransferJobMeta, log, message_proto::*, rendezvous_proto::ConnType,
|
allow_err, fs::TransferJobMeta, log, message_proto::*, rendezvous_proto::ConnType,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
use crate::clipboard_file::*;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::*,
|
client::*,
|
||||||
ui_interface::has_hwcodec,
|
ui_interface::has_hwcodec,
|
||||||
|
@ -11,7 +11,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use clipboard::{cliprdr::CliprdrClientContext, empty_clipboard, ContextSend};
|
use clipboard::{cliprdr::CliprdrClientContext, empty_clipboard, set_conn_enabled, ContextSend};
|
||||||
use serde_derive::Serialize;
|
use serde_derive::Serialize;
|
||||||
|
|
||||||
use crate::ipc::{self, new_listener, Connection, Data};
|
use crate::ipc::{self, new_listener, Connection, Data};
|
||||||
@ -247,10 +247,10 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
|||||||
.await
|
.await
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
clipboard::set_conn_enabled(conn_id, enabled);
|
set_conn_enabled(conn_id, enabled);
|
||||||
if !enabled {
|
if !enabled {
|
||||||
ContextSend::proc(|context: &mut Box<CliprdrClientContext>| -> u32 {
|
ContextSend::proc(|context: &mut Box<CliprdrClientContext>| -> u32 {
|
||||||
clipboard::empty_clipboard(context, conn_id);
|
empty_clipboard(context, conn_id);
|
||||||
0
|
0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,11 @@ pub fn has_rendezvous_service() -> bool {
|
|||||||
pub fn get_license() -> String {
|
pub fn get_license() -> String {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
if let Some(lic) = crate::platform::windows::get_license() {
|
if let Some(lic) = crate::platform::windows::get_license() {
|
||||||
|
#[cfg(feature = "flutter")]
|
||||||
|
{
|
||||||
|
return format!("Key: {}\nHost: {}\nApi: {}", lic.key, lic.host, lic.api);
|
||||||
|
}
|
||||||
|
// default license format is html formed (sciter)
|
||||||
return format!(
|
return format!(
|
||||||
"<br /> Key: {} <br /> Host: {} Api: {}",
|
"<br /> Key: {} <br /> Host: {} Api: {}",
|
||||||
lic.key, lic.host, lic.api
|
lic.key, lic.host, lic.api
|
||||||
|
Loading…
x
Reference in New Issue
Block a user