Refact. Flutter web desktop (#7539)
* Refact. Flutter web desktop Signed-off-by: fufesou <shuanglongchen@yeah.net> * Flutter web, prevent default context menu Signed-off-by: fufesou <shuanglongchen@yeah.net> --------- Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
810b980e6b
commit
6e44a91d0b
@ -29,6 +29,8 @@ import '../consts.dart';
|
|||||||
import 'common/widgets/overlay.dart';
|
import 'common/widgets/overlay.dart';
|
||||||
import 'mobile/pages/file_manager_page.dart';
|
import 'mobile/pages/file_manager_page.dart';
|
||||||
import 'mobile/pages/remote_page.dart';
|
import 'mobile/pages/remote_page.dart';
|
||||||
|
import 'desktop/pages/remote_page.dart' as desktop_remote;
|
||||||
|
import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
|
||||||
import 'models/input_model.dart';
|
import 'models/input_model.dart';
|
||||||
import 'models/model.dart';
|
import 'models/model.dart';
|
||||||
import 'models/platform_model.dart';
|
import 'models/platform_model.dart';
|
||||||
@ -48,7 +50,7 @@ final isMacOS = isMacOS_;
|
|||||||
final isLinux = isLinux_;
|
final isLinux = isLinux_;
|
||||||
final isDesktop = isDesktop_;
|
final isDesktop = isDesktop_;
|
||||||
final isWeb = isWeb_;
|
final isWeb = isWeb_;
|
||||||
var isWebDesktop = false;
|
final isWebDesktop = isWebDesktop_;
|
||||||
var isMobile = isAndroid || isIOS;
|
var isMobile = isAndroid || isIOS;
|
||||||
var version = '';
|
var version = '';
|
||||||
int androidVersion = 0;
|
int androidVersion = 0;
|
||||||
@ -60,6 +62,8 @@ DesktopType? desktopType;
|
|||||||
bool get isMainDesktopWindow =>
|
bool get isMainDesktopWindow =>
|
||||||
desktopType == DesktopType.main || desktopType == DesktopType.cm;
|
desktopType == DesktopType.main || desktopType == DesktopType.cm;
|
||||||
|
|
||||||
|
String get screenInfo => screenInfo_;
|
||||||
|
|
||||||
/// Check if the app is running with single view mode.
|
/// Check if the app is running with single view mode.
|
||||||
bool isSingleViewApp() {
|
bool isSingleViewApp() {
|
||||||
return desktopType == DesktopType.cm;
|
return desktopType == DesktopType.cm;
|
||||||
@ -233,11 +237,13 @@ class MyTheme {
|
|||||||
);
|
);
|
||||||
|
|
||||||
static SwitchThemeData switchTheme() {
|
static SwitchThemeData switchTheme() {
|
||||||
return SwitchThemeData(splashRadius: isDesktop ? 0 : kRadialReactionRadius);
|
return SwitchThemeData(
|
||||||
|
splashRadius: (isDesktop || isWebDesktop) ? 0 : kRadialReactionRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RadioThemeData radioTheme() {
|
static RadioThemeData radioTheme() {
|
||||||
return RadioThemeData(splashRadius: isDesktop ? 0 : kRadialReactionRadius);
|
return RadioThemeData(
|
||||||
|
splashRadius: (isDesktop || isWebDesktop) ? 0 : kRadialReactionRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checkbox
|
// Checkbox
|
||||||
@ -286,7 +292,7 @@ class MyTheme {
|
|||||||
static EdgeInsets dialogContentPadding({bool actions = true}) {
|
static EdgeInsets dialogContentPadding({bool actions = true}) {
|
||||||
final double p = dialogPadding;
|
final double p = dialogPadding;
|
||||||
|
|
||||||
return isDesktop
|
return (isDesktop || isWebDesktop)
|
||||||
? EdgeInsets.fromLTRB(p, p, p, actions ? (p - 4) : p)
|
? EdgeInsets.fromLTRB(p, p, p, actions ? (p - 4) : p)
|
||||||
: EdgeInsets.fromLTRB(p, p, p, actions ? (p / 2) : p);
|
: EdgeInsets.fromLTRB(p, p, p, actions ? (p / 2) : p);
|
||||||
}
|
}
|
||||||
@ -294,12 +300,12 @@ class MyTheme {
|
|||||||
static EdgeInsets dialogActionsPadding() {
|
static EdgeInsets dialogActionsPadding() {
|
||||||
final double p = dialogPadding;
|
final double p = dialogPadding;
|
||||||
|
|
||||||
return isDesktop
|
return (isDesktop || isWebDesktop)
|
||||||
? EdgeInsets.fromLTRB(p, 0, p, (p - 4))
|
? EdgeInsets.fromLTRB(p, 0, p, (p - 4))
|
||||||
: EdgeInsets.fromLTRB(p, 0, (p - mobileTextButtonPaddingLR), (p / 2));
|
: EdgeInsets.fromLTRB(p, 0, (p - mobileTextButtonPaddingLR), (p / 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
static EdgeInsets dialogButtonPadding = isDesktop
|
static EdgeInsets dialogButtonPadding = (isDesktop || isWebDesktop)
|
||||||
? EdgeInsets.only(left: dialogPadding)
|
? EdgeInsets.only(left: dialogPadding)
|
||||||
: EdgeInsets.only(left: dialogPadding / 3);
|
: EdgeInsets.only(left: dialogPadding / 3);
|
||||||
|
|
||||||
@ -371,10 +377,10 @@ class MyTheme {
|
|||||||
labelColor: Colors.black87,
|
labelColor: Colors.black87,
|
||||||
),
|
),
|
||||||
tooltipTheme: tooltipTheme(),
|
tooltipTheme: tooltipTheme(),
|
||||||
splashColor: isDesktop ? Colors.transparent : null,
|
splashColor: (isDesktop || isWebDesktop) ? Colors.transparent : null,
|
||||||
highlightColor: isDesktop ? Colors.transparent : null,
|
highlightColor: (isDesktop || isWebDesktop) ? Colors.transparent : null,
|
||||||
splashFactory: isDesktop ? NoSplash.splashFactory : null,
|
splashFactory: (isDesktop || isWebDesktop) ? NoSplash.splashFactory : null,
|
||||||
textButtonTheme: isDesktop
|
textButtonTheme: (isDesktop || isWebDesktop)
|
||||||
? TextButtonThemeData(
|
? TextButtonThemeData(
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
splashFactory: NoSplash.splashFactory,
|
splashFactory: NoSplash.splashFactory,
|
||||||
@ -414,7 +420,9 @@ class MyTheme {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
side: BorderSide(
|
side: BorderSide(
|
||||||
color: isDesktop ? Color(0xFFECECEC) : Colors.transparent),
|
color: (isDesktop || isWebDesktop)
|
||||||
|
? Color(0xFFECECEC)
|
||||||
|
: Colors.transparent),
|
||||||
borderRadius: BorderRadius.all(Radius.circular(8.0)),
|
borderRadius: BorderRadius.all(Radius.circular(8.0)),
|
||||||
)),
|
)),
|
||||||
).copyWith(
|
).copyWith(
|
||||||
@ -440,7 +448,7 @@ class MyTheme {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
scrollbarTheme: scrollbarThemeDark,
|
scrollbarTheme: scrollbarThemeDark,
|
||||||
inputDecorationTheme: isDesktop
|
inputDecorationTheme: (isDesktop || isWebDesktop)
|
||||||
? InputDecorationTheme(
|
? InputDecorationTheme(
|
||||||
fillColor: Color(0xFF24252B),
|
fillColor: Color(0xFF24252B),
|
||||||
filled: true,
|
filled: true,
|
||||||
@ -467,10 +475,10 @@ class MyTheme {
|
|||||||
labelColor: Colors.white70,
|
labelColor: Colors.white70,
|
||||||
),
|
),
|
||||||
tooltipTheme: tooltipTheme(),
|
tooltipTheme: tooltipTheme(),
|
||||||
splashColor: isDesktop ? Colors.transparent : null,
|
splashColor: (isDesktop || isWebDesktop) ? Colors.transparent : null,
|
||||||
highlightColor: isDesktop ? Colors.transparent : null,
|
highlightColor: (isDesktop || isWebDesktop) ? Colors.transparent : null,
|
||||||
splashFactory: isDesktop ? NoSplash.splashFactory : null,
|
splashFactory: (isDesktop || isWebDesktop) ? NoSplash.splashFactory : null,
|
||||||
textButtonTheme: isDesktop
|
textButtonTheme: (isDesktop || isWebDesktop)
|
||||||
? TextButtonThemeData(
|
? TextButtonThemeData(
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
splashFactory: NoSplash.splashFactory,
|
splashFactory: NoSplash.splashFactory,
|
||||||
@ -818,7 +826,7 @@ class OverlayDialogManager {
|
|||||||
Offstage(
|
Offstage(
|
||||||
offstage: !showCancel,
|
offstage: !showCancel,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: isDesktop
|
child: (isDesktop || isWebDesktop)
|
||||||
? dialogButton('Cancel', onPressed: cancel)
|
? dialogButton('Cancel', onPressed: cancel)
|
||||||
: TextButton(
|
: TextButton(
|
||||||
style: flatButtonStyle,
|
style: flatButtonStyle,
|
||||||
@ -1293,7 +1301,7 @@ class AndroidPermissionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future<bool> check(String type) {
|
static Future<bool> check(String type) {
|
||||||
if (isDesktop) {
|
if (isDesktop || isWeb) {
|
||||||
return Future.value(true);
|
return Future.value(true);
|
||||||
}
|
}
|
||||||
return gFFI.invokeMethod("check_permission", type);
|
return gFFI.invokeMethod("check_permission", type);
|
||||||
@ -1307,7 +1315,7 @@ class AndroidPermissionManager {
|
|||||||
/// We use XXPermissions to request permissions,
|
/// We use XXPermissions to request permissions,
|
||||||
/// for supported types, see https://github.com/getActivity/XXPermissions/blob/e46caea32a64ad7819df62d448fb1c825481cd28/library/src/main/java/com/hjq/permissions/Permission.java
|
/// for supported types, see https://github.com/getActivity/XXPermissions/blob/e46caea32a64ad7819df62d448fb1c825481cd28/library/src/main/java/com/hjq/permissions/Permission.java
|
||||||
static Future<bool> request(String type) {
|
static Future<bool> request(String type) {
|
||||||
if (isDesktop) {
|
if (isDesktop || isWeb) {
|
||||||
return Future.value(true);
|
return Future.value(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2197,13 +2205,29 @@ connect(BuildContext context, String id,
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Navigator.push(
|
if (isWebDesktop) {
|
||||||
context,
|
Navigator.push(
|
||||||
MaterialPageRoute(
|
context,
|
||||||
builder: (BuildContext context) => RemotePage(
|
MaterialPageRoute(
|
||||||
id: id, password: password, isSharedPassword: isSharedPassword),
|
builder: (BuildContext context) => desktop_remote.RemotePage(
|
||||||
),
|
key: ValueKey(id),
|
||||||
);
|
id: id,
|
||||||
|
toolbarState: ToolbarState(),
|
||||||
|
password: password,
|
||||||
|
forceRelay: forceRelay,
|
||||||
|
isSharedPassword: isSharedPassword,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (BuildContext context) => RemotePage(
|
||||||
|
id: id, password: password, isSharedPassword: isSharedPassword),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2398,7 +2422,7 @@ Widget dialogButton(String text,
|
|||||||
Widget? icon,
|
Widget? icon,
|
||||||
TextStyle? style,
|
TextStyle? style,
|
||||||
ButtonStyle? buttonStyle}) {
|
ButtonStyle? buttonStyle}) {
|
||||||
if (isDesktop) {
|
if (isDesktop || isWebDesktop) {
|
||||||
if (isOutline) {
|
if (isOutline) {
|
||||||
return icon == null
|
return icon == null
|
||||||
? OutlinedButton(
|
? OutlinedButton(
|
||||||
|
@ -63,7 +63,7 @@ class _AddressBookState extends State<AddressBook> {
|
|||||||
retry: null, // remove retry
|
retry: null, // remove retry
|
||||||
close: () => gFFI.abModel.currentAbPushError.value = ''),
|
close: () => gFFI.abModel.currentAbPushError.value = ''),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: isDesktop
|
child: (isDesktop || isWebDesktop)
|
||||||
? _buildAddressBookDesktop()
|
? _buildAddressBookDesktop()
|
||||||
: _buildAddressBookMobile())
|
: _buildAddressBookMobile())
|
||||||
],
|
],
|
||||||
@ -311,7 +311,7 @@ class _AddressBookState extends State<AddressBook> {
|
|||||||
return tagBuilder(e);
|
return tagBuilder(e);
|
||||||
});
|
});
|
||||||
final maxHeight = max(MediaQuery.of(context).size.height / 6, 100.0);
|
final maxHeight = max(MediaQuery.of(context).size.height / 6, 100.0);
|
||||||
return isDesktop
|
return (isDesktop || isWebDesktop)
|
||||||
? gridView
|
? gridView
|
||||||
: LimitedBox(maxHeight: maxHeight, child: gridView);
|
: LimitedBox(maxHeight: maxHeight, child: gridView);
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:bot_toast/bot_toast.dart';
|
import 'package:bot_toast/bot_toast.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -81,7 +80,7 @@ void changeIdDialog() {
|
|||||||
final Iterable violations = rules.where((r) => !r.validate(newId));
|
final Iterable violations = rules.where((r) => !r.validate(newId));
|
||||||
if (violations.isNotEmpty) {
|
if (violations.isNotEmpty) {
|
||||||
setState(() {
|
setState(() {
|
||||||
msg = isDesktop
|
msg = (isDesktop || isWebDesktop)
|
||||||
? '${translate('Prompt')}: ${violations.map((r) => r.name).join(', ')}'
|
? '${translate('Prompt')}: ${violations.map((r) => r.name).join(', ')}'
|
||||||
: violations.map((r) => r.name).join(', ');
|
: violations.map((r) => r.name).join(', ');
|
||||||
});
|
});
|
||||||
@ -106,7 +105,7 @@ void changeIdDialog() {
|
|||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
isInProgress = false;
|
isInProgress = false;
|
||||||
msg = isDesktop
|
msg = (isDesktop || isWebDesktop)
|
||||||
? '${translate('Prompt')}: ${translate(status)}'
|
? '${translate('Prompt')}: ${translate(status)}'
|
||||||
: translate(status);
|
: translate(status);
|
||||||
});
|
});
|
||||||
@ -143,7 +142,7 @@ void changeIdDialog() {
|
|||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 8.0,
|
height: 8.0,
|
||||||
),
|
),
|
||||||
isDesktop
|
(isDesktop || isWebDesktop)
|
||||||
? Obx(() => Wrap(
|
? Obx(() => Wrap(
|
||||||
runSpacing: 8,
|
runSpacing: 8,
|
||||||
spacing: 4,
|
spacing: 4,
|
||||||
@ -1109,7 +1108,7 @@ void showRequestElevationDialog(
|
|||||||
errorText: errPwd.isEmpty ? null : errPwd.value,
|
errorText: errPwd.isEmpty ? null : errPwd.value,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).marginOnly(left: isDesktop ? 35 : 0),
|
).marginOnly(left: (isDesktop || isWebDesktop) ? 35 : 0),
|
||||||
).marginOnly(top: 10),
|
).marginOnly(top: 10),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -47,7 +47,10 @@ class _MyGroupState extends State<MyGroup> {
|
|||||||
err: gFFI.groupModel.groupLoadError,
|
err: gFFI.groupModel.groupLoadError,
|
||||||
retry: null,
|
retry: null,
|
||||||
close: () => gFFI.groupModel.groupLoadError.value = ''),
|
close: () => gFFI.groupModel.groupLoadError.value = ''),
|
||||||
Expanded(child: isDesktop ? _buildDesktop() : _buildMobile())
|
Expanded(
|
||||||
|
child: (isDesktop || isWebDesktop)
|
||||||
|
? _buildDesktop()
|
||||||
|
: _buildMobile())
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -164,7 +167,7 @@ class _MyGroupState extends State<MyGroup> {
|
|||||||
itemCount: items.length,
|
itemCount: items.length,
|
||||||
itemBuilder: (context, index) => _buildUserItem(items[index]));
|
itemBuilder: (context, index) => _buildUserItem(items[index]));
|
||||||
var maxHeight = max(MediaQuery.of(context).size.height / 6, 100.0);
|
var maxHeight = max(MediaQuery.of(context).size.height / 6, 100.0);
|
||||||
return isDesktop
|
return (isDesktop || isWebDesktop)
|
||||||
? listView
|
? listView
|
||||||
: LimitedBox(maxHeight: maxHeight, child: listView);
|
: LimitedBox(maxHeight: maxHeight, child: listView);
|
||||||
});
|
});
|
||||||
|
@ -54,7 +54,7 @@ class DraggableChatWindow extends StatelessWidget {
|
|||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
appBar: CustomAppBar(
|
appBar: CustomAppBar(
|
||||||
onPanUpdate: onPanUpdate,
|
onPanUpdate: onPanUpdate,
|
||||||
appBar: isDesktop
|
appBar: (isDesktop || isWebDesktop)
|
||||||
? _buildDesktopAppBar(context)
|
? _buildDesktopAppBar(context)
|
||||||
: _buildMobileAppBar(context),
|
: _buildMobileAppBar(context),
|
||||||
),
|
),
|
||||||
|
@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/dialog.dart';
|
import 'package:flutter_hbb/common/widgets/dialog.dart';
|
||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/models/ab_model.dart';
|
|
||||||
import 'package:flutter_hbb/models/peer_tab_model.dart';
|
import 'package:flutter_hbb/models/peer_tab_model.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -52,7 +51,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
if (isDesktop) {
|
if (isDesktop || isWebDesktop) {
|
||||||
return _buildDesktop();
|
return _buildDesktop();
|
||||||
} else {
|
} else {
|
||||||
return _buildMobile();
|
return _buildMobile();
|
||||||
@ -883,8 +882,7 @@ class RecentPeerCard extends BasePeerCard {
|
|||||||
menuItems.add(_createShortCutAction(peer.id));
|
menuItems.add(_createShortCutAction(peer.id));
|
||||||
}
|
}
|
||||||
menuItems.add(MenuEntryDivider());
|
menuItems.add(MenuEntryDivider());
|
||||||
if (!isWeb) {
|
if (isDesktop || isWebDesktop) {
|
||||||
// TODO: support web version
|
|
||||||
menuItems.add(_renameAction(peer.id));
|
menuItems.add(_renameAction(peer.id));
|
||||||
}
|
}
|
||||||
if (await bind.mainPeerHasPassword(id: peer.id)) {
|
if (await bind.mainPeerHasPassword(id: peer.id)) {
|
||||||
@ -940,8 +938,7 @@ class FavoritePeerCard extends BasePeerCard {
|
|||||||
menuItems.add(_createShortCutAction(peer.id));
|
menuItems.add(_createShortCutAction(peer.id));
|
||||||
}
|
}
|
||||||
menuItems.add(MenuEntryDivider());
|
menuItems.add(MenuEntryDivider());
|
||||||
if (!isWeb) {
|
if (isDesktop || isWebDesktop) {
|
||||||
// TODO: support web version
|
|
||||||
menuItems.add(_renameAction(peer.id));
|
menuItems.add(_renameAction(peer.id));
|
||||||
}
|
}
|
||||||
if (await bind.mainPeerHasPassword(id: peer.id)) {
|
if (await bind.mainPeerHasPassword(id: peer.id)) {
|
||||||
@ -1046,8 +1043,7 @@ class AddressBookPeerCard extends BasePeerCard {
|
|||||||
}
|
}
|
||||||
if (gFFI.abModel.current.canWrite()) {
|
if (gFFI.abModel.current.canWrite()) {
|
||||||
menuItems.add(MenuEntryDivider());
|
menuItems.add(MenuEntryDivider());
|
||||||
if (!isWeb) {
|
if (isDesktop || isWebDesktop) {
|
||||||
// TODO: support web version
|
|
||||||
menuItems.add(_renameAction(peer.id));
|
menuItems.add(_renameAction(peer.id));
|
||||||
}
|
}
|
||||||
if (gFFI.abModel.current.isPersonal() && peer.hash.isNotEmpty) {
|
if (gFFI.abModel.current.isPersonal() && peer.hash.isNotEmpty) {
|
||||||
@ -1249,7 +1245,7 @@ void _rdpDialog(String id) async {
|
|||||||
).marginOnly(bottom: isDesktop ? 8 : 0),
|
).marginOnly(bottom: isDesktop ? 8 : 0),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
isDesktop
|
(isDesktop || isWebDesktop)
|
||||||
? ConstrainedBox(
|
? ConstrainedBox(
|
||||||
constraints: const BoxConstraints(minWidth: 140),
|
constraints: const BoxConstraints(minWidth: 140),
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -1260,15 +1256,17 @@ void _rdpDialog(String id) async {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: isDesktop ? null : translate('Username')),
|
labelText: (isDesktop || isWebDesktop)
|
||||||
|
? null
|
||||||
|
: translate('Username')),
|
||||||
controller: userController,
|
controller: userController,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).marginOnly(bottom: isDesktop ? 8 : 0),
|
).marginOnly(bottom: (isDesktop || isWebDesktop) ? 8 : 0),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
isDesktop
|
(isDesktop || isWebDesktop)
|
||||||
? ConstrainedBox(
|
? ConstrainedBox(
|
||||||
constraints: const BoxConstraints(minWidth: 140),
|
constraints: const BoxConstraints(minWidth: 140),
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -1280,7 +1278,9 @@ void _rdpDialog(String id) async {
|
|||||||
child: Obx(() => TextField(
|
child: Obx(() => TextField(
|
||||||
obscureText: secure.value,
|
obscureText: secure.value,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: isDesktop ? null : translate('Password'),
|
labelText: (isDesktop || isWebDesktop)
|
||||||
|
? null
|
||||||
|
: translate('Password'),
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
onPressed: () => secure.value = !secure.value,
|
onPressed: () => secure.value = !secure.value,
|
||||||
icon: Icon(secure.value
|
icon: Icon(secure.value
|
||||||
|
@ -37,7 +37,7 @@ class _TabEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EdgeInsets? _menuPadding() {
|
EdgeInsets? _menuPadding() {
|
||||||
return isDesktop ? kDesktopMenuPadding : null;
|
return (isDesktop || isWebDesktop) ? kDesktopMenuPadding : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PeerTabPageState extends State<PeerTabPage>
|
class _PeerTabPageState extends State<PeerTabPage>
|
||||||
@ -113,7 +113,9 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
height: 32,
|
height: 32,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: isDesktop ? null : EdgeInsets.symmetric(horizontal: 2),
|
padding: (isDesktop || isWebDesktop)
|
||||||
|
? null
|
||||||
|
: EdgeInsets.symmetric(horizontal: 2),
|
||||||
child: selectionWrap(Row(
|
child: selectionWrap(Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@ -127,7 +129,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
).paddingOnly(right: isDesktop ? 12 : 0),
|
).paddingOnly(right: (isDesktop || isWebDesktop) ? 12 : 0),
|
||||||
_createPeersView(),
|
_createPeersView(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -195,7 +197,8 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: child.marginSymmetric(vertical: isDesktop ? 12.0 : 6.0));
|
child: child.marginSymmetric(
|
||||||
|
vertical: (isDesktop || isWebDesktop) ? 12.0 : 6.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _createRefresh(
|
Widget _createRefresh(
|
||||||
|
@ -78,7 +78,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
|||||||
LoadEvent.lan: 'empty_lan_tip',
|
LoadEvent.lan: 'empty_lan_tip',
|
||||||
LoadEvent.addressBook: 'empty_address_book_tip',
|
LoadEvent.addressBook: 'empty_address_book_tip',
|
||||||
});
|
});
|
||||||
final space = isDesktop ? 12.0 : 8.0;
|
final space = (isDesktop || isWebDesktop) ? 12.0 : 8.0;
|
||||||
final _curPeers = <String>{};
|
final _curPeers = <String>{};
|
||||||
var _lastChangeTime = DateTime.now();
|
var _lastChangeTime = DateTime.now();
|
||||||
var _lastQueryPeers = <String>{};
|
var _lastQueryPeers = <String>{};
|
||||||
@ -200,7 +200,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
|||||||
Provider.of<PeerTabModel>(context, listen: false).currentTab;
|
Provider.of<PeerTabModel>(context, listen: false).currentTab;
|
||||||
final hideAbTagsPanel =
|
final hideAbTagsPanel =
|
||||||
bind.mainGetLocalOption(key: "hideAbTagsPanel").isNotEmpty;
|
bind.mainGetLocalOption(key: "hideAbTagsPanel").isNotEmpty;
|
||||||
return isDesktop
|
return (isDesktop || isWebDesktop)
|
||||||
? Obx(
|
? Obx(
|
||||||
() => SizedBox(
|
() => SizedBox(
|
||||||
width: peerCardUiType.value != PeerUiType.list
|
width: peerCardUiType.value != PeerUiType.list
|
||||||
|
@ -77,7 +77,7 @@ class _RawTouchGestureDetectorRegionState
|
|||||||
FFI get ffi => widget.ffi;
|
FFI get ffi => widget.ffi;
|
||||||
FfiModel get ffiModel => widget.ffiModel;
|
FfiModel get ffiModel => widget.ffiModel;
|
||||||
InputModel get inputModel => widget.inputModel;
|
InputModel get inputModel => widget.inputModel;
|
||||||
bool get handleTouch => isDesktop || ffiModel.touchMode;
|
bool get handleTouch => (isDesktop || isWebDesktop) || ffiModel.touchMode;
|
||||||
SessionID get sessionId => ffi.sessionId;
|
SessionID get sessionId => ffi.sessionId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -183,7 +183,7 @@ class _RawTouchGestureDetectorRegionState
|
|||||||
if (lastDeviceKind != PointerDeviceKind.touch) {
|
if (lastDeviceKind != PointerDeviceKind.touch) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isDesktop || !ffiModel.touchMode) {
|
if ((isDesktop || isWebDesktop) || !ffiModel.touchMode) {
|
||||||
inputModel.tap(MouseButtons.right);
|
inputModel.tap(MouseButtons.right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,7 +262,7 @@ class _RawTouchGestureDetectorRegionState
|
|||||||
if (lastDeviceKind != PointerDeviceKind.touch) {
|
if (lastDeviceKind != PointerDeviceKind.touch) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isDesktop) {
|
if ((isDesktop || isWebDesktop)) {
|
||||||
final scale = ((d.scale - _scale) * 1000).toInt();
|
final scale = ((d.scale - _scale) * 1000).toInt();
|
||||||
_scale = d.scale;
|
_scale = d.scale;
|
||||||
|
|
||||||
@ -286,7 +286,7 @@ class _RawTouchGestureDetectorRegionState
|
|||||||
if (lastDeviceKind != PointerDeviceKind.touch) {
|
if (lastDeviceKind != PointerDeviceKind.touch) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isDesktop) {
|
if ((isDesktop || isWebDesktop)) {
|
||||||
bind.sessionSendPointer(
|
bind.sessionSendPointer(
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
msg: json.encode(
|
msg: json.encode(
|
||||||
@ -409,7 +409,9 @@ class RawPointerMouseRegion extends StatelessWidget {
|
|||||||
onPointerPanZoomUpdate: inputModel.onPointerPanZoomUpdate,
|
onPointerPanZoomUpdate: inputModel.onPointerPanZoomUpdate,
|
||||||
onPointerPanZoomEnd: inputModel.onPointerPanZoomEnd,
|
onPointerPanZoomEnd: inputModel.onPointerPanZoomEnd,
|
||||||
child: MouseRegion(
|
child: MouseRegion(
|
||||||
cursor: cursor ?? MouseCursor.defer,
|
cursor: inputModel.isViewOnly
|
||||||
|
? MouseCursor.defer
|
||||||
|
: (cursor ?? MouseCursor.defer),
|
||||||
onEnter: onEnter,
|
onEnter: onEnter,
|
||||||
onExit: onExit,
|
onExit: onExit,
|
||||||
child: child,
|
child: child,
|
||||||
|
@ -209,10 +209,10 @@ List<Widget> ServerConfigImportExportWidgets(
|
|||||||
List<(String, String)> otherDefaultSettings() {
|
List<(String, String)> otherDefaultSettings() {
|
||||||
List<(String, String)> v = [
|
List<(String, String)> v = [
|
||||||
('View Mode', 'view_only'),
|
('View Mode', 'view_only'),
|
||||||
if (isDesktop) ('show_monitors_tip', kKeyShowMonitorsToolbar),
|
if ((isDesktop || isWebDesktop)) ('show_monitors_tip', kKeyShowMonitorsToolbar),
|
||||||
if (isDesktop) ('Collapse toolbar', 'collapse_toolbar'),
|
if ((isDesktop || isWebDesktop)) ('Collapse toolbar', 'collapse_toolbar'),
|
||||||
('Show remote cursor', 'show_remote_cursor'),
|
('Show remote cursor', 'show_remote_cursor'),
|
||||||
if (isDesktop) ('Zoom cursor', 'zoom-cursor'),
|
if ((isDesktop || isWebDesktop)) ('Zoom cursor', 'zoom-cursor'),
|
||||||
('Show quality monitor', 'show_quality_monitor'),
|
('Show quality monitor', 'show_quality_monitor'),
|
||||||
('Mute', 'disable_audio'),
|
('Mute', 'disable_audio'),
|
||||||
if (isDesktop) ('Enable file copy and paste', 'enable_file_transfer'),
|
if (isDesktop) ('Enable file copy and paste', 'enable_file_transfer'),
|
||||||
|
@ -94,7 +94,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
Text(translate(pi.isHeadless ? 'OS Account' : 'OS Password')),
|
Text(translate(pi.isHeadless ? 'OS Account' : 'OS Password')),
|
||||||
]),
|
]),
|
||||||
trailingIcon: Transform.scale(
|
trailingIcon: Transform.scale(
|
||||||
scale: isDesktop ? 0.8 : 1,
|
scale: (isDesktop || isWebDesktop) ? 0.8 : 1,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (isMobile && Navigator.canPop(context)) {
|
if (isMobile && Navigator.canPop(context)) {
|
||||||
@ -160,7 +160,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// divider
|
// divider
|
||||||
if (isDesktop) {
|
if (isDesktop || isWebDesktop) {
|
||||||
v.add(TTextMenu(child: Offstage(), onPressed: () {}, divider: true));
|
v.add(TTextMenu(child: Offstage(), onPressed: () {}, divider: true));
|
||||||
}
|
}
|
||||||
// ctrlAltDel
|
// ctrlAltDel
|
||||||
@ -229,7 +229,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
// record
|
// record
|
||||||
if (!isDesktop &&
|
if (!(isDesktop || isWeb) &&
|
||||||
(ffi.recordingModel.start || (perms["recording"] != false))) {
|
(ffi.recordingModel.start || (perms["recording"] != false))) {
|
||||||
v.add(TTextMenu(
|
v.add(TTextMenu(
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -250,7 +250,7 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
onPressed: () => ffi.recordingModel.toggle()));
|
onPressed: () => ffi.recordingModel.toggle()));
|
||||||
}
|
}
|
||||||
// fingerprint
|
// fingerprint
|
||||||
if (!isDesktop) {
|
if (!(isDesktop || isWebDesktop)) {
|
||||||
v.add(TTextMenu(
|
v.add(TTextMenu(
|
||||||
child: Text(translate('Copy Fingerprint')),
|
child: Text(translate('Copy Fingerprint')),
|
||||||
onPressed: () => onCopyFingerprint(FingerprintState.find(id).value),
|
onPressed: () => onCopyFingerprint(FingerprintState.find(id).value),
|
||||||
@ -511,8 +511,8 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
|
|||||||
child: Text(translate('Show displays as individual windows'))));
|
child: Text(translate('Show displays as individual windows'))));
|
||||||
}
|
}
|
||||||
|
|
||||||
final screenList = await getScreenRectList();
|
final isMultiScreens = !isWeb && (await getScreenRectList()).length > 1;
|
||||||
if (useTextureRender && pi.isSupportMultiDisplay && screenList.length > 1) {
|
if (useTextureRender && pi.isSupportMultiDisplay && isMultiScreens) {
|
||||||
final value = bind.sessionGetUseAllMyDisplaysForTheRemoteSession(
|
final value = bind.sessionGetUseAllMyDisplaysForTheRemoteSession(
|
||||||
sessionId: ffi.sessionId) ==
|
sessionId: ffi.sessionId) ==
|
||||||
'Y';
|
'Y';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
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/models/state_model.dart';
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
|
@ -114,7 +114,7 @@ class _DesktopSettingPageState extends State<DesktopSettingPage>
|
|||||||
if (!bind.isIncomingOnly())
|
if (!bind.isIncomingOnly())
|
||||||
_TabInfo(
|
_TabInfo(
|
||||||
'Display', Icons.desktop_windows_outlined, Icons.desktop_windows),
|
'Display', Icons.desktop_windows_outlined, Icons.desktop_windows),
|
||||||
if (!bind.isIncomingOnly() && bind.pluginFeatureIsEnabled())
|
if (!isWeb && !bind.isIncomingOnly() && bind.pluginFeatureIsEnabled())
|
||||||
_TabInfo('Plugin', Icons.extension_outlined, Icons.extension),
|
_TabInfo('Plugin', Icons.extension_outlined, Icons.extension),
|
||||||
if (!bind.isDisableAccount())
|
if (!bind.isDisableAccount())
|
||||||
_TabInfo('Account', Icons.person_outline, Icons.person),
|
_TabInfo('Account', Icons.person_outline, Icons.person),
|
||||||
@ -129,7 +129,8 @@ class _DesktopSettingPageState extends State<DesktopSettingPage>
|
|||||||
if (!bind.isOutgoingOnly() && !bind.isDisableSettings()) _Safety(),
|
if (!bind.isOutgoingOnly() && !bind.isDisableSettings()) _Safety(),
|
||||||
if (!bind.isDisableSettings()) _Network(),
|
if (!bind.isDisableSettings()) _Network(),
|
||||||
if (!bind.isIncomingOnly()) _Display(),
|
if (!bind.isIncomingOnly()) _Display(),
|
||||||
if (!bind.isIncomingOnly() && bind.pluginFeatureIsEnabled()) _Plugin(),
|
if (!isWeb && !bind.isIncomingOnly() && bind.pluginFeatureIsEnabled())
|
||||||
|
_Plugin(),
|
||||||
if (!bind.isDisableAccount()) _Account(),
|
if (!bind.isDisableAccount()) _Account(),
|
||||||
_About(),
|
_About(),
|
||||||
];
|
];
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
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';
|
||||||
|
@ -35,13 +35,13 @@ class RemotePage extends StatefulWidget {
|
|||||||
RemotePage({
|
RemotePage({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.sessionId,
|
|
||||||
required this.tabWindowId,
|
|
||||||
required this.display,
|
|
||||||
required this.displays,
|
|
||||||
required this.password,
|
|
||||||
required this.toolbarState,
|
required this.toolbarState,
|
||||||
required this.tabController,
|
this.sessionId,
|
||||||
|
this.tabWindowId,
|
||||||
|
this.password,
|
||||||
|
this.display,
|
||||||
|
this.displays,
|
||||||
|
this.tabController,
|
||||||
this.switchUuid,
|
this.switchUuid,
|
||||||
this.forceRelay,
|
this.forceRelay,
|
||||||
this.isSharedPassword,
|
this.isSharedPassword,
|
||||||
@ -58,7 +58,7 @@ class RemotePage extends StatefulWidget {
|
|||||||
final bool? forceRelay;
|
final bool? forceRelay;
|
||||||
final bool? isSharedPassword;
|
final bool? isSharedPassword;
|
||||||
final SimpleWrapper<State<RemotePage>?> _lastState = SimpleWrapper(null);
|
final SimpleWrapper<State<RemotePage>?> _lastState = SimpleWrapper(null);
|
||||||
final DesktopTabController tabController;
|
final DesktopTabController? tabController;
|
||||||
|
|
||||||
FFI get ffi => (_lastState.value! as _RemotePageState)._ffi;
|
FFI get ffi => (_lastState.value! as _RemotePageState)._ffi;
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ffi.ffiModel.updateEventListener(sessionId, widget.id);
|
_ffi.ffiModel.updateEventListener(sessionId, widget.id);
|
||||||
bind.pluginSyncUi(syncTo: kAppTypeDesktopRemote);
|
if (!isWeb) bind.pluginSyncUi(syncTo: kAppTypeDesktopRemote);
|
||||||
_ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId);
|
_ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId);
|
||||||
// Session option should be set after models.dart/FFI.start
|
// Session option should be set after models.dart/FFI.start
|
||||||
_showRemoteCursor.value = bind.sessionGetToggleOptionSync(
|
_showRemoteCursor.value = bind.sessionGetToggleOptionSync(
|
||||||
@ -150,7 +150,7 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
_blockableOverlayState.applyFfi(_ffi);
|
_blockableOverlayState.applyFfi(_ffi);
|
||||||
widget.tabController.onSelected?.call(widget.id);
|
widget.tabController?.onSelected?.call(widget.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -431,9 +431,9 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
Widget getBodyForDesktop(BuildContext context) {
|
Widget getBodyForDesktop(BuildContext context) {
|
||||||
var paints = <Widget>[
|
var paints = <Widget>[
|
||||||
MouseRegion(onEnter: (evt) {
|
MouseRegion(onEnter: (evt) {
|
||||||
bind.hostStopSystemKeyPropagate(stopped: false);
|
if (!isWeb) bind.hostStopSystemKeyPropagate(stopped: false);
|
||||||
}, onExit: (evt) {
|
}, onExit: (evt) {
|
||||||
bind.hostStopSystemKeyPropagate(stopped: true);
|
if (!isWeb) bind.hostStopSystemKeyPropagate(stopped: true);
|
||||||
}, child: LayoutBuilder(builder: (context, constraints) {
|
}, child: LayoutBuilder(builder: (context, constraints) {
|
||||||
Future.delayed(Duration.zero, () {
|
Future.delayed(Duration.zero, () {
|
||||||
Provider.of<CanvasModel>(context, listen: false).updateViewStyle();
|
Provider.of<CanvasModel>(context, listen: false).updateViewStyle();
|
||||||
@ -669,6 +669,11 @@ class _ImagePaintState extends State<ImagePaint> {
|
|||||||
|
|
||||||
MouseCursor _buildCursorOfCache(
|
MouseCursor _buildCursorOfCache(
|
||||||
CursorModel cursor, double scale, CursorData? cache) {
|
CursorModel cursor, double scale, CursorData? cache) {
|
||||||
|
// TODO: web cursor
|
||||||
|
if (isWeb) {
|
||||||
|
return MouseCursor.defer;
|
||||||
|
}
|
||||||
|
|
||||||
if (cache == null) {
|
if (cache == null) {
|
||||||
return MouseCursor.defer;
|
return MouseCursor.defer;
|
||||||
} else {
|
} else {
|
||||||
|
@ -491,7 +491,7 @@ class _RemoteToolbarState extends State<RemoteToolbar> {
|
|||||||
toolbarItems.add(_ChatMenu(id: widget.id, ffi: widget.ffi));
|
toolbarItems.add(_ChatMenu(id: widget.id, ffi: widget.ffi));
|
||||||
toolbarItems.add(_VoiceCallMenu(id: widget.id, ffi: widget.ffi));
|
toolbarItems.add(_VoiceCallMenu(id: widget.id, ffi: widget.ffi));
|
||||||
}
|
}
|
||||||
toolbarItems.add(_RecordMenu());
|
if (!isWeb) toolbarItems.add(_RecordMenu());
|
||||||
toolbarItems.add(_CloseMenu(id: widget.id, ffi: widget.ffi));
|
toolbarItems.add(_CloseMenu(id: widget.id, ffi: widget.ffi));
|
||||||
final toolbarBorderRadius = BorderRadius.all(Radius.circular(4.0));
|
final toolbarBorderRadius = BorderRadius.all(Radius.circular(4.0));
|
||||||
return Column(
|
return Column(
|
||||||
@ -940,13 +940,12 @@ class ScreenAdjustor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateScreen() async {
|
updateScreen() async {
|
||||||
final v = await rustDeskWinManager.call(
|
final String info =
|
||||||
WindowType.Main, kWindowGetWindowInfo, '');
|
isWeb ? screenInfo : await _getScreenInfoDesktop() ?? '';
|
||||||
final String valueStr = v.result;
|
if (info.isEmpty) {
|
||||||
if (valueStr.isEmpty) {
|
|
||||||
_screen = null;
|
_screen = null;
|
||||||
} else {
|
} else {
|
||||||
final screenMap = jsonDecode(valueStr);
|
final screenMap = jsonDecode(info);
|
||||||
_screen = window_size.Screen(
|
_screen = window_size.Screen(
|
||||||
Rect.fromLTRB(screenMap['frame']['l'], screenMap['frame']['t'],
|
Rect.fromLTRB(screenMap['frame']['l'], screenMap['frame']['t'],
|
||||||
screenMap['frame']['r'], screenMap['frame']['b']),
|
screenMap['frame']['r'], screenMap['frame']['b']),
|
||||||
@ -959,15 +958,23 @@ class ScreenAdjustor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getScreenInfoDesktop() async {
|
||||||
|
final v = await rustDeskWinManager.call(
|
||||||
|
WindowType.Main, kWindowGetWindowInfo, '');
|
||||||
|
return v.result;
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> isWindowCanBeAdjusted() async {
|
Future<bool> isWindowCanBeAdjusted() async {
|
||||||
final viewStyle =
|
final viewStyle =
|
||||||
await bind.sessionGetViewStyle(sessionId: ffi.sessionId) ?? '';
|
await bind.sessionGetViewStyle(sessionId: ffi.sessionId) ?? '';
|
||||||
if (viewStyle != kRemoteViewStyleOriginal) {
|
if (viewStyle != kRemoteViewStyleOriginal) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final remoteCount = RemoteCountState.find().value;
|
if (!isWeb) {
|
||||||
if (remoteCount != 1) {
|
final remoteCount = RemoteCountState.find().value;
|
||||||
return false;
|
if (remoteCount != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (_screen == null) {
|
if (_screen == null) {
|
||||||
return false;
|
return false;
|
||||||
@ -1325,6 +1332,14 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> {
|
|||||||
final display = json.decode(mainDisplay);
|
final display = json.decode(mainDisplay);
|
||||||
if (display['w'] != null && display['h'] != null) {
|
if (display['w'] != null && display['h'] != null) {
|
||||||
_localResolution = Resolution(display['w'], display['h']);
|
_localResolution = Resolution(display['w'], display['h']);
|
||||||
|
if (isWeb) {
|
||||||
|
if (display['scaleFactor'] != null) {
|
||||||
|
_localResolution = Resolution(
|
||||||
|
(display['w'] / display['scaleFactor']).toInt(),
|
||||||
|
(display['h'] / display['scaleFactor']).toInt(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Failed to decode $mainDisplay, $e');
|
debugPrint('Failed to decode $mainDisplay, $e');
|
||||||
|
@ -125,10 +125,7 @@ void runMainApp(bool startService) async {
|
|||||||
await Future.wait([gFFI.abModel.loadCache(), gFFI.groupModel.loadCache()]);
|
await Future.wait([gFFI.abModel.loadCache(), gFFI.groupModel.loadCache()]);
|
||||||
gFFI.userModel.refreshCurrentUser();
|
gFFI.userModel.refreshCurrentUser();
|
||||||
runApp(App());
|
runApp(App());
|
||||||
if (isWeb) {
|
|
||||||
// Web does not support window manager.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Set window option.
|
// Set window option.
|
||||||
WindowOptions windowOptions = getHiddenTitleBarWindowOptions();
|
WindowOptions windowOptions = getHiddenTitleBarWindowOptions();
|
||||||
windowManager.waitUntilReadyToShow(windowOptions, () async {
|
windowManager.waitUntilReadyToShow(windowOptions, () async {
|
||||||
|
@ -3,7 +3,6 @@ import 'dart:async';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
|
import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
|
||||||
import 'package:flutter_hbb/models/file_model.dart';
|
import 'package:flutter_hbb/models/file_model.dart';
|
||||||
import 'package:flutter_hbb/models/platform_model.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:toggle_switch/toggle_switch.dart';
|
import 'package:toggle_switch/toggle_switch.dart';
|
||||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||||
|
@ -193,6 +193,7 @@ class InputModel {
|
|||||||
bool get keyboardPerm => parent.target!.ffiModel.keyboard;
|
bool get keyboardPerm => parent.target!.ffiModel.keyboard;
|
||||||
String get id => parent.target?.id ?? '';
|
String get id => parent.target?.id ?? '';
|
||||||
String? get peerPlatform => parent.target?.ffiModel.pi.platform;
|
String? get peerPlatform => parent.target?.ffiModel.pi.platform;
|
||||||
|
bool get isViewOnly => parent.target!.ffiModel.viewOnly;
|
||||||
|
|
||||||
InputModel(this.parent) {
|
InputModel(this.parent) {
|
||||||
sessionId = parent.target!.sessionId;
|
sessionId = parent.target!.sessionId;
|
||||||
@ -207,7 +208,7 @@ class InputModel {
|
|||||||
|
|
||||||
updateKeyboardMode() async {
|
updateKeyboardMode() async {
|
||||||
// * Currently mobile does not enable map mode
|
// * Currently mobile does not enable map mode
|
||||||
if (isDesktop) {
|
if (isDesktop || isWebDesktop) {
|
||||||
if (keyboardMode.isEmpty) {
|
if (keyboardMode.isEmpty) {
|
||||||
keyboardMode =
|
keyboardMode =
|
||||||
await bind.sessionGetKeyboardMode(sessionId: sessionId) ??
|
await bind.sessionGetKeyboardMode(sessionId: sessionId) ??
|
||||||
@ -217,7 +218,8 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KeyEventResult handleRawKeyEvent(RawKeyEvent e) {
|
KeyEventResult handleRawKeyEvent(RawKeyEvent e) {
|
||||||
if (isDesktop && !isInputSourceFlutter) {
|
if (isViewOnly) return KeyEventResult.handled;
|
||||||
|
if ((isDesktop || isWebDesktop) && !isInputSourceFlutter) {
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,7 +258,7 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// * Currently mobile does not enable map mode
|
// * Currently mobile does not enable map mode
|
||||||
if (isDesktop && keyboardMode == 'map') {
|
if ((isDesktop || isWebDesktop) && keyboardMode == 'map') {
|
||||||
mapKeyboardMode(e);
|
mapKeyboardMode(e);
|
||||||
} else {
|
} else {
|
||||||
legacyKeyboardMode(e);
|
legacyKeyboardMode(e);
|
||||||
@ -467,6 +469,7 @@ class InputModel {
|
|||||||
|
|
||||||
void onPointHoverImage(PointerHoverEvent e) {
|
void onPointHoverImage(PointerHoverEvent e) {
|
||||||
_stopFling = true;
|
_stopFling = true;
|
||||||
|
if (isViewOnly) return;
|
||||||
if (e.kind != ui.PointerDeviceKind.mouse) return;
|
if (e.kind != ui.PointerDeviceKind.mouse) return;
|
||||||
if (!isPhysicalMouse.value) {
|
if (!isPhysicalMouse.value) {
|
||||||
isPhysicalMouse.value = true;
|
isPhysicalMouse.value = true;
|
||||||
@ -479,7 +482,7 @@ class InputModel {
|
|||||||
void onPointerPanZoomStart(PointerPanZoomStartEvent e) {
|
void onPointerPanZoomStart(PointerPanZoomStartEvent e) {
|
||||||
_lastScale = 1.0;
|
_lastScale = 1.0;
|
||||||
_stopFling = true;
|
_stopFling = true;
|
||||||
|
if (isViewOnly) return;
|
||||||
if (peerPlatform == kPeerPlatformAndroid) {
|
if (peerPlatform == kPeerPlatformAndroid) {
|
||||||
handlePointerEvent('touch', 'pan_start', e.position);
|
handlePointerEvent('touch', 'pan_start', e.position);
|
||||||
}
|
}
|
||||||
@ -487,6 +490,7 @@ class InputModel {
|
|||||||
|
|
||||||
// https://docs.flutter.dev/release/breaking-changes/trackpad-gestures
|
// https://docs.flutter.dev/release/breaking-changes/trackpad-gestures
|
||||||
void onPointerPanZoomUpdate(PointerPanZoomUpdateEvent e) {
|
void onPointerPanZoomUpdate(PointerPanZoomUpdateEvent e) {
|
||||||
|
if (isViewOnly) return;
|
||||||
if (peerPlatform != kPeerPlatformAndroid) {
|
if (peerPlatform != kPeerPlatformAndroid) {
|
||||||
final scale = ((e.scale - _lastScale) * 1000).toInt();
|
final scale = ((e.scale - _lastScale) * 1000).toInt();
|
||||||
_lastScale = e.scale;
|
_lastScale = e.scale;
|
||||||
@ -612,6 +616,7 @@ class InputModel {
|
|||||||
void onPointDownImage(PointerDownEvent e) {
|
void onPointDownImage(PointerDownEvent e) {
|
||||||
debugPrint("onPointDownImage ${e.kind}");
|
debugPrint("onPointDownImage ${e.kind}");
|
||||||
_stopFling = true;
|
_stopFling = true;
|
||||||
|
if (isViewOnly) return;
|
||||||
if (e.kind != ui.PointerDeviceKind.mouse) {
|
if (e.kind != ui.PointerDeviceKind.mouse) {
|
||||||
if (isPhysicalMouse.value) {
|
if (isPhysicalMouse.value) {
|
||||||
isPhysicalMouse.value = false;
|
isPhysicalMouse.value = false;
|
||||||
@ -623,6 +628,7 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onPointUpImage(PointerUpEvent e) {
|
void onPointUpImage(PointerUpEvent e) {
|
||||||
|
if (isViewOnly) return;
|
||||||
if (e.kind != ui.PointerDeviceKind.mouse) return;
|
if (e.kind != ui.PointerDeviceKind.mouse) return;
|
||||||
if (isPhysicalMouse.value) {
|
if (isPhysicalMouse.value) {
|
||||||
handleMouse(_getMouseEvent(e, _kMouseEventUp), e.position);
|
handleMouse(_getMouseEvent(e, _kMouseEventUp), e.position);
|
||||||
@ -630,6 +636,7 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onPointMoveImage(PointerMoveEvent e) {
|
void onPointMoveImage(PointerMoveEvent e) {
|
||||||
|
if (isViewOnly) return;
|
||||||
if (e.kind != ui.PointerDeviceKind.mouse) return;
|
if (e.kind != ui.PointerDeviceKind.mouse) return;
|
||||||
if (isPhysicalMouse.value) {
|
if (isPhysicalMouse.value) {
|
||||||
handleMouse(_getMouseEvent(e, _kMouseEventMove), e.position);
|
handleMouse(_getMouseEvent(e, _kMouseEventMove), e.position);
|
||||||
@ -637,6 +644,7 @@ class InputModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onPointerSignalImage(PointerSignalEvent e) {
|
void onPointerSignalImage(PointerSignalEvent e) {
|
||||||
|
if (isViewOnly) return;
|
||||||
if (e is PointerScrollEvent) {
|
if (e is PointerScrollEvent) {
|
||||||
var dx = e.scrollDelta.dx.toInt();
|
var dx = e.scrollDelta.dx.toInt();
|
||||||
var dy = e.scrollDelta.dy.toInt();
|
var dy = e.scrollDelta.dy.toInt();
|
||||||
@ -902,9 +910,11 @@ class InputModel {
|
|||||||
int minX = rect.left.toInt();
|
int minX = rect.left.toInt();
|
||||||
// https://github.com/rustdesk/rustdesk/issues/6678
|
// https://github.com/rustdesk/rustdesk/issues/6678
|
||||||
// For Windows, [0,maxX], [0,maxY] should be set to enable window snapping.
|
// For Windows, [0,maxX], [0,maxY] should be set to enable window snapping.
|
||||||
int maxX = (rect.left + rect.width).toInt() - (peerPlatform == kPeerPlatformWindows ? 0 : 1);
|
int maxX = (rect.left + rect.width).toInt() -
|
||||||
|
(peerPlatform == kPeerPlatformWindows ? 0 : 1);
|
||||||
int minY = rect.top.toInt();
|
int minY = rect.top.toInt();
|
||||||
int maxY = (rect.top + rect.height).toInt() - (peerPlatform == kPeerPlatformWindows ? 0 : 1);
|
int maxY = (rect.top + rect.height).toInt() -
|
||||||
|
(peerPlatform == kPeerPlatformWindows ? 0 : 1);
|
||||||
evtX = trySetNearestRange(evtX, minX, maxX, 5);
|
evtX = trySetNearestRange(evtX, minX, maxX, 5);
|
||||||
evtY = trySetNearestRange(evtY, minY, maxY, 5);
|
evtY = trySetNearestRange(evtY, minY, maxY, 5);
|
||||||
if (kind == kPointerEventKindMouse) {
|
if (kind == kPointerEventKindMouse) {
|
||||||
|
@ -429,7 +429,7 @@ class FfiModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleAliasChanged(Map<String, dynamic> evt) {
|
handleAliasChanged(Map<String, dynamic> evt) {
|
||||||
if (!isDesktop) return;
|
if (!(isDesktop || isWebDesktop)) return;
|
||||||
final String peerId = evt['id'];
|
final String peerId = evt['id'];
|
||||||
final String alias = evt['alias'];
|
final String alias = evt['alias'];
|
||||||
String label = getDesktopTabLabel(peerId, alias);
|
String label = getDesktopTabLabel(peerId, alias);
|
||||||
@ -767,7 +767,7 @@ class FfiModel with ChangeNotifier {
|
|||||||
_pi.isSet.value = true;
|
_pi.isSet.value = true;
|
||||||
stateGlobal.resetLastResolutionGroupValues(peerId);
|
stateGlobal.resetLastResolutionGroupValues(peerId);
|
||||||
|
|
||||||
if (isDesktop) {
|
if (isDesktop || isWebDesktop) {
|
||||||
checkDesktopKeyboardMode();
|
checkDesktopKeyboardMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1114,7 +1114,7 @@ class ImageModel with ChangeNotifier {
|
|||||||
|
|
||||||
update(ui.Image? image) async {
|
update(ui.Image? image) async {
|
||||||
if (_image == null && image != null) {
|
if (_image == null && image != null) {
|
||||||
if (isWebDesktop || isDesktop) {
|
if (isDesktop || isWebDesktop) {
|
||||||
await parent.target?.canvasModel.updateViewStyle();
|
await parent.target?.canvasModel.updateViewStyle();
|
||||||
await parent.target?.canvasModel.updateScrollStyle();
|
await parent.target?.canvasModel.updateScrollStyle();
|
||||||
} else {
|
} else {
|
||||||
@ -1288,18 +1288,15 @@ class CanvasModel with ChangeNotifier {
|
|||||||
double get scrollX => _scrollX;
|
double get scrollX => _scrollX;
|
||||||
double get scrollY => _scrollY;
|
double get scrollY => _scrollY;
|
||||||
|
|
||||||
static double get leftToEdge => (isDesktop || isWebDesktop)
|
static double get leftToEdge =>
|
||||||
? windowBorderWidth + kDragToResizeAreaPadding.left
|
isDesktop ? windowBorderWidth + kDragToResizeAreaPadding.left : 0;
|
||||||
: 0;
|
static double get rightToEdge =>
|
||||||
static double get rightToEdge => (isDesktop || isWebDesktop)
|
isDesktop ? windowBorderWidth + kDragToResizeAreaPadding.right : 0;
|
||||||
? windowBorderWidth + kDragToResizeAreaPadding.right
|
static double get topToEdge => isDesktop
|
||||||
: 0;
|
|
||||||
static double get topToEdge => (isDesktop || isWebDesktop)
|
|
||||||
? tabBarHeight + windowBorderWidth + kDragToResizeAreaPadding.top
|
? tabBarHeight + windowBorderWidth + kDragToResizeAreaPadding.top
|
||||||
: 0;
|
: 0;
|
||||||
static double get bottomToEdge => (isDesktop || isWebDesktop)
|
static double get bottomToEdge =>
|
||||||
? windowBorderWidth + kDragToResizeAreaPadding.bottom
|
isDesktop ? windowBorderWidth + kDragToResizeAreaPadding.bottom : 0;
|
||||||
: 0;
|
|
||||||
|
|
||||||
updateViewStyle({refreshMousePos = true}) async {
|
updateViewStyle({refreshMousePos = true}) async {
|
||||||
Size getSize() {
|
Size getSize() {
|
||||||
@ -1422,7 +1419,7 @@ class CanvasModel with ChangeNotifier {
|
|||||||
// If keyboard is not permitted, do not move cursor when mouse is moving.
|
// If keyboard is not permitted, do not move cursor when mouse is moving.
|
||||||
if (parent.target != null && parent.target!.ffiModel.keyboard) {
|
if (parent.target != null && parent.target!.ffiModel.keyboard) {
|
||||||
// Draw cursor if is not desktop.
|
// Draw cursor if is not desktop.
|
||||||
if (!isDesktop) {
|
if (!(isDesktop || isWebDesktop)) {
|
||||||
parent.target!.cursorModel.moveLocal(x, y);
|
parent.target!.cursorModel.moveLocal(x, y);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
@ -2495,7 +2492,8 @@ class PeerInfo with ChangeNotifier {
|
|||||||
List<int> get virtualDisplays => List<int>.from(
|
List<int> get virtualDisplays => List<int>.from(
|
||||||
platformAdditions[kPlatformAdditionsVirtualDisplays] ?? []);
|
platformAdditions[kPlatformAdditionsVirtualDisplays] ?? []);
|
||||||
|
|
||||||
bool get isSupportMultiDisplay => isDesktop && isSupportMultiUiSession;
|
bool get isSupportMultiDisplay =>
|
||||||
|
(isDesktop || isWebDesktop) && isSupportMultiUiSession;
|
||||||
|
|
||||||
bool get cursorEmbedded => tryGetDisplay()?.cursorEmbedded ?? false;
|
bool get cursorEmbedded => tryGetDisplay()?.cursorEmbedded ?? false;
|
||||||
|
|
||||||
|
@ -98,9 +98,11 @@ class PlatformFFI {
|
|||||||
sessionId: sessionId, display: display, ptr: ptr);
|
sessionId: sessionId, display: display, ptr: ptr);
|
||||||
|
|
||||||
Future<void> init(String appType) async {
|
Future<void> init(String appType) async {
|
||||||
isWebDesktop = !context.callMethod('isMobile');
|
|
||||||
context.callMethod('init');
|
context.callMethod('init');
|
||||||
version = getByName('version');
|
version = getByName('version');
|
||||||
|
window.onContextMenu.listen((event) {
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
context['onRegisteredEvent'] = (String message) {
|
context['onRegisteredEvent'] = (String message) {
|
||||||
try {
|
try {
|
||||||
|
@ -6,5 +6,8 @@ final isWindows_ = Platform.isWindows;
|
|||||||
final isMacOS_ = Platform.isMacOS;
|
final isMacOS_ = Platform.isMacOS;
|
||||||
final isLinux_ = Platform.isLinux;
|
final isLinux_ = Platform.isLinux;
|
||||||
final isWeb_ = false;
|
final isWeb_ = false;
|
||||||
|
final isWebDesktop_ = false;
|
||||||
|
|
||||||
final isDesktop_ = Platform.isWindows || Platform.isMacOS || Platform.isLinux;
|
final isDesktop_ = Platform.isWindows || Platform.isMacOS || Platform.isLinux;
|
||||||
|
|
||||||
|
String get screenInfo_ => '';
|
||||||
|
@ -3,6 +3,8 @@ import 'dart:ui' as ui;
|
|||||||
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'package:flutter_hbb/common.dart';
|
||||||
|
|
||||||
Future<ui.Image> decodeImageFromPixels(
|
Future<ui.Image> decodeImageFromPixels(
|
||||||
Uint8List pixels,
|
Uint8List pixels,
|
||||||
int width,
|
int width,
|
||||||
@ -79,6 +81,11 @@ class ImagePainter extends CustomPainter {
|
|||||||
paint.filterQuality = FilterQuality.high;
|
paint.filterQuality = FilterQuality.high;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// It's strange that if (scale < 0.5 && paint.filterQuality == FilterQuality.medium)
|
||||||
|
// The canvas.drawImage will not work on web
|
||||||
|
if (isWeb) {
|
||||||
|
paint.filterQuality = FilterQuality.high;
|
||||||
|
}
|
||||||
canvas.drawImage(
|
canvas.drawImage(
|
||||||
image!, Offset(x.toInt().toDouble(), y.toInt().toDouble()), paint);
|
image!, Offset(x.toInt().toDouble(), y.toInt().toDouble()), paint);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ import 'dart:typed_data';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
import 'package:flutter_hbb/consts.dart';
|
||||||
|
|
||||||
final _privateConstructorUsedError = UnsupportedError(
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
@ -237,7 +239,6 @@ class RustdeskImpl {
|
|||||||
|
|
||||||
Future<String?> sessionGetViewStyle(
|
Future<String?> sessionGetViewStyle(
|
||||||
{required UuidValue sessionId, dynamic hint}) {
|
{required UuidValue sessionId, dynamic hint}) {
|
||||||
// TODO: default values
|
|
||||||
return Future(() =>
|
return Future(() =>
|
||||||
js.context.callMethod('getByName', ['option:session', 'view_style']));
|
js.context.callMethod('getByName', ['option:session', 'view_style']));
|
||||||
}
|
}
|
||||||
@ -252,7 +253,6 @@ class RustdeskImpl {
|
|||||||
|
|
||||||
Future<String?> sessionGetScrollStyle(
|
Future<String?> sessionGetScrollStyle(
|
||||||
{required UuidValue sessionId, dynamic hint}) {
|
{required UuidValue sessionId, dynamic hint}) {
|
||||||
// TODO: default values
|
|
||||||
return Future(() =>
|
return Future(() =>
|
||||||
js.context.callMethod('getByName', ['option:session', 'scroll_style']));
|
js.context.callMethod('getByName', ['option:session', 'scroll_style']));
|
||||||
}
|
}
|
||||||
@ -266,9 +266,7 @@ class RustdeskImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> sessionGetImageQuality(
|
Future<String?> sessionGetImageQuality(
|
||||||
// TODO: default values
|
{required UuidValue sessionId, dynamic hint}) {
|
||||||
{required UuidValue sessionId,
|
|
||||||
dynamic hint}) {
|
|
||||||
return Future(() => js.context
|
return Future(() => js.context
|
||||||
.callMethod('getByName', ['option:session', 'image_quality']));
|
.callMethod('getByName', ['option:session', 'image_quality']));
|
||||||
}
|
}
|
||||||
@ -283,9 +281,9 @@ class RustdeskImpl {
|
|||||||
|
|
||||||
Future<String?> sessionGetKeyboardMode(
|
Future<String?> sessionGetKeyboardMode(
|
||||||
{required UuidValue sessionId, dynamic hint}) {
|
{required UuidValue sessionId, dynamic hint}) {
|
||||||
// TODO: default values
|
final mode =
|
||||||
return Future(() => js.context
|
js.context.callMethod('getByName', ['option:session', 'keyboard_mode']);
|
||||||
.callMethod('getByName', ['option:session', 'keyboard_mode']));
|
return Future(() => mode == '' ? null : mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sessionSetKeyboardMode(
|
Future<void> sessionSetKeyboardMode(
|
||||||
@ -345,7 +343,7 @@ class RustdeskImpl {
|
|||||||
|
|
||||||
bool sessionIsKeyboardModeSupported(
|
bool sessionIsKeyboardModeSupported(
|
||||||
{required UuidValue sessionId, required String mode, dynamic hint}) {
|
{required UuidValue sessionId, required String mode, dynamic hint}) {
|
||||||
throw UnimplementedError();
|
return mode == kKeyLegacyMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sessionSetCustomImageQuality(
|
Future<void> sessionSetCustomImageQuality(
|
||||||
@ -748,8 +746,7 @@ class RustdeskImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> mainCheckConnectStatus({dynamic hint}) {
|
Future<void> mainCheckConnectStatus({dynamic hint}) {
|
||||||
return Future(
|
throw UnimplementedError();
|
||||||
() => js.context.callMethod('setByName', ["check_conn_status"]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> mainIsUsingPublicServer({dynamic hint}) {
|
Future<bool> mainIsUsingPublicServer({dynamic hint}) {
|
||||||
@ -929,12 +926,14 @@ class RustdeskImpl {
|
|||||||
|
|
||||||
Future<void> mainSetUserDefaultOption(
|
Future<void> mainSetUserDefaultOption(
|
||||||
{required String key, required String value, dynamic hint}) {
|
{required String key, required String value, dynamic hint}) {
|
||||||
// TODO: do we need the default option?
|
return js.context.callMethod('getByName', [
|
||||||
throw UnimplementedError();
|
'option:user:default',
|
||||||
|
jsonEncode({'name': key, 'value': value})
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
String mainGetUserDefaultOption({required String key, dynamic hint}) {
|
String mainGetUserDefaultOption({required String key, dynamic hint}) {
|
||||||
throw UnimplementedError();
|
return js.context.callMethod('getByName', ['option:user:default', key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> mainHandleRelayId({required String id, dynamic hint}) {
|
Future<String> mainHandleRelayId({required String id, dynamic hint}) {
|
||||||
@ -946,7 +945,7 @@ class RustdeskImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String mainGetMainDisplay({dynamic hint}) {
|
String mainGetMainDisplay({dynamic hint}) {
|
||||||
throw UnimplementedError();
|
return js.context.callMethod('getByName', ['main_display']);
|
||||||
}
|
}
|
||||||
|
|
||||||
String mainGetDisplays({dynamic hint}) {
|
String mainGetDisplays({dynamic hint}) {
|
||||||
@ -1399,7 +1398,7 @@ class RustdeskImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool mainHasPixelbufferTextureRender({dynamic hint}) {
|
bool mainHasPixelbufferTextureRender({dynamic hint}) {
|
||||||
throw UnimplementedError();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mainHasFileClipboard({dynamic hint}) {
|
bool mainHasFileClipboard({dynamic hint}) {
|
||||||
@ -1553,7 +1552,9 @@ class RustdeskImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String mainSupportedInputSource({dynamic hint}) {
|
String mainSupportedInputSource({dynamic hint}) {
|
||||||
return jsonEncode(['Input source 2', 'input_source_2_tip']);
|
return jsonEncode([
|
||||||
|
['Input source 2', 'input_source_2_tip']
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> mainGenerate2Fa({dynamic hint}) {
|
Future<String> mainGenerate2Fa({dynamic hint}) {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:js' as js;
|
||||||
|
|
||||||
final isAndroid_ = false;
|
final isAndroid_ = false;
|
||||||
final isIOS_ = false;
|
final isIOS_ = false;
|
||||||
@ -5,5 +6,8 @@ final isWindows_ = false;
|
|||||||
final isMacOS_ = false;
|
final isMacOS_ = false;
|
||||||
final isLinux_ = false;
|
final isLinux_ = false;
|
||||||
final isWeb_ = true;
|
final isWeb_ = true;
|
||||||
|
final isWebDesktop_ = !js.context.callMethod('isMobile');
|
||||||
|
|
||||||
final isDesktop_ = false;
|
final isDesktop_ = false;
|
||||||
|
|
||||||
|
String get screenInfo_ => js.context.callMethod('getByName', ['screen_info']);
|
||||||
|
@ -652,7 +652,7 @@ export default class Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getOption(name: string): any {
|
getOption(name: string): any {
|
||||||
return this._options[name];
|
return this._options[name] ?? globals.getUserDefaultOption(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
getToggleOption(name: string): Boolean {
|
getToggleOption(name: string): Boolean {
|
||||||
@ -839,6 +839,52 @@ export default class Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleOption(name: string) {
|
toggleOption(name: string) {
|
||||||
|
|
||||||
|
// } else if name == "block-input" {
|
||||||
|
// option.block_input = BoolOption::Yes.into();
|
||||||
|
// } else if name == "unblock-input" {
|
||||||
|
// option.block_input = BoolOption::No.into();
|
||||||
|
// } else if name == "show-quality-monitor" {
|
||||||
|
// config.show_quality_monitor.v = !config.show_quality_monitor.v;
|
||||||
|
// } else if name == "allow_swap_key" {
|
||||||
|
// config.allow_swap_key.v = !config.allow_swap_key.v;
|
||||||
|
// } else if name == "view-only" {
|
||||||
|
// config.view_only.v = !config.view_only.v;
|
||||||
|
// let f = |b: bool| {
|
||||||
|
// if b {
|
||||||
|
// BoolOption::Yes.into()
|
||||||
|
// } else {
|
||||||
|
// BoolOption::No.into()
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// if config.view_only.v {
|
||||||
|
// option.disable_keyboard = f(true);
|
||||||
|
// option.disable_clipboard = f(true);
|
||||||
|
// option.show_remote_cursor = f(true);
|
||||||
|
// option.enable_file_transfer = f(false);
|
||||||
|
// option.lock_after_session_end = f(false);
|
||||||
|
// } else {
|
||||||
|
// option.disable_keyboard = f(false);
|
||||||
|
// option.disable_clipboard = f(self.get_toggle_option("disable-clipboard"));
|
||||||
|
// option.show_remote_cursor = f(self.get_toggle_option("show-remote-cursor"));
|
||||||
|
// option.enable_file_transfer = f(self.config.enable_file_transfer.v);
|
||||||
|
// option.lock_after_session_end = f(self.config.lock_after_session_end.v);
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// let is_set = self
|
||||||
|
// .options
|
||||||
|
// .get(&name)
|
||||||
|
// .map(|o| !o.is_empty())
|
||||||
|
// .unwrap_or(false);
|
||||||
|
// if is_set {
|
||||||
|
// self.config.options.remove(&name);
|
||||||
|
// } else {
|
||||||
|
// self.config.options.insert(name, "Y".to_owned());
|
||||||
|
// }
|
||||||
|
// self.config.store(&self.id);
|
||||||
|
// return None;
|
||||||
|
// }
|
||||||
|
|
||||||
const v = !this._options[name];
|
const v = !this._options[name];
|
||||||
const option = message.OptionMessage.fromPartial({});
|
const option = message.OptionMessage.fromPartial({});
|
||||||
const v2 = v
|
const v2 = v
|
||||||
@ -860,13 +906,43 @@ export default class Connection {
|
|||||||
case "privacy-mode":
|
case "privacy-mode":
|
||||||
option.privacy_mode = v2;
|
option.privacy_mode = v2;
|
||||||
break;
|
break;
|
||||||
|
case "enable-file-transfer":
|
||||||
|
option.enable_file_transfer = v2;
|
||||||
|
break;
|
||||||
case "block-input":
|
case "block-input":
|
||||||
option.block_input = message.OptionMessage_BoolOption.Yes;
|
option.block_input = message.OptionMessage_BoolOption.Yes;
|
||||||
break;
|
break;
|
||||||
case "unblock-input":
|
case "unblock-input":
|
||||||
option.block_input = message.OptionMessage_BoolOption.No;
|
option.block_input = message.OptionMessage_BoolOption.No;
|
||||||
break;
|
break;
|
||||||
|
case "show-quality-monitor":
|
||||||
|
case "allow-swap-key":
|
||||||
|
break;
|
||||||
|
case "view-only":
|
||||||
|
if (v) {
|
||||||
|
option.disable_keyboard = message.OptionMessage_BoolOption.Yes;
|
||||||
|
option.disable_clipboard = message.OptionMessage_BoolOption.Yes;
|
||||||
|
option.show_remote_cursor = message.OptionMessage_BoolOption.Yes;
|
||||||
|
option.enable_file_transfer = message.OptionMessage_BoolOption.No;
|
||||||
|
option.lock_after_session_end = message.OptionMessage_BoolOption.No;
|
||||||
|
} else {
|
||||||
|
option.disable_keyboard = message.OptionMessage_BoolOption.No;
|
||||||
|
option.disable_clipboard = this.getToggleOption("disable-clipboard")
|
||||||
|
? message.OptionMessage_BoolOption.Yes
|
||||||
|
: message.OptionMessage_BoolOption.No;
|
||||||
|
option.show_remote_cursor = this.getToggleOption("show-remote-cursor")
|
||||||
|
? message.OptionMessage_BoolOption.Yes
|
||||||
|
: message.OptionMessage_BoolOption.No;
|
||||||
|
option.enable_file_transfer = this.getToggleOption("enable-file-transfer")
|
||||||
|
? message.OptionMessage_BoolOption.Yes
|
||||||
|
: message.OptionMessage_BoolOption.No;
|
||||||
|
option.lock_after_session_end = this.getToggleOption("lock-after-session-end")
|
||||||
|
? message.OptionMessage_BoolOption.Yes
|
||||||
|
: message.OptionMessage_BoolOption.No;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
|
this.setOption(name, this._options[name] ? undefined : "Y");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (name.indexOf("block-input") < 0) this.setOption(name, v);
|
if (name.indexOf("block-input") < 0) this.setOption(name, v);
|
||||||
|
@ -211,7 +211,7 @@ window.setByName = (name, value) => {
|
|||||||
curConn.refresh();
|
curConn.refresh();
|
||||||
break;
|
break;
|
||||||
case 'reconnect':
|
case 'reconnect':
|
||||||
curConn.reconnect();
|
curConn?.reconnect();
|
||||||
break;
|
break;
|
||||||
case 'toggle_option':
|
case 'toggle_option':
|
||||||
curConn.toggleOption(value);
|
curConn.toggleOption(value);
|
||||||
@ -244,6 +244,7 @@ window.setByName = (name, value) => {
|
|||||||
curConn.inputString(value);
|
curConn.inputString(value);
|
||||||
break;
|
break;
|
||||||
case 'send_mouse':
|
case 'send_mouse':
|
||||||
|
if (!curConn) return;
|
||||||
let mask = 0;
|
let mask = 0;
|
||||||
value = JSON.parse(value);
|
value = JSON.parse(value);
|
||||||
switch (value.type) {
|
switch (value.type) {
|
||||||
@ -288,6 +289,9 @@ window.setByName = (name, value) => {
|
|||||||
value = JSON.parse(value);
|
value = JSON.parse(value);
|
||||||
localStorage.setItem(name + ':' + value.name, value.value);
|
localStorage.setItem(name + ':' + value.name, value.value);
|
||||||
break;
|
break;
|
||||||
|
case 'option:user:default':
|
||||||
|
setUserDefaultOption(value);
|
||||||
|
break;
|
||||||
case 'option:session':
|
case 'option:session':
|
||||||
value = JSON.parse(value);
|
value = JSON.parse(value);
|
||||||
curConn.setOption(value.name, value.value);
|
curConn.setOption(value.name, value.value);
|
||||||
@ -295,12 +299,11 @@ window.setByName = (name, value) => {
|
|||||||
case 'option:peer':
|
case 'option:peer':
|
||||||
setPeerOption(value);
|
setPeerOption(value);
|
||||||
break;
|
break;
|
||||||
|
case 'option:toggle':
|
||||||
|
return curConn.toggleOption(value);
|
||||||
case 'input_os_password':
|
case 'input_os_password':
|
||||||
curConn.inputOsPassword(value);
|
curConn.inputOsPassword(value);
|
||||||
break;
|
break;
|
||||||
case 'check_conn_status':
|
|
||||||
curConn.checkConnStatus();
|
|
||||||
break;
|
|
||||||
case 'session_add_sync':
|
case 'session_add_sync':
|
||||||
return sessionAdd(value);
|
return sessionAdd(value);
|
||||||
case 'session_start':
|
case 'session_start':
|
||||||
@ -374,8 +377,14 @@ function _getByName(name, arg) {
|
|||||||
case 'translate':
|
case 'translate':
|
||||||
arg = JSON.parse(arg);
|
arg = JSON.parse(arg);
|
||||||
return translate(arg.locale, arg.text);
|
return translate(arg.locale, arg.text);
|
||||||
|
case 'option:user:default':
|
||||||
|
return getUserDefaultOption(arg);
|
||||||
case 'option:session':
|
case 'option:session':
|
||||||
return curConn.getOption(arg);
|
if (curConn) {
|
||||||
|
return curConn.getOption(arg);
|
||||||
|
} else {
|
||||||
|
return getUserDefaultOption(arg);
|
||||||
|
}
|
||||||
case 'option:peer':
|
case 'option:peer':
|
||||||
return getPeerOption(arg);
|
return getPeerOption(arg);
|
||||||
case 'option:toggle':
|
case 'option:toggle':
|
||||||
@ -412,6 +421,28 @@ function _getByName(name, arg) {
|
|||||||
return getAuditServer(arg);
|
return getAuditServer(arg);
|
||||||
case 'alternative_codecs':
|
case 'alternative_codecs':
|
||||||
return getAlternativeCodecs();
|
return getAlternativeCodecs();
|
||||||
|
case 'screen_info':
|
||||||
|
return JSON.stringify({
|
||||||
|
frame: {
|
||||||
|
l: window.screenX,
|
||||||
|
t: window.screenY,
|
||||||
|
r: window.screenX + window.innerWidth,
|
||||||
|
b: window.screenY + window.innerHeight,
|
||||||
|
},
|
||||||
|
visibleFrame: {
|
||||||
|
l: window.screen.availLeft,
|
||||||
|
t: window.screen.availTop,
|
||||||
|
r: window.screen.availLeft + window.screen.availWidth,
|
||||||
|
b: window.screen.availTop + window.screen.availHeight,
|
||||||
|
},
|
||||||
|
scaleFactor: window.devicePixelRatio,
|
||||||
|
});
|
||||||
|
case 'main_display':
|
||||||
|
return JSON.stringify({
|
||||||
|
w: window.screen.availWidth,
|
||||||
|
h: window.screen.availHeight,
|
||||||
|
scaleFactor: window.devicePixelRatio,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@ -521,20 +552,52 @@ export function getVersionNumber(v) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================== options begin ==========================
|
||||||
|
function setUserDefaultOption(value) {
|
||||||
|
try {
|
||||||
|
const ojb = JSON.parse(value);
|
||||||
|
const userDefaultOptions = JSON.parse(localStorage.getItem('user-default-options')) || {};
|
||||||
|
userDefaultOptions[ojb.name] = ojb.value;
|
||||||
|
localStorage.setItem('user-default-options', JSON.stringify(userDefaultOptions));
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error('Failed to set user default options: ' + e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserDefaultOption(value) {
|
||||||
|
const defaultOptions = {
|
||||||
|
'view_style': 'original',
|
||||||
|
'scroll_style': 'scrollauto',
|
||||||
|
'image_quality': 'balanced',
|
||||||
|
'codec-preference': 'auto',
|
||||||
|
'custom_image_quality': '50',
|
||||||
|
'custom-fps': '30',
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const userDefaultOptions = JSON.parse(localStorage.getItem('user-default-options')) || {};
|
||||||
|
return userDefaultOptions[value] || defaultOptions[value] || '';
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error('Failed to get user default options: ' + e.message);
|
||||||
|
return defaultOptions[value] || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getPeerOption(value) {
|
function getPeerOption(value) {
|
||||||
try {
|
try {
|
||||||
const obj = JSON.parse(value);
|
const obj = JSON.parse(value);
|
||||||
const options = getPeers()[obj.id] || {};
|
const options = getPeers()[obj.id] || {};
|
||||||
return options[obj.name] || '';
|
return options[obj.name] ?? getUserDefaultOption(obj.name);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.error('Failed to get peer option: "' + value + '", ' + e.message);
|
console.error('Failed to get peer option: "' + value + '", ' + e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPeerOption(value) {
|
function setPeerOption(param) {
|
||||||
try {
|
try {
|
||||||
const obj = JSON.parse(value);
|
const obj = JSON.parse(param);
|
||||||
const id = obj.id;
|
const id = obj.id;
|
||||||
const name = obj.name;
|
const name = obj.name;
|
||||||
const value = obj.value;
|
const value = obj.value;
|
||||||
@ -554,6 +617,7 @@ function setPeerOption(value) {
|
|||||||
console.error('Failed to set peer option: "' + value + '", ' + e.message);
|
console.error('Failed to set peer option: "' + value + '", ' + e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ========================= options end ===========================
|
||||||
|
|
||||||
// ========================== peers begin ==========================
|
// ========================== peers begin ==========================
|
||||||
function getRecentPeers() {
|
function getRecentPeers() {
|
||||||
@ -668,10 +732,10 @@ function increasePort(host, offset) {
|
|||||||
|
|
||||||
function getAlternativeCodecs() {
|
function getAlternativeCodecs() {
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
vp8: 1,
|
vp8: true,
|
||||||
av1: 0,
|
av1: false,
|
||||||
h264: 1,
|
h264: false,
|
||||||
h265: 1,
|
h265: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// ========================== settings end ===========================
|
// ========================== settings end ===========================
|
||||||
|
Loading…
x
Reference in New Issue
Block a user