mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge branch 'master' of https://github.com/rustdesk/rustdesk
This commit is contained in:
@@ -1418,7 +1418,7 @@ bool isRunningInPortableMode() {
|
||||
}
|
||||
|
||||
/// Window status callback
|
||||
void onActiveWindowChanged() async {
|
||||
Future<void> onActiveWindowChanged() async {
|
||||
print(
|
||||
"[MultiWindowHandler] active window changed: ${rustDeskWinManager.getActiveWindows()}");
|
||||
if (rustDeskWinManager.getActiveWindows().isEmpty) {
|
||||
|
||||
@@ -100,6 +100,8 @@ const kRemoteImageQualityLow = 'low';
|
||||
/// [kRemoteImageQualityCustom] Custom image quality.
|
||||
const kRemoteImageQualityCustom = 'custom';
|
||||
|
||||
const kIgnoreDpi = true;
|
||||
|
||||
/// flutter/packages/flutter/lib/src/services/keyboard_key.dart -> _keyLabels
|
||||
/// see [LogicalKeyboardKey.keyLabel]
|
||||
const Map<int, String> logicalKeyMap = <int, String>{
|
||||
|
||||
@@ -6,7 +6,6 @@ import 'dart:io';
|
||||
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common/widgets/address_book.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -16,7 +15,6 @@ import 'package:window_manager/window_manager.dart';
|
||||
import '../../common.dart';
|
||||
import '../../common/formatter/id_formatter.dart';
|
||||
import '../../common/widgets/peer_tab_page.dart';
|
||||
import '../../common/widgets/peers_view.dart';
|
||||
import '../../models/platform_model.dart';
|
||||
import '../widgets/button.dart';
|
||||
|
||||
@@ -172,6 +170,7 @@ class _ConnectionPageState extends State<ConnectionPage>
|
||||
Expanded(
|
||||
child: Obx(
|
||||
() => TextField(
|
||||
maxLength: 90,
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
@@ -179,12 +178,13 @@ class _ConnectionPageState extends State<ConnectionPage>
|
||||
style: const TextStyle(
|
||||
fontFamily: 'WorkSans',
|
||||
fontSize: 22,
|
||||
height: 1,
|
||||
height: 1.25,
|
||||
),
|
||||
maxLines: 1,
|
||||
cursorColor:
|
||||
Theme.of(context).textTheme.titleLarge?.color,
|
||||
decoration: InputDecoration(
|
||||
counterText: '',
|
||||
hintText: _idInputFocused.value
|
||||
? null
|
||||
: translate('Enter Remote ID'),
|
||||
|
||||
@@ -42,6 +42,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
var svcStopped = false.obs;
|
||||
var watchIsCanScreenRecording = false;
|
||||
var watchIsProcessTrust = false;
|
||||
var watchIsInputMonitoring = false;
|
||||
Timer? _updateTimer;
|
||||
|
||||
@override
|
||||
@@ -334,6 +335,12 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
bind.mainIsProcessTrusted(prompt: true);
|
||||
watchIsProcessTrust = true;
|
||||
}, help: 'Help', link: translate("doc_mac_permission"));
|
||||
} else if (!bind.mainIsCanInputMonitoring(prompt: false)) {
|
||||
return buildInstallCard("Permissions", "config_input", "Configure",
|
||||
() async {
|
||||
bind.mainIsCanInputMonitoring(prompt: true);
|
||||
watchIsInputMonitoring = true;
|
||||
}, help: 'Help', link: translate("doc_mac_permission"));
|
||||
} else if (!svcStopped.value &&
|
||||
bind.mainIsInstalled() &&
|
||||
!bind.mainIsInstalledDaemon(prompt: false)) {
|
||||
@@ -467,6 +474,12 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
if (watchIsInputMonitoring) {
|
||||
if (bind.mainIsCanInputMonitoring(prompt: false)) {
|
||||
watchIsInputMonitoring = false;
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
});
|
||||
Get.put<RxBool>(svcStopped, tag: 'stop-service');
|
||||
rustDeskWinManager.registerActiveWindowListener(onActiveWindowChanged);
|
||||
@@ -500,9 +513,9 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
} else if (call.method == kWindowActionRebuild) {
|
||||
reloadCurrentWindow();
|
||||
} else if (call.method == kWindowEventShow) {
|
||||
rustDeskWinManager.registerActiveWindow(call.arguments["id"]);
|
||||
await rustDeskWinManager.registerActiveWindow(call.arguments["id"]);
|
||||
} else if (call.method == kWindowEventHide) {
|
||||
rustDeskWinManager.unregisterActiveWindow(call.arguments["id"]);
|
||||
await rustDeskWinManager.unregisterActiveWindow(call.arguments["id"]);
|
||||
} else if (call.method == kWindowConnect) {
|
||||
await connectMainDesktop(
|
||||
call.arguments['id'],
|
||||
|
||||
@@ -274,6 +274,15 @@ class _GeneralState extends State<_General> {
|
||||
_OptionCheckBox(context, 'Confirm before closing multiple tabs',
|
||||
'enable-confirm-closing-tabs'),
|
||||
_OptionCheckBox(context, 'Adaptive Bitrate', 'enable-abr'),
|
||||
if (Platform.isLinux)
|
||||
Tooltip(
|
||||
message: translate('software_render_tip'),
|
||||
child: _OptionCheckBox(
|
||||
context,
|
||||
"Always use software rendering",
|
||||
'allow-always-software-render',
|
||||
),
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1223,7 +1232,7 @@ Widget _OptionCheckBox(BuildContext context, String label, String key,
|
||||
ref.value = option;
|
||||
if (reverse) option = !option;
|
||||
String value = bool2option(key, option);
|
||||
bind.mainSetOption(key: key, value: value);
|
||||
await bind.mainSetOption(key: key, value: value);
|
||||
update?.call();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,35 +402,36 @@ class _ImagePaintState extends State<ImagePaint> {
|
||||
onHover: (evt) {},
|
||||
child: child));
|
||||
|
||||
if (c.scrollStyle == ScrollStyle.scrollbar) {
|
||||
if (c.imageOverflow.isTrue && c.scrollStyle == ScrollStyle.scrollbar) {
|
||||
final imageWidth = c.getDisplayWidth() * s;
|
||||
final imageHeight = c.getDisplayHeight() * s;
|
||||
final imageSize = Size(imageWidth, imageHeight);
|
||||
final imageWidget = CustomPaint(
|
||||
size: Size(imageWidth, imageHeight),
|
||||
size: imageSize,
|
||||
painter: ImagePainter(image: m.image, x: 0, y: 0, scale: s),
|
||||
);
|
||||
|
||||
return NotificationListener<ScrollNotification>(
|
||||
onNotification: (notification) {
|
||||
final percentX = _horizontal.hasClients
|
||||
? _horizontal.position.extentBefore /
|
||||
(_horizontal.position.extentBefore +
|
||||
_horizontal.position.extentInside +
|
||||
_horizontal.position.extentAfter)
|
||||
: 0.0;
|
||||
final percentY = _vertical.hasClients
|
||||
? _vertical.position.extentBefore /
|
||||
(_vertical.position.extentBefore +
|
||||
_vertical.position.extentInside +
|
||||
_vertical.position.extentAfter)
|
||||
: 0.0;
|
||||
c.setScrollPercent(percentX, percentY);
|
||||
return false;
|
||||
},
|
||||
child: mouseRegion(
|
||||
child: _buildCrossScrollbar(context, _buildListener(imageWidget),
|
||||
Size(imageWidth, imageHeight))),
|
||||
);
|
||||
onNotification: (notification) {
|
||||
final percentX = _horizontal.hasClients
|
||||
? _horizontal.position.extentBefore /
|
||||
(_horizontal.position.extentBefore +
|
||||
_horizontal.position.extentInside +
|
||||
_horizontal.position.extentAfter)
|
||||
: 0.0;
|
||||
final percentY = _vertical.hasClients
|
||||
? _vertical.position.extentBefore /
|
||||
(_vertical.position.extentBefore +
|
||||
_vertical.position.extentInside +
|
||||
_vertical.position.extentAfter)
|
||||
: 0.0;
|
||||
c.setScrollPercent(percentX, percentY);
|
||||
return false;
|
||||
},
|
||||
child: mouseRegion(
|
||||
child: Obx(() => _buildCrossScrollbarFromLayout(
|
||||
context, _buildListener(imageWidget), c.size, imageSize)),
|
||||
));
|
||||
} else {
|
||||
final imageWidget = CustomPaint(
|
||||
size: Size(c.size.width, c.size.height),
|
||||
@@ -565,24 +566,6 @@ class _ImagePaintState extends State<ImagePaint> {
|
||||
return widget;
|
||||
}
|
||||
|
||||
Widget _buildCrossScrollbar(BuildContext context, Widget child, Size size) {
|
||||
var layoutSize = MediaQuery.of(context).size;
|
||||
// If minimized, w or h may be negative here.
|
||||
final w = layoutSize.width - kWindowBorderWidth * 2;
|
||||
final h =
|
||||
layoutSize.height - kWindowBorderWidth * 2 - kDesktopRemoteTabBarHeight;
|
||||
layoutSize = Size(
|
||||
w < 0 ? 0 : w,
|
||||
h < 0 ? 0 : h,
|
||||
);
|
||||
bool overflow =
|
||||
layoutSize.width < size.width || layoutSize.height < size.height;
|
||||
return overflow
|
||||
? Obx(() =>
|
||||
_buildCrossScrollbarFromLayout(context, child, layoutSize, size))
|
||||
: _buildCrossScrollbarFromLayout(context, child, layoutSize, size);
|
||||
}
|
||||
|
||||
Widget _buildListener(Widget child) {
|
||||
if (listenerBuilder != null) {
|
||||
return listenerBuilder!(child);
|
||||
|
||||
@@ -118,6 +118,15 @@ abstract class MenuEntryBase<T> {
|
||||
this.enabled,
|
||||
});
|
||||
List<mod_menu.PopupMenuEntry<T>> build(BuildContext context, MenuConfig conf);
|
||||
|
||||
enabledStyle(BuildContext context) => TextStyle(
|
||||
color: Theme.of(context).textTheme.titleLarge?.color,
|
||||
fontSize: MenuConfig.fontSize,
|
||||
fontWeight: FontWeight.normal);
|
||||
disabledStyle() => TextStyle(
|
||||
color: Colors.grey,
|
||||
fontSize: MenuConfig.fontSize,
|
||||
fontWeight: FontWeight.normal);
|
||||
}
|
||||
|
||||
class MenuEntryDivider<T> extends MenuEntryBase<T> {
|
||||
@@ -189,54 +198,76 @@ class MenuEntryRadios<T> extends MenuEntryBase<T> {
|
||||
|
||||
mod_menu.PopupMenuEntry<T> _buildMenuItem(
|
||||
BuildContext context, MenuConfig conf, MenuEntryRadioOption opt) {
|
||||
Widget getTextChild() {
|
||||
final enabledTextChild = Text(
|
||||
opt.text,
|
||||
style: enabledStyle(context),
|
||||
);
|
||||
final disabledTextChild = Text(
|
||||
opt.text,
|
||||
style: disabledStyle(),
|
||||
);
|
||||
if (opt.enabled == null) {
|
||||
return enabledTextChild;
|
||||
} else {
|
||||
return Obx(
|
||||
() => opt.enabled!.isTrue ? enabledTextChild : disabledTextChild);
|
||||
}
|
||||
}
|
||||
|
||||
final child = Container(
|
||||
padding: padding,
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
constraints:
|
||||
BoxConstraints(minHeight: conf.height, maxHeight: conf.height),
|
||||
child: Row(
|
||||
children: [
|
||||
getTextChild(),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Transform.scale(
|
||||
scale: MenuConfig.iconScale,
|
||||
child: Obx(() => opt.value == curOption.value
|
||||
? IconButton(
|
||||
padding:
|
||||
const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 0.0),
|
||||
hoverColor: Colors.transparent,
|
||||
focusColor: Colors.transparent,
|
||||
onPressed: () {},
|
||||
icon: Icon(
|
||||
Icons.check,
|
||||
color: (opt.enabled ?? true.obs).isTrue
|
||||
? conf.commonColor
|
||||
: Colors.grey,
|
||||
))
|
||||
: const SizedBox.shrink()),
|
||||
))),
|
||||
],
|
||||
),
|
||||
);
|
||||
onPressed() {
|
||||
if (opt.dismissOnClicked && Navigator.canPop(context)) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
setOption(opt.value);
|
||||
}
|
||||
|
||||
return mod_menu.PopupMenuItem(
|
||||
padding: EdgeInsets.zero,
|
||||
height: conf.height,
|
||||
child: Container(
|
||||
width: conf.boxWidth,
|
||||
child: TextButton(
|
||||
child: Container(
|
||||
padding: padding,
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
constraints: BoxConstraints(
|
||||
minHeight: conf.height, maxHeight: conf.height),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
opt.text,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).textTheme.titleLarge?.color,
|
||||
fontSize: MenuConfig.fontSize,
|
||||
fontWeight: FontWeight.normal),
|
||||
),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Transform.scale(
|
||||
scale: MenuConfig.iconScale,
|
||||
child: Obx(() => opt.value == curOption.value
|
||||
? IconButton(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
8.0, 0.0, 8.0, 0.0),
|
||||
hoverColor: Colors.transparent,
|
||||
focusColor: Colors.transparent,
|
||||
onPressed: () {},
|
||||
icon: Icon(
|
||||
Icons.check,
|
||||
color: conf.commonColor,
|
||||
))
|
||||
: const SizedBox.shrink()),
|
||||
))),
|
||||
],
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
if (opt.dismissOnClicked && Navigator.canPop(context)) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
setOption(opt.value);
|
||||
},
|
||||
)),
|
||||
width: conf.boxWidth,
|
||||
child: opt.enabled == null
|
||||
? TextButton(
|
||||
child: child,
|
||||
onPressed: onPressed,
|
||||
)
|
||||
: Obx(() => TextButton(
|
||||
child: child,
|
||||
onPressed: opt.enabled!.isTrue ? onPressed : null,
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -567,12 +598,9 @@ class MenuEntrySubMenu<T> extends MenuEntryBase<T> {
|
||||
const SizedBox(width: MenuConfig.midPadding),
|
||||
Obx(() => Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: super.enabled!.value
|
||||
? Theme.of(context).textTheme.titleLarge?.color
|
||||
: Colors.grey,
|
||||
fontSize: MenuConfig.fontSize,
|
||||
fontWeight: FontWeight.normal),
|
||||
style: super.enabled!.value
|
||||
? enabledStyle(context)
|
||||
: disabledStyle(),
|
||||
)),
|
||||
Expanded(
|
||||
child: Align(
|
||||
@@ -605,14 +633,6 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
|
||||
);
|
||||
|
||||
Widget _buildChild(BuildContext context, MenuConfig conf) {
|
||||
final enabledStyle = TextStyle(
|
||||
color: Theme.of(context).textTheme.titleLarge?.color,
|
||||
fontSize: MenuConfig.fontSize,
|
||||
fontWeight: FontWeight.normal);
|
||||
const disabledStyle = TextStyle(
|
||||
color: Colors.grey,
|
||||
fontSize: MenuConfig.fontSize,
|
||||
fontWeight: FontWeight.normal);
|
||||
super.enabled ??= true.obs;
|
||||
return Obx(() => Container(
|
||||
width: conf.boxWidth,
|
||||
@@ -631,7 +651,7 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
|
||||
constraints:
|
||||
BoxConstraints(minHeight: conf.height, maxHeight: conf.height),
|
||||
child: childBuilder(
|
||||
super.enabled!.value ? enabledStyle : disabledStyle),
|
||||
super.enabled!.value ? enabledStyle(context) : disabledStyle()),
|
||||
),
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -699,7 +699,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
if (_screen == null) {
|
||||
return false;
|
||||
}
|
||||
double scale = _screen!.scaleFactor;
|
||||
final scale = kIgnoreDpi ? 1.0 : _screen!.scaleFactor;
|
||||
double selfWidth = _screen!.visibleFrame.width;
|
||||
double selfHeight = _screen!.visibleFrame.height;
|
||||
if (isFullscreen) {
|
||||
@@ -936,11 +936,13 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
text: translate('ScrollAuto'),
|
||||
value: kRemoteScrollStyleAuto,
|
||||
dismissOnClicked: true,
|
||||
enabled: widget.ffi.canvasModel.imageOverflow,
|
||||
),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Scrollbar'),
|
||||
value: kRemoteScrollStyleBar,
|
||||
dismissOnClicked: true,
|
||||
enabled: widget.ffi.canvasModel.imageOverflow,
|
||||
),
|
||||
],
|
||||
curOptionGetter: () async =>
|
||||
@@ -986,15 +988,17 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
wndRect.bottom - wndRect.top - mediaSize.height * scale;
|
||||
|
||||
final canvasModel = widget.ffi.canvasModel;
|
||||
final width = (canvasModel.getDisplayWidth() +
|
||||
canvasModel.windowBorderWidth * 2) *
|
||||
scale +
|
||||
magicWidth;
|
||||
final height = (canvasModel.getDisplayHeight() +
|
||||
canvasModel.tabBarHeight +
|
||||
canvasModel.windowBorderWidth * 2) *
|
||||
scale +
|
||||
magicHeight;
|
||||
final width =
|
||||
(canvasModel.getDisplayWidth() * canvasModel.scale +
|
||||
canvasModel.windowBorderWidth * 2) *
|
||||
scale +
|
||||
magicWidth;
|
||||
final height =
|
||||
(canvasModel.getDisplayHeight() * canvasModel.scale +
|
||||
canvasModel.tabBarHeight +
|
||||
canvasModel.windowBorderWidth * 2) *
|
||||
scale +
|
||||
magicHeight;
|
||||
double left = wndRect.left + (wndRect.width - width) / 2;
|
||||
double top = wndRect.top + (wndRect.height - height) / 2;
|
||||
|
||||
@@ -1198,7 +1202,6 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
},
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.sessionSetKeyboardMode(id: widget.id, value: newValue);
|
||||
widget.ffi.canvasModel.updateViewStyle();
|
||||
},
|
||||
)
|
||||
];
|
||||
|
||||
@@ -527,13 +527,19 @@ class WindowActionPanelState extends State<WindowActionPanel>
|
||||
void onWindowClose() async {
|
||||
// hide window on close
|
||||
if (widget.isMainWindow) {
|
||||
await rustDeskWinManager.unregisterActiveWindow(0);
|
||||
// `hide` must be placed after unregisterActiveWindow, because once all windows are hidden,
|
||||
// flutter closes the application on macOS. We should ensure the post-run logic has ran successfully.
|
||||
// e.g.: saving window position.
|
||||
await windowManager.hide();
|
||||
rustDeskWinManager.unregisterActiveWindow(0);
|
||||
} else {
|
||||
widget.onClose?.call();
|
||||
// it's safe to hide the subwindow
|
||||
await WindowController.fromWindowId(windowId!).hide();
|
||||
rustDeskWinManager
|
||||
.call(WindowType.Main, kWindowEventHide, {"id": windowId!});
|
||||
await Future.wait([
|
||||
rustDeskWinManager
|
||||
.call(WindowType.Main, kWindowEventHide, {"id": windowId!}),
|
||||
widget.onClose?.call() ?? Future.microtask(() => null)
|
||||
]);
|
||||
}
|
||||
super.onWindowClose();
|
||||
}
|
||||
|
||||
@@ -196,6 +196,8 @@ void runMultiWindow(
|
||||
// no such appType
|
||||
exit(0);
|
||||
}
|
||||
// show window from hidden status
|
||||
WindowController.fromWindowId(windowId!).show();
|
||||
}
|
||||
|
||||
void runConnectionManagerScreen(bool hide) async {
|
||||
|
||||
@@ -466,15 +466,21 @@ class InputModel {
|
||||
evt['y'] = '${y.round()}';
|
||||
var buttons = '';
|
||||
switch (evt['buttons']) {
|
||||
case 1:
|
||||
case kPrimaryMouseButton:
|
||||
buttons = 'left';
|
||||
break;
|
||||
case 2:
|
||||
case kSecondaryMouseButton:
|
||||
buttons = 'right';
|
||||
break;
|
||||
case 4:
|
||||
case kMiddleMouseButton:
|
||||
buttons = 'wheel';
|
||||
break;
|
||||
case kBackMouseButton:
|
||||
buttons = 'back';
|
||||
break;
|
||||
case kForwardMouseButton:
|
||||
buttons = 'forward';
|
||||
break;
|
||||
}
|
||||
evt['buttons'] = buttons;
|
||||
bind.sessionSendMouse(id: id, msg: json.encode(evt));
|
||||
|
||||
@@ -22,6 +22,7 @@ import 'package:tuple/tuple.dart';
|
||||
import 'package:image/image.dart' as img2;
|
||||
import 'package:flutter_custom_cursor/cursor_manager.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../common.dart';
|
||||
import '../common/shared_state.dart';
|
||||
@@ -528,6 +529,7 @@ class CanvasModel with ChangeNotifier {
|
||||
double _y = 0;
|
||||
// image scale
|
||||
double _scale = 1.0;
|
||||
Size _size = Size.zero;
|
||||
// the tabbar over the image
|
||||
// double tabBarHeight = 0.0;
|
||||
// the window border's width
|
||||
@@ -541,6 +543,8 @@ class CanvasModel with ChangeNotifier {
|
||||
ScrollStyle _scrollStyle = ScrollStyle.scrollauto;
|
||||
ViewStyle _lastViewStyle = ViewStyle();
|
||||
|
||||
final _imageOverflow = false.obs;
|
||||
|
||||
WeakReference<FFI> parent;
|
||||
|
||||
CanvasModel(this.parent);
|
||||
@@ -548,8 +552,10 @@ class CanvasModel with ChangeNotifier {
|
||||
double get x => _x;
|
||||
double get y => _y;
|
||||
double get scale => _scale;
|
||||
Size get size => _size;
|
||||
ScrollStyle get scrollStyle => _scrollStyle;
|
||||
ViewStyle get viewStyle => _lastViewStyle;
|
||||
RxBool get imageOverflow => _imageOverflow;
|
||||
|
||||
_resetScroll() => setScrollPercent(0.0, 0.0);
|
||||
|
||||
@@ -562,18 +568,26 @@ class CanvasModel with ChangeNotifier {
|
||||
double get scrollY => _scrollY;
|
||||
|
||||
updateViewStyle() async {
|
||||
Size getSize() {
|
||||
final size = MediaQueryData.fromWindow(ui.window).size;
|
||||
// If minimized, w or h may be negative here.
|
||||
double w = size.width - windowBorderWidth * 2;
|
||||
double h = size.height - tabBarHeight - windowBorderWidth * 2;
|
||||
return Size(w < 0 ? 0 : w, h < 0 ? 0 : h);
|
||||
}
|
||||
|
||||
final style = await bind.sessionGetViewStyle(id: id);
|
||||
if (style == null) {
|
||||
return;
|
||||
}
|
||||
final sizeWidth = size.width;
|
||||
final sizeHeight = size.height;
|
||||
|
||||
_size = getSize();
|
||||
final displayWidth = getDisplayWidth();
|
||||
final displayHeight = getDisplayHeight();
|
||||
final viewStyle = ViewStyle(
|
||||
style: style,
|
||||
width: sizeWidth,
|
||||
height: sizeHeight,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
displayWidth: displayWidth,
|
||||
displayHeight: displayHeight,
|
||||
);
|
||||
@@ -585,8 +599,13 @@ class CanvasModel with ChangeNotifier {
|
||||
}
|
||||
_lastViewStyle = viewStyle;
|
||||
_scale = viewStyle.scale;
|
||||
_x = (sizeWidth - displayWidth * _scale) / 2;
|
||||
_y = (sizeHeight - displayHeight * _scale) / 2;
|
||||
|
||||
if (kIgnoreDpi && style == kRemoteViewStyleOriginal) {
|
||||
_scale = 1.0 / ui.window.devicePixelRatio;
|
||||
}
|
||||
_x = (size.width - displayWidth * _scale) / 2;
|
||||
_y = (size.height - displayHeight * _scale) / 2;
|
||||
_imageOverflow.value = _x < 0 || y < 0;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -628,14 +647,6 @@ class CanvasModel with ChangeNotifier {
|
||||
double get windowBorderWidth => stateGlobal.windowBorderWidth.value;
|
||||
double get tabBarHeight => stateGlobal.tabBarHeight;
|
||||
|
||||
Size get size {
|
||||
final size = MediaQueryData.fromWindow(ui.window).size;
|
||||
// If minimized, w or h may be negative here.
|
||||
double w = size.width - windowBorderWidth * 2;
|
||||
double h = size.height - tabBarHeight - windowBorderWidth * 2;
|
||||
return Size(w < 0 ? 0 : w, h < 0 ? 0 : h);
|
||||
}
|
||||
|
||||
moveDesktopMouse(double x, double y) {
|
||||
// On mobile platforms, move the canvas with the cursor.
|
||||
final dw = getDisplayWidth() * _scale;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
@@ -34,7 +35,7 @@ class RustDeskMultiWindowManager {
|
||||
static final instance = RustDeskMultiWindowManager._();
|
||||
|
||||
final List<int> _activeWindows = List.empty(growable: true);
|
||||
final List<VoidCallback> _windowActiveCallbacks = List.empty(growable: true);
|
||||
final List<AsyncCallback> _windowActiveCallbacks = List.empty(growable: true);
|
||||
int? _remoteDesktopWindowId;
|
||||
int? _fileTransferWindowId;
|
||||
int? _portForwardWindowId;
|
||||
@@ -191,19 +192,19 @@ class RustDeskMultiWindowManager {
|
||||
return _activeWindows;
|
||||
}
|
||||
|
||||
void _notifyActiveWindow() {
|
||||
Future<void> _notifyActiveWindow() async {
|
||||
for (final callback in _windowActiveCallbacks) {
|
||||
callback.call();
|
||||
await callback.call();
|
||||
}
|
||||
}
|
||||
|
||||
void registerActiveWindow(int windowId) {
|
||||
Future<void> registerActiveWindow(int windowId) async {
|
||||
if (_activeWindows.contains(windowId)) {
|
||||
// ignore
|
||||
} else {
|
||||
_activeWindows.add(windowId);
|
||||
}
|
||||
_notifyActiveWindow();
|
||||
await _notifyActiveWindow();
|
||||
}
|
||||
|
||||
/// Remove active window which has [`windowId`]
|
||||
@@ -212,20 +213,20 @@ class RustDeskMultiWindowManager {
|
||||
/// This function should only be called from main window.
|
||||
/// For other windows, please post a unregister(hide) event to main window handler:
|
||||
/// `rustDeskWinManager.call(WindowType.Main, kWindowEventHide, {"id": windowId!});`
|
||||
void unregisterActiveWindow(int windowId) {
|
||||
Future<void> unregisterActiveWindow(int windowId) async {
|
||||
if (!_activeWindows.contains(windowId)) {
|
||||
// ignore
|
||||
} else {
|
||||
_activeWindows.remove(windowId);
|
||||
}
|
||||
_notifyActiveWindow();
|
||||
await _notifyActiveWindow();
|
||||
}
|
||||
|
||||
void registerActiveWindowListener(VoidCallback callback) {
|
||||
void registerActiveWindowListener(AsyncCallback callback) {
|
||||
_windowActiveCallbacks.add(callback);
|
||||
}
|
||||
|
||||
void unregisterActiveWindowListener(VoidCallback callback) {
|
||||
void unregisterActiveWindowListener(AsyncCallback callback) {
|
||||
_windowActiveCallbacks.remove(callback);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user