diff --git a/flutter/lib/cm_main.dart b/flutter/lib/cm_main.dart index 584d74869..99db02232 100644 --- a/flutter/lib/cm_main.dart +++ b/flutter/lib/cm_main.dart @@ -10,8 +10,8 @@ import 'desktop/pages/server_page.dart'; void main(List args) async { WidgetsFlutterBinding.ensureInitialized(); await windowManager.ensureInitialized(); - await initEnv(kAppTypeConnectionManager); - runApp(GetMaterialApp(theme: getCurrentTheme(), home: DesktopServerPage())); await windowManager.setSize(Size(400, 600)); await windowManager.setAlignment(Alignment.topRight); + await initEnv(kAppTypeConnectionManager); + runApp(GetMaterialApp(theme: getCurrentTheme(), home: DesktopServerPage())); } diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 20167aeb0..63be444e1 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -79,6 +79,15 @@ final ButtonStyle flatButtonStyle = TextButton.styleFrom( ), ); +String formatDurationToTime(Duration duration) { + var totalTime = duration.inSeconds; + final secs = totalTime % 60; + totalTime = (totalTime - secs) ~/ 60; + final mins = totalTime % 60; + totalTime = (totalTime - mins) ~/ 60; + return "${totalTime.toString().padLeft(2, "0")}:${mins.toString().padLeft(2, "0")}:${secs.toString().padLeft(2, "0")}"; +} + closeConnection({String? id}) { if (isAndroid || isIOS) { Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/")); @@ -440,12 +449,18 @@ class PermissionManager { } static Future check(String type) { + if (isDesktop) { + return Future.value(true); + } if (!permissions.contains(type)) return Future.error("Wrong permission!$type"); return gFFI.invokeMethod("check_permission", type); } static Future request(String type) { + if (isDesktop) { + return Future.value(true); + } if (!permissions.contains(type)) return Future.error("Wrong permission!$type"); diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index 34a8f94c4..c78552143 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -1,5 +1,8 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:get/get.dart'; + // import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:provider/provider.dart'; @@ -32,14 +35,14 @@ class DesktopServerPage extends StatefulWidget implements PageShape { padding: EdgeInsets.symmetric(horizontal: 16.0), value: "setPermanentPassword", enabled: - gFFI.serverModel.verificationMethod != kUseTemporaryPassword, + gFFI.serverModel.verificationMethod != kUseTemporaryPassword, ), PopupMenuItem( child: Text(translate("Set temporary password length")), padding: EdgeInsets.symmetric(horizontal: 16.0), value: "setTemporaryPasswordLength", enabled: - gFFI.serverModel.verificationMethod != kUsePermanentPassword, + gFFI.serverModel.verificationMethod != kUsePermanentPassword, ), const PopupMenuDivider(), PopupMenuItem( @@ -51,7 +54,7 @@ class DesktopServerPage extends StatefulWidget implements PageShape { trailing: Icon( Icons.check, color: gFFI.serverModel.verificationMethod == - kUseTemporaryPassword + kUseTemporaryPassword ? null : Color(0xFFFFFFFF), ))), @@ -64,7 +67,7 @@ class DesktopServerPage extends StatefulWidget implements PageShape { trailing: Icon( Icons.check, color: gFFI.serverModel.verificationMethod == - kUsePermanentPassword + kUsePermanentPassword ? null : Color(0xFFFFFFFF), )), @@ -77,9 +80,9 @@ class DesktopServerPage extends StatefulWidget implements PageShape { trailing: Icon( Icons.check, color: gFFI.serverModel.verificationMethod != - kUseTemporaryPassword && - gFFI.serverModel.verificationMethod != - kUsePermanentPassword + kUseTemporaryPassword && + gFFI.serverModel.verificationMethod != + kUsePermanentPassword ? null : Color(0xFFFFFFFF), )), @@ -115,16 +118,16 @@ class _DesktopServerPageState extends State value: gFFI.serverModel, child: Consumer( builder: (context, serverModel, child) => Material( - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded(child: ConnectionManager()), - SizedBox.fromSize(size: Size(0, 15.0)), - ], - ), - ), - ))); + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded(child: ConnectionManager()), + SizedBox.fromSize(size: Size(0, 15.0)), + ], + ), + ), + ))); } @override @@ -136,9 +139,9 @@ class ConnectionManager extends StatelessWidget { Widget build(BuildContext context) { final serverModel = Provider.of(context); // test case: - serverModel.clients.clear(); - serverModel.clients[0] = Client( - false, false, "Readmi-M21sdfsdf", "123123123", true, false, false); + // serverModel.clients.clear(); + // serverModel.clients[0] = Client( + // false, false, "Readmi-M21sdfsdf", "123123123", true, false, false); return serverModel.clients.isEmpty ? Center( child: Text(translate("Waiting")), @@ -150,11 +153,11 @@ class ConnectionManager extends StatelessWidget { children: [ SizedBox( height: kTextTabBarHeight, - child: TabBar( - isScrollable: true, - tabs: serverModel.clients.entries - .map((entry) => buildTab(entry)) - .toList(growable: false)), + child: TabBar( + isScrollable: true, + tabs: serverModel.clients.entries + .map((entry) => buildTab(entry)) + .toList(growable: false)), ), Expanded( child: TabBarView( @@ -170,9 +173,10 @@ class ConnectionManager extends StatelessWidget { Widget buildConnectionCard(MapEntry entry) { final client = entry.value; return Column( + key: ValueKey(entry.key), children: [ _CmHeader(client: client), - _PrivilegeBoard(client: client), + client.isFileTransfer ? Offstage() : _PrivilegeBoard(client: client), Expanded( child: Align( alignment: Alignment.bottomCenter, @@ -200,13 +204,39 @@ class ConnectionManager extends StatelessWidget { } } -class _CmHeader extends StatelessWidget { +class _CmHeader extends StatefulWidget { final Client client; const _CmHeader({Key? key, required this.client}) : super(key: key); + @override + State<_CmHeader> createState() => _CmHeaderState(); +} + +class _CmHeaderState extends State<_CmHeader> + with AutomaticKeepAliveClientMixin { + Client get client => widget.client; + + var _time = 0.obs; + Timer? _timer; + + @override + void initState() { + super.initState(); + _timer = Timer.periodic(Duration(seconds: 1), (_) { + _time.value = _time.value + 1; + }); + } + + @override + void dispose() { + _timer?.cancel(); + super.dispose(); + } + @override Widget build(BuildContext context) { + super.build(context); return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -242,13 +272,13 @@ class _CmHeader extends StatelessWidget { SizedBox( height: 16.0, ), - Offstage( - offstage: !client.authorized, - child: Row( - children: [ - Text("${translate("Connected")}"), - ], - )) + Row( + children: [ + Text("${translate("Connected")}").marginOnly(right: 8.0), + Obx(() => Text( + "${formatDurationToTime(Duration(seconds: _time.value))}")) + ], + ) ], ), ), @@ -264,6 +294,9 @@ class _CmHeader extends StatelessWidget { } void handleSendMsg() {} + + @override + bool get wantKeepAlive => true; } class _PrivilegeBoard extends StatelessWidget { @@ -277,7 +310,7 @@ class _PrivilegeBoard extends StatelessWidget { message: tooltip ?? "", child: Ink( decoration: - BoxDecoration(color: enabled ? MyTheme.accent80 : Colors.grey), + BoxDecoration(color: enabled ? MyTheme.accent80 : Colors.grey), padding: EdgeInsets.all(4.0), child: InkWell( onTap: () => onTap?.call(!enabled), @@ -437,9 +470,9 @@ class PaddingCard extends StatelessWidget { children: [ titleIcon != null ? Padding( - padding: EdgeInsets.only(right: 10), - child: Icon(titleIcon, - color: MyTheme.accent80, size: 30)) + padding: EdgeInsets.only(right: 10), + child: Icon(titleIcon, + color: MyTheme.accent80, size: 30)) : SizedBox.shrink(), Text( title!, @@ -486,12 +519,12 @@ Widget clientInfo(Client client) { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ - Text(client.name, - style: TextStyle(color: MyTheme.idColor, fontSize: 18)), - SizedBox(width: 8), - Text(client.peerId, - style: TextStyle(color: MyTheme.idColor, fontSize: 10)) - ])) + Text(client.name, + style: TextStyle(color: MyTheme.idColor, fontSize: 18)), + SizedBox(width: 8), + Text(client.peerId, + style: TextStyle(color: MyTheme.idColor, fontSize: 10)) + ])) ], ), ])); diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 7f3acc79f..ef4ec81c8 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -117,9 +117,23 @@ void runFileTransferScreen(Map argument) async { } void runConnectionManagerScreen() async { - await initEnv(kAppTypeConnectionManager); - await windowManager.setSize(Size(400, 600)); - await windowManager.setAlignment(Alignment.topRight); + // initialize window + WindowOptions windowOptions = WindowOptions( + size: Size(300, 400), + center: true, + backgroundColor: Colors.transparent, + skipTaskbar: false, + titleBarStyle: TitleBarStyle.normal, + ); + await Future.wait([ + initEnv(kAppTypeConnectionManager), + windowManager.waitUntilReadyToShow(windowOptions, () async { + await windowManager.setAlignment(Alignment.topRight); + await windowManager.show(); + await windowManager.focus(); + }) + ]); + ; runApp(GetMaterialApp(theme: getCurrentTheme(), home: DesktopServerPage())); } diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart index 9ba85eba1..0bbb0c13e 100644 --- a/flutter/lib/models/server_model.dart +++ b/flutter/lib/models/server_model.dart @@ -342,6 +342,10 @@ class ServerModel with ChangeNotifier { var res = await bind.mainGetClientsState(); try { final List clientsJson = jsonDecode(res); + if (isDesktop && clientsJson.isEmpty && _clients.isNotEmpty) { + // exit cm when >1 peers to no peers + exit(0); + } _clients.clear(); for (var clientJson in clientsJson) { final client = Client.fromJson(clientJson); diff --git a/flutter/linux/my_application.cc b/flutter/linux/my_application.cc index 25e9858cc..20513032d 100644 --- a/flutter/linux/my_application.cc +++ b/flutter/linux/my_application.cc @@ -1,7 +1,6 @@ #include "my_application.h" #include -// #include #ifdef GDK_WINDOWING_X11 #include #endif @@ -48,8 +47,8 @@ static void my_application_activate(GApplication* application) { gtk_window_set_title(window, "rustdesk"); } - // auto bdw = bitsdojo_window_from(window); // <--- add this line - // bdw->setCustomFrame(true); // <-- add this line + // auto bdw = bitsdojo_window_from(window); // <--- add this line + // bdw->setCustomFrame(true); // <-- add this line gtk_window_set_default_size(window, 1280, 720); // <-- comment this line gtk_widget_show(GTK_WIDGET(window)); diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index fcc7b5f49..caa12313d 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -66,7 +66,6 @@ dependencies: git: url: https://github.com/Kingtous/rustdesk_desktop_multi_window ref: 2b1176d53f195cc55e8d37151bb3d9f6bd52fad3 - # bitsdojo_window: ^0.1.2 freezed_annotation: ^2.0.3 tray_manager: 0.1.7 get: ^4.6.5