diff --git a/ClashApi.hpp b/ClashApi.hpp index d2fbd77..fba4b4e 100644 --- a/ClashApi.hpp +++ b/ClashApi.hpp @@ -163,6 +163,14 @@ class ClashApi return json::parse(res.data).get(); } + bool UpdateProxyGroup(std::string_view group, std::string_view selectProxy) + { + std::wstring path = L"/proxies/"; + path.append(Utf8ToUtf16(group)); + auto res = Request(path.c_str(), L"PUT", { {"name", selectProxy} }); + return res.statusCode == 204; // HTTP 204 No Content + } + private: void Connect() { diff --git a/ClashModel.hpp b/ClashModel.hpp index 2b81259..bdf9226 100644 --- a/ClashModel.hpp +++ b/ClashModel.hpp @@ -1,5 +1,15 @@ #pragma once +struct ClashProxySpeedHistory +{ + // Date time; // FIXME + uint32_t delay; + + friend void from_json(const json& j, ClashProxySpeedHistory& value) { + JSON_FROM(delay); + } +}; + struct ClashProxy { using Name = std::string; // UTF-8 @@ -24,16 +34,17 @@ struct ClashProxy }; Name name; - Type type; + Type type = Type::Unknown; std::vector all; - // ClashProxySpeedHistory history; // FIXME + std::vector history; std::optional now; friend void from_json(const json& j, ClashProxy& value) { JSON_FROM(name); JSON_FROM(type); JSON_TRY_FROM(all); - try { value.now.emplace(j.at("now").get()); } CATCH_LOG(); + JSON_FROM(history); + try { value.now.emplace(j.at("now").get()); } catch (...) {} } }; @@ -57,13 +68,17 @@ NLOHMANN_JSON_SERIALIZE_ENUM(ClashProxy::Type, { struct ClashProxies { - std::map proxiesMap; + using MapType = std::unordered_map; + + MapType proxies; friend void from_json(const json& j, ClashProxies& value) { - j.at("proxies").get_to(value.proxiesMap); + JSON_FROM(proxies); } }; +ClashProxies g_clashProxies; + struct ClashProvider { using Name = std::string; // UTF-8 @@ -104,9 +119,9 @@ NLOHMANN_JSON_SERIALIZE_ENUM(ClashProvider::VehicleType, { struct ClashProviders { - std::map allProviders; + std::map providers; - friend void from_json(const json& j, ClashProviders& value) { - j.at("providers").get_to(value.allProviders); - } + /*friend void from_json(const json& j, ClashProviders& value) { + JSON_FROM(providers); + }*/ }; diff --git a/ClashXW.cpp b/ClashXW.cpp index 1888a06..5250bce 100644 --- a/ClashXW.cpp +++ b/ClashXW.cpp @@ -301,169 +301,186 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_COMMAND: { WORD wmId = LOWORD(wParam); - auto type = static_cast(wmId >> 14); - if (type == MenuIdType::Command) + switch (wmId) { - switch (wmId) - { - case IDM_MODE_GLOBAL: - case IDM_MODE_RULE: - case IDM_MODE_DIRECT: - { - ClashProxyMode mode = static_cast(wmId - IDM_MODE_GLOBAL + 1); - [mode]() -> winrt::fire_and_forget { - co_await winrt::resume_background(); - try - { - if (!g_clashApi->UpdateProxyMode(mode)) - co_return; - g_clashConfig = g_clashApi->GetConfig(); - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - co_return; - } - co_await ResumeForeground(); - MenuUtil::UpdateMenus(); - }(); - } - break; - case IDM_ALLOWFROMLAN: - { - []() -> winrt::fire_and_forget { - co_await winrt::resume_background(); - try - { - if (!g_clashApi->UpdateAllowLan(!g_clashConfig.allowLan)) - co_return; - g_clashConfig = g_clashApi->GetConfig(); - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); + case IDM_MODE_GLOBAL: + case IDM_MODE_RULE: + case IDM_MODE_DIRECT: + { + ClashProxyMode mode = static_cast(wmId - IDM_MODE_GLOBAL + 1); + [mode]() -> winrt::fire_and_forget { + co_await winrt::resume_background(); + try + { + if (!g_clashApi->UpdateProxyMode(mode)) co_return; - } - co_await ResumeForeground(); - MenuUtil::UpdateMenus(); - }(); - } - break; - case IDM_LOGLEVEL_ERROR: - case IDM_LOGLEVEL_WARNING: - case IDM_LOGLEVEL_INFO: - case IDM_LOGLEVEL_DEBUG: - case IDM_LOGLEVEL_SILENT: - { - ClashLogLevel level = static_cast(wmId - IDM_LOGLEVEL_ERROR + 1); - [level]() -> winrt::fire_and_forget { - co_await winrt::resume_background(); - try - { - if (!g_clashApi->UpdateLogLevel(level)) - co_return; - g_clashConfig = g_clashApi->GetConfig(); - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); + g_clashConfig = g_clashApi->GetConfig(); + } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); + co_return; + } + co_await ResumeForeground(); + MenuUtil::UpdateMenus(); + }(); + } + break; + case IDM_ALLOWFROMLAN: + { + []() -> winrt::fire_and_forget { + co_await winrt::resume_background(); + try + { + if (!g_clashApi->UpdateAllowLan(!g_clashConfig.allowLan)) co_return; - } - co_await ResumeForeground(); - MenuUtil::UpdateMenus(); - }(); - } - break; - case IDM_SYSTEMPROXY: - EnableSystemProxy(!g_settings.systemProxy); - nid.hIcon = SelectNotifyIcon(); - Shell_NotifyIconW(NIM_MODIFY, &nid); - break; - case IDM_STARTATLOGIN: - StartAtLogin::SetEnable(!StartAtLogin::IsEnabled()); - break; - case IDM_HELP_ABOUT: - { - static bool opened = false; - if (!opened) + g_clashConfig = g_clashApi->GetConfig(); + } + catch (...) { - opened = true; - DialogBoxW(g_hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); - opened = false; + LOG_CAUGHT_EXCEPTION(); + co_return; } - } - break; - case IDM_DASHBOARD: - if (g_settings.openDashboardInBrowser) - ShellExecuteW(hWnd, nullptr, L"http://127.0.0.1:9090/ui/", nullptr, nullptr, SW_SHOWNORMAL); - else + co_await ResumeForeground(); + MenuUtil::UpdateMenus(); + }(); + } + break; + case IDM_LOGLEVEL_ERROR: + case IDM_LOGLEVEL_WARNING: + case IDM_LOGLEVEL_INFO: + case IDM_LOGLEVEL_DEBUG: + case IDM_LOGLEVEL_SILENT: + { + ClashLogLevel level = static_cast(wmId - IDM_LOGLEVEL_ERROR + 1); + [level]() -> winrt::fire_and_forget { + co_await winrt::resume_background(); + try { - if (EdgeWebView2::GetCount() == 0) - EdgeWebView2::Create(); + if (!g_clashApi->UpdateLogLevel(level)) + co_return; + g_clashConfig = g_clashApi->GetConfig(); } - break; - case IDM_CONFIG_OPENFOLDER: - ShellExecuteW(hWnd, nullptr, g_configPath.c_str(), nullptr, nullptr, SW_SHOWNORMAL); - break; - case IDM_CONFIG_RELOAD: - UpdateConfigFile({}); - break; - case IDM_REMOTECONFIG_MANAGE: - ShowRemoteConfigDialog(hWnd); - break; - case IDM_REMOTECONFIG_UPDATE: - []() -> winrt::fire_and_forget { - co_await winrt::resume_background(); - RemoteConfigManager::CheckUpdate(true); - }(); - break; - case IDM_REMOTECONFIG_AUTOUPDATE: - g_settings.configAutoUpdate = !g_settings.configAutoUpdate; - CheckMenuItem(g_hContextMenu, IDM_REMOTECONFIG_AUTOUPDATE, MF_BYCOMMAND | (g_settings.configAutoUpdate ? MF_CHECKED : MF_UNCHECKED)); - break; - case IDM_EXPERIMENTAL_SETBENCHURL: - { - auto benchmarkUrl = g_settings.benchmarkUrl; - int button = 0; - auto hr = TaskDialogInput(hWnd, g_hInst, _(L"Set benchmark url"), nullptr, _(L"Set benchmark url"), TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON, MAKEINTRESOURCEW(IDI_CLASHXW), &button, benchmarkUrl); - if (SUCCEEDED(hr) && button == IDOK) + catch (...) { - if (IsUrlVaild(benchmarkUrl.c_str())) - g_settings.benchmarkUrl = benchmarkUrl; - else - TaskDialog(hWnd, nullptr, _(L"Warning"), nullptr, _(L"URL is not valid"), TDCBF_OK_BUTTON, TD_WARNING_ICON, nullptr); + LOG_CAUGHT_EXCEPTION(); + co_return; } + co_await ResumeForeground(); + MenuUtil::UpdateMenus(); + }(); + } + break; + case IDM_SYSTEMPROXY: + EnableSystemProxy(!g_settings.systemProxy); + nid.hIcon = SelectNotifyIcon(); + Shell_NotifyIconW(NIM_MODIFY, &nid); + break; + case IDM_STARTATLOGIN: + StartAtLogin::SetEnable(!StartAtLogin::IsEnabled()); + break; + case IDM_HELP_ABOUT: + { + static bool opened = false; + if (!opened) + { + opened = true; + DialogBoxW(g_hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); + opened = false; + } + } + break; + case IDM_DASHBOARD: + if (g_settings.openDashboardInBrowser) + ShellExecuteW(hWnd, nullptr, L"http://127.0.0.1:9090/ui/", nullptr, nullptr, SW_SHOWNORMAL); + else + { + if (EdgeWebView2::GetCount() == 0) + EdgeWebView2::Create(); } break; - case IDM_EXPERIMENTAL_OPENDASHBOARDINBROWSER: - g_settings.openDashboardInBrowser = !g_settings.openDashboardInBrowser; - CheckMenuItem(g_hContextMenu, IDM_EXPERIMENTAL_OPENDASHBOARDINBROWSER, MF_BYCOMMAND | (g_settings.openDashboardInBrowser ? MF_CHECKED : MF_UNCHECKED)); - break; - case IDM_EXPERIMENTAL_SHOWCLASHCONSOLE: + case IDM_CONFIG_OPENFOLDER: + ShellExecuteW(hWnd, nullptr, g_configPath.c_str(), nullptr, nullptr, SW_SHOWNORMAL); + break; + case IDM_CONFIG_RELOAD: + UpdateConfigFile({}); + break; + case IDM_REMOTECONFIG_MANAGE: + ShowRemoteConfigDialog(hWnd); + break; + case IDM_REMOTECONFIG_UPDATE: + []() -> winrt::fire_and_forget { + co_await winrt::resume_background(); + RemoteConfigManager::CheckUpdate(true); + }(); + break; + case IDM_REMOTECONFIG_AUTOUPDATE: + g_settings.configAutoUpdate = !g_settings.configAutoUpdate; + CheckMenuItem(g_hContextMenu, IDM_REMOTECONFIG_AUTOUPDATE, MF_BYCOMMAND | (g_settings.configAutoUpdate ? MF_CHECKED : MF_UNCHECKED)); + break; + case IDM_EXPERIMENTAL_SETBENCHURL: + { + auto benchmarkUrl = g_settings.benchmarkUrl; + int button = 0; + auto hr = TaskDialogInput(hWnd, g_hInst, _(L"Set benchmark url"), nullptr, _(L"Set benchmark url"), TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON, MAKEINTRESOURCEW(IDI_CLASHXW), &button, benchmarkUrl); + if (SUCCEEDED(hr) && button == IDOK) { - const HWND hWndConsole = ProcessManager::GetConsoleWindow(); - if (hWndConsole) - { - ShowConsoleWindow(!IsWindowVisible(hWndConsole)); - } + if (IsUrlVaild(benchmarkUrl.c_str())) + g_settings.benchmarkUrl = benchmarkUrl; else - ShowConsoleWindow(false); + TaskDialog(hWnd, nullptr, _(L"Warning"), nullptr, _(L"URL is not valid"), TDCBF_OK_BUTTON, TD_WARNING_ICON, nullptr); } + } + break; + case IDM_EXPERIMENTAL_OPENDASHBOARDINBROWSER: + g_settings.openDashboardInBrowser = !g_settings.openDashboardInBrowser; + CheckMenuItem(g_hContextMenu, IDM_EXPERIMENTAL_OPENDASHBOARDINBROWSER, MF_BYCOMMAND | (g_settings.openDashboardInBrowser ? MF_CHECKED : MF_UNCHECKED)); break; - case IDM_QUIT: - DestroyWindow(hWnd); - break; - default: - return DefWindowProcW(hWnd, message, wParam, lParam); + case IDM_EXPERIMENTAL_SHOWCLASHCONSOLE: + { + const HWND hWndConsole = ProcessManager::GetConsoleWindow(); + if (hWndConsole) + { + ShowConsoleWindow(!IsWindowVisible(hWndConsole)); } + else + ShowConsoleWindow(false); } - else if (type == MenuIdType::Config) + break; + case IDM_QUIT: + DestroyWindow(hWnd); + break; + default: + return DefWindowProcW(hWnd, message, wParam, lParam); + } + } + break; + case WM_MENUCOMMAND: + { + size_t i = static_cast(wParam); + HMENU hMenu = reinterpret_cast(lParam); + + if (hMenu == g_hTopMenu || hMenu == g_hPortsMenu) {} + else if (hMenu == g_hContextMenu || + hMenu == g_hProxyModeMenu || + hMenu == g_hLogLevelMenu) + { + MenuUtil::PostCommandByMenuIndex(hWnd, hMenu, i); + } + else if (hMenu == g_hConfigMenu) { - size_t i = wmId & 0x3FFF; if (i < g_configFilesList.size()) { UpdateConfigFile(g_configFilesList[i]); } + else + { + MenuUtil::PostCommandByMenuIndex(hWnd, hMenu, i); + } + } + else if (!MenuUtil::HandleProxyGroupsItemChoose(hMenu, i)) + { + MenuUtil::PostCommandByMenuIndex(hWnd, hMenu, i); } } break; @@ -514,10 +531,14 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) try { g_clashConfig = g_clashApi->GetConfig(); + g_clashProxies = g_clashApi->GetProxies(); } CATCH_LOG(); - }().wait_for(100ms); - +#ifdef _DEBUG + }().wait_for(1000ms); +#else + }().wait_for(200ms); +#endif co_await ResumeForeground(); MenuUtil::UpdateMenus(); MenuUtil::ShowContextMenu(hWnd, GET_X_LPARAM(wParam), GET_Y_LPARAM(wParam)); diff --git a/ClashXW.h b/ClashXW.h index b504a8b..6ac70da 100644 --- a/ClashXW.h +++ b/ClashXW.h @@ -80,6 +80,7 @@ constexpr auto CLASH_CTL_SECRET = L""; #include "SettingsUtil.hpp" #include "ClashModel.hpp" #include "ClashApi.hpp" +std::unique_ptr g_clashApi; #include "ConfigFileManager.hpp" #include "MenuUtil.hpp" #include "ProcessManager.hpp" @@ -93,7 +94,4 @@ constexpr auto CLASH_CTL_SECRET = L""; #include "PortableModeUtil.hpp" #include "RegistryUtil.hpp" #include "URLProtocolUtil.hpp" - -std::unique_ptr g_clashApi; - #include "EdgeWebView2.hpp" diff --git a/MenuUtil.hpp b/MenuUtil.hpp index ceebcbf..097a65e 100644 --- a/MenuUtil.hpp +++ b/MenuUtil.hpp @@ -63,12 +63,6 @@ const std::unordered_map MenuAccel = { }; wil::unique_hhook g_hMenuHook; -enum class MenuIdType -{ - Command, - Config, -}; - namespace MenuUtil { BOOL SetMenuItemText(HMENU hMenu, UINT pos, const wchar_t* text) noexcept @@ -83,6 +77,16 @@ namespace MenuUtil namespace { + struct MenuProxyGroup + { + ClashProxy* model; + std::vector allProxies; + HMENU hMenu; + }; + + std::vector s_menuProxyGroups; + ClashProxies::MapType s_menuProxies; + bool ProcessAccelerator(LPMSG msg) { if (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN) @@ -107,9 +111,9 @@ namespace MenuUtil void UpdateContextMenu() noexcept { - CheckMenuItem(g_hContextMenu, 2, MF_BYPOSITION | (g_settings.systemProxy ? MF_CHECKED : MF_UNCHECKED)); - CheckMenuItem(g_hContextMenu, 5, MF_BYPOSITION | (StartAtLogin::IsEnabled() ? MF_CHECKED : MF_UNCHECKED)); - CheckMenuItem(g_hContextMenu, 7, MF_BYPOSITION | (g_clashConfig.allowLan ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(g_hContextMenu, IDM_SYSTEMPROXY, g_settings.systemProxy ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(g_hContextMenu, IDM_STARTATLOGIN, StartAtLogin::IsEnabled() ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(g_hContextMenu, IDM_ALLOWFROMLAN, g_clashConfig.allowLan ? MF_CHECKED : MF_UNCHECKED); } void UpdateProxyModeMenu() @@ -150,10 +154,9 @@ namespace MenuUtil auto stem = n.stem(); MENUITEMINFOW mii = { .cbSize = sizeof(mii), - .fMask = MIIM_FTYPE | MIIM_STATE | MIIM_ID | MIIM_STRING, + .fMask = MIIM_FTYPE | MIIM_STATE | MIIM_STRING, .fType = MFT_RADIOCHECK, .fState = static_cast(n == g_settings.configFile ? MFS_CHECKED : MFS_UNCHECKED), - .wID = (static_cast(MenuIdType::Config) << 14) | i, .dwTypeData = const_cast(stem.c_str()) }; InsertMenuItemW(g_hConfigMenu, i, TRUE, &mii); @@ -178,6 +181,121 @@ namespace MenuUtil swprintf_s(text, _(L"Mixed Port: %hu"), g_clashConfig.mixedPort); SetMenuItemText(g_hPortsMenu, 2, text); } + + void AddMenuProxyGroup(ClashProxy& group) + { + std::vector allProxies; + allProxies.reserve(group.all.size()); + for (const auto& key : group.all) + { + auto& proxy = s_menuProxies.at(key); + allProxies.emplace_back(&proxy); + } + s_menuProxyGroups.emplace_back(&group, std::move(allProxies)); + } + + void BuildMenuProxyGroups() + { + s_menuProxyGroups.clear(); + s_menuProxies = std::move(g_clashProxies.proxies); + + auto& global = s_menuProxies.at("GLOBAL"); + if (g_clashConfig.mode == ClashProxyMode::Global) + { + AddMenuProxyGroup(global); + } + else + { + static const std::unordered_set unusedProxy = { "DIRECT", "REJECT", "GLOBAL" }; + for (const auto& key : global.all) + { + if (unusedProxy.contains(key)) + continue; + auto& proxy = s_menuProxies.at(key); + if (proxy.type == ClashProxy::Type::URLTest || + proxy.type == ClashProxy::Type::Fallback || + proxy.type == ClashProxy::Type::LoadBalance || + proxy.type == ClashProxy::Type::Selector) // group type + { + AddMenuProxyGroup(proxy); + } + } + } + } + + void DeleteProxyGroupsMenu() + { + const size_t count = s_menuProxyGroups.size() + 2 + (s_menuProxyGroups.empty() ? 0 : 1); + for (size_t i = 2; i < count; ++i) + DeleteMenu(g_hContextMenu, static_cast(i), MF_BYPOSITION); + } + + void RebuildProxyGroupsMenu() + { + UINT i = 2; + for (auto& group : s_menuProxyGroups) + { + wil::unique_hmenu hMenu(CreatePopupMenu()); + THROW_LAST_ERROR_IF_NULL(hMenu); + + for (auto proxy : group.allProxies) + { + bool selected = group.model->now && *group.model->now == proxy->name; + std::wstring text = Utf8ToUtf16(proxy->name); + if (!proxy->history.empty()) + { + text += L'\t'; + auto delay = proxy->history.back().delay; + if (delay == 0) + { + text += L"fail"; + } + else + { + text += std::to_wstring(delay); + text += L"ms"; + } + } + AppendMenuW(hMenu.get(), selected ? MF_CHECKED : 0, 0, text.c_str()); + } + + group.hMenu = hMenu.release(); + InsertMenuW(g_hContextMenu, i, MF_BYPOSITION | MF_POPUP, reinterpret_cast(group.hMenu), Utf8ToUtf16(group.model->name).c_str()); + ++i; + } + + if (!s_menuProxyGroups.empty()) + { + InsertMenuW(g_hContextMenu, i, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr); + } + } + + void UpdateProxyGroupsMenu() + { + bool rebuild = s_menuProxies.size() != g_clashProxies.proxies.size(); + if (!rebuild) + { + for (auto& [name, proxy] : s_menuProxies) + { + auto it = g_clashProxies.proxies.find(name); + if (it == g_clashProxies.proxies.end()) + { + rebuild = true; + break; + } + auto& newProxy = it->second; + proxy.history = newProxy.history; // copy + proxy.now = newProxy.now; + } + } + + if (rebuild) + { + DeleteProxyGroupsMenu(); + BuildMenuProxyGroups(); + RebuildProxyGroupsMenu(); + } + } } void SetupMenu() noexcept @@ -189,6 +307,13 @@ namespace MenuUtil g_hContextMenu = GetSubMenu(g_hTopMenu, 0); THROW_LAST_ERROR_IF_NULL(g_hContextMenu); + MENUINFO mi = { + .cbSize = sizeof(mi), + .fMask = MIM_STYLE, + .dwStyle = MNS_NOTIFYBYPOS + }; + SetMenuInfo(g_hContextMenu, &mi); + g_hProxyModeMenu = GetSubMenu(g_hContextMenu, 0); THROW_LAST_ERROR_IF_NULL(g_hProxyModeMenu); @@ -210,19 +335,28 @@ namespace MenuUtil if (code == MSGF_MENU) { auto msg = reinterpret_cast(lParam); + //LOG_HR_MSG(E_FAIL, "menu: %d", msg->message); if (msg->message == WM_SYSKEYDOWN && msg->wParam == VK_MENU) return TRUE; // Prevent menu closing if (ProcessAccelerator(msg)) msg->wParam = VK_ESCAPE; // Force menu to close } return CallNextHookEx(g_hMenuHook.get(), code, wParam, lParam); - }, nullptr, GetCurrentThreadId())); + }, nullptr, GetCurrentThreadId())); } CATCH_FAIL_FAST(); } void ShowContextMenu(HWND hWnd, int x, int y) noexcept { + static wil::unique_hhook mouseHook; + mouseHook.reset(SetWindowsHookExW(WH_MOUSE_LL, [](int code, WPARAM wParam, LPARAM lParam) -> LRESULT { + if (code == HC_ACTION) + { + //LOG_HR_MSG(E_FAIL, "mouse hook: %d", wParam); + } + return CallNextHookEx(mouseHook.get(), code, wParam, lParam); + }, nullptr, 0)); try { THROW_IF_WIN32_BOOL_FALSE(SetForegroundWindow(hWnd)); @@ -234,7 +368,9 @@ namespace MenuUtil THROW_IF_WIN32_BOOL_FALSE(TrackPopupMenuEx(g_hContextMenu, flags, x, y, hWnd, nullptr)); } - CATCH_LOG_RETURN(); + CATCH_LOG(); + mouseHook.reset(); + //UnregisterTouchWindow(hWnd); } void UpdateMenus() @@ -244,5 +380,52 @@ namespace MenuUtil UpdateConfigMenu(); UpdateLogLevelMenu(); UpdatePortsMenu(); + UpdateProxyGroupsMenu(); + } + + void PostCommandByMenuIndex(HWND hWnd, HMENU hMenu, size_t i) + { + UINT id = GetMenuItemID(hMenu, static_cast(i)); + if (id != UINT_ERROR) + PostMessageW(hWnd, WM_COMMAND, MAKEWPARAM(id, 1), 0); + } + + bool HandleProxyGroupsItemChoose(HMENU hMenu, size_t i) + { + auto it = std::find_if(s_menuProxyGroups.begin(), s_menuProxyGroups.end(), [hMenu](const MenuProxyGroup& g) { return g.hMenu == hMenu; }); + if (it != s_menuProxyGroups.end()) + { + MenuProxyGroup& group = *it; + if (i < group.allProxies.size()) + { + [](HMENU hMenu, size_t i, MenuProxyGroup& group) -> winrt::fire_and_forget { + co_await winrt::resume_background(); + try + { + if (!g_clashApi->UpdateProxyGroup(group.model->name, group.allProxies[i]->name)) + co_return; + } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); + co_return; + } + + group.model->now = group.allProxies[i]->name; + + co_await ResumeForeground(); + DWORD ret; + UINT j = 0; + do + { + ret = CheckMenuItem(hMenu, j, MF_BYPOSITION | MF_UNCHECKED); + ++j; + } while (ret != DWORD_ERROR); + CheckMenuItem(hMenu, static_cast(i), MF_BYPOSITION | MF_CHECKED); + }(hMenu, i, group); + } + return true; + } + return false; } } diff --git a/framework.h b/framework.h index 15c830c..d69e21a 100644 --- a/framework.h +++ b/framework.h @@ -35,5 +35,6 @@ #include #include #include +#include #include diff --git a/pch.h b/pch.h index ad4056f..bc6f16d 100644 --- a/pch.h +++ b/pch.h @@ -28,7 +28,8 @@ #define JSON_TO(k) j[#k] = value.k; #define JSON_FROM(k) j.at(#k).get_to(value.k); -#define JSON_TRY_FROM(k) try { j.at(#k).get_to(value.k); } CATCH_LOG() +#define JSON_TRY_FROM(k) try { j.at(#k).get_to(value.k); } catch (...) {} +#define JSON_TRY_FROM_LOG(k) try { j.at(#k).get_to(value.k); } CATCH_LOG() // Edge WebView2 #include "WebView2.h"