[FancyZones] Reset zone settings after restart fix. (#13322)

* updated virtual desktop retrieving

* save with null-guid

* moved guid utils

* moved DeviceIdData related functions

* replaced strings with data structs

* default value

* clean up

* save app zone history with zones settings

* compare with null guid

* updated tests

* refactoring

* logs

* sync vd ids

* logs

* refactoring

* check virtual desktop id

* OnDisplayChange call

* compare device ids in editor
This commit is contained in:
Seraphima Zykova
2021-09-23 00:39:48 +03:00
committed by GitHub
parent 423faf7af1
commit b05378cdf7
23 changed files with 777 additions and 484 deletions

View File

@@ -241,6 +241,8 @@ FancyZones::Run() noexcept
PostMessage(m_window, WM_HOTKEY, 1, 0);
}
});
FancyZonesDataInstance().SetVirtualDesktopCheckCallback(std::bind(&VirtualDesktop::IsVirtualDesktopIdSavedInRegistry, &m_virtualDesktop, std::placeholders::_1));
}
// IFancyZones
@@ -683,7 +685,7 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
}
else if (message == WM_PRIV_VD_UPDATE)
{
RegisterVirtualDesktopUpdates();
OnDisplayChange(DisplayChangeType::Initialization);
}
else if (message == WM_PRIV_EDITOR)
{
@@ -755,7 +757,14 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
changeType == DisplayChangeType::Initialization)
{
m_previousDesktopId = m_currentDesktopId;
auto currentVirtualDesktopId = m_virtualDesktop.GetCurrentVirtualDesktopId();
auto currentVirtualDesktopId = m_virtualDesktop.GetCurrentVirtualDesktopIdFromRegistry();
if (!currentVirtualDesktopId.has_value())
{
Logger::info("Virtual Desktop id from top level window");
currentVirtualDesktopId = m_virtualDesktop.GetDesktopIdByTopLevelWindows();
}
if (currentVirtualDesktopId.has_value())
{
m_currentDesktopId = *currentVirtualDesktopId;
@@ -792,33 +801,49 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, const std::wstring& deviceId) n
_TRACER_;
if (m_workAreaHandler.IsNewWorkArea(m_currentDesktopId, monitor))
{
wil::unique_cotaskmem_string virtualDesktopId;
if (SUCCEEDED(StringFromCLSID(m_currentDesktopId, &virtualDesktopId)))
wil::unique_cotaskmem_string virtualDesktopIdStr;
if (!SUCCEEDED(StringFromCLSID(m_currentDesktopId, &virtualDesktopIdStr)))
{
std::wstring uniqueId;
Logger::debug(L"Add new work area on virtual desktop {}", virtualDesktopIdStr.get());
}
FancyZonesDataTypes::DeviceIdData uniqueId;
uniqueId.virtualDesktopId = m_currentDesktopId;
if (monitor)
{
uniqueId = FancyZonesUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId.get());
}
else
{
uniqueId = FancyZonesUtils::GenerateUniqueIdAllMonitorsArea(virtualDesktopId.get());
}
if (monitor)
{
uniqueId.deviceName = FancyZonesUtils::TrimDeviceId(deviceId);
std::wstring parentId{};
auto parentArea = m_workAreaHandler.GetWorkArea(m_previousDesktopId, monitor);
if (parentArea)
MONITORINFOEXW mi;
mi.cbSize = sizeof(mi);
if (GetMonitorInfo(monitor, &mi))
{
parentId = parentArea->UniqueId();
const FancyZonesUtils::Rect monitorRect(mi.rcMonitor);
uniqueId.width = monitorRect.width();
uniqueId.height = monitorRect.height();
}
}
else
{
uniqueId.deviceName = ZonedWindowProperties::MultiMonitorDeviceID;
RECT combinedResolution = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFO::rcMonitor>();
uniqueId.width = combinedResolution.right - combinedResolution.left;
uniqueId.height = combinedResolution.bottom - combinedResolution.top;
}
auto workArea = MakeWorkArea(m_hinstance, monitor, uniqueId, parentId, GetZoneColors(), m_settings->GetSettings()->overlappingZonesAlgorithm);
if (workArea)
{
m_workAreaHandler.AddWorkArea(m_currentDesktopId, monitor, workArea);
FancyZonesDataInstance().SaveZoneSettings();
}
FancyZonesDataTypes::DeviceIdData parentId{};
auto parentArea = m_workAreaHandler.GetWorkArea(m_previousDesktopId, monitor);
if (parentArea)
{
parentId = parentArea->UniqueId();
}
auto workArea = MakeWorkArea(m_hinstance, monitor, uniqueId, parentId, GetZoneColors(), m_settings->GetSettings()->overlappingZonesAlgorithm);
if (workArea)
{
m_workAreaHandler.AddWorkArea(m_currentDesktopId, monitor, workArea);
FancyZonesDataInstance().SaveZoneSettings();
}
}
}
@@ -839,34 +864,34 @@ LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam,
void FancyZones::UpdateZoneWindows() noexcept
{
// Mapping between display device name and device index (operating system identifies each display device with an index value).
std::unordered_map<std::wstring, DWORD> displayDeviceIdxMap;
struct capture
{
FancyZones* fancyZones;
std::unordered_map<std::wstring, DWORD>* displayDeviceIdx;
};
auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL {
capture* params = reinterpret_cast<capture*>(data);
MONITORINFOEX mi{ { .cbSize = sizeof(mi) } };
if (GetMonitorInfoW(monitor, &mi))
{
auto& displayDeviceIdxMap = *(params->displayDeviceIdx);
FancyZones* fancyZones = params->fancyZones;
std::wstring deviceId = FancyZonesUtils::GetDisplayDeviceId(mi.szDevice, displayDeviceIdxMap);
fancyZones->AddZoneWindow(monitor, deviceId);
}
return TRUE;
};
if (m_settings->GetSettings()->spanZonesAcrossMonitors)
{
AddZoneWindow(nullptr, {});
}
else
{
// Mapping between display device name and device index (operating system identifies each display device with an index value).
std::unordered_map<std::wstring, DWORD> displayDeviceIdxMap;
struct capture
{
FancyZones* fancyZones;
std::unordered_map<std::wstring, DWORD>* displayDeviceIdx;
};
auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL {
capture* params = reinterpret_cast<capture*>(data);
MONITORINFOEX mi{ { .cbSize = sizeof(mi) } };
if (GetMonitorInfoW(monitor, &mi))
{
auto& displayDeviceIdxMap = *(params->displayDeviceIdx);
FancyZones* fancyZones = params->fancyZones;
std::wstring deviceId = FancyZonesUtils::GetDisplayDeviceId(mi.szDevice, displayDeviceIdxMap);
fancyZones->AddZoneWindow(monitor, deviceId);
}
return TRUE;
};
capture capture{ this, &displayDeviceIdxMap };
EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast<LPARAM>(&capture));
}
@@ -874,22 +899,15 @@ void FancyZones::UpdateZoneWindows() noexcept
void FancyZones::UpdateWindowsPositions() noexcept
{
auto callback = [](HWND window, LPARAM data) -> BOOL {
for (const auto [window, desktopId] : m_virtualDesktop.GetWindowsRelatedToDesktops())
{
auto zoneIndexSet = GetZoneIndexSet(window);
auto strongThis = reinterpret_cast<FancyZones*>(data);
auto desktopId = strongThis->m_virtualDesktop.GetWindowDesktopId(window);
if (desktopId.has_value())
auto zoneWindow = m_workAreaHandler.GetWorkArea(window, desktopId);
if (zoneWindow)
{
auto zoneWindow = strongThis->m_workAreaHandler.GetWorkArea(window, *desktopId);
if (zoneWindow)
{
strongThis->m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, zoneWindow);
}
m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, zoneWindow);
}
return TRUE;
};
EnumWindows(callback, reinterpret_cast<LPARAM>(this));
}
}
bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexcept
@@ -1107,28 +1125,14 @@ void FancyZones::RegisterVirtualDesktopUpdates() noexcept
{
_TRACER_;
auto guids = m_virtualDesktop.GetVirtualDesktopIds();
std::vector<std::wstring> guidStrings{};
auto guids = m_virtualDesktop.GetVirtualDesktopIdsFromRegistry();
if (guids.has_value())
{
m_workAreaHandler.RegisterUpdates(*guids);
for (auto& guid : *guids)
{
auto guidString = FancyZonesUtils::GuidToString(guid);
if (guidString.has_value())
{
guidStrings.push_back(*guidString);
}
}
if (!guidStrings.empty())
{
FancyZonesDataInstance().UpdatePrimaryDesktopData(guidStrings[0]);
}
FancyZonesDataInstance().RemoveDeletedDesktops(guidStrings);
FancyZonesDataInstance().RemoveDeletedDesktops(*guids);
}
FancyZonesDataInstance().SyncVirtualDesktops(m_currentDesktopId);
}
void FancyZones::OnSettingsChanged() noexcept
@@ -1163,6 +1167,7 @@ void FancyZones::OnEditorExitEvent() noexcept
{
// Collect information about changes in zone layout after editor exited.
FancyZonesDataInstance().LoadFancyZonesData();
FancyZonesDataInstance().SyncVirtualDesktops(m_currentDesktopId);
UpdateZoneSets();
}

View File

@@ -5,6 +5,7 @@
#include "ZoneSet.h"
#include "Settings.h"
#include "CallTracer.h"
#include "GuidUtils.h"
#include <common/Display/dpi_aware.h>
#include <common/utils/json.h>
@@ -30,7 +31,6 @@ namespace NonLocalizable
const wchar_t FancyZonesDataFile[] = L"zones-settings.json";
const wchar_t FancyZonesAppZoneHistoryFile[] = L"app-zone-history.json";
const wchar_t FancyZonesEditorParametersFile[] = L"editor-parameters.json";
const wchar_t DefaultGuid[] = L"{00000000-0000-0000-0000-000000000000}";
const wchar_t RegistryPath[] = L"Software\\SuperFancyZones";
}
@@ -157,6 +157,11 @@ FancyZonesData::FancyZonesData()
editorParametersFileName = saveFolderPath + L"\\" + std::wstring(NonLocalizable::FancyZonesEditorParametersFile);
}
void FancyZonesData::SetVirtualDesktopCheckCallback(std::function<bool(GUID)> callback)
{
m_virtualDesktopCheckCallback = callback;
}
const JSONHelpers::TDeviceInfoMap& FancyZonesData::GetDeviceInfoMap() const
{
std::scoped_lock lock{ dataLock };
@@ -175,7 +180,7 @@ const std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneH
return appZoneHistoryMap;
}
std::optional<FancyZonesDataTypes::DeviceInfoData> FancyZonesData::FindDeviceInfo(const std::wstring& zoneWindowId) const
std::optional<FancyZonesDataTypes::DeviceInfoData> FancyZonesData::FindDeviceInfo(const FancyZonesDataTypes::DeviceIdData& zoneWindowId) const
{
std::scoped_lock lock{ dataLock };
auto it = deviceInfoMap.find(zoneWindowId);
@@ -189,7 +194,7 @@ std::optional<FancyZonesDataTypes::CustomZoneSetData> FancyZonesData::FindCustom
return it != end(customZoneSetsMap) ? std::optional{ it->second } : std::nullopt;
}
bool FancyZonesData::AddDevice(const std::wstring& deviceId)
bool FancyZonesData::AddDevice(const FancyZonesDataTypes::DeviceIdData& deviceId)
{
_TRACER_;
using namespace FancyZonesDataTypes;
@@ -197,6 +202,12 @@ bool FancyZonesData::AddDevice(const std::wstring& deviceId)
std::scoped_lock lock{ dataLock };
if (!deviceInfoMap.contains(deviceId))
{
wil::unique_cotaskmem_string virtualDesktopId;
if (SUCCEEDED(StringFromCLSID(deviceId.virtualDesktopId, &virtualDesktopId)))
{
Logger::info(L"Create new device on virtual desktop {}", virtualDesktopId.get());
}
// Creates default entry in map when WorkArea is created
GUID guid;
auto result{ CoCreateGuid(&guid) };
@@ -218,7 +229,7 @@ bool FancyZonesData::AddDevice(const std::wstring& deviceId)
return false;
}
void FancyZonesData::CloneDeviceInfo(const std::wstring& source, const std::wstring& destination)
void FancyZonesData::CloneDeviceInfo(const FancyZonesDataTypes::DeviceIdData& source, const FancyZonesDataTypes::DeviceIdData& destination)
{
if (source == destination)
{
@@ -235,7 +246,7 @@ void FancyZonesData::CloneDeviceInfo(const std::wstring& source, const std::wstr
deviceInfoMap[destination] = deviceInfoMap[source];
}
void FancyZonesData::UpdatePrimaryDesktopData(const std::wstring& desktopId)
void FancyZonesData::SyncVirtualDesktops(GUID currentVirtualDesktopId)
{
_TRACER_;
// Explorer persists current virtual desktop identifier to registry on a per session basis,
@@ -244,9 +255,6 @@ void FancyZonesData::UpdatePrimaryDesktopData(const std::wstring& desktopId)
// that case (00000000-0000-0000-0000-000000000000).
// This method will go through all our persisted data with default GUID and update it with
// valid one.
auto replaceDesktopId = [&desktopId](const std::wstring& deviceId) {
return deviceId.substr(0, deviceId.rfind('_') + 1) + desktopId;
};
std::scoped_lock lock{ dataLock };
bool dirtyFlag = false;
@@ -255,53 +263,89 @@ void FancyZonesData::UpdatePrimaryDesktopData(const std::wstring& desktopId)
{
for (auto& data : perDesktopData)
{
if (ExtractVirtualDesktopId(data.deviceId) == NonLocalizable::DefaultGuid)
if (data.deviceId.virtualDesktopId == GUID_NULL)
{
data.deviceId = replaceDesktopId(data.deviceId);
data.deviceId.virtualDesktopId = currentVirtualDesktopId;
dirtyFlag = true;
}
else
{
if (m_virtualDesktopCheckCallback && !m_virtualDesktopCheckCallback(data.deviceId.virtualDesktopId))
{
data.deviceId.virtualDesktopId = GUID_NULL;
dirtyFlag = true;
}
}
}
}
std::vector<FancyZonesDataTypes::DeviceIdData> replaceWithCurrentId{};
std::vector<FancyZonesDataTypes::DeviceIdData> replaceWithNullId{};
for (const auto& [desktopId, data] : deviceInfoMap)
{
if (desktopId.virtualDesktopId == GUID_NULL)
{
replaceWithCurrentId.push_back(desktopId);
dirtyFlag = true;
}
else
{
if (m_virtualDesktopCheckCallback && !m_virtualDesktopCheckCallback(desktopId.virtualDesktopId))
{
replaceWithNullId.push_back(desktopId);
dirtyFlag = true;
}
}
}
std::vector<std::wstring> toReplace{};
for (const auto& [id, data] : deviceInfoMap)
{
if (ExtractVirtualDesktopId(id) == NonLocalizable::DefaultGuid)
{
toReplace.push_back(id);
dirtyFlag = true;
}
}
for (const auto& id : toReplace)
for (const auto& id : replaceWithCurrentId)
{
auto mapEntry = deviceInfoMap.extract(id);
mapEntry.key() = replaceDesktopId(id);
mapEntry.key().virtualDesktopId = currentVirtualDesktopId;
deviceInfoMap.insert(std::move(mapEntry));
}
for (const auto& id : replaceWithNullId)
{
auto mapEntry = deviceInfoMap.extract(id);
mapEntry.key().virtualDesktopId = GUID_NULL;
deviceInfoMap.insert(std::move(mapEntry));
}
// TODO: when updating the primary desktop GUID, the app zone history also needs to be updated
if (dirtyFlag)
{
SaveZoneSettings();
wil::unique_cotaskmem_string virtualDesktopIdStr;
if (SUCCEEDED(StringFromCLSID(currentVirtualDesktopId, &virtualDesktopIdStr)))
{
Logger::info(L"Update Virtual Desktop id to {}", virtualDesktopIdStr.get());
}
SaveAppZoneHistoryAndZoneSettings();
}
}
void FancyZonesData::RemoveDeletedDesktops(const std::vector<std::wstring>& activeDesktops)
void FancyZonesData::RemoveDeletedDesktops(const std::vector<GUID>& activeDesktops)
{
std::unordered_set<std::wstring> active(std::begin(activeDesktops), std::end(activeDesktops));
std::unordered_set<GUID> active(std::begin(activeDesktops), std::end(activeDesktops));
std::scoped_lock lock{ dataLock };
bool dirtyFlag = false;
for (auto it = std::begin(deviceInfoMap); it != std::end(deviceInfoMap);)
{
std::wstring desktopId = ExtractVirtualDesktopId(it->first);
if (desktopId != NonLocalizable::DefaultGuid)
GUID desktopId = it->first.virtualDesktopId;
if (desktopId != GUID_NULL)
{
auto foundId = active.find(desktopId);
if (foundId == std::end(active))
{
wil::unique_cotaskmem_string virtualDesktopIdStr;
if (SUCCEEDED(StringFromCLSID(desktopId, &virtualDesktopIdStr)))
{
Logger::info(L"Remove Virtual Desktop id {}", virtualDesktopIdStr.get());
}
RemoveDesktopAppZoneHistory(desktopId);
it = deviceInfoMap.erase(it);
dirtyFlag = true;
@@ -317,7 +361,7 @@ void FancyZonesData::RemoveDeletedDesktops(const std::vector<std::wstring>& acti
}
}
bool FancyZonesData::IsAnotherWindowOfApplicationInstanceZoned(HWND window, const std::wstring_view& deviceId) const
bool FancyZonesData::IsAnotherWindowOfApplicationInstanceZoned(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId) const
{
std::scoped_lock lock{ dataLock };
auto processPath = get_process_path(window);
@@ -329,7 +373,7 @@ bool FancyZonesData::IsAnotherWindowOfApplicationInstanceZoned(HWND window, cons
auto& perDesktopData = history->second;
for (auto& data : perDesktopData)
{
if (data.deviceId == deviceId)
if (data.deviceId.isEqualWithNullVirtualDesktopId(deviceId))
{
DWORD processId = 0;
GetWindowThreadProcessId(window, &processId);
@@ -352,7 +396,7 @@ bool FancyZonesData::IsAnotherWindowOfApplicationInstanceZoned(HWND window, cons
return false;
}
void FancyZonesData::UpdateProcessIdToHandleMap(HWND window, const std::wstring_view& deviceId)
void FancyZonesData::UpdateProcessIdToHandleMap(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId)
{
std::scoped_lock lock{ dataLock };
auto processPath = get_process_path(window);
@@ -364,7 +408,7 @@ void FancyZonesData::UpdateProcessIdToHandleMap(HWND window, const std::wstring_
auto& perDesktopData = history->second;
for (auto& data : perDesktopData)
{
if (data.deviceId == deviceId)
if (data.deviceId.isEqualWithNullVirtualDesktopId(deviceId))
{
DWORD processId = 0;
GetWindowThreadProcessId(window, &processId);
@@ -376,7 +420,7 @@ void FancyZonesData::UpdateProcessIdToHandleMap(HWND window, const std::wstring_
}
}
ZoneIndexSet FancyZonesData::GetAppLastZoneIndexSet(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const
ZoneIndexSet FancyZonesData::GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId, const std::wstring_view& zoneSetId) const
{
std::scoped_lock lock{ dataLock };
auto processPath = get_process_path(window);
@@ -388,7 +432,7 @@ ZoneIndexSet FancyZonesData::GetAppLastZoneIndexSet(HWND window, const std::wstr
const auto& perDesktopData = history->second;
for (const auto& data : perDesktopData)
{
if (data.zoneSetUuid == zoneSetId && data.deviceId == deviceId)
if (data.zoneSetUuid == zoneSetId && data.deviceId.isEqualWithNullVirtualDesktopId(deviceId))
{
return data.zoneIndexSet;
}
@@ -399,7 +443,7 @@ ZoneIndexSet FancyZonesData::GetAppLastZoneIndexSet(HWND window, const std::wstr
return {};
}
bool FancyZonesData::RemoveAppLastZone(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId)
bool FancyZonesData::RemoveAppLastZone(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId, const std::wstring_view& zoneSetId)
{
_TRACER_;
std::scoped_lock lock{ dataLock };
@@ -412,7 +456,7 @@ bool FancyZonesData::RemoveAppLastZone(HWND window, const std::wstring_view& dev
auto& perDesktopData = history->second;
for (auto data = std::begin(perDesktopData); data != std::end(perDesktopData);)
{
if (data->deviceId == deviceId && data->zoneSetUuid == zoneSetId)
if (data->deviceId.isEqualWithNullVirtualDesktopId(deviceId) && data->zoneSetUuid == zoneSetId)
{
if (!IsAnotherWindowOfApplicationInstanceZoned(window, deviceId))
{
@@ -452,7 +496,7 @@ bool FancyZonesData::RemoveAppLastZone(HWND window, const std::wstring_view& dev
return false;
}
bool FancyZonesData::SetAppLastZones(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, const ZoneIndexSet& zoneIndexSet)
bool FancyZonesData::SetAppLastZones(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId, const std::wstring& zoneSetId, const ZoneIndexSet& zoneIndexSet)
{
_TRACER_;
std::scoped_lock lock{ dataLock };
@@ -477,7 +521,7 @@ bool FancyZonesData::SetAppLastZones(HWND window, const std::wstring& deviceId,
auto& perDesktopData = history->second;
for (auto& data : perDesktopData)
{
if (data.deviceId == deviceId)
if (data.deviceId.isEqualWithNullVirtualDesktopId(deviceId))
{
// application already has history on this work area, update it with new window position
data.processIdToHandleMap[processId] = window;
@@ -511,7 +555,7 @@ bool FancyZonesData::SetAppLastZones(HWND window, const std::wstring& deviceId,
return true;
}
void FancyZonesData::SetActiveZoneSet(const std::wstring& deviceId, const FancyZonesDataTypes::ZoneSetData& data)
void FancyZonesData::SetActiveZoneSet(const FancyZonesDataTypes::DeviceIdData& deviceId, const FancyZonesDataTypes::ZoneSetData& data)
{
std::scoped_lock lock{ dataLock };
@@ -576,14 +620,67 @@ void FancyZonesData::SaveZoneSettings() const
{
_TRACER_;
std::scoped_lock lock{ dataLock };
JSONHelpers::SaveZoneSettings(zonesSettingsFileName, deviceInfoMap, customZoneSetsMap, quickKeysMap);
bool dirtyFlag = false;
JSONHelpers::TDeviceInfoMap updatedDeviceInfoMap;
if (m_virtualDesktopCheckCallback)
{
for (const auto& [id, data] : deviceInfoMap)
{
auto updatedId = id;
if (!m_virtualDesktopCheckCallback(id.virtualDesktopId))
{
updatedId.virtualDesktopId = GUID_NULL;
dirtyFlag = true;
}
updatedDeviceInfoMap.insert({ updatedId, data });
}
}
if (dirtyFlag)
{
JSONHelpers::SaveZoneSettings(zonesSettingsFileName, updatedDeviceInfoMap, customZoneSetsMap, quickKeysMap);
}
else
{
JSONHelpers::SaveZoneSettings(zonesSettingsFileName, deviceInfoMap, customZoneSetsMap, quickKeysMap);
}
}
void FancyZonesData::SaveAppZoneHistory() const
{
_TRACER_;
std::scoped_lock lock{ dataLock };
JSONHelpers::SaveAppZoneHistory(appZoneHistoryFileName, appZoneHistoryMap);
bool dirtyFlag = false;
std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>> updatedHistory;
if (m_virtualDesktopCheckCallback)
{
for (const auto& [path, dataVector] : appZoneHistoryMap)
{
auto updatedVector = dataVector;
for (auto& data : updatedVector)
{
if (!m_virtualDesktopCheckCallback(data.deviceId.virtualDesktopId))
{
data.deviceId.virtualDesktopId = GUID_NULL;
dirtyFlag = true;
}
}
updatedHistory.insert(std::make_pair(path, updatedVector));
}
}
if (dirtyFlag)
{
JSONHelpers::SaveAppZoneHistory(appZoneHistoryFileName, updatedHistory);
}
else
{
JSONHelpers::SaveAppZoneHistory(appZoneHistoryFileName, appZoneHistoryMap);
}
}
void FancyZonesData::SaveFancyZonesEditorParameters(bool spanZonesAcrossMonitors, const std::wstring& virtualDesktopId, const HMONITOR& targetMonitor, const std::vector<std::pair<HMONITOR, MONITORINFOEX>>& allMonitors) const
@@ -650,14 +747,14 @@ void FancyZonesData::SaveFancyZonesEditorParameters(bool spanZonesAcrossMonitors
json::to_file(editorParametersFileName, JSONHelpers::EditorArgs::ToJson(argsJson));
}
void FancyZonesData::RemoveDesktopAppZoneHistory(const std::wstring& desktopId)
void FancyZonesData::RemoveDesktopAppZoneHistory(GUID desktopId)
{
for (auto it = std::begin(appZoneHistoryMap); it != std::end(appZoneHistoryMap);)
{
auto& perDesktopData = it->second;
for (auto desktopIt = std::begin(perDesktopData); desktopIt != std::end(perDesktopData);)
{
if (ExtractVirtualDesktopId(desktopIt->deviceId) == desktopId)
if (desktopIt->deviceId.virtualDesktopId == desktopId)
{
desktopIt = perDesktopData.erase(desktopIt);
}

View File

@@ -44,14 +44,13 @@ class FancyZonesData
public:
FancyZonesData();
std::optional<FancyZonesDataTypes::DeviceInfoData> FindDeviceInfo(const std::wstring& zoneWindowId) const;
void SetVirtualDesktopCheckCallback(std::function<bool(GUID)> callback);
std::optional<FancyZonesDataTypes::DeviceInfoData> FindDeviceInfo(const FancyZonesDataTypes::DeviceIdData& zoneWindowId) const;
std::optional<FancyZonesDataTypes::CustomZoneSetData> FindCustomZoneSet(const std::wstring& guid) const;
const JSONHelpers::TDeviceInfoMap& GetDeviceInfoMap() const;
const JSONHelpers::TCustomZoneSetsMap& GetCustomZoneSetsMap() const;
const std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>>& GetAppZoneHistoryMap() const;
inline const JSONHelpers::TLayoutQuickKeysMap& GetLayoutQuickKeys() const
@@ -70,18 +69,18 @@ public:
return settingsFileName;
}
bool AddDevice(const std::wstring& deviceId);
void CloneDeviceInfo(const std::wstring& source, const std::wstring& destination);
void UpdatePrimaryDesktopData(const std::wstring& desktopId);
void RemoveDeletedDesktops(const std::vector<std::wstring>& activeDesktops);
bool AddDevice(const FancyZonesDataTypes::DeviceIdData& deviceId);
void CloneDeviceInfo(const FancyZonesDataTypes::DeviceIdData& source, const FancyZonesDataTypes::DeviceIdData& destination);
void SyncVirtualDesktops(GUID desktopId);
void RemoveDeletedDesktops(const std::vector<GUID>& activeDesktops);
bool IsAnotherWindowOfApplicationInstanceZoned(HWND window, const std::wstring_view& deviceId) const;
void UpdateProcessIdToHandleMap(HWND window, const std::wstring_view& deviceId);
ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const;
bool RemoveAppLastZone(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId);
bool SetAppLastZones(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, const ZoneIndexSet& zoneIndexSet);
bool IsAnotherWindowOfApplicationInstanceZoned(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId) const;
void UpdateProcessIdToHandleMap(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId);
ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId, const std::wstring_view& zoneSetId) const;
bool RemoveAppLastZone(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId, const std::wstring_view& zoneSetId);
bool SetAppLastZones(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId, const std::wstring& zoneSetId, const ZoneIndexSet& zoneIndexSet);
void SetActiveZoneSet(const std::wstring& deviceId, const FancyZonesDataTypes::ZoneSetData& zoneSet);
void SetActiveZoneSet(const FancyZonesDataTypes::DeviceIdData& deviceId, const FancyZonesDataTypes::ZoneSetData& zoneSet);
json::JsonObject GetPersistFancyZonesJSON();
@@ -100,7 +99,7 @@ private:
friend class FancyZonesUnitTests::WorkAreaCreationUnitTests;
friend class FancyZonesUnitTests::ZoneSetCalculateZonesUnitTests;
inline void SetDeviceInfo(const std::wstring& deviceId, FancyZonesDataTypes::DeviceInfoData data)
inline void SetDeviceInfo(const FancyZonesDataTypes::DeviceIdData& deviceId, FancyZonesDataTypes::DeviceInfoData data)
{
deviceInfoMap[deviceId] = data;
}
@@ -130,7 +129,7 @@ private:
appZoneHistoryFileName = result + L"\\" + std::wstring(L"app-zone-history.json");
}
#endif
void RemoveDesktopAppZoneHistory(const std::wstring& desktopId);
void RemoveDesktopAppZoneHistory(GUID desktopId);
// Maps app path to app's zone history data
std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>> appZoneHistoryMap{};
@@ -146,6 +145,8 @@ private:
std::wstring appZoneHistoryFileName;
std::wstring editorParametersFileName;
std::function<bool(GUID)> m_virtualDesktopCheckCallback;
mutable std::recursive_mutex dataLock;
};

View File

@@ -126,4 +126,186 @@ namespace FancyZonesDataTypes
return high + 1;
}
std::optional<DeviceIdData> DeviceIdData::ParseDeviceId(const std::wstring& str)
{
FancyZonesDataTypes::DeviceIdData data;
std::wstring temp;
std::wstringstream wss(str);
/*
Important fix for device info that contains a '_' in the name:
1. first search for '#'
2. Then split the remaining string by '_'
*/
// Step 1: parse the name until the #, then to the '_'
if (str.find(L'#') != std::string::npos)
{
std::getline(wss, temp, L'#');
data.deviceName = temp;
if (!std::getline(wss, temp, L'_'))
{
return std::nullopt;
}
data.deviceName += L"#" + temp;
}
else if (std::getline(wss, temp, L'_') && !temp.empty())
{
data.deviceName = temp;
}
else
{
return std::nullopt;
}
// Step 2: parse the rest of the id
std::vector<std::wstring> parts;
while (std::getline(wss, temp, L'_'))
{
parts.push_back(temp);
}
if (parts.size() != 3 && parts.size() != 4)
{
return std::nullopt;
}
/*
Refer to ZoneWindowUtils::GenerateUniqueId parts contain:
1. monitor id [string]
2. width of device [int]
3. height of device [int]
4. virtual desktop id (GUID) [string]
*/
try
{
for (const auto& c : parts[0])
{
std::stoi(std::wstring(&c));
}
for (const auto& c : parts[1])
{
std::stoi(std::wstring(&c));
}
data.width = std::stoi(parts[0]);
data.height = std::stoi(parts[1]);
}
catch (const std::exception&)
{
return std::nullopt;
}
if (!SUCCEEDED(CLSIDFromString(parts[2].c_str(), &data.virtualDesktopId)))
{
return std::nullopt;
}
if (parts.size() == 4)
{
data.monitorId = parts[3]; //could be empty
}
return data;
}
bool DeviceIdData::IsValidDeviceId(const std::wstring& str)
{
std::wstring monitorName;
std::wstring temp;
std::vector<std::wstring> parts;
std::wstringstream wss(str);
/*
Important fix for device info that contains a '_' in the name:
1. first search for '#'
2. Then split the remaining string by '_'
*/
// Step 1: parse the name until the #, then to the '_'
if (str.find(L'#') != std::string::npos)
{
std::getline(wss, temp, L'#');
monitorName = temp;
if (!std::getline(wss, temp, L'_'))
{
return false;
}
monitorName += L"#" + temp;
parts.push_back(monitorName);
}
// Step 2: parse the rest of the id
while (std::getline(wss, temp, L'_'))
{
parts.push_back(temp);
}
if (parts.size() != 4)
{
return false;
}
/*
Refer to ZoneWindowUtils::GenerateUniqueId parts contain:
1. monitor id [string]
2. width of device [int]
3. height of device [int]
4. virtual desktop id (GUID) [string]
*/
try
{
//check if resolution contain only digits
for (const auto& c : parts[1])
{
std::stoi(std::wstring(&c));
}
for (const auto& c : parts[2])
{
std::stoi(std::wstring(&c));
}
}
catch (const std::exception&)
{
return false;
}
if (!FancyZonesUtils::IsValidGuid(parts[3]) || parts[0].empty())
{
return false;
}
return true;
}
std::wstring DeviceIdData::toString() const
{
wil::unique_cotaskmem_string virtualDesktopIdStr;
if (!SUCCEEDED(StringFromCLSID(virtualDesktopId, &virtualDesktopIdStr)))
{
return std::wstring();
}
std::wstring result = deviceName + L"_" + std::to_wstring(width) + L"_" + std::to_wstring(height) + L"_" + virtualDesktopIdStr.get();
if (!monitorId.empty())
{
result += L"_" + monitorId;
}
return result;
}
bool DeviceIdData::isEqualWithNullVirtualDesktopId(const DeviceIdData& other) const
{
return deviceName.compare(other.deviceName) == 0 && width == other.width && height == other.height && (virtualDesktopId == other.virtualDesktopId || virtualDesktopId == GUID_NULL || other.virtualDesktopId == GUID_NULL) && monitorId.compare(other.monitorId) == 0;
}
}

View File

@@ -113,24 +113,30 @@ namespace FancyZonesDataTypes
ZoneSetLayoutType type;
};
struct DeviceIdData
{
std::wstring deviceName = L"FallbackDevice";
int width;
int height;
GUID virtualDesktopId;
std::wstring monitorId;
static std::optional<DeviceIdData> ParseDeviceId(const std::wstring& str);
static bool IsValidDeviceId(const std::wstring& str);
std::wstring toString() const;
bool isEqualWithNullVirtualDesktopId(const DeviceIdData& other) const;
};
struct AppZoneHistoryData
{
std::unordered_map<DWORD, HWND> processIdToHandleMap; // Maps process id(DWORD) of application to zoned window handle(HWND)
std::wstring zoneSetUuid;
std::wstring deviceId;
DeviceIdData deviceId;
ZoneIndexSet zoneIndexSet;
};
struct DeviceIdData
{
std::wstring deviceName;
int width;
int height;
GUID virtualDesktopId;
std::wstring monitorId;
};
struct DeviceInfoData
{
ZoneSetData activeZoneSet;
@@ -139,4 +145,31 @@ namespace FancyZonesDataTypes
int zoneCount;
int sensitivityRadius;
};
inline bool operator==(const DeviceIdData& lhs, const DeviceIdData& rhs)
{
return lhs.deviceName.compare(rhs.deviceName) == 0 && lhs.width == rhs.width && lhs.height == rhs.height && lhs.virtualDesktopId == rhs.virtualDesktopId && lhs.monitorId.compare(rhs.monitorId) == 0;
}
inline bool operator!=(const DeviceIdData& lhs, const DeviceIdData& rhs)
{
return !(lhs == rhs);
}
inline bool operator<(const DeviceIdData& lhs, const DeviceIdData& rhs)
{
return lhs.deviceName.compare(rhs.deviceName) < 0 || lhs.width < rhs.width || lhs.height < rhs.height || lhs.monitorId.compare(rhs.monitorId) < 0;
}
}
namespace std
{
template<>
struct hash<FancyZonesDataTypes::DeviceIdData>
{
size_t operator()(const FancyZonesDataTypes::DeviceIdData& Value) const
{
return 0;
}
};
}

View File

@@ -43,6 +43,7 @@
<ClInclude Include="FancyZonesWinHookEventIDs.h" />
<ClInclude Include="GenericKeyHook.h" />
<ClInclude Include="FancyZonesData.h" />
<ClInclude Include="GuidUtils.h" />
<ClInclude Include="JsonHelpers.h" />
<ClInclude Include="KeyState.h" />
<ClInclude Include="MonitorUtils.h" />

View File

@@ -90,6 +90,9 @@
<ClInclude Include="FancyZonesWindowProperties.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GuidUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">

View File

@@ -0,0 +1,40 @@
#pragma once
#include "gdiplus.h"
namespace std
{
template<>
struct hash<GUID>
{
size_t operator()(const GUID& Value) const
{
RPC_STATUS status = RPC_S_OK;
return ::UuidHash(&const_cast<GUID&>(Value), &status);
}
};
}
inline bool operator<(const GUID& guid1, const GUID& guid2)
{
if (guid1.Data1 != guid2.Data1)
{
return guid1.Data1 < guid2.Data1;
}
if (guid1.Data2 != guid2.Data2)
{
return guid1.Data2 < guid2.Data2;
}
if (guid1.Data3 != guid2.Data3)
{
return guid1.Data3 < guid2.Data3;
}
for (int i = 0; i < 8; i++)
{
if (guid1.Data4[i] != guid2.Data4[i])
{
return guid1.Data4[i] < guid2.Data4[i];
}
}
return false;
}

View File

@@ -109,10 +109,17 @@ namespace
data.zoneIndexSet = { static_cast<ZoneIndex>(json.GetNamedNumber(NonLocalizable::ZoneIndexStr)) };
}
data.deviceId = json.GetNamedString(NonLocalizable::DeviceIdStr);
std::wstring deviceIdStr = json.GetNamedString(NonLocalizable::DeviceIdStr).c_str();
auto deviceId = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(deviceIdStr);
if (!deviceId.has_value())
{
return std::nullopt;
}
data.deviceId = *deviceId;
data.zoneSetUuid = json.GetNamedString(NonLocalizable::ZoneSetUuidStr);
if (!FancyZonesUtils::IsValidGuid(data.zoneSetUuid) || !FancyZonesUtils::IsValidDeviceId(data.deviceId))
if (!FancyZonesUtils::IsValidGuid(data.zoneSetUuid))
{
return std::nullopt;
}
@@ -377,7 +384,7 @@ namespace JSONHelpers
}
desktopData.SetNamedValue(NonLocalizable::ZoneIndexSetStr, jsonIndexSet);
desktopData.SetNamedValue(NonLocalizable::DeviceIdStr, json::value(data.deviceId));
desktopData.SetNamedValue(NonLocalizable::DeviceIdStr, json::value(data.deviceId.toString()));
desktopData.SetNamedValue(NonLocalizable::ZoneSetUuidStr, json::value(data.zoneSetUuid));
appHistoryArray.Append(desktopData);
@@ -432,7 +439,7 @@ namespace JSONHelpers
{
json::JsonObject result{};
result.SetNamedValue(NonLocalizable::DeviceIdStr, json::value(device.deviceId));
result.SetNamedValue(NonLocalizable::DeviceIdStr, json::value(device.deviceId.toString()));
result.SetNamedValue(NonLocalizable::ActiveZoneSetStr, JSONHelpers::ZoneSetDataJSON::ToJson(device.data.activeZoneSet));
result.SetNamedValue(NonLocalizable::EditorShowSpacingStr, json::value(device.data.showSpacing));
result.SetNamedValue(NonLocalizable::EditorSpacingStr, json::value(device.data.spacing));
@@ -448,12 +455,15 @@ namespace JSONHelpers
{
DeviceInfoJSON result;
result.deviceId = device.GetNamedString(NonLocalizable::DeviceIdStr);
if (!FancyZonesUtils::IsValidDeviceId(result.deviceId))
std::wstring deviceIdStr = device.GetNamedString(NonLocalizable::DeviceIdStr).c_str();
auto deviceId = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(deviceIdStr);
if (!deviceId.has_value())
{
return std::nullopt;
}
result.deviceId = *deviceId;
if (auto zoneSet = JSONHelpers::ZoneSetDataJSON::FromJson(device.GetNamedObject(NonLocalizable::ActiveZoneSetStr)); zoneSet.has_value())
{
result.data.activeZoneSet = std::move(zoneSet.value());

View File

@@ -48,7 +48,7 @@ namespace JSONHelpers
struct DeviceInfoJSON
{
std::wstring deviceId;
FancyZonesDataTypes::DeviceIdData deviceId;
FancyZonesDataTypes::DeviceInfoData data;
static json::JsonObject ToJson(const DeviceInfoJSON& device);
@@ -65,7 +65,7 @@ namespace JSONHelpers
};
using TAppZoneHistoryMap = std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>>;
using TDeviceInfoMap = std::unordered_map<std::wstring, FancyZonesDataTypes::DeviceInfoData>;
using TDeviceInfoMap = std::unordered_map<FancyZonesDataTypes::DeviceIdData, FancyZonesDataTypes::DeviceInfoData>;
using TCustomZoneSetsMap = std::unordered_map<std::wstring, FancyZonesDataTypes::CustomZoneSetData>;
using TLayoutQuickKeysMap = std::unordered_map<std::wstring, int>;

View File

@@ -1,6 +1,7 @@
#include "pch.h"
#include "MonitorWorkAreaHandler.h"
#include "VirtualDesktop.h"
#include "util.h"
winrt::com_ptr<IWorkArea> MonitorWorkAreaHandler::GetWorkArea(const GUID& desktopId, HMONITOR monitor)
{

View File

@@ -1,22 +1,11 @@
#pragma once
#include "GuidUtils.h"
interface IWorkArea;
struct ZoneColors;
enum struct OverlappingZonesAlgorithm;
namespace std
{
template<>
struct hash<GUID>
{
size_t operator()(const GUID& Value) const
{
RPC_STATUS status = RPC_S_OK;
return ::UuidHash(&const_cast<GUID&>(Value), &status);
}
};
}
class MonitorWorkAreaHandler
{
public:

View File

@@ -1,6 +1,8 @@
#include "pch.h"
#include "VirtualDesktop.h"
#include <common/logger/logger.h>
// Non-Localizable strings
namespace NonLocalizable
{
@@ -17,6 +19,7 @@ IServiceProvider* GetServiceProvider()
IServiceProvider* provider{ nullptr };
if (FAILED(CoCreateInstance(CLSID_ImmersiveShell, nullptr, CLSCTX_LOCAL_SERVER, __uuidof(provider), (PVOID*)&provider)))
{
Logger::error("Failed to get ServiceProvider for VirtualDesktopManager");
return nullptr;
}
return provider;
@@ -28,6 +31,7 @@ IVirtualDesktopManager* GetVirtualDesktopManager()
IServiceProvider* serviceProvider = GetServiceProvider();
if (serviceProvider == nullptr || FAILED(serviceProvider->QueryService(__uuidof(manager), &manager)))
{
Logger::error("Failed to get VirtualDesktopManager");
return nullptr;
}
return manager;
@@ -77,14 +81,6 @@ std::optional<GUID> GetDesktopIdFromCurrentSession()
return std::nullopt;
}
bool GetZoneWindowDesktopId(IWorkArea* zoneWindow, GUID* desktopId)
{
// Format: <device-id>_<resolution>_<virtual-desktop-id>
std::wstring uniqueId = zoneWindow->UniqueId();
std::wstring virtualDesktopId = uniqueId.substr(uniqueId.rfind('_') + 1);
return SUCCEEDED(CLSIDFromString(virtualDesktopId.c_str(), desktopId));
}
HKEY OpenVirtualDesktopsRegKey()
{
HKEY hKey{ nullptr };
@@ -124,18 +120,7 @@ void VirtualDesktop::UnInit()
}
}
std::optional<GUID> VirtualDesktop::GetWindowDesktopId(HWND topLevelWindow) const
{
GUID desktopId{};
if (m_vdManager && SUCCEEDED(m_vdManager->GetWindowDesktopId(topLevelWindow, &desktopId)))
{
return desktopId;
}
return std::nullopt;
}
std::optional<GUID> VirtualDesktop::GetCurrentVirtualDesktopId() const
std::optional<GUID> VirtualDesktop::GetCurrentVirtualDesktopIdFromRegistry() const
{
// On newer Windows builds, the current virtual desktop is persisted to
// a totally different reg key. Look there first.
@@ -160,19 +145,17 @@ std::optional<GUID> VirtualDesktop::GetCurrentVirtualDesktopId() const
// switch occurred in current session.
else
{
auto ids = GetVirtualDesktopIds();
auto ids = GetVirtualDesktopIdsFromRegistry();
if (ids.has_value() && ids->size() > 0)
{
return ids->at(0);
}
}
desktopId = GetDesktopIdByTopLevelWindows();
return desktopId;
return std::nullopt;
}
std::optional<std::vector<GUID>> VirtualDesktop::GetVirtualDesktopIds(HKEY hKey) const
std::optional<std::vector<GUID>> VirtualDesktop::GetVirtualDesktopIdsFromRegistry(HKEY hKey) const
{
if (!hKey)
{
@@ -205,9 +188,9 @@ std::optional<std::vector<GUID>> VirtualDesktop::GetVirtualDesktopIds(HKEY hKey)
return temp;
}
std::optional<std::vector<GUID>> VirtualDesktop::GetVirtualDesktopIds() const
std::optional<std::vector<GUID>> VirtualDesktop::GetVirtualDesktopIdsFromRegistry() const
{
return GetVirtualDesktopIds(GetVirtualDesktopsRegKey());
return GetVirtualDesktopIdsFromRegistry(GetVirtualDesktopsRegKey());
}
bool VirtualDesktop::IsWindowOnCurrentDesktop(HWND window) const
@@ -232,25 +215,50 @@ std::optional<GUID> VirtualDesktop::GetDesktopId(HWND window) const
return std::nullopt;
}
std::optional<GUID> VirtualDesktop::GetDesktopIdByTopLevelWindows() const
std::vector<std::pair<HWND, GUID>> VirtualDesktop::GetWindowsRelatedToDesktops() const
{
using result_t = std::vector<HWND>;
result_t result;
result_t windows;
auto callback = [](HWND window, LPARAM data) -> BOOL {
result_t& result = *reinterpret_cast<result_t*>(data);
result.push_back(window);
return TRUE;
};
EnumWindows(callback, reinterpret_cast<LPARAM>(&result));
EnumWindows(callback, reinterpret_cast<LPARAM>(&windows));
for (const auto window : result)
std::vector<std::pair<HWND, GUID>> result;
for (auto window : windows)
{
auto desktop = GetDesktopId(window);
if (desktop.has_value())
{
result.push_back({ window, *desktop });
}
}
return result;
}
std::optional<GUID> VirtualDesktop::GetDesktopIdByTopLevelWindows() const
{
using result_t = std::vector<HWND>;
result_t windows;
auto callback = [](HWND window, LPARAM data) -> BOOL {
result_t& result = *reinterpret_cast<result_t*>(data);
result.push_back(window);
return TRUE;
};
EnumWindows(callback, reinterpret_cast<LPARAM>(&windows));
for (const auto window : windows)
{
std::optional<GUID> id = GetDesktopId(window);
if (id.has_value())
{
// Otherwise keep checking other windows
return id;
return *id;
}
}

View File

@@ -9,15 +9,36 @@ public:
VirtualDesktop(const std::function<void()>& vdInitCallback, const std::function<void()>& vdUpdatedCallback);
~VirtualDesktop() = default;
inline bool IsVirtualDesktopIdSavedInRegistry(GUID id) const
{
auto ids = GetVirtualDesktopIdsFromRegistry();
if (!ids.has_value())
{
return false;
}
for (const auto& regId : *ids)
{
if (regId == id)
{
return true;
}
}
return false;
}
void Init();
void UnInit();
std::optional<GUID> GetWindowDesktopId(HWND topLevelWindow) const;
std::optional<GUID> GetCurrentVirtualDesktopId() const;
std::optional<std::vector<GUID>> GetVirtualDesktopIds() const;
std::optional<GUID> GetCurrentVirtualDesktopIdFromRegistry() const;
std::optional<std::vector<GUID>> GetVirtualDesktopIdsFromRegistry() const;
bool IsWindowOnCurrentDesktop(HWND window) const;
std::optional<GUID> GetDesktopId(HWND window) const;
std::optional<GUID> GetDesktopIdByTopLevelWindows() const;
std::vector<std::pair<HWND, GUID>> GetWindowsRelatedToDesktops() const;
private:
std::function<void()> m_vdInitCallback;
@@ -28,7 +49,6 @@ private:
OnThreadExecutor m_virtualDesktopTrackerThread;
wil::unique_handle m_terminateVirtualDesktopTrackerEvent;
std::optional<std::vector<GUID>> GetVirtualDesktopIds(HKEY hKey) const;
std::optional<GUID> GetDesktopIdByTopLevelWindows() const;
std::optional<std::vector<GUID>> GetVirtualDesktopIdsFromRegistry(HKEY hKey) const;
void HandleVirtualDesktopUpdates();
};

View File

@@ -109,7 +109,7 @@ public:
WorkArea(HINSTANCE hinstance);
~WorkArea();
bool Init(HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, const std::wstring& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm);
bool Init(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm);
IFACEMETHODIMP MoveSizeEnter(HWND window) noexcept;
IFACEMETHODIMP MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled, bool selectManyZones) noexcept;
@@ -124,7 +124,7 @@ public:
MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle) noexcept;
IFACEMETHODIMP_(bool)
ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noexcept;
IFACEMETHODIMP_(std::wstring)
IFACEMETHODIMP_(FancyZonesDataTypes::DeviceIdData)
UniqueId() const noexcept { return { m_uniqueId }; }
IFACEMETHODIMP_(void)
SaveWindowProcessToZoneIndex(HWND window) noexcept;
@@ -151,7 +151,7 @@ protected:
static LRESULT CALLBACK s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept;
private:
void InitializeZoneSets(const std::wstring& parentUniqueId) noexcept;
void InitializeZoneSets(const FancyZonesDataTypes::DeviceIdData& parentUniqueId) noexcept;
void CalculateZoneSet(OverlappingZonesAlgorithm overlappingAlgorithm) noexcept;
void UpdateActiveZoneSet(_In_opt_ IZoneSet* zoneSet) noexcept;
LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept;
@@ -159,7 +159,7 @@ private:
void SetAsTopmostWindow() noexcept;
HMONITOR m_monitor{};
std::wstring m_uniqueId; // Parsed deviceId + resolution + virtualDesktopId
FancyZonesDataTypes::DeviceIdData m_uniqueId;
HWND m_window{}; // Hidden tool window used to represent current monitor desktop work area.
HWND m_windowMoveSize{};
winrt::com_ptr<IZoneSet> m_activeZoneSet;
@@ -189,7 +189,7 @@ WorkArea::~WorkArea()
windowPool.FreeZoneWindow(m_window);
}
bool WorkArea::Init(HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, const std::wstring& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm)
bool WorkArea::Init(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm)
{
m_zoneColors = zoneColors;
m_overlappingAlgorithm = overlappingAlgorithm;
@@ -467,11 +467,17 @@ WorkArea::SetOverlappingZonesAlgorithm(OverlappingZonesAlgorithm overlappingAlgo
#pragma region private
void WorkArea::InitializeZoneSets(const std::wstring& parentUniqueId) noexcept
void WorkArea::InitializeZoneSets(const FancyZonesDataTypes::DeviceIdData& parentUniqueId) noexcept
{
wil::unique_cotaskmem_string virtualDesktopId;
if (SUCCEEDED(StringFromCLSID(m_uniqueId.virtualDesktopId, &virtualDesktopId)))
{
Logger::debug(L"Initialize zone sets on the virtual desktop {}", virtualDesktopId.get());
}
bool deviceAdded = FancyZonesDataInstance().AddDevice(m_uniqueId);
// If the device has been added, check if it should inherit the parent's layout
if (deviceAdded && !parentUniqueId.empty())
if (deviceAdded && parentUniqueId.virtualDesktopId != GUID_NULL)
{
FancyZonesDataInstance().CloneDeviceInfo(parentUniqueId, m_uniqueId);
}
@@ -618,7 +624,7 @@ LRESULT CALLBACK WorkArea::s_WndProc(HWND window, UINT message, WPARAM wparam, L
DefWindowProc(window, message, wparam, lparam);
}
winrt::com_ptr<IWorkArea> MakeWorkArea(HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, const std::wstring& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm) noexcept
winrt::com_ptr<IWorkArea> MakeWorkArea(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm) noexcept
{
auto self = winrt::make_self<WorkArea>(hinstance);
if (self->Init(hinstance, monitor, uniqueId, parentUniqueId, zoneColors, overlappingAlgorithm))

View File

@@ -2,6 +2,7 @@
#include "FancyZones.h"
#include "FancyZonesLib/ZoneSet.h"
#include "FancyZonesLib/ZoneColors.h"
#include "FancyZonesLib/FancyZonesDataTypes.h"
/**
* Class representing single work area, which is defined by monitor and virtual desktop.
@@ -97,7 +98,7 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IWorkArea :
/**
* @returns Unique work area identifier. Format: <device-id>_<resolution>_<virtual-desktop-id>
*/
IFACEMETHOD_(std::wstring, UniqueId)() const = 0;
IFACEMETHOD_(FancyZonesDataTypes::DeviceIdData, UniqueId)() const = 0;
/**
* @returns Active zone layout for this work area.
*/
@@ -130,4 +131,4 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IWorkArea :
IFACEMETHOD_(void, SetOverlappingZonesAlgorithm)(OverlappingZonesAlgorithm overlappingAlgorithm) = 0;
};
winrt::com_ptr<IWorkArea> MakeWorkArea(HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, const std::wstring& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm) noexcept;
winrt::com_ptr<IWorkArea> MakeWorkArea(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::DeviceIdData& uniqueId, const FancyZonesDataTypes::DeviceIdData& parentUniqueId, const ZoneColors& zoneColors, OverlappingZonesAlgorithm overlappingAlgorithm) noexcept;

View File

@@ -84,94 +84,6 @@ namespace FancyZonesUtils
}
}
std::optional<FancyZonesDataTypes::DeviceIdData> ParseDeviceId(const std::wstring& str)
{
FancyZonesDataTypes::DeviceIdData data;
std::wstring temp;
std::wstringstream wss(str);
/*
Important fix for device info that contains a '_' in the name:
1. first search for '#'
2. Then split the remaining string by '_'
*/
// Step 1: parse the name until the #, then to the '_'
if (str.find(L'#') != std::string::npos)
{
std::getline(wss, temp, L'#');
data.deviceName = temp;
if (!std::getline(wss, temp, L'_'))
{
return std::nullopt;
}
data.deviceName += L"#" + temp;
}
else if (std::getline(wss, temp, L'_') && !temp.empty())
{
data.deviceName = temp;
}
else
{
return std::nullopt;
}
// Step 2: parse the rest of the id
std::vector<std::wstring> parts;
while (std::getline(wss, temp, L'_'))
{
parts.push_back(temp);
}
if (parts.size() != 3 && parts.size() != 4)
{
return std::nullopt;
}
/*
Refer to ZoneWindowUtils::GenerateUniqueId parts contain:
1. monitor id [string]
2. width of device [int]
3. height of device [int]
4. virtual desktop id (GUID) [string]
*/
try
{
for (const auto& c : parts[0])
{
std::stoi(std::wstring(&c));
}
for (const auto& c : parts[1])
{
std::stoi(std::wstring(&c));
}
data.width = std::stoi(parts[0]);
data.height = std::stoi(parts[1]);
}
catch (const std::exception&)
{
return std::nullopt;
}
if (!SUCCEEDED(CLSIDFromString(parts[2].c_str(), &data.virtualDesktopId)))
{
return std::nullopt;
}
if (parts.size() == 4)
{
data.monitorId = parts[3]; //could be empty
}
return data;
}
typedef BOOL(WINAPI* GetDpiForMonitorInternalFunc)(HMONITOR, UINT, UINT*, UINT*);
std::wstring GetDisplayDeviceId(const std::wstring& device, std::unordered_map<std::wstring, DWORD>& displayDeviceIdxMap)
@@ -617,78 +529,6 @@ namespace FancyZonesUtils
return std::nullopt;
}
bool IsValidDeviceId(const std::wstring& str)
{
std::wstring monitorName;
std::wstring temp;
std::vector<std::wstring> parts;
std::wstringstream wss(str);
/*
Important fix for device info that contains a '_' in the name:
1. first search for '#'
2. Then split the remaining string by '_'
*/
// Step 1: parse the name until the #, then to the '_'
if (str.find(L'#') != std::string::npos)
{
std::getline(wss, temp, L'#');
monitorName = temp;
if (!std::getline(wss, temp, L'_'))
{
return false;
}
monitorName += L"#" + temp;
parts.push_back(monitorName);
}
// Step 2: parse the rest of the id
while (std::getline(wss, temp, L'_'))
{
parts.push_back(temp);
}
if (parts.size() != 4)
{
return false;
}
/*
Refer to ZoneWindowUtils::GenerateUniqueId parts contain:
1. monitor id [string]
2. width of device [int]
3. height of device [int]
4. virtual desktop id (GUID) [string]
*/
try
{
//check if resolution contain only digits
for (const auto& c : parts[1])
{
std::stoi(std::wstring(&c));
}
for (const auto& c : parts[2])
{
std::stoi(std::wstring(&c));
}
}
catch (const std::exception&)
{
return false;
}
if (!IsValidGuid(parts[3]) || parts[0].empty())
{
return false;
}
return true;
}
std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& deviceId, const std::wstring& virtualDesktopId)
{
MONITORINFOEXW mi;

View File

@@ -207,10 +207,7 @@ namespace FancyZonesUtils
std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& devideId, const std::wstring& virtualDesktopId);
std::wstring GenerateUniqueIdAllMonitorsArea(const std::wstring& virtualDesktopId);
std::wstring TrimDeviceId(const std::wstring& deviceId);
std::optional<FancyZonesDataTypes::DeviceIdData> ParseDeviceId(const std::wstring& deviceId);
bool IsValidDeviceId(const std::wstring& str);
RECT PrepareRectForCycling(RECT windowRect, RECT zoneWindowRect, DWORD vkCode) noexcept;
size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector<RECT>& zoneRects) noexcept;