123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008 |
- import 'dart:async';
- import 'dart:io';
- import 'dart:convert';
- import 'package:auto_size_text/auto_size_text.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:flutter_hbb/common.dart';
- import 'package:flutter_hbb/common/widgets/animated_rotation_widget.dart';
- import 'package:flutter_hbb/common/widgets/custom_password.dart';
- import 'package:flutter_hbb/consts.dart';
- import 'package:flutter_hbb/desktop/pages/connection_page.dart';
- import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart';
- import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
- import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
- import 'package:flutter_hbb/models/platform_model.dart';
- import 'package:flutter_hbb/models/server_model.dart';
- import 'package:flutter_hbb/plugin/ui_manager.dart';
- import 'package:flutter_hbb/utils/multi_window_manager.dart';
- import 'package:get/get.dart';
- import 'package:provider/provider.dart';
- import 'package:url_launcher/url_launcher.dart';
- import 'package:window_manager/window_manager.dart';
- import 'package:window_size/window_size.dart' as window_size;
- import '../widgets/button.dart';
- class DesktopHomePage extends StatefulWidget {
- const DesktopHomePage({Key? key}) : super(key: key);
- @override
- State<DesktopHomePage> createState() => _DesktopHomePageState();
- }
- const borderColor = Color(0xFF2F65BA);
- class _DesktopHomePageState extends State<DesktopHomePage>
- with AutomaticKeepAliveClientMixin {
- final _leftPaneScrollController = ScrollController();
- @override
- bool get wantKeepAlive => true;
- var updateUrl = '';
- var systemError = '';
- StreamSubscription? _uniLinksSubscription;
- var svcStopped = false.obs;
- var watchIsCanScreenRecording = false;
- var watchIsProcessTrust = false;
- var watchIsInputMonitoring = false;
- var watchIsCanRecordAudio = false;
- Timer? _updateTimer;
- bool isCardClosed = false;
- final RxBool _editHover = false.obs;
- final GlobalKey _childKey = GlobalKey();
- @override
- Widget build(BuildContext context) {
- super.build(context);
- final isIncomingOnly = bind.isIncomingOnly();
- return Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- buildLeftPane(context),
- if (!isIncomingOnly) const VerticalDivider(width: 1),
- if (!isIncomingOnly) Expanded(child: buildRightPane(context)),
- ],
- );
- }
- Widget buildLeftPane(BuildContext context) {
- final isIncomingOnly = bind.isIncomingOnly();
- final isOutgoingOnly = bind.isOutgoingOnly();
- final children = <Widget>[
- if (!isOutgoingOnly) buildPresetPasswordWarning(),
- if (bind.isCustomClient())
- Align(
- alignment: Alignment.center,
- child: loadPowered(context),
- ),
- Align(
- alignment: Alignment.center,
- child: loadLogo(),
- ),
- buildTip(context),
- if (!isOutgoingOnly) buildIDBoard(context),
- if (!isOutgoingOnly) buildPasswordBoard(context),
- FutureBuilder<Widget>(
- future: buildHelpCards(),
- builder: (_, data) {
- if (data.hasData) {
- if (isIncomingOnly) {
- if (isInHomePage()) {
- Future.delayed(Duration(milliseconds: 300), () {
- _updateWindowSize();
- });
- }
- }
- return data.data!;
- } else {
- return const Offstage();
- }
- },
- ),
- buildPluginEntry(),
- ];
- if (isIncomingOnly) {
- children.addAll([
- Divider(),
- OnlineStatusWidget(
- onSvcStatusChanged: () {
- if (isInHomePage()) {
- Future.delayed(Duration(milliseconds: 300), () {
- _updateWindowSize();
- });
- }
- },
- ).marginOnly(bottom: 6, right: 6)
- ]);
- }
- final textColor = Theme.of(context).textTheme.titleLarge?.color;
- return ChangeNotifierProvider.value(
- value: gFFI.serverModel,
- child: Container(
- width: isIncomingOnly ? 280.0 : 200.0,
- color: Theme.of(context).colorScheme.background,
- child: DesktopScrollWrapper(
- scrollController: _leftPaneScrollController,
- child: Stack(
- children: [
- SingleChildScrollView(
- controller: _leftPaneScrollController,
- physics: DraggableNeverScrollableScrollPhysics(),
- child: Column(
- key: _childKey,
- children: children,
- ),
- ),
- if (isOutgoingOnly)
- Positioned(
- bottom: 6,
- left: 12,
- child: Align(
- alignment: Alignment.centerLeft,
- child: InkWell(
- child: Obx(
- () => Icon(
- Icons.settings,
- color: _editHover.value
- ? textColor
- : Colors.grey.withOpacity(0.5),
- size: 22,
- ),
- ),
- onTap: () => {
- if (DesktopSettingPage.tabKeys.isNotEmpty)
- {
- DesktopSettingPage.switch2page(
- DesktopSettingPage.tabKeys[0])
- }
- },
- onHover: (value) => _editHover.value = value,
- ),
- ),
- )
- ],
- ),
- ),
- ),
- );
- }
- buildRightPane(BuildContext context) {
- return Container(
- color: Theme.of(context).scaffoldBackgroundColor,
- child: ConnectionPage(),
- );
- }
- buildIDBoard(BuildContext context) {
- final model = gFFI.serverModel;
- return Container(
- margin: const EdgeInsets.only(left: 20, right: 11),
- height: 57,
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.baseline,
- textBaseline: TextBaseline.alphabetic,
- children: [
- Container(
- width: 2,
- decoration: const BoxDecoration(color: MyTheme.accent),
- ).marginOnly(top: 5),
- Expanded(
- child: Padding(
- padding: const EdgeInsets.only(left: 7),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Container(
- height: 25,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- translate("ID"),
- style: TextStyle(
- fontSize: 14,
- color: Theme.of(context)
- .textTheme
- .titleLarge
- ?.color
- ?.withOpacity(0.5)),
- ).marginOnly(top: 5),
- buildPopupMenu(context)
- ],
- ),
- ),
- Flexible(
- child: GestureDetector(
- onDoubleTap: () {
- Clipboard.setData(
- ClipboardData(text: model.serverId.text));
- showToast(translate("Copied"));
- },
- child: TextFormField(
- controller: model.serverId,
- readOnly: true,
- decoration: InputDecoration(
- border: InputBorder.none,
- contentPadding: EdgeInsets.only(top: 10, bottom: 10),
- ),
- style: TextStyle(
- fontSize: 22,
- ),
- ),
- ),
- )
- ],
- ),
- ),
- ),
- ],
- ),
- );
- }
- Widget buildPopupMenu(BuildContext context) {
- final textColor = Theme.of(context).textTheme.titleLarge?.color;
- RxBool hover = false.obs;
- return InkWell(
- onTap: DesktopTabPage.onAddSetting,
- child: Tooltip(
- message: translate('Settings'),
- child: Obx(
- () => CircleAvatar(
- radius: 15,
- backgroundColor: hover.value
- ? Theme.of(context).scaffoldBackgroundColor
- : Theme.of(context).colorScheme.background,
- child: Icon(
- Icons.more_vert_outlined,
- size: 20,
- color: hover.value ? textColor : textColor?.withOpacity(0.5),
- ),
- ),
- ),
- ),
- onHover: (value) => hover.value = value,
- );
- }
- buildPasswordBoard(BuildContext context) {
- return ChangeNotifierProvider.value(
- value: gFFI.serverModel,
- child: Consumer<ServerModel>(
- builder: (context, model, child) {
- return buildPasswordBoard2(context, model);
- },
- ));
- }
- buildPasswordBoard2(BuildContext context, ServerModel model) {
- RxBool refreshHover = false.obs;
- RxBool editHover = false.obs;
- final textColor = Theme.of(context).textTheme.titleLarge?.color;
- final showOneTime = model.approveMode != 'click' &&
- model.verificationMethod != kUsePermanentPassword;
- return Container(
- margin: EdgeInsets.only(left: 20.0, right: 16, top: 13, bottom: 13),
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.baseline,
- textBaseline: TextBaseline.alphabetic,
- children: [
- Container(
- width: 2,
- height: 52,
- decoration: BoxDecoration(color: MyTheme.accent),
- ),
- Expanded(
- child: Padding(
- padding: const EdgeInsets.only(left: 7),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- AutoSizeText(
- translate("One-time Password"),
- style: TextStyle(
- fontSize: 14, color: textColor?.withOpacity(0.5)),
- maxLines: 1,
- ),
- Row(
- children: [
- Expanded(
- child: GestureDetector(
- onDoubleTap: () {
- if (showOneTime) {
- Clipboard.setData(
- ClipboardData(text: model.serverPasswd.text));
- showToast(translate("Copied"));
- }
- },
- child: TextFormField(
- controller: model.serverPasswd,
- readOnly: true,
- decoration: InputDecoration(
- border: InputBorder.none,
- contentPadding:
- EdgeInsets.only(top: 14, bottom: 10),
- ),
- style: TextStyle(fontSize: 15),
- ),
- ),
- ),
- if (showOneTime)
- AnimatedRotationWidget(
- onPressed: () => bind.mainUpdateTemporaryPassword(),
- child: Tooltip(
- message: translate('Refresh Password'),
- child: Obx(() => RotatedBox(
- quarterTurns: 2,
- child: Icon(
- Icons.refresh,
- color: refreshHover.value
- ? textColor
- : Color(0xFFDDDDDD),
- size: 22,
- ))),
- ),
- onHover: (value) => refreshHover.value = value,
- ).marginOnly(right: 8, top: 4),
- if (!bind.isDisableSettings())
- InkWell(
- child: Tooltip(
- message: translate('Change Password'),
- child: Obx(
- () => Icon(
- Icons.edit,
- color: editHover.value
- ? textColor
- : Color(0xFFDDDDDD),
- size: 22,
- ).marginOnly(right: 8, top: 4),
- ),
- ),
- onTap: () => DesktopSettingPage.switch2page(
- SettingsTabKey.safety),
- onHover: (value) => editHover.value = value,
- ),
- ],
- ),
- ],
- ),
- ),
- ),
- ],
- ),
- );
- }
- buildTip(BuildContext context) {
- final isOutgoingOnly = bind.isOutgoingOnly();
- return Padding(
- padding:
- const EdgeInsets.only(left: 20.0, right: 16, top: 16.0, bottom: 5),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Column(
- children: [
- if (!isOutgoingOnly)
- Align(
- alignment: Alignment.centerLeft,
- child: Text(
- translate("Your Desktop"),
- style: Theme.of(context).textTheme.titleLarge,
- ),
- ),
- ],
- ),
- SizedBox(
- height: 10.0,
- ),
- if (!isOutgoingOnly)
- Text(
- translate("desk_tip"),
- overflow: TextOverflow.clip,
- style: Theme.of(context).textTheme.bodySmall,
- ),
- if (isOutgoingOnly)
- Text(
- translate("outgoing_only_desk_tip"),
- overflow: TextOverflow.clip,
- style: Theme.of(context).textTheme.bodySmall,
- ),
- ],
- ),
- );
- }
- Future<Widget> buildHelpCards() async {
- if (!bind.isCustomClient() &&
- updateUrl.isNotEmpty &&
- !isCardClosed &&
- bind.mainUriPrefixSync().contains('rustdesk')) {
- return buildInstallCard(
- "Status",
- "There is a newer version of ${bind.mainGetAppNameSync()} ${bind.mainGetNewVersion()} available.",
- "Click to download", () async {
- final Uri url = Uri.parse('https://rustdesk.com/download');
- await launchUrl(url);
- }, closeButton: true);
- }
- if (systemError.isNotEmpty) {
- return buildInstallCard("", systemError, "", () {});
- }
- if (isWindows && !bind.isDisableInstallation()) {
- if (!bind.mainIsInstalled()) {
- return buildInstallCard(
- "", bind.isOutgoingOnly() ? "" : "install_tip", "Install",
- () async {
- await rustDeskWinManager.closeAllSubWindows();
- bind.mainGotoInstall();
- });
- } else if (bind.mainIsInstalledLowerVersion()) {
- return buildInstallCard(
- "Status", "Your installation is lower version.", "Click to upgrade",
- () async {
- await rustDeskWinManager.closeAllSubWindows();
- bind.mainUpdateMe();
- });
- }
- } else if (isMacOS) {
- final isOutgoingOnly = bind.isOutgoingOnly();
- if (!(isOutgoingOnly || bind.mainIsCanScreenRecording(prompt: false))) {
- return buildInstallCard("Permissions", "config_screen", "Configure",
- () async {
- bind.mainIsCanScreenRecording(prompt: true);
- watchIsCanScreenRecording = true;
- }, help: 'Help', link: translate("doc_mac_permission"));
- } else if (!isOutgoingOnly && !bind.mainIsProcessTrusted(prompt: false)) {
- return buildInstallCard("Permissions", "config_acc", "Configure",
- () async {
- 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 (!isOutgoingOnly &&
- !svcStopped.value &&
- bind.mainIsInstalled() &&
- !bind.mainIsInstalledDaemon(prompt: false)) {
- return buildInstallCard("", "install_daemon_tip", "Install", () async {
- bind.mainIsInstalledDaemon(prompt: true);
- });
- }
- //// Disable microphone configuration for macOS. We will request the permission when needed.
- // else if ((await osxCanRecordAudio() !=
- // PermissionAuthorizeType.authorized)) {
- // return buildInstallCard("Permissions", "config_microphone", "Configure",
- // () async {
- // osxRequestAudio();
- // watchIsCanRecordAudio = true;
- // });
- // }
- } else if (isLinux) {
- if (bind.isOutgoingOnly()) {
- return Container();
- }
- final LinuxCards = <Widget>[];
- if (bind.isSelinuxEnforcing()) {
- // Check is SELinux enforcing, but show user a tip of is SELinux enabled for simple.
- final keyShowSelinuxHelpTip = "show-selinux-help-tip";
- if (bind.mainGetLocalOption(key: keyShowSelinuxHelpTip) != 'N') {
- LinuxCards.add(buildInstallCard(
- "Warning",
- "selinux_tip",
- "",
- () async {},
- marginTop: LinuxCards.isEmpty ? 20.0 : 5.0,
- help: 'Help',
- link:
- 'https://rustdesk.com/docs/en/client/linux/#permissions-issue',
- closeButton: true,
- closeOption: keyShowSelinuxHelpTip,
- ));
- }
- }
- if (bind.mainCurrentIsWayland()) {
- LinuxCards.add(buildInstallCard(
- "Warning", "wayland_experiment_tip", "", () async {},
- marginTop: LinuxCards.isEmpty ? 20.0 : 5.0,
- help: 'Help',
- link: 'https://rustdesk.com/docs/en/client/linux/#x11-required'));
- } else if (bind.mainIsLoginWayland()) {
- LinuxCards.add(buildInstallCard("Warning",
- "Login screen using Wayland is not supported", "", () async {},
- marginTop: LinuxCards.isEmpty ? 20.0 : 5.0,
- help: 'Help',
- link: 'https://rustdesk.com/docs/en/client/linux/#login-screen'));
- }
- if (LinuxCards.isNotEmpty) {
- return Column(
- children: LinuxCards,
- );
- }
- }
- if (bind.isIncomingOnly()) {
- return Align(
- alignment: Alignment.centerRight,
- child: OutlinedButton(
- onPressed: () {
- SystemNavigator.pop(); // Close the application
- // https://github.com/flutter/flutter/issues/66631
- if (isWindows) {
- exit(0);
- }
- },
- child: Text(translate('Quit')),
- ),
- ).marginAll(14);
- }
- return Container();
- }
- Widget buildInstallCard(String title, String content, String btnText,
- GestureTapCallback onPressed,
- {double marginTop = 20.0,
- String? help,
- String? link,
- bool? closeButton,
- String? closeOption}) {
- if (bind.mainGetBuildinOption(key: kOptionHideHelpCards) == 'Y' &&
- content != 'install_daemon_tip') {
- return const SizedBox();
- }
- void closeCard() async {
- if (closeOption != null) {
- await bind.mainSetLocalOption(key: closeOption, value: 'N');
- if (bind.mainGetLocalOption(key: closeOption) == 'N') {
- setState(() {
- isCardClosed = true;
- });
- }
- } else {
- setState(() {
- isCardClosed = true;
- });
- }
- }
- return Stack(
- children: [
- Container(
- margin: EdgeInsets.fromLTRB(
- 0, marginTop, 0, bind.isIncomingOnly() ? marginTop : 0),
- child: Container(
- decoration: BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.centerLeft,
- end: Alignment.centerRight,
- colors: [
- Color.fromARGB(255, 226, 66, 188),
- Color.fromARGB(255, 244, 114, 124),
- ],
- )),
- padding: EdgeInsets.all(20),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: (title.isNotEmpty
- ? <Widget>[
- Center(
- child: Text(
- translate(title),
- style: TextStyle(
- color: Colors.white,
- fontWeight: FontWeight.bold,
- fontSize: 15),
- ).marginOnly(bottom: 6)),
- ]
- : <Widget>[]) +
- <Widget>[
- if (content.isNotEmpty)
- Text(
- translate(content),
- style: TextStyle(
- height: 1.5,
- color: Colors.white,
- fontWeight: FontWeight.normal,
- fontSize: 13),
- ).marginOnly(bottom: 20)
- ] +
- (btnText.isNotEmpty
- ? <Widget>[
- Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- FixedWidthButton(
- width: 150,
- padding: 8,
- isOutline: true,
- text: translate(btnText),
- textColor: Colors.white,
- borderColor: Colors.white,
- textSize: 20,
- radius: 10,
- onTap: onPressed,
- )
- ])
- ]
- : <Widget>[]) +
- (help != null
- ? <Widget>[
- Center(
- child: InkWell(
- onTap: () async =>
- await launchUrl(Uri.parse(link!)),
- child: Text(
- translate(help),
- style: TextStyle(
- decoration:
- TextDecoration.underline,
- color: Colors.white,
- fontSize: 12),
- )).marginOnly(top: 6)),
- ]
- : <Widget>[]))),
- ),
- if (closeButton != null && closeButton == true)
- Positioned(
- top: 18,
- right: 0,
- child: IconButton(
- icon: Icon(
- Icons.close,
- color: Colors.white,
- size: 20,
- ),
- onPressed: closeCard,
- ),
- ),
- ],
- );
- }
- @override
- void initState() {
- super.initState();
- if (!bind.isCustomClient()) {
- platformFFI.registerEventHandler(
- kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish,
- (Map<String, dynamic> evt) async {
- if (evt['url'] is String) {
- setState(() {
- updateUrl = evt['url'];
- });
- }
- });
- Timer(const Duration(seconds: 1), () async {
- bind.mainGetSoftwareUpdateUrl();
- });
- }
- _updateTimer = periodic_immediate(const Duration(seconds: 1), () async {
- await gFFI.serverModel.fetchID();
- final error = await bind.mainGetError();
- if (systemError != error) {
- systemError = error;
- setState(() {});
- }
- final v = await mainGetBoolOption(kOptionStopService);
- if (v != svcStopped.value) {
- svcStopped.value = v;
- setState(() {});
- }
- if (watchIsCanScreenRecording) {
- if (bind.mainIsCanScreenRecording(prompt: false)) {
- watchIsCanScreenRecording = false;
- setState(() {});
- }
- }
- if (watchIsProcessTrust) {
- if (bind.mainIsProcessTrusted(prompt: false)) {
- watchIsProcessTrust = false;
- setState(() {});
- }
- }
- if (watchIsInputMonitoring) {
- if (bind.mainIsCanInputMonitoring(prompt: false)) {
- watchIsInputMonitoring = false;
- // Do not notify for now.
- // Monitoring may not take effect until the process is restarted.
- // rustDeskWinManager.call(
- // WindowType.RemoteDesktop, kWindowDisableGrabKeyboard, '');
- setState(() {});
- }
- }
- if (watchIsCanRecordAudio) {
- if (isMacOS) {
- Future.microtask(() async {
- if ((await osxCanRecordAudio() ==
- PermissionAuthorizeType.authorized)) {
- watchIsCanRecordAudio = false;
- setState(() {});
- }
- });
- } else {
- watchIsCanRecordAudio = false;
- setState(() {});
- }
- }
- });
- Get.put<RxBool>(svcStopped, tag: 'stop-service');
- rustDeskWinManager.registerActiveWindowListener(onActiveWindowChanged);
- screenToMap(window_size.Screen screen) => {
- 'frame': {
- 'l': screen.frame.left,
- 't': screen.frame.top,
- 'r': screen.frame.right,
- 'b': screen.frame.bottom,
- },
- 'visibleFrame': {
- 'l': screen.visibleFrame.left,
- 't': screen.visibleFrame.top,
- 'r': screen.visibleFrame.right,
- 'b': screen.visibleFrame.bottom,
- },
- 'scaleFactor': screen.scaleFactor,
- };
- rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
- debugPrint(
- "[Main] call ${call.method} with args ${call.arguments} from window $fromWindowId");
- if (call.method == kWindowMainWindowOnTop) {
- windowOnTop(null);
- } else if (call.method == kWindowGetWindowInfo) {
- final screen = (await window_size.getWindowInfo()).screen;
- if (screen == null) {
- return '';
- } else {
- return jsonEncode(screenToMap(screen));
- }
- } else if (call.method == kWindowGetScreenList) {
- return jsonEncode(
- (await window_size.getScreenList()).map(screenToMap).toList());
- } else if (call.method == kWindowActionRebuild) {
- reloadCurrentWindow();
- } else if (call.method == kWindowEventShow) {
- await rustDeskWinManager.registerActiveWindow(call.arguments["id"]);
- } else if (call.method == kWindowEventHide) {
- await rustDeskWinManager.unregisterActiveWindow(call.arguments['id']);
- } else if (call.method == kWindowConnect) {
- await connectMainDesktop(
- call.arguments['id'],
- isFileTransfer: call.arguments['isFileTransfer'],
- isTcpTunneling: call.arguments['isTcpTunneling'],
- isRDP: call.arguments['isRDP'],
- password: call.arguments['password'],
- forceRelay: call.arguments['forceRelay'],
- connToken: call.arguments['connToken'],
- );
- } else if (call.method == kWindowEventMoveTabToNewWindow) {
- final args = call.arguments.split(',');
- int? windowId;
- try {
- windowId = int.parse(args[0]);
- } catch (e) {
- debugPrint("Failed to parse window id '${call.arguments}': $e");
- }
- if (windowId != null) {
- await rustDeskWinManager.moveTabToNewWindow(
- windowId, args[1], args[2]);
- }
- } else if (call.method == kWindowEventOpenMonitorSession) {
- final args = jsonDecode(call.arguments);
- final windowId = args['window_id'] as int;
- final peerId = args['peer_id'] as String;
- final display = args['display'] as int;
- final displayCount = args['display_count'] as int;
- final screenRect = parseParamScreenRect(args);
- await rustDeskWinManager.openMonitorSession(
- windowId, peerId, display, displayCount, screenRect);
- } else if (call.method == kWindowEventRemoteWindowCoords) {
- final windowId = int.tryParse(call.arguments);
- if (windowId != null) {
- return jsonEncode(
- await rustDeskWinManager.getOtherRemoteWindowCoords(windowId));
- }
- }
- });
- _uniLinksSubscription = listenUniLinks();
- if (bind.isIncomingOnly()) {
- WidgetsBinding.instance.addPostFrameCallback((_) {
- _updateWindowSize();
- });
- }
- }
- _updateWindowSize() {
- RenderObject? renderObject = _childKey.currentContext?.findRenderObject();
- if (renderObject == null) {
- return;
- }
- if (renderObject is RenderBox) {
- final size = renderObject.size;
- if (size != imcomingOnlyHomeSize) {
- imcomingOnlyHomeSize = size;
- windowManager.setSize(getIncomingOnlyHomeSize());
- }
- }
- }
- @override
- void dispose() {
- _uniLinksSubscription?.cancel();
- Get.delete<RxBool>(tag: 'stop-service');
- _updateTimer?.cancel();
- if (!bind.isCustomClient()) {
- platformFFI.unregisterEventHandler(
- kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish);
- }
- super.dispose();
- }
- Widget buildPluginEntry() {
- final entries = PluginUiManager.instance.entries.entries;
- return Offstage(
- offstage: entries.isEmpty,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- ...entries.map((entry) {
- return entry.value;
- })
- ],
- ),
- );
- }
- }
- void setPasswordDialog({VoidCallback? notEmptyCallback}) async {
- final pw = await bind.mainGetPermanentPassword();
- final p0 = TextEditingController(text: pw);
- final p1 = TextEditingController(text: pw);
- var errMsg0 = "";
- var errMsg1 = "";
- final RxString rxPass = pw.trim().obs;
- final rules = [
- DigitValidationRule(),
- UppercaseValidationRule(),
- LowercaseValidationRule(),
- // SpecialCharacterValidationRule(),
- MinCharactersValidationRule(8),
- ];
- final maxLength = bind.mainMaxEncryptLen();
- gFFI.dialogManager.show((setState, close, context) {
- submit() {
- setState(() {
- errMsg0 = "";
- errMsg1 = "";
- });
- final pass = p0.text.trim();
- if (pass.isNotEmpty) {
- final Iterable violations = rules.where((r) => !r.validate(pass));
- if (violations.isNotEmpty) {
- setState(() {
- errMsg0 =
- '${translate('Prompt')}: ${violations.map((r) => r.name).join(', ')}';
- });
- return;
- }
- }
- if (p1.text.trim() != pass) {
- setState(() {
- errMsg1 =
- '${translate('Prompt')}: ${translate("The confirmation is not identical.")}';
- });
- return;
- }
- bind.mainSetPermanentPassword(password: pass);
- if (pass.isNotEmpty) {
- notEmptyCallback?.call();
- }
- close();
- }
- return CustomAlertDialog(
- title: Text(translate("Set Password")),
- content: ConstrainedBox(
- constraints: const BoxConstraints(minWidth: 500),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- const SizedBox(
- height: 8.0,
- ),
- Row(
- children: [
- Expanded(
- child: TextField(
- obscureText: true,
- decoration: InputDecoration(
- labelText: translate('Password'),
- errorText: errMsg0.isNotEmpty ? errMsg0 : null),
- controller: p0,
- autofocus: true,
- onChanged: (value) {
- rxPass.value = value.trim();
- setState(() {
- errMsg0 = '';
- });
- },
- maxLength: maxLength,
- ),
- ),
- ],
- ),
- Row(
- children: [
- Expanded(child: PasswordStrengthIndicator(password: rxPass)),
- ],
- ).marginSymmetric(vertical: 8),
- const SizedBox(
- height: 8.0,
- ),
- Row(
- children: [
- Expanded(
- child: TextField(
- obscureText: true,
- decoration: InputDecoration(
- labelText: translate('Confirmation'),
- errorText: errMsg1.isNotEmpty ? errMsg1 : null),
- controller: p1,
- onChanged: (value) {
- setState(() {
- errMsg1 = '';
- });
- },
- maxLength: maxLength,
- ),
- ),
- ],
- ),
- const SizedBox(
- height: 8.0,
- ),
- Obx(() => Wrap(
- runSpacing: 8,
- spacing: 4,
- children: rules.map((e) {
- var checked = e.validate(rxPass.value.trim());
- return Chip(
- label: Text(
- e.name,
- style: TextStyle(
- color: checked
- ? const Color(0xFF0A9471)
- : Color.fromARGB(255, 198, 86, 157)),
- ),
- backgroundColor: checked
- ? const Color(0xFFD0F7ED)
- : Color.fromARGB(255, 247, 205, 232));
- }).toList(),
- ))
- ],
- ),
- ),
- actions: [
- dialogButton("Cancel", onPressed: close, isOutline: true),
- dialogButton("OK", onPressed: submit),
- ],
- onSubmit: submit,
- onCancel: close,
- );
- });
- }
|