From 991c18b53f3640c18e3914a5d6fddb81238f84ba Mon Sep 17 00:00:00 2001 From: vanzue <13770338596@163.com> Date: Tue, 1 Apr 2025 14:58:58 +0800 Subject: [PATCH 01/13] Workspaces fix: capture steam games. --- .../Workspaces/WorkspacesLib/AppUtils.cpp | 26 ++- .../Workspaces/WorkspacesLib/AppUtils.h | 1 + .../WorkspacesLib/SteamGameHelper.cpp | 158 ++++++++++++++++++ .../Workspaces/WorkspacesLib/SteamHelper.h | 26 +++ .../WorkspacesLib/WorkspacesLib.vcxproj | 2 + .../WorkspacesLib.vcxproj.filters | 6 + .../workspaces-common/WindowFilter.h | 3 +- 7 files changed, 219 insertions(+), 3 deletions(-) create mode 100644 src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp create mode 100644 src/modules/Workspaces/WorkspacesLib/SteamHelper.h diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp index 19b33214b7f2..9895c38c5852 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "AppUtils.h" +#include "SteamHelper.h" #include #include @@ -34,6 +35,10 @@ namespace Utils constexpr const wchar_t* EdgeFilename = L"msedge.exe"; constexpr const wchar_t* ChromeFilename = L"chrome.exe"; + + constexpr const wchar_t* SteamWindowExeName = L"steamwebhelper.exe"; + constexpr const wchar_t* SteamUrlProtocol = L"steam:"; + constexpr const wchar_t* SteamExeName = L"steam.exe"; } AppList IterateAppsFolder() @@ -137,7 +142,26 @@ namespace Utils } else if (prop == NonLocalizable::PackageInstallPathProp || prop == NonLocalizable::InstallPathProp) { - data.installPath = propVariantString.m_pData; + const std::wstring path = propVariantString.m_pData; + + if (!path.empty()) + { + const bool isSteamProtocol = path.rfind(NonLocalizable::SteamUrlProtocol, 0) == 0; + + if (isSteamProtocol) + { + Logger::info(L"Found steam game: {}", path); + data.protocolPath = path; + + auto gameId = Steam::GetGameIdFromUrlProtocolPath(path); + auto gameFolder = Steam::GetSteamGameInfoFromAcfFile(gameId); + data.installPath = gameFolder->gameInstallationPath; + } + else + { + data.installPath = path; + } + } } } diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.h b/src/modules/Workspaces/WorkspacesLib/AppUtils.h index 3c81049f8354..8482eb939c9d 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.h +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.h @@ -13,6 +13,7 @@ namespace Utils std::wstring packageFullName; std::wstring appUserModelId; std::wstring pwaAppId; + std::wstring protocolPath; bool canLaunchElevated = false; bool IsEdge() const; diff --git a/src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp b/src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp new file mode 100644 index 000000000000..11e6cde10854 --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp @@ -0,0 +1,158 @@ +#include "pch.h" +#include "SteamHelper.h" +#include +#include +#include +#include +#include +#include + +namespace Utils +{ + + static std::wstring Utf8ToWide(const std::string& utf8) + { + if (utf8.empty()) + return L""; + + int size = MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast(utf8.size()), nullptr, 0); + if (size <= 0) + return L""; + + std::wstring wide(size, L'\0'); + MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast(utf8.size()), wide.data(), size); + return wide; + } + + namespace Steam + { + using namespace std; + namespace fs = std::filesystem; + + static std::optional GetSteamExePathFromRegistry() + { + static std::optional cachedPath; + if (cachedPath.has_value()) + { + return cachedPath; + } + + const std::vector roots = { HKEY_CLASSES_ROOT, HKEY_LOCAL_MACHINE, HKEY_USERS }; + const std::vector subKeys = { + L"steam\\shell\\open\\command", + L"Software\\Classes\\steam\\shell\\open\\command", + }; + + for (HKEY root : roots) + { + for (const auto& subKey : subKeys) + { + HKEY hKey; + if (RegOpenKeyExW(root, subKey.c_str(), 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + wchar_t value[512]; + DWORD size = sizeof(value); + DWORD type = 0; + + if (RegQueryValueExW(hKey, nullptr, nullptr, &type, reinterpret_cast(value), &size) == ERROR_SUCCESS && + (type == REG_SZ || type == REG_EXPAND_SZ)) + { + std::wregex exeRegex(LR"delim("([^"]+steam\.exe)")delim"); + std::wcmatch match; + if (std::regex_search(value, match, exeRegex) && match.size() > 1) + { + RegCloseKey(hKey); + cachedPath = match[1].str(); + return cachedPath; + } + } + + RegCloseKey(hKey); + } + } + } + + cachedPath = std::nullopt; + return std::nullopt; + } + + static fs::path GetAcfFilePath(const std::wstring& gameId) + { + auto steamFolderOpt = GetSteamExePathFromRegistry(); + if (!steamFolderOpt) + { + return {}; + } + + return fs::path(*steamFolderOpt).parent_path() / L"steamapps" / (L"appmanifest_" + gameId + L".acf"); + } + + static fs::path GetGameInstallPath(const std::wstring& gameFolderName) + { + auto steamFolderOpt = GetSteamExePathFromRegistry(); + if (!steamFolderOpt) + { + return {}; + } + + return fs::path(*steamFolderOpt).parent_path() / L"steamapps" / L"common" / gameFolderName; + } + + static unordered_map ParseAcfFile(const fs::path& acfPath) + { + unordered_map result; + + ifstream file(acfPath); + if (!file.is_open()) + return result; + + string line; + while (getline(file, line)) + { + smatch matches; + static const regex pattern(R"delim("([^"]+)"\s+"([^"]+)")delim"); + + if (regex_search(line, matches, pattern) && matches.size() == 3) + { + wstring key = Utf8ToWide(matches[1].str()); + wstring value = Utf8ToWide(matches[2].str()); + result[key] = value; + } + } + + return result; + } + + std::unique_ptr GetSteamGameInfoFromAcfFile(const std::wstring& gameId) + { + fs::path acfPath = Steam::GetAcfFilePath(gameId); + + if (!fs::exists(acfPath)) + return nullptr; + + auto kv = ParseAcfFile(acfPath); + if (kv.empty() || kv.find(L"installdir") == kv.end()) + return nullptr; + + fs::path gamePath = Steam::GetGameInstallPath(kv[L"installdir"]); + + auto game = std::make_unique(); + game->gameId = gameId; + game->gameInstallationPath = gamePath.wstring(); + return game; + } + + std::wstring GetGameIdFromUrlProtocolPath(const std::wstring& urlPath) + { + const std::wstring steamGamePrefix = L"steam://rungameid/"; + + if (urlPath.rfind(steamGamePrefix, 0) == 0) + { + return urlPath.substr(steamGamePrefix.length()); + } + + return L""; + } + + } +} \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib/SteamHelper.h b/src/modules/Workspaces/WorkspacesLib/SteamHelper.h new file mode 100644 index 000000000000..40ab467ebc7c --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib/SteamHelper.h @@ -0,0 +1,26 @@ +#pragma once + +#include "pch.h" + +namespace Utils +{ + namespace NonLocalizable + { + // When steam is running, window process's exe is this. + const std::wstring SteamWindowExePath = L"steamwebhelper.exe"; + const std::wstring AcfFileNameTemplate = L"appmanifest_.acfs"; + } + + namespace Steam + { + struct SteamGame + { + std::wstring gameId; + std::wstring gameInstallationPath; + }; + + std::unique_ptr GetSteamGameInfoFromAcfFile(const std::wstring& gameId); + + std::wstring GetGameIdFromUrlProtocolPath(const std::wstring& urlPath); + } +} diff --git a/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj b/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj index 27394e29ee0b..7d29741a0deb 100644 --- a/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj +++ b/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj @@ -41,6 +41,7 @@ + @@ -57,6 +58,7 @@ Create + diff --git a/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj.filters b/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj.filters index b066c16a578e..f4f17c55ee5b 100644 --- a/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj.filters +++ b/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj.filters @@ -53,6 +53,9 @@ Header Files + + Header Files + @@ -88,6 +91,9 @@ Source Files + + Source Files + diff --git a/src/modules/Workspaces/workspaces-common/WindowFilter.h b/src/modules/Workspaces/workspaces-common/WindowFilter.h index c76ad81237c3..3487c8f5a839 100644 --- a/src/modules/Workspaces/workspaces-common/WindowFilter.h +++ b/src/modules/Workspaces/workspaces-common/WindowFilter.h @@ -9,10 +9,9 @@ namespace WindowFilter { auto style = GetWindowLong(window, GWL_STYLE); bool isPopup = WindowUtils::HasStyle(style, WS_POPUP); - bool hasThickFrame = WindowUtils::HasStyle(style, WS_THICKFRAME); bool hasCaption = WindowUtils::HasStyle(style, WS_CAPTION); bool hasMinimizeMaximizeButtons = WindowUtils::HasStyle(style, WS_MINIMIZEBOX) || WindowUtils::HasStyle(style, WS_MAXIMIZEBOX); - if (isPopup && !(hasThickFrame && (hasCaption || hasMinimizeMaximizeButtons))) + if (isPopup && !(hasCaption || hasMinimizeMaximizeButtons)) { // popup windows we want to snap: e.g. Calculator, Telegram // popup windows we don't want to snap: start menu, notification popup, tray window, etc. From e189a5965aaa4fdeaa709ea32fea66822df83696 Mon Sep 17 00:00:00 2001 From: vanzue <13770338596@163.com> Date: Tue, 1 Apr 2025 15:14:09 +0800 Subject: [PATCH 02/13] minor fix --- .github/actions/spell-check/allow/code.txt | 6 ++++++ src/modules/Workspaces/WorkspacesLib/AppUtils.cpp | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/actions/spell-check/allow/code.txt b/.github/actions/spell-check/allow/code.txt index 619a036b32c5..61563c41e5a4 100644 --- a/.github/actions/spell-check/allow/code.txt +++ b/.github/actions/spell-check/allow/code.txt @@ -274,3 +274,9 @@ testhost #Tools OIP + +# Steam Support +acfs +gameid +installdir +steamapps \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp index 9895c38c5852..661966ac9caa 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp @@ -36,9 +36,7 @@ namespace Utils constexpr const wchar_t* EdgeFilename = L"msedge.exe"; constexpr const wchar_t* ChromeFilename = L"chrome.exe"; - constexpr const wchar_t* SteamWindowExeName = L"steamwebhelper.exe"; constexpr const wchar_t* SteamUrlProtocol = L"steam:"; - constexpr const wchar_t* SteamExeName = L"steam.exe"; } AppList IterateAppsFolder() From 22de8b224d2ac71ec9a7b6a3a91e7341c43159a9 Mon Sep 17 00:00:00 2001 From: vanzue <13770338596@163.com> Date: Tue, 1 Apr 2025 16:23:56 +0800 Subject: [PATCH 03/13] Launch steam apps by url appmodeluserid instead of directly exe call. --- src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp b/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp index 4d4b47ea125e..c2f45bc3a24c 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp +++ b/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp @@ -120,7 +120,7 @@ namespace AppLauncher // packaged apps: try launching first by AppUserModel.ID // usage example: elevated Terminal - if (!launched && !app.appUserModelId.empty() && !app.packageFullName.empty()) + if (!launched && !app.appUserModelId.empty()) { Logger::trace(L"Launching {} as {}", app.name, app.appUserModelId); auto res = LaunchApp(L"shell:AppsFolder\\" + app.appUserModelId, app.commandLineArgs, app.isElevated); @@ -130,6 +130,7 @@ namespace AppLauncher } else { + Logger::error(L"Failed to launch by appUserModelId, {}, {}", app.name, app.appUserModelId); launchErrors.push_back({ std::filesystem::path(app.path).filename(), res.error() }); } } From 5f8f87a95a62a7f5f6310fb274aadf482e6cd988 Mon Sep 17 00:00:00 2001 From: vanzue <13770338596@163.com> Date: Mon, 7 Apr 2025 11:35:37 +0800 Subject: [PATCH 04/13] fix copilot comment --- src/modules/Workspaces/WorkspacesLib/AppUtils.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp index 661966ac9caa..63a4c4daa6d1 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp @@ -145,6 +145,7 @@ namespace Utils if (!path.empty()) { const bool isSteamProtocol = path.rfind(NonLocalizable::SteamUrlProtocol, 0) == 0; + data.installPath = path; if (isSteamProtocol) { @@ -153,11 +154,10 @@ namespace Utils auto gameId = Steam::GetGameIdFromUrlProtocolPath(path); auto gameFolder = Steam::GetSteamGameInfoFromAcfFile(gameId); - data.installPath = gameFolder->gameInstallationPath; - } - else - { - data.installPath = path; + if (gameFolder) + { + data.installPath = gameFolder->gameInstallationPath; + } } } } From 7fb545b6d392ec066481f8e0c677c0ec5108b813 Mon Sep 17 00:00:00 2001 From: vanzue <13770338596@163.com> Date: Wed, 9 Apr 2025 13:13:38 +0800 Subject: [PATCH 05/13] fix --- .../WorkspacesLauncher/AppLauncher.cpp | 17 ++++++++++- .../Workspaces/WorkspacesLib/AppUtils.cpp | 29 ++++++++++++------- .../WorkspacesLib/SteamGameHelper.cpp | 17 +++++++++-- .../WorkspacesSnapshotTool/SnapshotUtils.cpp | 6 +++- .../workspaces-common/WindowFilter.h | 3 ++ 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp b/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp index c2f45bc3a24c..7075da2ea7b6 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp +++ b/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp @@ -120,6 +120,22 @@ namespace AppLauncher // packaged apps: try launching first by AppUserModel.ID // usage example: elevated Terminal + if (!launched && !app.appUserModelId.empty() && !app.packageFullName.empty()) + { + Logger::trace(L"Launching {} as {} - {app.packageFullName}", app.name, app.appUserModelId, app.packageFullName); + auto res = LaunchApp(L"shell:AppsFolder\\" + app.appUserModelId, app.commandLineArgs, app.isElevated); + if (res.isOk()) + { + launched = true; + } + else + { + launchErrors.push_back({ std::filesystem::path(app.path).filename(), res.error() }); + } + } + + // win32 app with appUserModelId: + // usage example: steam games if (!launched && !app.appUserModelId.empty()) { Logger::trace(L"Launching {} as {}", app.name, app.appUserModelId); @@ -130,7 +146,6 @@ namespace AppLauncher } else { - Logger::error(L"Failed to launch by appUserModelId, {}, {}", app.name, app.appUserModelId); launchErrors.push_back({ std::filesystem::path(app.path).filename(), res.error() }); } } diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp index 63a4c4daa6d1..fb7a039319c9 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp @@ -140,23 +140,32 @@ namespace Utils } else if (prop == NonLocalizable::PackageInstallPathProp || prop == NonLocalizable::InstallPathProp) { - const std::wstring path = propVariantString.m_pData; + data.installPath = propVariantString.m_pData; - if (!path.empty()) + if (!data.installPath.empty()) { - const bool isSteamProtocol = path.rfind(NonLocalizable::SteamUrlProtocol, 0) == 0; - data.installPath = path; + const bool isSteamProtocol = data.installPath.rfind(NonLocalizable::SteamUrlProtocol, 0) == 0; if (isSteamProtocol) { - Logger::info(L"Found steam game: {}", path); - data.protocolPath = path; + Logger::info(L"Found steam game: {}", data.installPath); + data.protocolPath = data.installPath; - auto gameId = Steam::GetGameIdFromUrlProtocolPath(path); - auto gameFolder = Steam::GetSteamGameInfoFromAcfFile(gameId); - if (gameFolder) + try { - data.installPath = gameFolder->gameInstallationPath; + auto gameId = Steam::GetGameIdFromUrlProtocolPath(data.installPath); + auto gameFolder = Steam::GetSteamGameInfoFromAcfFile(gameId); + + if (gameFolder) + { + Logger::info(L"Found steam game: {}", data.installPath); + data.installPath = gameFolder->gameInstallationPath; + } + } + catch (std::exception ex) + { + Logger::error(L"Failed to get installPath for game {}", data.installPath); + Logger::error("Error: {}", ex.what()); } } } diff --git a/src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp b/src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp index 11e6cde10854..74b606f2c3c1 100644 --- a/src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp +++ b/src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp @@ -76,6 +76,17 @@ namespace Utils return std::nullopt; } + static fs::path GetSteamBasePath() + { + auto steamFolderOpt = GetSteamExePathFromRegistry(); + if (!steamFolderOpt) + { + return {}; + } + + return fs::path(*steamFolderOpt).parent_path() / L"steamapps"; + } + static fs::path GetAcfFilePath(const std::wstring& gameId) { auto steamFolderOpt = GetSteamExePathFromRegistry(); @@ -84,7 +95,7 @@ namespace Utils return {}; } - return fs::path(*steamFolderOpt).parent_path() / L"steamapps" / (L"appmanifest_" + gameId + L".acf"); + return GetSteamBasePath() / (L"appmanifest_" + gameId + L".acf"); } static fs::path GetGameInstallPath(const std::wstring& gameFolderName) @@ -95,7 +106,7 @@ namespace Utils return {}; } - return fs::path(*steamFolderOpt).parent_path() / L"steamapps" / L"common" / gameFolderName; + return GetSteamBasePath() / L"steamapps" / L"common" / gameFolderName; } static unordered_map ParseAcfFile(const fs::path& acfPath) @@ -135,6 +146,8 @@ namespace Utils return nullptr; fs::path gamePath = Steam::GetGameInstallPath(kv[L"installdir"]); + if (!fs::exists(gamePath)) + return nullptr; auto game = std::make_unique(); game->gameId = gameId; diff --git a/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp b/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp index 1d5bc8a17963..0db106e3e561 100644 --- a/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp +++ b/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp @@ -71,6 +71,8 @@ namespace SnapshotUtils continue; } + Logger::info("Try to get window app:{}", reinterpret_cast(window)); + DWORD pid{}; GetWindowThreadProcessId(window, &pid); @@ -118,10 +120,12 @@ namespace SnapshotUtils auto data = Utils::Apps::GetApp(processPath, pid, installedApps); if (!data.has_value() || data->name.empty()) { - Logger::info(L"Installed app not found: {}", processPath); + Logger::info(L"Installed app not found:{},{}", reinterpret_cast(window), processPath); continue; } + Logger::info(L"Found app for window:{},{}", reinterpret_cast(window), processPath); + auto appData = data.value(); bool isEdge = appData.IsEdge(); diff --git a/src/modules/Workspaces/workspaces-common/WindowFilter.h b/src/modules/Workspaces/workspaces-common/WindowFilter.h index 3487c8f5a839..8ae1a5411bb2 100644 --- a/src/modules/Workspaces/workspaces-common/WindowFilter.h +++ b/src/modules/Workspaces/workspaces-common/WindowFilter.h @@ -11,6 +11,9 @@ namespace WindowFilter bool isPopup = WindowUtils::HasStyle(style, WS_POPUP); bool hasCaption = WindowUtils::HasStyle(style, WS_CAPTION); bool hasMinimizeMaximizeButtons = WindowUtils::HasStyle(style, WS_MINIMIZEBOX) || WindowUtils::HasStyle(style, WS_MAXIMIZEBOX); + + Logger::info("Style for window: {}, {:#x}", reinterpret_cast(window), style); + if (isPopup && !(hasCaption || hasMinimizeMaximizeButtons)) { // popup windows we want to snap: e.g. Calculator, Telegram From b74f2b78414a7647d65afb287e59b5aa826e6430 Mon Sep 17 00:00:00 2001 From: vanzue <13770338596@163.com> Date: Wed, 9 Apr 2025 13:33:11 +0800 Subject: [PATCH 06/13] remove unnecessary string --- src/modules/Workspaces/WorkspacesLib/SteamHelper.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modules/Workspaces/WorkspacesLib/SteamHelper.h b/src/modules/Workspaces/WorkspacesLib/SteamHelper.h index 40ab467ebc7c..a80a942f4a09 100644 --- a/src/modules/Workspaces/WorkspacesLib/SteamHelper.h +++ b/src/modules/Workspaces/WorkspacesLib/SteamHelper.h @@ -6,8 +6,6 @@ namespace Utils { namespace NonLocalizable { - // When steam is running, window process's exe is this. - const std::wstring SteamWindowExePath = L"steamwebhelper.exe"; const std::wstring AcfFileNameTemplate = L"appmanifest_.acfs"; } From a9e4375f629d31fde251f727568b4e1a615f37ef Mon Sep 17 00:00:00 2001 From: vanzue <13770338596@163.com> Date: Wed, 9 Apr 2025 14:04:21 +0800 Subject: [PATCH 07/13] expect words --- .github/actions/spell-check/allow/code.txt | 8 +------- .github/actions/spell-check/expect.txt | 3 ++- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/actions/spell-check/allow/code.txt b/.github/actions/spell-check/allow/code.txt index 61563c41e5a4..10e94732584f 100644 --- a/.github/actions/spell-check/allow/code.txt +++ b/.github/actions/spell-check/allow/code.txt @@ -273,10 +273,4 @@ mengyuanchen testhost #Tools -OIP - -# Steam Support -acfs -gameid -installdir -steamapps \ No newline at end of file +OIP \ No newline at end of file diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 6e4439e4a2d9..b57ab24b242c 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1990,4 +1990,5 @@ zoomit ZOOMITX ZXk ZXNs -zzz \ No newline at end of file +zzz +steamapps \ No newline at end of file From 7f79fad37cb5578e0e3ac3b7dd2d5cd6b52493e5 Mon Sep 17 00:00:00 2001 From: vanzue <13770338596@163.com> Date: Tue, 15 Apr 2025 14:10:19 +0800 Subject: [PATCH 08/13] white list words --- .github/actions/spell-check/expect.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 9d927b6cc531..f6be9c73aee1 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1957,4 +1957,7 @@ ZOOMITX ZXk ZXNs zzz -steamapps \ No newline at end of file +steamapps +acfs +gameid +installdir \ No newline at end of file From 769e4332d0165b6a63edbe9ec7482286302c0422 Mon Sep 17 00:00:00 2001 From: vanzue <13770338596@163.com> Date: Tue, 15 Apr 2025 14:15:41 +0800 Subject: [PATCH 09/13] Order of alphabet --- .github/actions/spell-check/expect.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index f6be9c73aee1..f32fbbf6e565 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -8,6 +8,7 @@ Acceleratorkeys ACCEPTFILES ACCESSDENIED ACCESSTOKEN +acfs AClient AColumn acrt @@ -520,6 +521,7 @@ FZE gacutil Gaeilge Gaidhlig +gameid GC'ed GCLP gdi @@ -708,6 +710,7 @@ INPUTSINK INPUTTYPE INSTALLDESKTOPSHORTCUT INSTALLDIR +installdir INSTALLFOLDER INSTALLFOLDERTOBOOTSTRAPPERINSTALLFOLDER INSTALLFOLDERTOPREVIOUSINSTALLFOLDER @@ -1557,6 +1560,7 @@ stdcpp stdcpplatest STDMETHODCALLTYPE STDMETHODIMP +steamapps STGC STGM STGMEDIUM @@ -1956,8 +1960,4 @@ zoomit ZOOMITX ZXk ZXNs -zzz -steamapps -acfs -gameid -installdir \ No newline at end of file +zzz \ No newline at end of file From 884ab9416b7251231fb9e3b17b5293dda58ad479 Mon Sep 17 00:00:00 2001 From: vanzue Date: Wed, 16 Apr 2025 14:29:30 +0800 Subject: [PATCH 10/13] exclude thin frame if it's not a steam game. --- src/modules/Workspaces/WorkspacesLib/AppUtils.cpp | 5 +++++ src/modules/Workspaces/WorkspacesLib/AppUtils.h | 1 + .../Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp | 6 ++++++ src/modules/Workspaces/workspaces-common/WindowFilter.h | 6 ++++++ 4 files changed, 18 insertions(+) diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp index fb7a039319c9..b64047b4c6df 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp @@ -428,5 +428,10 @@ namespace Utils { return installPath.ends_with(NonLocalizable::ChromeFilename); } + + bool AppData::IsSteamGame() const + { + return protocolPath.rfind(NonLocalizable::SteamUrlProtocol, 0) == 0; + } } } \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.h b/src/modules/Workspaces/WorkspacesLib/AppUtils.h index 8482eb939c9d..df8ffdd31e73 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.h +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.h @@ -18,6 +18,7 @@ namespace Utils bool IsEdge() const; bool IsChrome() const; + bool AppData::IsSteamGame() const; }; using AppList = std::vector; diff --git a/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp b/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp index 0db106e3e561..0af66cd27032 100644 --- a/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp +++ b/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp @@ -124,6 +124,12 @@ namespace SnapshotUtils continue; } + if (!data->IsSteamGame() && !WindowFilter::HasThickFrame(window)) + { + // Only care about steam games without thick frame. + continue; + } + Logger::info(L"Found app for window:{},{}", reinterpret_cast(window), processPath); auto appData = data.value(); diff --git a/src/modules/Workspaces/workspaces-common/WindowFilter.h b/src/modules/Workspaces/workspaces-common/WindowFilter.h index 8ae1a5411bb2..bdeeacf4a7f0 100644 --- a/src/modules/Workspaces/workspaces-common/WindowFilter.h +++ b/src/modules/Workspaces/workspaces-common/WindowFilter.h @@ -59,4 +59,10 @@ namespace WindowFilter return true; } + + inline bool HasThickFrame(HWND window) + { + auto style = GetWindowLong(window, GWL_STYLE); + return WindowUtils::HasStyle(style, WS_THICKFRAME); + } } From 1f0186c985f5d684bbe2b4cd770c8c284e5d6188 Mon Sep 17 00:00:00 2001 From: vanzue Date: Wed, 16 Apr 2025 14:56:15 +0800 Subject: [PATCH 11/13] fix build --- src/modules/Workspaces/WorkspacesLib/AppUtils.cpp | 4 ++-- src/modules/Workspaces/WorkspacesLib/AppUtils.h | 2 +- .../Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp | 2 +- .../Workspaces/WorkspacesWindowArranger/WindowArranger.cpp | 7 +++++++ src/modules/Workspaces/workspaces-common/WindowFilter.h | 6 ------ src/modules/Workspaces/workspaces-common/WindowUtils.h | 7 +++++++ 6 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp index b64047b4c6df..a37d82f8caec 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp @@ -148,7 +148,7 @@ namespace Utils if (isSteamProtocol) { - Logger::info(L"Found steam game: {}", data.installPath); + Logger::info(L"Found steam game: protocol path: {}", data.installPath); data.protocolPath = data.installPath; try @@ -158,8 +158,8 @@ namespace Utils if (gameFolder) { - Logger::info(L"Found steam game: {}", data.installPath); data.installPath = gameFolder->gameInstallationPath; + Logger::info(L"Found steam game: physical path: {}", data.installPath); } } catch (std::exception ex) diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.h b/src/modules/Workspaces/WorkspacesLib/AppUtils.h index df8ffdd31e73..80b5e2fd4932 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.h +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.h @@ -18,7 +18,7 @@ namespace Utils bool IsEdge() const; bool IsChrome() const; - bool AppData::IsSteamGame() const; + bool IsSteamGame() const; }; using AppList = std::vector; diff --git a/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp b/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp index 0af66cd27032..5fa17603caf2 100644 --- a/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp +++ b/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp @@ -124,7 +124,7 @@ namespace SnapshotUtils continue; } - if (!data->IsSteamGame() && !WindowFilter::HasThickFrame(window)) + if (!data->IsSteamGame() && !WindowUtils::HasThickFrame(window)) { // Only care about steam games without thick frame. continue; diff --git a/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp b/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp index 7b04135d1a9e..9f27d15522ef 100644 --- a/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp +++ b/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp @@ -200,6 +200,13 @@ std::optional WindowArranger::GetNearestWindow(const Workspa } auto data = Utils::Apps::GetApp(processPath, pid, m_installedApps); + + if (!data->IsSteamGame() && !WindowUtils::HasThickFrame(window)) + { + // Only care about steam games without thick frame. + continue; + } + if (!data.has_value()) { continue; diff --git a/src/modules/Workspaces/workspaces-common/WindowFilter.h b/src/modules/Workspaces/workspaces-common/WindowFilter.h index bdeeacf4a7f0..8ae1a5411bb2 100644 --- a/src/modules/Workspaces/workspaces-common/WindowFilter.h +++ b/src/modules/Workspaces/workspaces-common/WindowFilter.h @@ -59,10 +59,4 @@ namespace WindowFilter return true; } - - inline bool HasThickFrame(HWND window) - { - auto style = GetWindowLong(window, GWL_STYLE); - return WindowUtils::HasStyle(style, WS_THICKFRAME); - } } diff --git a/src/modules/Workspaces/workspaces-common/WindowUtils.h b/src/modules/Workspaces/workspaces-common/WindowUtils.h index 8424591dfac5..79051f4ea284 100644 --- a/src/modules/Workspaces/workspaces-common/WindowUtils.h +++ b/src/modules/Workspaces/workspaces-common/WindowUtils.h @@ -121,4 +121,11 @@ namespace WindowUtils return std::wstring(title); } + + + inline bool HasThickFrame(HWND window) + { + auto style = GetWindowLong(window, GWL_STYLE); + return WindowUtils::HasStyle(style, WS_THICKFRAME); + } } \ No newline at end of file From 9e8e3c9c0ec1d025f40754b45e3229aa9dbbf8bb Mon Sep 17 00:00:00 2001 From: vanzue Date: Wed, 16 Apr 2025 15:34:05 +0800 Subject: [PATCH 12/13] fix regression --- src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp b/src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp index 74b606f2c3c1..404002e28486 100644 --- a/src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp +++ b/src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp @@ -106,7 +106,7 @@ namespace Utils return {}; } - return GetSteamBasePath() / L"steamapps" / L"common" / gameFolderName; + return GetSteamBasePath() / L"common" / gameFolderName; } static unordered_map ParseAcfFile(const fs::path& acfPath) From 1852d98b760f74a03f97623ee6d4564e75874e2a Mon Sep 17 00:00:00 2001 From: vanzue Date: Wed, 16 Apr 2025 16:02:33 +0800 Subject: [PATCH 13/13] adjust comment --- .../Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp | 3 ++- .../Workspaces/WorkspacesWindowArranger/WindowArranger.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp b/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp index 5fa17603caf2..a8b7c131087f 100644 --- a/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp +++ b/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp @@ -126,7 +126,8 @@ namespace SnapshotUtils if (!data->IsSteamGame() && !WindowUtils::HasThickFrame(window)) { - // Only care about steam games without thick frame. + // Only care about steam games if it has no thick frame to remain consistent with + // the behavior as before. continue; } diff --git a/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp b/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp index 9f27d15522ef..538579979f58 100644 --- a/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp +++ b/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp @@ -203,7 +203,8 @@ std::optional WindowArranger::GetNearestWindow(const Workspa if (!data->IsSteamGame() && !WindowUtils::HasThickFrame(window)) { - // Only care about steam games without thick frame. + // Only care about steam games if it has no thick frame to remain consistent with + // the behavior as before. continue; }