[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;

View File

@@ -88,61 +88,61 @@ namespace FancyZonesUnitTests
TEST_METHOD (DeviceId)
{
const auto deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
Assert::IsTrue(IsValidDeviceId(deviceId));
Assert::IsTrue(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
}
TEST_METHOD (DeviceIdWithoutHashInName)
{
const auto deviceId = L"LOCALDISPLAY_5120_1440_{00000000-0000-0000-0000-000000000000}";
Assert::IsTrue(IsValidDeviceId(deviceId));
Assert::IsTrue(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
}
TEST_METHOD (DeviceIdWithoutHashInNameButWithUnderscores)
{
const auto deviceId = L"LOCAL_DISPLAY_5120_1440_{00000000-0000-0000-0000-000000000000}";
Assert::IsFalse(IsValidDeviceId(deviceId));
Assert::IsFalse(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
}
TEST_METHOD (DeviceIdWithUnderscoresInName)
{
const auto deviceId = L"Default_Monitor#1&1f0c3c2f&0&UID256_5120_1440_{00000000-0000-0000-0000-000000000000}";
Assert::IsTrue(IsValidDeviceId(deviceId));
Assert::IsTrue(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
}
TEST_METHOD (DeviceIdInvalidFormat)
{
const auto deviceId = L"_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
Assert::IsFalse(IsValidDeviceId(deviceId));
Assert::IsFalse(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
}
TEST_METHOD (DeviceIdInvalidFormat2)
{
const auto deviceId = L"AOC2460#4&fe3a015&0&UID65793_19201200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
Assert::IsFalse(IsValidDeviceId(deviceId));
Assert::IsFalse(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
}
TEST_METHOD (DeviceIdInvalidDecimals)
{
const auto deviceId = L"AOC2460#4&fe3a015&0&UID65793_aaaa_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
Assert::IsFalse(IsValidDeviceId(deviceId));
Assert::IsFalse(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
}
TEST_METHOD (DeviceIdInvalidDecimals2)
{
const auto deviceId = L"AOC2460#4&fe3a015&0&UID65793_19a0_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
Assert::IsFalse(IsValidDeviceId(deviceId));
Assert::IsFalse(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
}
TEST_METHOD (DeviceIdInvalidDecimals3)
{
const auto deviceId = L"AOC2460#4&fe3a015&0&UID65793_1900_120000000000000_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
Assert::IsFalse(IsValidDeviceId(deviceId));
Assert::IsFalse(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
}
TEST_METHOD (DeviceIdInvalidGuid)
{
const auto deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-4B5D-8851-4791D66B1539}";
Assert::IsFalse(IsValidDeviceId(deviceId));
Assert::IsFalse(FancyZonesDataTypes::DeviceIdData::IsValidDeviceId(deviceId));
}
};
TEST_CLASS (ZoneSetLayoutTypeUnitTest)
@@ -779,7 +779,7 @@ namespace FancyZonesUnitTests
.zoneSetUuid = L"zoneset-uuid", .deviceId = L"device-id", .zoneIndexSet = { 54321 }
};
AppZoneHistoryJSON appZoneHistory{ L"appPath", std::vector<AppZoneHistoryData>{ data } };
json::JsonObject expected = json::JsonObject::Parse(L"{\"app-path\": \"appPath\", \"history\":[{\"zone-index-set\": [54321], \"device-id\": \"device-id\", \"zoneset-uuid\": \"zoneset-uuid\"}]}");
json::JsonObject expected = json::JsonObject::Parse(L"{\"app-path\": \"appPath\", \"history\":[{\"zone-index-set\": [54321], \"device-id\": \"device-id_0_0_{00000000-0000-0000-0000-000000000000}\", \"zoneset-uuid\": \"zoneset-uuid\"}]}");
auto actual = AppZoneHistoryJSON::ToJson(appZoneHistory);
compareJsonObjects(expected, actual);
@@ -788,7 +788,8 @@ namespace FancyZonesUnitTests
TEST_METHOD (FromJson)
{
AppZoneHistoryData data{
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}", .zoneIndexSet = { 54321 }
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}").value(), .zoneIndexSet = {
54321 }
};
AppZoneHistoryJSON expected{ L"appPath", std::vector<AppZoneHistoryData>{ data } };
json::JsonObject json = json::JsonObject::Parse(L"{\"app-path\": \"appPath\", \"history\": [{\"device-id\": \"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}\", \"zoneset-uuid\": \"{33A2B101-06E0-437B-A61E-CDBECF502906}\", \"zone-index\": 54321}]}");
@@ -799,7 +800,7 @@ namespace FancyZonesUnitTests
Assert::AreEqual(expected.appPath.c_str(), actual->appPath.c_str());
Assert::AreEqual(expected.data.size(), actual->data.size());
Assert::IsTrue(expected.data[0].zoneIndexSet == actual->data[0].zoneIndexSet);
Assert::AreEqual(expected.data[0].deviceId.c_str(), actual->data[0].deviceId.c_str());
Assert::IsTrue(expected.data[0].deviceId == actual->data[0].deviceId);
Assert::AreEqual(expected.data[0].zoneSetUuid.c_str(), actual->data[0].zoneSetUuid.c_str());
}
@@ -855,7 +856,7 @@ namespace FancyZonesUnitTests
AppZoneHistoryJSON appZoneHistory{
L"appPath", std::vector<AppZoneHistoryData>{ data1, data2 }
};
json::JsonObject expected = json::JsonObject::Parse(L"{\"app-path\": \"appPath\", \"history\": [{\"zone-index-set\": [54321], \"device-id\": \"device-id1\", \"zoneset-uuid\": \"zoneset-uuid1\"}, {\"zone-index-set\": [12345], \"device-id\": \"device-id2\", \"zoneset-uuid\": \"zoneset-uuid2\"}]}");
json::JsonObject expected = json::JsonObject::Parse(L"{\"app-path\": \"appPath\", \"history\": [{\"zone-index-set\": [54321], \"device-id\": \"device-id1_0_0_{00000000-0000-0000-0000-000000000000}\", \"zoneset-uuid\": \"zoneset-uuid1\"}, {\"zone-index-set\": [12345], \"device-id\": \"device-id2_0_0_{00000000-0000-0000-0000-000000000000}\", \"zoneset-uuid\": \"zoneset-uuid2\"}]}");
auto actual = AppZoneHistoryJSON::ToJson(appZoneHistory);
std::wstring s = actual.Stringify().c_str();
@@ -865,10 +866,11 @@ namespace FancyZonesUnitTests
TEST_METHOD (FromJsonMultipleDesktopAppHistory)
{
AppZoneHistoryData data1{
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}", .zoneIndexSet = { 54321 }
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}").value(), .zoneIndexSet = { 54321 }
};
AppZoneHistoryData data2{
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{8a0b9205-6128-45a2-934a-b97f5b271235}", .zoneIndexSet = { 12345 }
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{8a0b9205-6128-45a2-934a-b97f5b271235}").value(), .zoneIndexSet = {
12345 }
};
AppZoneHistoryJSON expected{
L"appPath", std::vector<AppZoneHistoryData>{ data1, data2 }
@@ -884,7 +886,7 @@ namespace FancyZonesUnitTests
for (size_t i = 0; i < expected.data.size(); ++i)
{
Assert::IsTrue(expected.data[i].zoneIndexSet == actual->data[i].zoneIndexSet);
Assert::AreEqual(expected.data[i].deviceId.c_str(), actual->data[i].deviceId.c_str());
Assert::IsTrue(expected.data[i].deviceId == actual->data[i].deviceId);
Assert::AreEqual(expected.data[i].zoneSetUuid.c_str(), actual->data[i].zoneSetUuid.c_str());
}
}
@@ -893,10 +895,16 @@ namespace FancyZonesUnitTests
TEST_CLASS (DeviceInfoUnitTests)
{
private:
const std::wstring m_defaultDeviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
FancyZonesDataTypes::DeviceIdData m_defaultDeviceId{ L"AOC2460#4&fe3a015&0&UID65793", 1920, 1200, };
DeviceInfoJSON m_defaultDeviceInfo = DeviceInfoJSON{ m_defaultDeviceId, DeviceInfoData{ ZoneSetData{ L"{33A2B101-06E0-437B-A61E-CDBECF502906}", ZoneSetLayoutType::Custom }, true, 16, 3 } };
json::JsonObject m_defaultJson = json::JsonObject::Parse(L"{\"device-id\": \"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}\", \"active-zoneset\": {\"type\": \"custom\", \"uuid\": \"{33A2B101-06E0-437B-A61E-CDBECF502906}\"}, \"editor-show-spacing\": true, \"editor-spacing\": 16, \"editor-zone-count\": 3}");
TEST_METHOD_INITIALIZE(Init)
{
CLSIDFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}", &m_defaultDeviceId.virtualDesktopId);
m_defaultDeviceInfo.deviceId = m_defaultDeviceId;
}
public:
TEST_METHOD (ToJson)
{
@@ -916,7 +924,7 @@ namespace FancyZonesUnitTests
auto actual = DeviceInfoJSON::FromJson(json);
Assert::IsTrue(actual.has_value());
Assert::AreEqual(expected.deviceId.c_str(), actual->deviceId.c_str(), L"device id");
Assert::IsTrue(expected.deviceId == actual->deviceId);
Assert::AreEqual(expected.data.zoneCount, actual->data.zoneCount, L"zone count");
Assert::AreEqual((int)expected.data.activeZoneSet.type, (int)actual->data.activeZoneSet.type, L"zone set type");
Assert::AreEqual(expected.data.activeZoneSet.uuid.c_str(), actual->data.activeZoneSet.uuid.c_str(), L"zone set uuid");
@@ -1016,11 +1024,13 @@ namespace FancyZonesUnitTests
{
private:
const std::wstring_view m_moduleName = L"FancyZonesUnitTests";
const std::wstring m_defaultDeviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
const std::wstring m_defaultDeviceIdStr = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
const std::wstring m_defaultCustomDeviceStr = L"{\"device-id\": \"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}\", \"active-zoneset\": {\"type\": \"custom\", \"uuid\": \"{33A2B101-06E0-437B-A61E-CDBECF502906}\"}, \"editor-show-spacing\": true, \"editor-spacing\": 16, \"editor-zone-count\": 3}";
const json::JsonValue m_defaultCustomDeviceValue = json::JsonValue::Parse(m_defaultCustomDeviceStr);
const json::JsonObject m_defaultCustomDeviceObj = json::JsonObject::Parse(m_defaultCustomDeviceStr);
const FancyZonesDataTypes::DeviceIdData m_defaultDeviceId = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(m_defaultDeviceIdStr).value();
GUID m_defaultVDId;
HINSTANCE m_hInst{};
@@ -1177,7 +1187,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (AppZoneHistoryParseSingle)
{
const std::wstring expectedAppPath = L"appPath";
const std::wstring expectedDeviceId = m_defaultDeviceId;
const auto expectedDeviceId = m_defaultDeviceId;
const std::wstring expectedZoneSetId = L"{33A2B101-06E0-437B-A61E-CDBECF502906}";
const size_t expectedIndex = 54321;
@@ -1200,7 +1210,7 @@ namespace FancyZonesUnitTests
const auto entryData = entry->second;
Assert::AreEqual(expected.data.size(), entryData.size());
Assert::AreEqual(expectedZoneSetId.c_str(), entryData[0].zoneSetUuid.c_str());
Assert::AreEqual(expectedDeviceId.c_str(), entryData[0].deviceId.c_str());
Assert::IsTrue(expectedDeviceId == entryData[0].deviceId);
Assert::IsTrue(std::vector<ZoneIndex>{ expectedIndex } == entryData[0].zoneIndexSet);
}
@@ -1209,16 +1219,20 @@ namespace FancyZonesUnitTests
json::JsonObject json;
json::JsonArray zoneHistoryArray;
AppZoneHistoryData data1{
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502900}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1530}", .zoneIndexSet = { 1 }
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502900}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1530}").value(), .zoneIndexSet = {
1 }
};
AppZoneHistoryData data2{
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502901}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1531}", .zoneIndexSet = { 2 }
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502901}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1531}").value(), .zoneIndexSet = {
2 }
};
AppZoneHistoryData data3{
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502902}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1532}", .zoneIndexSet = { 3 }
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502902}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1532}").value(), .zoneIndexSet = {
3 }
};
AppZoneHistoryData data4{
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502903}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1533}", .zoneIndexSet = { 4 }
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502903}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1533}").value(), .zoneIndexSet = {
4 }
};
zoneHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ L"app-path-1", std::vector<AppZoneHistoryData>{ data1 } }));
zoneHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ L"app-path-2", std::vector<AppZoneHistoryData>{ data2 } }));
@@ -1238,7 +1252,7 @@ namespace FancyZonesUnitTests
const auto& actual = appZoneHistoryMap.at(expected->appPath);
Assert::AreEqual(expected->data.size(), actual.size());
Assert::AreEqual(expected->data[0].deviceId.c_str(), actual[0].deviceId.c_str());
Assert::IsTrue(expected->data[0].deviceId == actual[0].deviceId);
Assert::AreEqual(expected->data[0].zoneSetUuid.c_str(), actual[0].zoneSetUuid.c_str());
Assert::IsTrue(expected->data[0].zoneIndexSet == actual[0].zoneIndexSet);
@@ -1253,19 +1267,23 @@ namespace FancyZonesUnitTests
const auto appPath = L"app-path";
AppZoneHistoryData data1{
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502900}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1530}", .zoneIndexSet = { 1 }
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502900}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1530}").value(), .zoneIndexSet = {
1 }
};
zoneHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ appPath, std::vector<AppZoneHistoryData>{ data1 } }));
AppZoneHistoryData data2{
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502901}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1531}", .zoneIndexSet = { 2 }
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502901}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1531}").value(), .zoneIndexSet = {
2 }
};
zoneHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ appPath, std::vector<AppZoneHistoryData>{ data2 } }));
AppZoneHistoryData data3{
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502902}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1532}", .zoneIndexSet = { 3 }
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502902}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1532}").value(), .zoneIndexSet = {
3 }
};
zoneHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ appPath, std::vector<AppZoneHistoryData>{ data3 } }));
AppZoneHistoryData expected{
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502903}", .deviceId = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1533}", .zoneIndexSet = { 4 }
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502903}", .deviceId = DeviceIdData::ParseDeviceId(L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1533}").value(), .zoneIndexSet = {
4 }
};
zoneHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ appPath, std::vector<AppZoneHistoryData>{ expected } }));
json.SetNamedValue(L"app-zone-history", json::JsonValue::Parse(zoneHistoryArray.Stringify()));
@@ -1276,7 +1294,7 @@ namespace FancyZonesUnitTests
const auto& actual = appZoneHistoryMap.at(appPath);
Assert::AreEqual((size_t)1, actual.size());
Assert::AreEqual(expected.deviceId.c_str(), actual[0].deviceId.c_str());
Assert::IsTrue(expected.deviceId == actual[0].deviceId);
Assert::AreEqual(expected.zoneSetUuid.c_str(), actual[0].zoneSetUuid.c_str());
Assert::IsTrue(expected.zoneIndexSet == actual[0].zoneIndexSet);
}
@@ -1610,7 +1628,7 @@ namespace FancyZonesUnitTests
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const std::wstring uniqueId = m_defaultDeviceId;
const auto uniqueId = m_defaultDeviceId;
json::JsonArray devices;
devices.Append(m_defaultCustomDeviceValue);
@@ -1635,7 +1653,7 @@ namespace FancyZonesUnitTests
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const std::wstring expected = L"{39B25DD2-130D-4B5D-8851-4791D66B1539}";
const std::wstring uniqueId = m_defaultDeviceId;
const auto uniqueId = m_defaultDeviceId;
json::JsonArray devices;
devices.Append(m_defaultCustomDeviceValue);
@@ -1661,7 +1679,8 @@ namespace FancyZonesUnitTests
data.SetSettingsModulePath(m_moduleName);
const std::wstring expected = L"{33A2B101-06E0-437B-A61E-CDBECF502906}";
const std::wstring uniqueId = L"id-not-contained-by-device-info-map_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
auto uniqueId = m_defaultDeviceId;
uniqueId.deviceName = L"id-not-contained-by-device-info-map";
json::JsonArray devices;
devices.Append(m_defaultCustomDeviceValue);
@@ -1706,7 +1725,8 @@ namespace FancyZonesUnitTests
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = L"device-id", .zoneIndexSet = { 54321 }
};
AppZoneHistoryJSON appZoneHistory{ L"app-path", std::vector<AppZoneHistoryData>{ data } };
DeviceInfoJSON deviceInfo{ L"{33A2B101-06E0-437B-A61E-CDBECF502906}", DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 } };
DeviceInfoJSON deviceInfo { FancyZonesDataTypes::DeviceIdData{ L"device-id", 0, 0, m_defaultVDId }, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 } };
LayoutQuickKeyJSON quickKeys{ L"{33A2B101-06E0-437B-A61E-CDBECF502906}", 1 };
json::JsonArray zoneSetsArray, appZonesArray, deviceInfoArray, quickKeysArray;
zoneSetsArray.Append(CustomZoneSetJSON::ToJson(zoneSets));
@@ -1836,7 +1856,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (AppLastZoneIndex)
{
const std::wstring deviceId = L"device-id";
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
const std::wstring zoneSetId = L"zoneset-uuid";
const auto window = Mocks::WindowCreate(m_hInst);
FancyZonesData data;
@@ -1852,7 +1872,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (AppLastZoneIndexZero)
{
const std::wstring zoneSetId = L"zoneset-uuid";
const std::wstring deviceId = L"device-id";
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
const auto window = Mocks::WindowCreate(m_hInst);
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
@@ -1865,7 +1885,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (AppLastZoneIndexNegative)
{
const std::wstring zoneSetId = L"zoneset-uuid";
const std::wstring deviceId = L"device-id";
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
const auto window = Mocks::WindowCreate(m_hInst);
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
@@ -1878,7 +1898,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (AppLastZoneIndexOverflow)
{
const std::wstring zoneSetId = L"zoneset-uuid";
const std::wstring deviceId = L"device-id";
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
const auto window = Mocks::WindowCreate(m_hInst);
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
@@ -1891,7 +1911,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (AppLastZoneIndexOverride)
{
const std::wstring zoneSetId = L"zoneset-uuid";
const std::wstring deviceId = L"device-id";
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
const auto window = Mocks::WindowCreate(m_hInst);
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
@@ -1906,7 +1926,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (AppLastZoneInvalidWindow)
{
const std::wstring zoneSetId = L"zoneset-uuid";
const std::wstring deviceId = L"device-id";
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
const auto window = Mocks::Window();
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
@@ -1920,19 +1940,20 @@ namespace FancyZonesUnitTests
TEST_METHOD (AppLastZoneNullWindow)
{
const std::wstring zoneSetId = L"zoneset-uuid";
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
const auto window = nullptr;
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const int expectedZoneIndex = 1;
Assert::IsFalse(data.SetAppLastZones(window, L"device-id", zoneSetId, { expectedZoneIndex }));
Assert::IsFalse(data.SetAppLastZones(window, deviceId, zoneSetId, { expectedZoneIndex }));
}
TEST_METHOD (AppLastdeviceIdTest)
{
const std::wstring zoneSetId = L"zoneset-uuid";
const std::wstring deviceId1 = L"device-id-1";
const std::wstring deviceId2 = L"device-id-2";
const FancyZonesDataTypes::DeviceIdData deviceId1{ L"device-id-1" };
const FancyZonesDataTypes::DeviceIdData deviceId2{ L"device-id-2" };
const auto window = Mocks::WindowCreate(m_hInst);
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
@@ -1947,7 +1968,7 @@ namespace FancyZonesUnitTests
{
const std::wstring zoneSetId1 = L"zoneset-uuid-1";
const std::wstring zoneSetId2 = L"zoneset-uuid-2";
const std::wstring deviceId = L"device-id";
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
const auto window = Mocks::WindowCreate(m_hInst);
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
@@ -1961,7 +1982,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (AppLastZoneRemoveWindow)
{
const std::wstring zoneSetId = L"zoneset-uuid";
const std::wstring deviceId = L"device-id";
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
const auto window = Mocks::WindowCreate(m_hInst);
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
@@ -1974,7 +1995,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (AppLastZoneRemoveUnknownWindow)
{
const std::wstring zoneSetId = L"zoneset-uuid";
const std::wstring deviceId = L"device-id";
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
const auto window = Mocks::WindowCreate(m_hInst);
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
@@ -1987,7 +2008,7 @@ namespace FancyZonesUnitTests
{
const std::wstring zoneSetIdToInsert = L"zoneset-uuid-to-insert";
const std::wstring zoneSetIdToRemove = L"zoneset-uuid-to-remove";
const std::wstring deviceId = L"device-id";
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
const auto window = Mocks::WindowCreate(m_hInst);
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
@@ -2000,8 +2021,8 @@ namespace FancyZonesUnitTests
TEST_METHOD (AppLastZoneRemoveUnknownWindowId)
{
const std::wstring zoneSetId = L"zoneset-uuid";
const std::wstring deviceIdToInsert = L"device-id-insert";
const std::wstring deviceIdToRemove = L"device-id-remove";
const FancyZonesDataTypes::DeviceIdData deviceIdToInsert{ L"device-id-insert" };
const FancyZonesDataTypes::DeviceIdData deviceIdToRemove{ L"device-id-remove" };
const auto window = Mocks::WindowCreate(m_hInst);
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
@@ -2014,7 +2035,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (AppLastZoneRemoveNullWindow)
{
const std::wstring zoneSetId = L"zoneset-uuid";
const std::wstring deviceId = L"device-id";
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
const auto window = Mocks::WindowCreate(m_hInst);
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);

View File

@@ -71,7 +71,7 @@ namespace FancyZonesUnitTests
CLSIDFromString(expectedGuidStr, &guid);
const FancyZonesDataTypes::DeviceIdData expected{ L"AOC0001#5&37ac4db&0&UID160002", 1536, 960, guid };
const auto actual = ParseDeviceId(input);
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
Assert::IsTrue(actual.has_value());
Assert::AreEqual(expected.deviceName, actual->deviceName);
@@ -93,7 +93,7 @@ namespace FancyZonesUnitTests
CLSIDFromString(expectedGuidStr, &guid);
const FancyZonesDataTypes::DeviceIdData expected{ L"AOC0001#5&37ac4db&0&UID160002", 1536, 960, guid, L"monitorId" };
const auto actual = ParseDeviceId(input);
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
Assert::IsTrue(actual.has_value());
Assert::AreEqual(expected.deviceName, actual->deviceName);
@@ -116,7 +116,7 @@ namespace FancyZonesUnitTests
CLSIDFromString(expectedGuidStr, &guid);
const FancyZonesDataTypes::DeviceIdData expected{ L"AOC00015&37ac4db&0&UID160002", 1536, 960, guid };
const auto actual = ParseDeviceId(input);
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
Assert::IsTrue(actual.has_value());
Assert::AreEqual(expected.deviceName, actual->deviceName);
@@ -133,7 +133,7 @@ namespace FancyZonesUnitTests
{
// no width or height
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_1536960_{E0A2904E-889C-4532-95B1-28FE15C16F66}";
const auto actual = ParseDeviceId(input);
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
Assert::IsFalse(actual.has_value());
}
@@ -141,7 +141,7 @@ namespace FancyZonesUnitTests
{
// no width and height
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_{E0A2904E-889C-4532-95B1-28FE15C16F66}_monitorId";
const auto actual = ParseDeviceId(input);
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
Assert::IsFalse(actual.has_value());
}
@@ -149,7 +149,7 @@ namespace FancyZonesUnitTests
{
// no guid
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_1536960_";
const auto actual = ParseDeviceId(input);
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
Assert::IsFalse(actual.has_value());
}
@@ -157,7 +157,7 @@ namespace FancyZonesUnitTests
{
// invalid guid
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_1536960_{asdf}";
const auto actual = ParseDeviceId(input);
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
Assert::IsFalse(actual.has_value());
}
@@ -165,7 +165,7 @@ namespace FancyZonesUnitTests
{
// invalid width/height
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_15a6_960_{E0A2904E-889C-4532-95B1-28FE15C16F66}";
const auto actual = ParseDeviceId(input);
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
Assert::IsFalse(actual.has_value());
}
@@ -173,7 +173,7 @@ namespace FancyZonesUnitTests
{
// changed order
const std::wstring input = L"AOC00015&37ac4db&0&UID160002_15a6_960_monitorId_{E0A2904E-889C-4532-95B1-28FE15C16F66}";
const auto actual = ParseDeviceId(input);
const auto actual = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(input);
Assert::IsFalse(actual.has_value());
}

View File

@@ -23,8 +23,8 @@ namespace FancyZonesUnitTests
TEST_CLASS (WorkAreaCreationUnitTests)
{
std::wstringstream m_parentUniqueId;
std::wstringstream m_uniqueId;
FancyZonesDataTypes::DeviceIdData m_parentUniqueId;
FancyZonesDataTypes::DeviceIdData m_uniqueId;
HINSTANCE m_hInst{};
HMONITOR m_monitor{};
@@ -40,7 +40,7 @@ namespace FancyZonesUnitTests
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
Assert::IsNotNull(workArea.get());
Assert::AreEqual(m_uniqueId.str().c_str(), workArea->UniqueId().c_str());
Assert::IsTrue(m_uniqueId == workArea->UniqueId());
}
TEST_METHOD_INITIALIZE(Init)
@@ -51,9 +51,16 @@ namespace FancyZonesUnitTests
m_monitorInfo.cbSize = sizeof(m_monitorInfo);
Assert::AreNotEqual(0, GetMonitorInfoW(m_monitor, &m_monitorInfo));
m_parentUniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{61FA9FC0-26A6-4B37-A834-491C148DFC57}";
m_uniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
m_parentUniqueId.deviceName = L"DELA026#5&10a58c63&0&UID16777488";
m_parentUniqueId.width = m_monitorInfo.rcMonitor.right - m_monitorInfo.rcMonitor.left;
m_parentUniqueId.height = m_monitorInfo.rcMonitor.bottom - m_monitorInfo.rcMonitor.top;
CLSIDFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC57}", &m_parentUniqueId.virtualDesktopId);
m_uniqueId.deviceName = L"DELA026#5&10a58c63&0&UID16777488";
m_uniqueId.width = m_monitorInfo.rcMonitor.right - m_monitorInfo.rcMonitor.left;
m_uniqueId.height = m_monitorInfo.rcMonitor.bottom - m_monitorInfo.rcMonitor.top;
CLSIDFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}", &m_uniqueId.virtualDesktopId);
m_fancyZonesData.SetSettingsModulePath(L"FancyZonesUnitTests");
m_fancyZonesData.clear_data();
@@ -71,7 +78,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (CreateWorkArea)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
testWorkArea(workArea);
auto* activeZoneSet{ workArea->ActiveZoneSet() };
@@ -82,7 +89,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (CreateWorkAreaNoHinst)
{
auto workArea = MakeWorkArea({}, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea({}, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
testWorkArea(workArea);
auto* activeZoneSet{ workArea->ActiveZoneSet() };
@@ -93,7 +100,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (CreateWorkAreaNoHinstFlashZones)
{
auto workArea = MakeWorkArea({}, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea({}, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
testWorkArea(workArea);
auto* activeZoneSet{ workArea->ActiveZoneSet() };
@@ -104,21 +111,32 @@ namespace FancyZonesUnitTests
TEST_METHOD (CreateWorkAreaNoMonitor)
{
auto workArea = MakeWorkArea(m_hInst, {}, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, {}, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
testWorkArea(workArea);
}
TEST_METHOD (CreateWorkAreaNoDeviceId)
{
// Generate unique id without device id
std::wstring uniqueId = FancyZonesUtils::GenerateUniqueId(m_monitor, {}, m_virtualDesktopId);
auto workArea = MakeWorkArea(m_hInst, m_monitor, uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
FancyZonesDataTypes::DeviceIdData uniqueIdData;
uniqueIdData.virtualDesktopId = m_virtualDesktopGuid;
MONITORINFOEXW mi;
mi.cbSize = sizeof(mi);
if (GetMonitorInfo(m_monitor, &mi))
{
FancyZonesUtils::Rect const monitorRect(mi.rcMonitor);
uniqueIdData.width = monitorRect.width();
uniqueIdData.height = monitorRect.height();
}
auto workArea = MakeWorkArea(m_hInst, m_monitor, uniqueIdData, {}, m_zoneColors, m_overlappingAlgorithm);
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
const std::wstring expectedUniqueId = L"FallbackDevice_" + std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom) + L"_" + m_virtualDesktopId;
const FancyZonesDataTypes::DeviceIdData expectedUniqueId{ L"FallbackDevice", m_monitorInfo.rcMonitor.right - m_monitorInfo.rcMonitor.left, m_monitorInfo.rcMonitor.bottom - m_monitorInfo.rcMonitor.top, m_virtualDesktopGuid };
Assert::IsNotNull(workArea.get());
Assert::AreEqual(expectedUniqueId.c_str(), workArea->UniqueId().c_str());
Assert::IsTrue(expectedUniqueId == workArea->UniqueId());
auto* activeZoneSet{ workArea->ActiveZoneSet() };
Assert::IsNotNull(activeZoneSet);
@@ -129,12 +147,22 @@ namespace FancyZonesUnitTests
TEST_METHOD (CreateWorkAreaNoDesktopId)
{
// Generate unique id without virtual desktop id
std::wstring uniqueId = FancyZonesUtils::GenerateUniqueId(m_monitor, m_deviceId, {});
FancyZonesDataTypes::DeviceIdData uniqueId;
uniqueId.deviceName = FancyZonesUtils::TrimDeviceId(m_deviceId);
MONITORINFOEXW mi;
mi.cbSize = sizeof(mi);
if (GetMonitorInfo(m_monitor, &mi))
{
FancyZonesUtils::Rect const monitorRect(mi.rcMonitor);
uniqueId.width = monitorRect.width();
uniqueId.height = monitorRect.height();
}
auto workArea = MakeWorkArea(m_hInst, m_monitor, uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
Assert::IsNotNull(workArea.get());
Assert::IsTrue(workArea->UniqueId().empty());
auto* activeZoneSet{ workArea->ActiveZoneSet() };
Assert::IsNotNull(activeZoneSet);
@@ -152,17 +180,17 @@ namespace FancyZonesUnitTests
const auto customSetGuid = Helpers::CreateGuidString();
const auto parentZoneSet = ZoneSetData{ customSetGuid, type };
const auto parentDeviceInfo = DeviceInfoData{ parentZoneSet, true, spacing, zoneCount };
m_fancyZonesData.SetDeviceInfo(m_parentUniqueId.str(), parentDeviceInfo);
m_fancyZonesData.SetDeviceInfo(m_parentUniqueId, parentDeviceInfo);
auto parentWorkArea = MakeWorkArea(m_hInst, m_monitor, m_parentUniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto parentWorkArea = MakeWorkArea(m_hInst, m_monitor, m_parentUniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
// newWorkArea = false - workArea won't be cloned from parent
auto actualWorkArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto actualWorkArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
Assert::IsNotNull(actualWorkArea->ActiveZoneSet());
Assert::IsTrue(m_fancyZonesData.GetDeviceInfoMap().contains(m_uniqueId.str()));
auto currentDeviceInfo = m_fancyZonesData.GetDeviceInfoMap().at(m_uniqueId.str());
Assert::IsTrue(m_fancyZonesData.GetDeviceInfoMap().contains(m_uniqueId));
auto currentDeviceInfo = m_fancyZonesData.GetDeviceInfoMap().at(m_uniqueId);
// default values
Assert::AreEqual(true, currentDeviceInfo.showSpacing);
Assert::AreEqual(3, currentDeviceInfo.zoneCount);
@@ -173,7 +201,7 @@ namespace FancyZonesUnitTests
TEST_CLASS (WorkAreaUnitTests)
{
std::wstringstream m_uniqueId;
FancyZonesDataTypes::DeviceIdData m_uniqueId;
HINSTANCE m_hInst{};
HMONITOR m_monitor{};
@@ -191,8 +219,11 @@ namespace FancyZonesUnitTests
m_monitorInfo.cbSize = sizeof(m_monitorInfo);
Assert::AreNotEqual(0, GetMonitorInfoW(m_monitor, &m_monitorInfo));
m_uniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
m_uniqueId.deviceName = L"DELA026#5&10a58c63&0&UID16777488";
m_uniqueId.width = m_monitorInfo.rcMonitor.right - m_monitorInfo.rcMonitor.left;
m_uniqueId.height = m_monitorInfo.rcMonitor.bottom - m_monitorInfo.rcMonitor.top;
CLSIDFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}", &m_uniqueId.virtualDesktopId);
m_fancyZonesData.SetSettingsModulePath(L"FancyZonesUnitTests");
m_fancyZonesData.clear_data();
@@ -207,7 +238,7 @@ namespace FancyZonesUnitTests
public:
TEST_METHOD (MoveSizeEnter)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
const auto expected = S_OK;
const auto actual = workArea->MoveSizeEnter(Mocks::Window());
@@ -217,7 +248,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveSizeEnterTwice)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
const auto expected = S_OK;
@@ -229,7 +260,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveSizeUpdate)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
const auto expected = S_OK;
const auto actual = workArea->MoveSizeUpdate(POINT{ 0, 0 }, true, false);
@@ -239,7 +270,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveSizeUpdatePointNegativeCoordinates)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
const auto expected = S_OK;
const auto actual = workArea->MoveSizeUpdate(POINT{ -10, -10 }, true, false);
@@ -249,7 +280,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveSizeUpdatePointBigCoordinates)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
const auto expected = S_OK;
const auto actual = workArea->MoveSizeUpdate(POINT{ m_monitorInfo.rcMonitor.right + 1, m_monitorInfo.rcMonitor.bottom + 1 }, true, false);
@@ -259,7 +290,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveSizeEnd)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
const auto window = Mocks::Window();
workArea->MoveSizeEnter(window);
@@ -276,7 +307,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveSizeEndWindowNotAdded)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
const auto window = Mocks::Window();
workArea->MoveSizeEnter(window);
@@ -292,7 +323,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveSizeEndDifferentWindows)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
const auto window = Mocks::Window();
workArea->MoveSizeEnter(window);
@@ -305,7 +336,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveSizeEndWindowNotSet)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
const auto expected = E_INVALIDARG;
const auto actual = workArea->MoveSizeEnd(Mocks::Window(), POINT{ 0, 0 });
@@ -315,7 +346,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveSizeEndInvalidPoint)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
const auto window = Mocks::Window();
workArea->MoveSizeEnter(window);
@@ -332,7 +363,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveWindowIntoZoneByIndex)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
Assert::IsNotNull(workArea->ActiveZoneSet());
workArea->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
@@ -342,7 +373,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveWindowIntoZoneByDirectionAndIndex)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
Assert::IsNotNull(workArea->ActiveZoneSet());
const auto window = Mocks::WindowCreate(m_hInst);
@@ -357,7 +388,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveWindowIntoZoneByDirectionManyTimes)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
Assert::IsNotNull(workArea->ActiveZoneSet());
const auto window = Mocks::WindowCreate(m_hInst);
@@ -374,7 +405,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (SaveWindowProcessToZoneIndexNullptrWindow)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
Assert::IsNotNull(workArea->ActiveZoneSet());
workArea->SaveWindowProcessToZoneIndex(nullptr);
@@ -385,7 +416,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (SaveWindowProcessToZoneIndexNoWindowAdded)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
Assert::IsNotNull(workArea->ActiveZoneSet());
auto window = Mocks::WindowCreate(m_hInst);
@@ -400,7 +431,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (SaveWindowProcessToZoneIndexNoWindowAddedWithFilledAppZoneHistory)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
Assert::IsNotNull(workArea->ActiveZoneSet());
const auto window = Mocks::WindowCreate(m_hInst);
@@ -428,7 +459,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (SaveWindowProcessToZoneIndexWindowAdded)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
Assert::IsNotNull(workArea->ActiveZoneSet());
auto window = Mocks::WindowCreate(m_hInst);
@@ -458,7 +489,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt)
{
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId.str(), {}, m_zoneColors, m_overlappingAlgorithm);
auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm);
Assert::IsNotNull(workArea->ActiveZoneSet());
auto window = Mocks::WindowCreate(m_hInst);

View File

@@ -1046,8 +1046,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (CustomZoneFromValidCanvasLayoutInfo)
{
//prepare device data
const std::wstring zoneUuid = L"default_device_id";
FancyZonesDataInstance().SetDeviceInfo(zoneUuid, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 });
FancyZonesDataInstance().SetDeviceInfo(FancyZonesDataTypes::DeviceIdData{ L"default_device_id" }, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 });
//prepare expected data
wil::unique_cotaskmem_string uuid;
@@ -1072,8 +1071,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (CustomZoneFromValidGridFullLayoutInfo)
{
//prepare device data
const std::wstring zoneUuid = L"default_device_id";
FancyZonesDataInstance().SetDeviceInfo(zoneUuid, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 });
FancyZonesDataInstance().SetDeviceInfo(FancyZonesDataTypes::DeviceIdData{ L"default_device_id" }, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 });
//prepare expected data
wil::unique_cotaskmem_string uuid;

View File

@@ -33,6 +33,9 @@ namespace FancyZonesEditor.Utils
// Non-localizable string: Multi-monitor id
private const string MultiMonitorId = "FancyZones#MultiMonitorDevice";
// Non-localizable string: default virtual desktop id
private const string DefaultVirtualDesktopGuid = "{00000000-0000-0000-0000-000000000000}";
private readonly IFileSystem _fileSystem = new FileSystem();
private readonly JsonSerializerOptions _options = new JsonSerializerOptions
@@ -725,7 +728,13 @@ namespace FancyZonesEditor.Utils
bool unused = true;
foreach (Monitor monitor in monitors)
{
if (monitor.Device.Id == device.DeviceId)
string deviceIdSaved = monitor.Device.Id.Substring(0, monitor.Device.Id.LastIndexOf("_"));
string deviceIdReadFromSettings = device.DeviceId.Substring(0, device.DeviceId.LastIndexOf("_"));
string virtualDesktopIdSaved = monitor.Device.Id.Substring(monitor.Device.Id.LastIndexOf("_") + 1);
string virtualDesktopIdReadFromSettings = device.DeviceId.Substring(device.DeviceId.LastIndexOf("_") + 1);
if (deviceIdSaved == deviceIdReadFromSettings && (virtualDesktopIdSaved == virtualDesktopIdReadFromSettings || virtualDesktopIdReadFromSettings == DefaultVirtualDesktopGuid))
{
var settings = new LayoutSettings
{