Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Workspaces] Saving app properties on launch and recapture #36751

Merged
merged 8 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actions/spell-check/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,7 @@ LOWORD
lparam
LPBITMAPINFOHEADER
LPCITEMIDLIST
LPCLSID
lpcmi
LPCMINVOKECOMMANDINFO
LPCREATESTRUCT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,34 @@ namespace WorkspacesWindowProperties
namespace Properties
{
const wchar_t LaunchedByWorkspacesID[] = L"PowerToys_LaunchedByWorkspaces";
const wchar_t WorkspacesAppID[] = L"PowerToys_WorkspacesAppId";
}

inline void StampWorkspacesLaunchedProperty(HWND window)
inline void StampWorkspacesLaunchedProperty(HWND window, const std::wstring appId)
SeraphimaZykova marked this conversation as resolved.
Show resolved Hide resolved
{
::SetPropW(window, Properties::LaunchedByWorkspacesID, reinterpret_cast<HANDLE>(1));

GUID guid;
HRESULT hr = CLSIDFromString(appId.c_str(), static_cast<LPCLSID> (&guid));
if (hr != S_OK)
{
return;
}

const size_t workspacesAppIDLength = wcslen(Properties::WorkspacesAppID);
wchar_t* workspacesAppIDPart = new wchar_t[workspacesAppIDLength + 2];
std::memcpy(&workspacesAppIDPart[0], &Properties::WorkspacesAppID, workspacesAppIDLength * sizeof(wchar_t));
workspacesAppIDPart[workspacesAppIDLength + 1] = 0;

// the size of the HANDLE type can vary on different systems: 4 or 8 bytes. As we can set only a HANDLE as a property, we need more properties (2 or 4) to be able to store a GUID (16 bytes)
const int numberOfProperties = sizeof(GUID) / sizeof(HANDLE);

uint64_t parts[numberOfProperties];
std::memcpy(&parts[0], &guid, sizeof(GUID));
for (unsigned char partIndex = 0; partIndex < numberOfProperties; partIndex++)
{
workspacesAppIDPart[workspacesAppIDLength] = '0' + partIndex;
::SetPropW(window, workspacesAppIDPart, reinterpret_cast<HANDLE>(parts[partIndex]));
}
}
}
19 changes: 15 additions & 4 deletions src/modules/Workspaces/WorkspacesEditor/Models/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -342,12 +342,23 @@ private Rectangle GetCommonBounds()
return new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY));
}

public void UpdateAfterLaunchAndEdit(Project other)
public void UpdateAfterLaunchAndEdit(Project projectBeforeLaunch)
{
Id = other.Id;
Name = other.Name;
Id = projectBeforeLaunch.Id;
Name = projectBeforeLaunch.Name;
IsRevertEnabled = true;
MoveExistingWindows = other.MoveExistingWindows;
MoveExistingWindows = projectBeforeLaunch.MoveExistingWindows;
foreach (Application app in Applications)
{
var sameAppBefore = projectBeforeLaunch.Applications.Where(x => x.Id.Equals(app.Id, StringComparison.OrdinalIgnoreCase));
if (sameAppBefore.Any())
{
var appBefore = sameAppBefore.FirstOrDefault();
app.CommandLineArguments = appBefore.CommandLineArguments;
app.IsElevated = appBefore.IsElevated;
app.PwaAppId = appBefore.PwaAppId;
SeraphimaZykova marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

internal void CloseExpanders()
Expand Down
34 changes: 34 additions & 0 deletions src/modules/Workspaces/WorkspacesLib/AppUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ namespace Utils

constexpr const wchar_t* EdgeFilename = L"msedge.exe";
constexpr const wchar_t* ChromeFilename = L"chrome.exe";

const wchar_t WorkspacesAppID[] = L"PowerToys_WorkspacesAppId";
SeraphimaZykova marked this conversation as resolved.
Show resolved Hide resolved
}

AppList IterateAppsFolder()
Expand Down Expand Up @@ -398,5 +400,37 @@ namespace Utils
return installPath.ends_with(NonLocalizable::ChromeFilename);
}

const std::wstring GetGuidFromHwnd(HWND window)
{
const size_t workspacesAppIDLength = wcslen(NonLocalizable::WorkspacesAppID);
wchar_t* workspacesAppIDPart = new wchar_t[workspacesAppIDLength + 2];
std::memcpy(&workspacesAppIDPart[0], &NonLocalizable::WorkspacesAppID, workspacesAppIDLength * sizeof(wchar_t));
workspacesAppIDPart[workspacesAppIDLength + 1] = 0;

// the size of the HANDLE type can vary on different systems: 4 or 8 bytes. As we can set only a HANDLE as a property, we need more properties (2 or 4) to be able to store a GUID (16 bytes)
const int numberOfProperties = sizeof(GUID) / sizeof(HANDLE);

uint64_t parts[numberOfProperties];
for (unsigned char partIndex = 0; partIndex < numberOfProperties; partIndex++)
{
workspacesAppIDPart[workspacesAppIDLength] = '0' + partIndex;
HANDLE rawData = GetPropW(window, workspacesAppIDPart);
if (rawData)
{
parts[partIndex] = reinterpret_cast<uint64_t>(rawData);
}
else
{
return L"";
}
}

GUID guid;
std::memcpy(&guid, &parts[0], sizeof(GUID));
WCHAR* guidString;
StringFromCLSID(guid, &guidString);

return guidString;
}
}
}
2 changes: 2 additions & 0 deletions src/modules/Workspaces/WorkspacesLib/AppUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ namespace Utils
const std::wstring& GetCurrentFolder();
const std::wstring& GetCurrentFolderUpper();

const std::wstring GetGuidFromHwnd(HWND window);

SeraphimaZykova marked this conversation as resolved.
Show resolved Hide resolved
AppList GetAppsList();
std::optional<AppData> GetApp(const std::wstring& appPath, DWORD pid, const AppList& apps);
std::optional<AppData> GetApp(HWND window, const AppList& apps);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,10 @@ namespace SnapshotUtils
rect.bottom = monitorRect.top + monitorRect.height;
}

std::wstring guid = Utils::Apps::GetGuidFromHwnd(window);
SeraphimaZykova marked this conversation as resolved.
Show resolved Hide resolved

WorkspacesData::WorkspacesProject::Application app{
.id = guid,
.name = appData.name,
.title = title,
.path = appData.installPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ bool WindowArranger::moveWindow(HWND window, const WorkspacesData::WorkspacesPro

if (PlacementHelper::SizeWindowToRect(window, currentMonitor, launchMinimized, launchMaximized, rect))
{
WorkspacesWindowProperties::StampWorkspacesLaunchedProperty(window);
WorkspacesWindowProperties::StampWorkspacesLaunchedProperty(window, app.id);
Logger::trace(L"Placed {} to ({},{}) [{}x{}]", app.name, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (LaunchedByWorkspaces)
{
HWND window = Mocks::WindowCreate(hInst, L"", L"", 0, WS_TILEDWINDOW);
WorkspacesWindowProperties::StampWorkspacesLaunchedProperty(window);
WorkspacesWindowProperties::StampWorkspacesLaunchedProperty(window, L"");

Assert::AreEqual(FancyZonesWindowProcessing::ProcessabilityType::Processable, FancyZonesWindowProcessing::DefineWindowType(window));
Assert::IsFalse(FancyZonesWindowProcessing::IsProcessableAutomatically(window));
Expand Down
Loading