mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 19:57:57 +01:00
[Workspaces]Fix for steam games capture&launch: capture and correctly launch steam games. (#38380)
* Workspaces fix: capture steam games. * minor fix * Launch steam apps by url appmodeluserid instead of directly exe call. * fix copilot comment * fix * remove unnecessary string * expect words * white list words * Order of alphabet * exclude thin frame if it's not a steam game. * fix build * fix regression * adjust comment
This commit is contained in:
4
.github/actions/spell-check/expect.txt
vendored
4
.github/actions/spell-check/expect.txt
vendored
@@ -8,6 +8,7 @@ Acceleratorkeys
|
|||||||
ACCEPTFILES
|
ACCEPTFILES
|
||||||
ACCESSDENIED
|
ACCESSDENIED
|
||||||
ACCESSTOKEN
|
ACCESSTOKEN
|
||||||
|
acfs
|
||||||
AClient
|
AClient
|
||||||
AColumn
|
AColumn
|
||||||
acrt
|
acrt
|
||||||
@@ -523,6 +524,7 @@ FZE
|
|||||||
gacutil
|
gacutil
|
||||||
Gaeilge
|
Gaeilge
|
||||||
Gaidhlig
|
Gaidhlig
|
||||||
|
gameid
|
||||||
GC'ed
|
GC'ed
|
||||||
GCLP
|
GCLP
|
||||||
gdi
|
gdi
|
||||||
@@ -712,6 +714,7 @@ INPUTSINK
|
|||||||
INPUTTYPE
|
INPUTTYPE
|
||||||
INSTALLDESKTOPSHORTCUT
|
INSTALLDESKTOPSHORTCUT
|
||||||
INSTALLDIR
|
INSTALLDIR
|
||||||
|
installdir
|
||||||
INSTALLFOLDER
|
INSTALLFOLDER
|
||||||
INSTALLFOLDERTOBOOTSTRAPPERINSTALLFOLDER
|
INSTALLFOLDERTOBOOTSTRAPPERINSTALLFOLDER
|
||||||
INSTALLFOLDERTOPREVIOUSINSTALLFOLDER
|
INSTALLFOLDERTOPREVIOUSINSTALLFOLDER
|
||||||
@@ -1569,6 +1572,7 @@ stdcpp
|
|||||||
stdcpplatest
|
stdcpplatest
|
||||||
STDMETHODCALLTYPE
|
STDMETHODCALLTYPE
|
||||||
STDMETHODIMP
|
STDMETHODIMP
|
||||||
|
steamapps
|
||||||
STGC
|
STGC
|
||||||
STGM
|
STGM
|
||||||
STGMEDIUM
|
STGMEDIUM
|
||||||
|
|||||||
@@ -121,6 +121,22 @@ namespace AppLauncher
|
|||||||
// packaged apps: try launching first by AppUserModel.ID
|
// packaged apps: try launching first by AppUserModel.ID
|
||||||
// usage example: elevated Terminal
|
// usage example: elevated Terminal
|
||||||
if (!launched && !app.appUserModelId.empty() && !app.packageFullName.empty())
|
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);
|
Logger::trace(L"Launching {} as {}", app.name, app.appUserModelId);
|
||||||
auto res = LaunchApp(L"shell:AppsFolder\\" + app.appUserModelId, app.commandLineArgs, app.isElevated);
|
auto res = LaunchApp(L"shell:AppsFolder\\" + app.appUserModelId, app.commandLineArgs, app.isElevated);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "AppUtils.h"
|
#include "AppUtils.h"
|
||||||
|
#include "SteamHelper.h"
|
||||||
|
|
||||||
#include <atlbase.h>
|
#include <atlbase.h>
|
||||||
#include <propvarutil.h>
|
#include <propvarutil.h>
|
||||||
@@ -34,6 +35,8 @@ namespace Utils
|
|||||||
|
|
||||||
constexpr const wchar_t* EdgeFilename = L"msedge.exe";
|
constexpr const wchar_t* EdgeFilename = L"msedge.exe";
|
||||||
constexpr const wchar_t* ChromeFilename = L"chrome.exe";
|
constexpr const wchar_t* ChromeFilename = L"chrome.exe";
|
||||||
|
|
||||||
|
constexpr const wchar_t* SteamUrlProtocol = L"steam:";
|
||||||
}
|
}
|
||||||
|
|
||||||
AppList IterateAppsFolder()
|
AppList IterateAppsFolder()
|
||||||
@@ -138,6 +141,34 @@ namespace Utils
|
|||||||
else if (prop == NonLocalizable::PackageInstallPathProp || prop == NonLocalizable::InstallPathProp)
|
else if (prop == NonLocalizable::PackageInstallPathProp || prop == NonLocalizable::InstallPathProp)
|
||||||
{
|
{
|
||||||
data.installPath = propVariantString.m_pData;
|
data.installPath = propVariantString.m_pData;
|
||||||
|
|
||||||
|
if (!data.installPath.empty())
|
||||||
|
{
|
||||||
|
const bool isSteamProtocol = data.installPath.rfind(NonLocalizable::SteamUrlProtocol, 0) == 0;
|
||||||
|
|
||||||
|
if (isSteamProtocol)
|
||||||
|
{
|
||||||
|
Logger::info(L"Found steam game: protocol path: {}", data.installPath);
|
||||||
|
data.protocolPath = data.installPath;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto gameId = Steam::GetGameIdFromUrlProtocolPath(data.installPath);
|
||||||
|
auto gameFolder = Steam::GetSteamGameInfoFromAcfFile(gameId);
|
||||||
|
|
||||||
|
if (gameFolder)
|
||||||
|
{
|
||||||
|
data.installPath = gameFolder->gameInstallationPath;
|
||||||
|
Logger::info(L"Found steam game: physical path: {}", data.installPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception ex)
|
||||||
|
{
|
||||||
|
Logger::error(L"Failed to get installPath for game {}", data.installPath);
|
||||||
|
Logger::error("Error: {}", ex.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,5 +428,10 @@ namespace Utils
|
|||||||
{
|
{
|
||||||
return installPath.ends_with(NonLocalizable::ChromeFilename);
|
return installPath.ends_with(NonLocalizable::ChromeFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AppData::IsSteamGame() const
|
||||||
|
{
|
||||||
|
return protocolPath.rfind(NonLocalizable::SteamUrlProtocol, 0) == 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,10 +13,12 @@ namespace Utils
|
|||||||
std::wstring packageFullName;
|
std::wstring packageFullName;
|
||||||
std::wstring appUserModelId;
|
std::wstring appUserModelId;
|
||||||
std::wstring pwaAppId;
|
std::wstring pwaAppId;
|
||||||
|
std::wstring protocolPath;
|
||||||
bool canLaunchElevated = false;
|
bool canLaunchElevated = false;
|
||||||
|
|
||||||
bool IsEdge() const;
|
bool IsEdge() const;
|
||||||
bool IsChrome() const;
|
bool IsChrome() const;
|
||||||
|
bool IsSteamGame() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
using AppList = std::vector<AppData>;
|
using AppList = std::vector<AppData>;
|
||||||
|
|||||||
171
src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp
Normal file
171
src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "SteamHelper.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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<int>(utf8.size()), nullptr, 0);
|
||||||
|
if (size <= 0)
|
||||||
|
return L"";
|
||||||
|
|
||||||
|
std::wstring wide(size, L'\0');
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast<int>(utf8.size()), wide.data(), size);
|
||||||
|
return wide;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Steam
|
||||||
|
{
|
||||||
|
using namespace std;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
static std::optional<std::wstring> GetSteamExePathFromRegistry()
|
||||||
|
{
|
||||||
|
static std::optional<std::wstring> cachedPath;
|
||||||
|
if (cachedPath.has_value())
|
||||||
|
{
|
||||||
|
return cachedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<HKEY> roots = { HKEY_CLASSES_ROOT, HKEY_LOCAL_MACHINE, HKEY_USERS };
|
||||||
|
const std::vector<std::wstring> 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<LPBYTE>(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 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();
|
||||||
|
if (!steamFolderOpt)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetSteamBasePath() / (L"appmanifest_" + gameId + L".acf");
|
||||||
|
}
|
||||||
|
|
||||||
|
static fs::path GetGameInstallPath(const std::wstring& gameFolderName)
|
||||||
|
{
|
||||||
|
auto steamFolderOpt = GetSteamExePathFromRegistry();
|
||||||
|
if (!steamFolderOpt)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetSteamBasePath() / L"common" / gameFolderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unordered_map<wstring, wstring> ParseAcfFile(const fs::path& acfPath)
|
||||||
|
{
|
||||||
|
unordered_map<wstring, wstring> 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<Steam::SteamGame> 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"]);
|
||||||
|
if (!fs::exists(gamePath))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto game = std::make_unique<Steam::SteamGame>();
|
||||||
|
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"";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/modules/Workspaces/WorkspacesLib/SteamHelper.h
Normal file
24
src/modules/Workspaces/WorkspacesLib/SteamHelper.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
namespace Utils
|
||||||
|
{
|
||||||
|
namespace NonLocalizable
|
||||||
|
{
|
||||||
|
const std::wstring AcfFileNameTemplate = L"appmanifest_<gameid>.acfs";
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Steam
|
||||||
|
{
|
||||||
|
struct SteamGame
|
||||||
|
{
|
||||||
|
std::wstring gameId;
|
||||||
|
std::wstring gameInstallationPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<SteamGame> GetSteamGameInfoFromAcfFile(const std::wstring& gameId);
|
||||||
|
|
||||||
|
std::wstring GetGameIdFromUrlProtocolPath(const std::wstring& urlPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,6 +41,7 @@
|
|||||||
<ClInclude Include="pch.h" />
|
<ClInclude Include="pch.h" />
|
||||||
<ClInclude Include="PwaHelper.h" />
|
<ClInclude Include="PwaHelper.h" />
|
||||||
<ClInclude Include="Result.h" />
|
<ClInclude Include="Result.h" />
|
||||||
|
<ClInclude Include="SteamHelper.h" />
|
||||||
<ClInclude Include="StringUtils.h" />
|
<ClInclude Include="StringUtils.h" />
|
||||||
<ClInclude Include="utils.h" />
|
<ClInclude Include="utils.h" />
|
||||||
<ClInclude Include="WbemHelper.h" />
|
<ClInclude Include="WbemHelper.h" />
|
||||||
@@ -57,6 +58,7 @@
|
|||||||
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="PwaHelper.cpp" />
|
<ClCompile Include="PwaHelper.cpp" />
|
||||||
|
<ClCompile Include="SteamGameHelper.cpp" />
|
||||||
<ClCompile Include="two_way_pipe_message_ipc.cpp" />
|
<ClCompile Include="two_way_pipe_message_ipc.cpp" />
|
||||||
<ClCompile Include="WbemHelper.cpp" />
|
<ClCompile Include="WbemHelper.cpp" />
|
||||||
<ClCompile Include="WorkspacesData.cpp" />
|
<ClCompile Include="WorkspacesData.cpp" />
|
||||||
|
|||||||
@@ -53,6 +53,9 @@
|
|||||||
<ClInclude Include="StringUtils.h">
|
<ClInclude Include="StringUtils.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="SteamHelper.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="pch.cpp">
|
<ClCompile Include="pch.cpp">
|
||||||
@@ -88,6 +91,9 @@
|
|||||||
<ClCompile Include="WbemHelper.cpp">
|
<ClCompile Include="WbemHelper.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="SteamGameHelper.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ namespace SnapshotUtils
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger::info("Try to get window app:{}", reinterpret_cast<void*>(window));
|
||||||
|
|
||||||
DWORD pid{};
|
DWORD pid{};
|
||||||
GetWindowThreadProcessId(window, &pid);
|
GetWindowThreadProcessId(window, &pid);
|
||||||
|
|
||||||
@@ -118,10 +120,19 @@ namespace SnapshotUtils
|
|||||||
auto data = Utils::Apps::GetApp(processPath, pid, installedApps);
|
auto data = Utils::Apps::GetApp(processPath, pid, installedApps);
|
||||||
if (!data.has_value() || data->name.empty())
|
if (!data.has_value() || data->name.empty())
|
||||||
{
|
{
|
||||||
Logger::info(L"Installed app not found: {}", processPath);
|
Logger::info(L"Installed app not found:{},{}", reinterpret_cast<void*>(window), processPath);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!data->IsSteamGame() && !WindowUtils::HasThickFrame(window))
|
||||||
|
{
|
||||||
|
// Only care about steam games if it has no thick frame to remain consistent with
|
||||||
|
// the behavior as before.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::info(L"Found app for window:{},{}", reinterpret_cast<void*>(window), processPath);
|
||||||
|
|
||||||
auto appData = data.value();
|
auto appData = data.value();
|
||||||
|
|
||||||
bool isEdge = appData.IsEdge();
|
bool isEdge = appData.IsEdge();
|
||||||
|
|||||||
@@ -200,6 +200,14 @@ std::optional<WindowWithDistance> WindowArranger::GetNearestWindow(const Workspa
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto data = Utils::Apps::GetApp(processPath, pid, m_installedApps);
|
auto data = Utils::Apps::GetApp(processPath, pid, m_installedApps);
|
||||||
|
|
||||||
|
if (!data->IsSteamGame() && !WindowUtils::HasThickFrame(window))
|
||||||
|
{
|
||||||
|
// Only care about steam games if it has no thick frame to remain consistent with
|
||||||
|
// the behavior as before.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!data.has_value())
|
if (!data.has_value())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ namespace WindowFilter
|
|||||||
{
|
{
|
||||||
auto style = GetWindowLong(window, GWL_STYLE);
|
auto style = GetWindowLong(window, GWL_STYLE);
|
||||||
bool isPopup = WindowUtils::HasStyle(style, WS_POPUP);
|
bool isPopup = WindowUtils::HasStyle(style, WS_POPUP);
|
||||||
bool hasThickFrame = WindowUtils::HasStyle(style, WS_THICKFRAME);
|
|
||||||
bool hasCaption = WindowUtils::HasStyle(style, WS_CAPTION);
|
bool hasCaption = WindowUtils::HasStyle(style, WS_CAPTION);
|
||||||
bool hasMinimizeMaximizeButtons = WindowUtils::HasStyle(style, WS_MINIMIZEBOX) || WindowUtils::HasStyle(style, WS_MAXIMIZEBOX);
|
bool hasMinimizeMaximizeButtons = WindowUtils::HasStyle(style, WS_MINIMIZEBOX) || WindowUtils::HasStyle(style, WS_MAXIMIZEBOX);
|
||||||
if (isPopup && !(hasThickFrame && (hasCaption || hasMinimizeMaximizeButtons)))
|
|
||||||
|
Logger::info("Style for window: {}, {:#x}", reinterpret_cast<void*>(window), style);
|
||||||
|
|
||||||
|
if (isPopup && !(hasCaption || hasMinimizeMaximizeButtons))
|
||||||
{
|
{
|
||||||
// popup windows we want to snap: e.g. Calculator, Telegram
|
// 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.
|
// popup windows we don't want to snap: start menu, notification popup, tray window, etc.
|
||||||
|
|||||||
@@ -121,4 +121,11 @@ namespace WindowUtils
|
|||||||
|
|
||||||
return std::wstring(title);
|
return std::wstring(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline bool HasThickFrame(HWND window)
|
||||||
|
{
|
||||||
|
auto style = GetWindowLong(window, GWL_STYLE);
|
||||||
|
return WindowUtils::HasStyle(style, WS_THICKFRAME);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user