diff --git a/client/lib/main.dart b/client/lib/main.dart index 8a75226..55af956 100644 --- a/client/lib/main.dart +++ b/client/lib/main.dart @@ -46,17 +46,31 @@ class PangolinApp extends ConsumerStatefulWidget { class _PangolinAppState extends ConsumerState { AppLifecycleListener? _lifecycle; + // 是否正在「真退出」(托盘「退出」菜单触发)。区分:点关闭只隐藏到托盘、保持隧道。 + bool _isQuitting = false; @override void initState() { super.initState(); if (isDesktop) { - // 真退出(Cmd+Q / 托盘退出)前先停内核拆隧道,避免 sing-box 孤儿 + TUN 残留。 + // 关闭 / 退出请求的处理: + // - Windows:点 X / Alt+F4 会触发 onExitRequested,但桌面端要常驻托盘、隧道不断 + // → 取消退出并隐藏到托盘。真正退出只走托盘「退出」菜单(见 onBeforeQuit)。 + // (否则会出现:连接被拆、窗口却只是隐藏,即「关掉就断网但进程还在」。) + // - macOS:Cmd+Q 等真退出 → 先停内核拆隧道再退出(红叉关闭走 onWindowClose 隐藏)。 _lifecycle = AppLifecycleListener(onExitRequested: () async { + if (Platform.isWindows && !_isQuitting) { + await windowManager.hide(); + return ui.AppExitResponse.cancel; + } await _teardownVpn(); return ui.AppExitResponse.exit; }); - TrayService.instance.onBeforeQuit = _teardownVpn; + // 托盘「退出」前置钩子:标记真退出 + 停内核拆隧道(避免 sing-box 孤儿 + TUN 残留)。 + TrayService.instance.onBeforeQuit = () async { + _isQuitting = true; + await _teardownVpn(); + }; } } diff --git a/client/pubspec.yaml b/client/pubspec.yaml index e697407..9d57dbb 100644 --- a/client/pubspec.yaml +++ b/client/pubspec.yaml @@ -1,7 +1,7 @@ name: pangolin_vpn description: 穿山甲 · Pangolin — 极简、稳定、跨平台网络加速客户端。 publish_to: "none" -version: 1.0.1+2 +version: 1.0.2+3 environment: sdk: ^3.5.0 diff --git a/client/windows/installer/pangolin.iss b/client/windows/installer/pangolin.iss index 93df1d2..6dbdb72 100644 --- a/client/windows/installer/pangolin.iss +++ b/client/windows/installer/pangolin.iss @@ -3,7 +3,7 @@ ; 前置: 先在 client/ 跑 `flutter build windows`(Release),产物在 ; ..\..\build\windows\x64\runner\Release #define MyAppName "穿山甲 Pangolin" -#define MyAppVersion "1.0.1" +#define MyAppVersion "1.0.2" #define MyAppPublisher "Pangolin" #define MyAppExeName "pangolin_vpn.exe" #define BuildDir "..\..\build\windows\x64\runner\Release" diff --git a/client/windows/runner/win32_window.cpp b/client/windows/runner/win32_window.cpp index 60608d0..c37ab98 100644 --- a/client/windows/runner/win32_window.cpp +++ b/client/windows/runner/win32_window.cpp @@ -144,6 +144,25 @@ bool Win32Window::Create(const std::wstring& title, return false; } + // 显式给窗口设大/小两种图标:任务栏 / Alt-Tab 用 ICON_BIG(32px),标题栏用 + // ICON_SMALL(16px)。仅靠 WNDCLASS.hIcon(LoadIcon 取单一尺寸)时,任务栏可能 + // 拿不到正确尺寸而沿用旧/默认图标 —— 从多尺寸 app_icon.ico 按系统尺寸各取一份。 + HINSTANCE instance = GetModuleHandle(nullptr); + HANDLE big_icon = LoadImage(instance, MAKEINTRESOURCE(IDI_APP_ICON), IMAGE_ICON, + GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR); + HANDLE small_icon = LoadImage(instance, MAKEINTRESOURCE(IDI_APP_ICON), IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR); + if (big_icon) { + SendMessage(window, WM_SETICON, ICON_BIG, + reinterpret_cast(big_icon)); + } + if (small_icon) { + SendMessage(window, WM_SETICON, ICON_SMALL, + reinterpret_cast(small_icon)); + } + UpdateTheme(window); return OnCreate();