diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 4e6c8ab14..63ab39df2 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1,12 +1,15 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:ffi' hide Size; import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; import 'package:back_button_interceptor/back_button_interceptor.dart'; import 'package:desktop_multi_window/desktop_multi_window.dart'; -import 'package:device_info_plus/device_info_plus.dart'; +import 'package:ffi/ffi.dart'; +import 'package:flutter/foundation.dart'; +import 'package:win32/win32.dart' as win32; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -42,6 +45,7 @@ var isWeb = false; var isWebDesktop = false; var version = ""; int androidVersion = 0; + /// only avaliable for Windows target int windowsBuildNumber = 0; DesktopType? desktopType; @@ -1412,11 +1416,12 @@ Timer periodic_immediate(Duration duration, Future Function() callback) { await callback(); }); } + /// return a human readable windows version WindowsTarget getWindowsTarget(int buildNumber) { if (!Platform.isWindows) { return WindowsTarget.naw; - } + } if (buildNumber >= 22000) { return WindowsTarget.w11; } else if (buildNumber >= 10240) { @@ -1434,3 +1439,47 @@ WindowsTarget getWindowsTarget(int buildNumber) { return WindowsTarget.xp; } } + +/// Get windows target build number. +/// +/// [Note] +/// Please use this function wrapped with `Platform.isWindows`. +int getWindowsTargetBuildNumber() { + final rtlGetVersion = DynamicLibrary.open('ntdll.dll').lookupFunction< + Void Function(Pointer), + void Function(Pointer)>('RtlGetVersion'); + final osVersionInfo = getOSVERSIONINFOEXPointer(); + rtlGetVersion(osVersionInfo); + int buildNumber = osVersionInfo.ref.dwBuildNumber; + calloc.free(osVersionInfo); + return buildNumber; +} + +/// Get Windows OS version pointer +/// +/// [Note] +/// Please use this function wrapped with `Platform.isWindows`. +Pointer getOSVERSIONINFOEXPointer() { + final pointer = calloc(); + pointer.ref + ..dwOSVersionInfoSize = sizeOf() + ..dwBuildNumber = 0 + ..dwMajorVersion = 0 + ..dwMinorVersion = 0 + ..dwPlatformId = 0 + ..szCSDVersion = '' + ..wServicePackMajor = 0 + ..wServicePackMinor = 0 + ..wSuiteMask = 0 + ..wProductType = 0 + ..wReserved = 0; + return pointer; +} + +/// Indicating we need to use compatible ui mode. +/// +/// [Conditions] +/// - Windows 7, window will overflow when we use frameless ui. +bool get kUseCompatibleUiMode => + Platform.isWindows && + const [WindowsTarget.w7].contains(windowsBuildNumber.windowsVersion); diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 506399e6f..e868b37e2 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -266,7 +266,8 @@ class DesktopTab extends StatelessWidget { Widget build(BuildContext context) { return Column(children: [ Obx(() => Offstage( - offstage: !stateGlobal.showTabBar.isTrue, + offstage: !stateGlobal.showTabBar.isTrue || + (kUseCompatibleUiMode && isHideSingleItem()), child: SizedBox( height: _kTabBarHeight, child: Column( @@ -335,6 +336,15 @@ class DesktopTab extends StatelessWidget { .toList(growable: false)))); } + /// Check whether to show ListView + /// + /// Conditions: + /// - hide single item when only has one item (home) on [DesktopTabPage]. + bool isHideSingleItem() { + return state.value.tabs.length == 1 && + controller.tabType == DesktopTabType.main; + } + Widget _buildBar() { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -362,23 +372,26 @@ class DesktopTab extends StatelessWidget { child: const SizedBox( width: 78, )), - Row(children: [ - Offstage( - offstage: !showLogo, - child: SvgPicture.asset( - 'assets/logo.svg', - width: 16, - height: 16, - )), - Offstage( - offstage: !showTitle, - child: const Text( - "RustDesk", - style: TextStyle(fontSize: 13), - ).marginOnly(left: 2)) - ]).marginOnly( - left: 5, - right: 10, + Offstage( + offstage: kUseCompatibleUiMode, + child: Row(children: [ + Offstage( + offstage: !showLogo, + child: SvgPicture.asset( + 'assets/logo.svg', + width: 16, + height: 16, + )), + Offstage( + offstage: !showTitle, + child: const Text( + "RustDesk", + style: TextStyle(fontSize: 13), + ).marginOnly(left: 2)) + ]).marginOnly( + left: 5, + right: 10, + ), ), Expanded( child: Listener( @@ -407,16 +420,20 @@ class DesktopTab extends StatelessWidget { unSelectedTabBackgroundColor))), ], ))), - WindowActionPanel( - isMainWindow: isMainWindow, - tabType: tabType, - state: state, - tail: tail, - isMaximized: isMaximized, - showMinimize: showMinimize, - showMaximize: showMaximize, - showClose: showClose, - onClose: onWindowCloseButton, + // hide simulated action buttons when we in compatible ui mode, because of reusing system title bar. + Offstage( + offstage: kUseCompatibleUiMode, + child: WindowActionPanel( + isMainWindow: isMainWindow, + tabType: tabType, + state: state, + tail: tail, + isMaximized: isMaximized, + showMinimize: showMinimize, + showMaximize: showMaximize, + showClose: showClose, + onClose: onWindowCloseButton, + ), ) ], ); diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 08095e54c..2015c02b2 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -177,8 +177,7 @@ void runMultiWindow( MyTheme.currentThemeMode(), ); // we do not hide titlebar on win7 because of the frame overflow. - if (Platform.isWindows && - const [WindowsTarget.w7].contains(windowsBuildNumber.windowsVersion)) { + if (kUseCompatibleUiMode) { WindowController.fromWindowId(windowId!).showTitleBar(true); } switch (appType) { @@ -283,8 +282,7 @@ void runInstallPage() async { WindowOptions getHiddenTitleBarWindowOptions({Size? size}) { var defaultTitleBarStyle = TitleBarStyle.hidden; // we do not hide titlebar on win7 because of the frame overflow. - if (Platform.isWindows && - const [WindowsTarget.w7].contains(windowsBuildNumber.windowsVersion)) { + if (kUseCompatibleUiMode) { defaultTitleBarStyle = TitleBarStyle.normal; } return WindowOptions( diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index be80bb65b..dab658c25 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -10,6 +10,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:win32/win32.dart' as win32; import '../common.dart'; import '../generated_bridge.dart'; @@ -142,12 +143,14 @@ class PlatformFFI { id = linuxInfo.machineId ?? linuxInfo.id; } else if (Platform.isWindows) { try { + // request windows build number to fix overflow on win7 + windowsBuildNumber = getWindowsTargetBuildNumber(); WindowsDeviceInfo winInfo = await deviceInfo.windowsInfo; name = winInfo.computerName; id = winInfo.computerName; - windowsBuildNumber = winInfo.buildNumber; - } catch (e) { - debugPrint("$e"); + } catch (e, stacktrace) { + debugPrint("get windows device info failed: $e"); + debugPrintStack(stackTrace: stacktrace); name = "unknown"; id = "unknown"; } diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index f84cbedcd..8de0be4d6 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: provider: ^6.0.3 tuple: ^2.0.0 wakelock: ^0.6.2 - device_info_plus: ^8.0.0 + device_info_plus: ^4.1.2 #firebase_analytics: ^9.1.5 package_info_plus: ^1.4.2 url_launcher: ^6.0.9 @@ -102,6 +102,8 @@ dependencies: path: ^1.8.1 auto_size_text: ^3.0.0 bot_toast: ^4.0.3 + win32: any + dev_dependencies: icons_launcher: ^2.0.4