feat: windows, custom client, update (#13687)

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou
2026-02-27 21:50:20 +08:00
committed by GitHub
parent d49ae493b2
commit 4abdb2e08b
15 changed files with 957 additions and 124 deletions

View File

@@ -3938,7 +3938,9 @@ void earlyAssert() {
void checkUpdate() {
if (!isWeb) {
if (!bind.isCustomClient()) {
final isWindowsInstalled = isWindows && bind.mainIsInstalled();
final shouldCheckUpdate = isWindowsInstalled || !bind.isCustomClient();
if (shouldCheckUpdate) {
platformFFI.registerEventHandler(
kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish,
(Map<String, dynamic> evt) async {

View File

@@ -430,10 +430,12 @@ class _DesktopHomePageState extends State<DesktopHomePage>
}
Widget buildHelpCards(String updateUrl) {
if (!bind.isCustomClient() &&
updateUrl.isNotEmpty &&
final isWindowsInstalled = isWindows && bind.mainIsInstalled();
if (updateUrl.isNotEmpty &&
!isCardClosed &&
bind.mainUriPrefixSync().contains('rustdesk')) {
(isWindowsInstalled ||
(!bind.isCustomClient() &&
bind.mainUriPrefixSync().contains('rustdesk')))) {
final isToUpdate = (isWindows || isMacOS) && bind.mainIsInstalled();
String btnText = isToUpdate ? 'Update' : 'Download';
GestureTapCallback onPressed = () async {

View File

@@ -473,8 +473,7 @@ class _GeneralState extends State<_General> {
}
Widget other() {
final showAutoUpdate =
isWindows && bind.mainIsInstalled() && !bind.isCustomClient();
final showAutoUpdate = isWindows && bind.mainIsInstalled();
final children = <Widget>[
if (!isWeb && !bind.isIncomingOnly())
_OptionCheckBox(context, 'Confirm before closing multiple tabs',

View File

@@ -7,6 +7,7 @@
#include <cstdlib> // for getenv and _putenv
#include <cstring> // for strcmp
#include <string> // for std::wstring
namespace {
@@ -15,6 +16,43 @@ constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
// The number of Win32Window objects that currently exist.
static int g_active_window_count = 0;
// Static variable to hold the custom icon (needs cleanup on exit)
static HICON g_custom_icon_ = nullptr;
// Try to load icon from data\flutter_assets\assets\icon.ico if it exists.
// Returns nullptr if the file doesn't exist or can't be loaded.
HICON LoadCustomIcon() {
if (g_custom_icon_ != nullptr) {
return g_custom_icon_;
}
wchar_t exe_path[MAX_PATH];
if (!GetModuleFileNameW(nullptr, exe_path, MAX_PATH)) {
return nullptr;
}
std::wstring icon_path = exe_path;
size_t last_slash = icon_path.find_last_of(L"\\/");
if (last_slash == std::wstring::npos) {
return nullptr;
}
icon_path = icon_path.substr(0, last_slash + 1);
icon_path += L"data\\flutter_assets\\assets\\icon.ico";
// Check file attributes - reject if missing, directory, or reparse point (symlink/junction)
DWORD file_attr = GetFileAttributesW(icon_path.c_str());
if (file_attr == INVALID_FILE_ATTRIBUTES ||
(file_attr & FILE_ATTRIBUTE_DIRECTORY) ||
(file_attr & FILE_ATTRIBUTE_REPARSE_POINT)) {
return nullptr;
}
g_custom_icon_ = (HICON)LoadImageW(
nullptr, icon_path.c_str(), IMAGE_ICON, 0, 0,
LR_LOADFROMFILE | LR_DEFAULTSIZE);
return g_custom_icon_;
}
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
// Scale helper to convert logical scaler values to physical using passed in
@@ -81,8 +119,16 @@ const wchar_t* WindowClassRegistrar::GetWindowClass() {
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = GetModuleHandle(nullptr);
window_class.hIcon =
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
// Try to load icon from data\flutter_assets\assets\icon.ico if it exists
HICON custom_icon = LoadCustomIcon();
if (custom_icon != nullptr) {
window_class.hIcon = custom_icon;
} else {
window_class.hIcon =
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
}
window_class.hbrBackground = 0;
window_class.lpszMenuName = nullptr;
window_class.lpfnWndProc = Win32Window::WndProc;
@@ -95,6 +141,12 @@ const wchar_t* WindowClassRegistrar::GetWindowClass() {
void WindowClassRegistrar::UnregisterWindowClass() {
UnregisterClass(kWindowClassName, nullptr);
class_registered_ = false;
// Clean up the custom icon if it was loaded
if (g_custom_icon_ != nullptr) {
DestroyIcon(g_custom_icon_);
g_custom_icon_ = nullptr;
}
}
Win32Window::Win32Window() {