mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 02:36:19 +02:00
[FancyZones] Editor multi monitor support (#6562)
Co-authored-by: Enrico Giordani <enrico.giordani@gmail.com> Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com>
This commit is contained in:
@@ -592,133 +592,99 @@ void FancyZones::ToggleEditor() noexcept
|
||||
m_terminateEditorEvent.reset(CreateEvent(nullptr, true, false, nullptr));
|
||||
}
|
||||
|
||||
HMONITOR monitor{};
|
||||
HWND foregroundWindow{};
|
||||
|
||||
const bool use_cursorpos_editor_startupscreen = m_settings->GetSettings()->use_cursorpos_editor_startupscreen;
|
||||
POINT currentCursorPos{};
|
||||
if (use_cursorpos_editor_startupscreen)
|
||||
{
|
||||
GetCursorPos(¤tCursorPos);
|
||||
monitor = MonitorFromPoint(currentCursorPos, MONITOR_DEFAULTTOPRIMARY);
|
||||
}
|
||||
else
|
||||
{
|
||||
foregroundWindow = GetForegroundWindow();
|
||||
monitor = MonitorFromWindow(foregroundWindow, MONITOR_DEFAULTTOPRIMARY);
|
||||
}
|
||||
|
||||
if (!monitor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
winrt::com_ptr<IZoneWindow> zoneWindow;
|
||||
|
||||
std::shared_lock readLock(m_lock);
|
||||
|
||||
if (m_settings->GetSettings()->spanZonesAcrossMonitors)
|
||||
HMONITOR targetMonitor{};
|
||||
|
||||
const bool use_cursorpos_editor_startupscreen = m_settings->GetSettings()->use_cursorpos_editor_startupscreen;
|
||||
if (use_cursorpos_editor_startupscreen)
|
||||
{
|
||||
zoneWindow = m_workAreaHandler.GetWorkArea(m_currentDesktopId, NULL);
|
||||
POINT currentCursorPos{};
|
||||
GetCursorPos(¤tCursorPos);
|
||||
targetMonitor = MonitorFromPoint(currentCursorPos, MONITOR_DEFAULTTOPRIMARY);
|
||||
}
|
||||
else
|
||||
{
|
||||
zoneWindow = m_workAreaHandler.GetWorkArea(m_currentDesktopId, monitor);
|
||||
targetMonitor = MonitorFromWindow(GetForegroundWindow(), MONITOR_DEFAULTTOPRIMARY);
|
||||
}
|
||||
|
||||
if (!zoneWindow)
|
||||
if (!targetMonitor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::wstring editorLocation;
|
||||
/*
|
||||
* Divider: /
|
||||
* Parts:
|
||||
* (1) Process id
|
||||
* (2) Span zones across monitors
|
||||
* (3) Monitor id where the Editor should be opened
|
||||
* (4) Monitors count
|
||||
*
|
||||
* Data for each monitor:
|
||||
* (5) Monitor id
|
||||
* (6) DPI
|
||||
* (7) monitor left
|
||||
* (8) monitor top
|
||||
* ...
|
||||
*/
|
||||
std::wstring params;
|
||||
const std::wstring divider = L"/";
|
||||
params += std::to_wstring(GetCurrentProcessId()) + divider; /* Process id */
|
||||
|
||||
if (m_settings->GetSettings()->spanZonesAcrossMonitors)
|
||||
const bool spanZonesAcrossMonitors = m_settings->GetSettings()->spanZonesAcrossMonitors;
|
||||
params += std::to_wstring(spanZonesAcrossMonitors) + divider; /* Span zones */
|
||||
|
||||
std::vector<std::pair<HMONITOR, MONITORINFOEX>> allMonitors;
|
||||
allMonitors = FancyZonesUtils::GetAllMonitorInfo<&MONITORINFOEX::rcWork>();
|
||||
|
||||
bool showDpiWarning = false;
|
||||
int prevDpiX = -1, prevDpiY = -1;
|
||||
std::wstring monitorsData;
|
||||
for (auto& monitor : allMonitors)
|
||||
{
|
||||
std::vector<std::pair<HMONITOR, RECT>> allMonitors;
|
||||
|
||||
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] {
|
||||
allMonitors = FancyZonesUtils::GetAllMonitorRects<&MONITORINFOEX::rcWork>();
|
||||
} })
|
||||
.wait();
|
||||
|
||||
UINT currentDpi = 0;
|
||||
for (const auto& monitor : allMonitors)
|
||||
auto monitorId = FancyZonesUtils::GenerateMonitorId(monitor.second, monitor.first, m_currentDesktopId);
|
||||
if (monitor.first == targetMonitor)
|
||||
{
|
||||
params += *monitorId + divider; /* Monitor id where the Editor should be opened */
|
||||
}
|
||||
|
||||
if (monitorId.has_value())
|
||||
{
|
||||
monitorsData += std::move(*monitorId) + divider; /* Monitor id */
|
||||
|
||||
UINT dpiX = 0;
|
||||
UINT dpiY = 0;
|
||||
if (GetDpiForMonitor(monitor.first, MDT_EFFECTIVE_DPI, &dpiX, &dpiY) == S_OK)
|
||||
{
|
||||
if (currentDpi == 0)
|
||||
monitorsData += std::to_wstring(dpiX) + divider; /* DPI */
|
||||
if (spanZonesAcrossMonitors && prevDpiX != -1 && (prevDpiX != dpiX || prevDpiY != dpiY))
|
||||
{
|
||||
currentDpi = dpiX;
|
||||
continue;
|
||||
showDpiWarning = true;
|
||||
}
|
||||
if (currentDpi != dpiX)
|
||||
{
|
||||
MessageBoxW(NULL,
|
||||
GET_RESOURCE_STRING(IDS_SPAN_ACROSS_ZONES_WARNING).c_str(),
|
||||
GET_RESOURCE_STRING(IDS_POWERTOYS_FANCYZONES).c_str(),
|
||||
MB_OK | MB_ICONWARNING);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& [monitor, workArea] : allMonitors)
|
||||
{
|
||||
const auto x = workArea.left;
|
||||
const auto y = workArea.top;
|
||||
const auto width = workArea.right - workArea.left;
|
||||
const auto height = workArea.bottom - workArea.top;
|
||||
std::wstring editorLocationPart =
|
||||
std::to_wstring(x) + L"_" +
|
||||
std::to_wstring(y) + L"_" +
|
||||
std::to_wstring(width) + L"_" +
|
||||
std::to_wstring(height);
|
||||
prevDpiX = dpiX;
|
||||
prevDpiY = dpiY;
|
||||
}
|
||||
|
||||
if (editorLocation.empty())
|
||||
{
|
||||
editorLocation = std::move(editorLocationPart);
|
||||
}
|
||||
else
|
||||
{
|
||||
editorLocation += L'/';
|
||||
editorLocation += editorLocationPart;
|
||||
}
|
||||
monitorsData += std::to_wstring(monitor.second.rcMonitor.left) + divider;
|
||||
monitorsData += std::to_wstring(monitor.second.rcMonitor.top) + divider;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
params += std::to_wstring(allMonitors.size()) + divider; /* Monitors count */
|
||||
params += monitorsData;
|
||||
|
||||
if (showDpiWarning)
|
||||
{
|
||||
MONITORINFOEX mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
|
||||
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] {
|
||||
GetMonitorInfo(monitor, &mi);
|
||||
} })
|
||||
.wait();
|
||||
|
||||
const auto x = mi.rcWork.left;
|
||||
const auto y = mi.rcWork.top;
|
||||
const auto width = mi.rcWork.right - mi.rcWork.left;
|
||||
const auto height = mi.rcWork.bottom - mi.rcWork.top;
|
||||
editorLocation =
|
||||
std::to_wstring(x) + L"_" +
|
||||
std::to_wstring(y) + L"_" +
|
||||
std::to_wstring(width) + L"_" +
|
||||
std::to_wstring(height);
|
||||
MessageBoxW(NULL,
|
||||
GET_RESOURCE_STRING(IDS_SPAN_ACROSS_ZONES_WARNING).c_str(),
|
||||
GET_RESOURCE_STRING(IDS_POWERTOYS_FANCYZONES).c_str(),
|
||||
MB_OK | MB_ICONWARNING);
|
||||
}
|
||||
|
||||
const auto& fancyZonesData = FancyZonesDataInstance();
|
||||
|
||||
if (!fancyZonesData.SerializeDeviceInfoToTmpFile(zoneWindow->UniqueId()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const std::wstring params =
|
||||
/*1*/ editorLocation + L" " +
|
||||
/*2*/ L"\"" + std::to_wstring(GetCurrentProcessId()) + L"\"";
|
||||
fancyZonesData.SerializeDeviceInfoToTmpFile(m_currentDesktopId);
|
||||
|
||||
SHELLEXECUTEINFO sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
|
||||
@@ -930,11 +896,11 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, const std::wstring& deviceId) n
|
||||
|
||||
if (monitor)
|
||||
{
|
||||
uniqueId = ZoneWindowUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId.get());
|
||||
uniqueId = FancyZonesUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
uniqueId = ZoneWindowUtils::GenerateUniqueIdAllMonitorsArea(virtualDesktopId.get());
|
||||
uniqueId = FancyZonesUtils::GenerateUniqueIdAllMonitorsArea(virtualDesktopId.get());
|
||||
}
|
||||
|
||||
std::wstring parentId{};
|
||||
|
||||
@@ -478,47 +478,41 @@ void FancyZonesData::SetActiveZoneSet(const std::wstring& deviceId, const FancyZ
|
||||
}
|
||||
}
|
||||
|
||||
bool FancyZonesData::SerializeDeviceInfoToTmpFile(const std::wstring& uniqueId) const
|
||||
void FancyZonesData::SerializeDeviceInfoToTmpFile(const GUID& currentVirtualDesktop) const
|
||||
{
|
||||
const auto deviceInfo = FindDeviceInfo(uniqueId);
|
||||
if (!deviceInfo.has_value())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
JSONHelpers::DeviceInfoJSON deviceInfoJson{ uniqueId, *deviceInfo };
|
||||
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoJson, activeZoneSetTmpFileName);
|
||||
|
||||
return true;
|
||||
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoMap, currentVirtualDesktop, activeZoneSetTmpFileName);
|
||||
}
|
||||
|
||||
void FancyZonesData::ParseDataFromTmpFiles()
|
||||
{
|
||||
ParseDeviceInfoFromTmpFile(activeZoneSetTmpFileName);
|
||||
ParseDeletedCustomZoneSetsFromTmpFile(deletedCustomZoneSetsTmpFileName);
|
||||
ParseCustomZoneSetFromTmpFile(appliedZoneSetTmpFileName);
|
||||
ParseCustomZoneSetsFromTmpFile(appliedZoneSetTmpFileName);
|
||||
SaveFancyZonesData();
|
||||
}
|
||||
|
||||
void FancyZonesData::ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath)
|
||||
{
|
||||
std::scoped_lock lock{ dataLock };
|
||||
const auto& deviceInfo = JSONHelpers::ParseDeviceInfoFromTmpFile(tmpFilePath);
|
||||
const auto& appliedZonesets = JSONHelpers::ParseDeviceInfoFromTmpFile(tmpFilePath);
|
||||
|
||||
if (deviceInfo)
|
||||
if (appliedZonesets)
|
||||
{
|
||||
deviceInfoMap[deviceInfo->deviceId] = std::move(deviceInfo->data);
|
||||
for (const auto& zoneset : *appliedZonesets)
|
||||
{
|
||||
deviceInfoMap[zoneset.first] = std::move(zoneset.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZonesData::ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath)
|
||||
void FancyZonesData::ParseCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath)
|
||||
{
|
||||
std::scoped_lock lock{ dataLock };
|
||||
const auto& customZoneSet = JSONHelpers::ParseCustomZoneSetFromTmpFile(tmpFilePath);
|
||||
const auto& customZoneSets = JSONHelpers::ParseCustomZoneSetsFromTmpFile(tmpFilePath);
|
||||
|
||||
if (customZoneSet)
|
||||
for (const auto& zoneSet : customZoneSets)
|
||||
{
|
||||
customZoneSetsMap[customZoneSet->uuid] = std::move(customZoneSet->data);
|
||||
customZoneSetsMap[zoneSet.uuid] = zoneSet.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
namespace FancyZonesDataTypes
|
||||
{
|
||||
struct ZoneSetData;
|
||||
struct DeviceIdData;
|
||||
struct DeviceInfoData;
|
||||
struct CustomZoneSetData;
|
||||
struct AppZoneHistoryData;
|
||||
@@ -71,7 +72,7 @@ public:
|
||||
|
||||
void SetActiveZoneSet(const std::wstring& deviceId, const FancyZonesDataTypes::ZoneSetData& zoneSet);
|
||||
|
||||
bool SerializeDeviceInfoToTmpFile(const std::wstring& uniqueId) const;
|
||||
void SerializeDeviceInfoToTmpFile(const GUID& currentVirtualDesktop) const;
|
||||
void ParseDataFromTmpFiles();
|
||||
|
||||
json::JsonObject GetPersistFancyZonesJSON();
|
||||
@@ -113,7 +114,7 @@ private:
|
||||
}
|
||||
#endif
|
||||
void ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath);
|
||||
void ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath);
|
||||
void ParseCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
|
||||
void ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
|
||||
|
||||
void RemoveDesktopAppZoneHistory(const std::wstring& desktopId);
|
||||
|
||||
@@ -108,6 +108,15 @@ namespace FancyZonesDataTypes
|
||||
std::vector<size_t> zoneIndexSet;
|
||||
};
|
||||
|
||||
struct DeviceIdData
|
||||
{
|
||||
std::wstring deviceName;
|
||||
int width;
|
||||
int height;
|
||||
GUID virtualDesktopId;
|
||||
std::wstring monitorId;
|
||||
};
|
||||
|
||||
struct DeviceInfoData
|
||||
{
|
||||
ZoneSetData activeZoneSet;
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
namespace NonLocalizable
|
||||
{
|
||||
const wchar_t ActiveZoneSetStr[] = L"active-zoneset";
|
||||
const wchar_t AppliedZonesets[] = L"applied-zonesets";
|
||||
const wchar_t AppPathStr[] = L"app-path";
|
||||
const wchar_t AppZoneHistoryStr[] = L"app-zone-history";
|
||||
const wchar_t CanvasStr[] = L"canvas";
|
||||
const wchar_t CellChildMapStr[] = L"cell-child-map";
|
||||
const wchar_t ColumnsPercentageStr[] = L"columns-percentage";
|
||||
const wchar_t ColumnsStr[] = L"columns";
|
||||
const wchar_t CreatedCustomZoneSets[] = L"created-custom-zone-sets";
|
||||
const wchar_t CustomZoneSetsStr[] = L"custom-zone-sets";
|
||||
const wchar_t DeletedCustomZoneSetsStr[] = L"deleted-custom-zone-sets";
|
||||
const wchar_t DeviceIdStr[] = L"device-id";
|
||||
@@ -445,6 +447,64 @@ namespace JSONHelpers
|
||||
}
|
||||
}
|
||||
|
||||
json::JsonObject AppliedZonesetsJSON::ToJson(const TDeviceInfoMap& deviceInfoMap)
|
||||
{
|
||||
json::JsonObject result{};
|
||||
|
||||
json::JsonArray array;
|
||||
for (const auto& info : deviceInfoMap)
|
||||
{
|
||||
JSONHelpers::DeviceInfoJSON deviceInfoJson{ info.first, info.second };
|
||||
array.Append(JSONHelpers::DeviceInfoJSON::ToJson(deviceInfoJson));
|
||||
}
|
||||
|
||||
result.SetNamedValue(NonLocalizable::AppliedZonesets, array);
|
||||
return result;
|
||||
}
|
||||
|
||||
json::JsonObject AppliedZonesetsJSON::ToJson(const TDeviceInfoMap& deviceInfoMap, const GUID& currentVirtualDesktop)
|
||||
{
|
||||
json::JsonObject result{};
|
||||
|
||||
json::JsonArray array;
|
||||
for (const auto& info : deviceInfoMap)
|
||||
{
|
||||
std::optional<FancyZonesDataTypes::DeviceIdData> id = FancyZonesUtils::ParseDeviceId(info.first);
|
||||
if (id.has_value() && id->virtualDesktopId == currentVirtualDesktop)
|
||||
{
|
||||
JSONHelpers::DeviceInfoJSON deviceInfoJson{ info.first, info.second };
|
||||
array.Append(JSONHelpers::DeviceInfoJSON::ToJson(deviceInfoJson));
|
||||
}
|
||||
}
|
||||
|
||||
result.SetNamedValue(NonLocalizable::AppliedZonesets, array);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<TDeviceInfoMap> AppliedZonesetsJSON::FromJson(const json::JsonObject& json)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::unordered_map<std::wstring, FancyZonesDataTypes::DeviceInfoData> appliedZonesets;
|
||||
|
||||
auto zonesets = json.GetNamedArray(NonLocalizable::AppliedZonesets);
|
||||
for (const auto& zoneset : zonesets)
|
||||
{
|
||||
std::optional<DeviceInfoJSON> device = DeviceInfoJSON::FromJson(zoneset.GetObjectW());
|
||||
if (device.has_value())
|
||||
{
|
||||
appliedZonesets.insert(std::make_pair(device->deviceId, device->data));
|
||||
}
|
||||
}
|
||||
|
||||
return appliedZonesets;
|
||||
}
|
||||
catch (const winrt::hresult_error&)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
json::JsonObject GetPersistFancyZonesJSON(const std::wstring& zonesSettingsFileName, const std::wstring& appZoneHistoryFileName)
|
||||
{
|
||||
auto result = json::from_file(zonesSettingsFileName);
|
||||
@@ -603,20 +663,34 @@ namespace JSONHelpers
|
||||
return customZoneSetsJSON;
|
||||
}
|
||||
|
||||
void SerializeDeviceInfoToTmpFile(const JSONHelpers::DeviceInfoJSON& deviceInfo, std::wstring_view tmpFilePath)
|
||||
void SerializeDeviceInfoToTmpFile(const TDeviceInfoMap& deviceInfoMap, const GUID& currentVirtualDesktop, std::wstring_view tmpFilePath)
|
||||
{
|
||||
json::JsonObject deviceInfoJson = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
|
||||
json::to_file(tmpFilePath, deviceInfoJson);
|
||||
json::to_file(tmpFilePath, JSONHelpers::AppliedZonesetsJSON::ToJson(deviceInfoMap, currentVirtualDesktop));
|
||||
}
|
||||
|
||||
std::optional<DeviceInfoJSON> ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath)
|
||||
void SerializeCustomZoneSetsToTmpFile(const TCustomZoneSetsMap& customZoneSetsMap, std::wstring_view tmpFilePath)
|
||||
{
|
||||
std::optional<DeviceInfoJSON> result{ std::nullopt };
|
||||
json::JsonObject result{};
|
||||
|
||||
json::JsonArray array;
|
||||
for (const auto& zoneSet : customZoneSetsMap)
|
||||
{
|
||||
CustomZoneSetJSON json{ zoneSet.first, zoneSet.second };
|
||||
array.Append(JSONHelpers::CustomZoneSetJSON::ToJson(json));
|
||||
}
|
||||
|
||||
result.SetNamedValue(NonLocalizable::CreatedCustomZoneSets, array);
|
||||
json::to_file(tmpFilePath, result);
|
||||
}
|
||||
|
||||
std::optional<TDeviceInfoMap> ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath)
|
||||
{
|
||||
std::optional<TDeviceInfoMap> result{ std::nullopt };
|
||||
if (std::filesystem::exists(tmpFilePath))
|
||||
{
|
||||
if (auto zoneSetJson = json::from_file(tmpFilePath); zoneSetJson.has_value())
|
||||
{
|
||||
if (auto deviceInfo = JSONHelpers::DeviceInfoJSON::FromJson(zoneSetJson.value()); deviceInfo.has_value())
|
||||
if (auto deviceInfo = JSONHelpers::AppliedZonesetsJSON::FromJson(zoneSetJson.value()); deviceInfo.has_value())
|
||||
{
|
||||
result = std::move(deviceInfo);
|
||||
}
|
||||
@@ -627,24 +701,27 @@ namespace JSONHelpers
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<CustomZoneSetJSON> ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath)
|
||||
std::vector<CustomZoneSetJSON> ParseCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath)
|
||||
{
|
||||
std::optional<CustomZoneSetJSON> result{ std::nullopt };
|
||||
std::vector<CustomZoneSetJSON> result;
|
||||
if (std::filesystem::exists(tmpFilePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (auto customZoneSetJson = json::from_file(tmpFilePath); customZoneSetJson.has_value())
|
||||
{
|
||||
if (auto customZoneSet = JSONHelpers::CustomZoneSetJSON::FromJson(customZoneSetJson.value()); customZoneSet.has_value())
|
||||
auto zoneSetArray = customZoneSetJson.value().GetNamedArray(NonLocalizable::CreatedCustomZoneSets);
|
||||
for (const auto& zoneSet : zoneSetArray)
|
||||
{
|
||||
result = std::move(customZoneSet);
|
||||
if (auto customZoneSet = JSONHelpers::CustomZoneSetJSON::FromJson(zoneSet.GetObjectW()); customZoneSet.has_value())
|
||||
{
|
||||
result.emplace_back(std::move(*customZoneSet));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const winrt::hresult_error&)
|
||||
{
|
||||
result = std::nullopt;
|
||||
}
|
||||
|
||||
DeleteTmpFile(tmpFilePath);
|
||||
@@ -676,4 +753,4 @@ namespace JSONHelpers
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,6 @@ namespace JSONHelpers
|
||||
static std::optional<AppZoneHistoryJSON> FromJson(const json::JsonObject& zoneSet);
|
||||
};
|
||||
|
||||
|
||||
struct DeviceInfoJSON
|
||||
{
|
||||
std::wstring deviceId;
|
||||
@@ -60,6 +59,13 @@ namespace JSONHelpers
|
||||
using TDeviceInfoMap = std::unordered_map<std::wstring, FancyZonesDataTypes::DeviceInfoData>;
|
||||
using TCustomZoneSetsMap = std::unordered_map<std::wstring, FancyZonesDataTypes::CustomZoneSetData>;
|
||||
|
||||
struct AppliedZonesetsJSON
|
||||
{
|
||||
static json::JsonObject ToJson(const TDeviceInfoMap& deviceInfoMap);
|
||||
static json::JsonObject ToJson(const TDeviceInfoMap& deviceInfoMap, const GUID& currentVirtualDesktop);
|
||||
static std::optional<TDeviceInfoMap> FromJson(const json::JsonObject& json);
|
||||
};
|
||||
|
||||
json::JsonObject GetPersistFancyZonesJSON(const std::wstring& zonesSettingsFileName, const std::wstring& appZoneHistoryFileName);
|
||||
void SaveFancyZonesData(const std::wstring& zonesSettingsFileName,
|
||||
const std::wstring& appZoneHistoryFileName,
|
||||
@@ -76,8 +82,13 @@ namespace JSONHelpers
|
||||
TCustomZoneSetsMap ParseCustomZoneSets(const json::JsonObject& fancyZonesDataJSON);
|
||||
json::JsonArray SerializeCustomZoneSets(const TCustomZoneSetsMap& customZoneSetsMap);
|
||||
|
||||
void SerializeDeviceInfoToTmpFile(const JSONHelpers::DeviceInfoJSON& deviceInfo, std::wstring_view tmpFilePath);
|
||||
std::optional<DeviceInfoJSON> ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath);
|
||||
std::optional<CustomZoneSetJSON> ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath);
|
||||
void SerializeDeviceInfoToTmpFile(const TDeviceInfoMap& deviceInfoMap, const GUID& currentVirtualDesktop, std::wstring_view tmpFilePath);
|
||||
std::optional<TDeviceInfoMap> ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath);
|
||||
std::vector<CustomZoneSetJSON> ParseCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
|
||||
std::vector<std::wstring> ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
|
||||
|
||||
#if defined(UNIT_TESTS)
|
||||
void SerializeCustomZoneSetsToTmpFile(const TCustomZoneSetsMap& customZoneSetsMap, std::wstring_view tmpFilePath);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -25,44 +25,6 @@ namespace NonLocalizable
|
||||
|
||||
using namespace FancyZonesUtils;
|
||||
|
||||
namespace ZoneWindowUtils
|
||||
{
|
||||
std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& deviceId, const std::wstring& virtualDesktopId)
|
||||
{
|
||||
MONITORINFOEXW mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
if (!virtualDesktopId.empty() && GetMonitorInfo(monitor, &mi))
|
||||
{
|
||||
Rect const monitorRect(mi.rcMonitor);
|
||||
// Unique identifier format: <parsed-device-id>_<width>_<height>_<virtual-desktop-id>
|
||||
return ParseDeviceId(deviceId) +
|
||||
L'_' +
|
||||
std::to_wstring(monitorRect.width()) +
|
||||
L'_' +
|
||||
std::to_wstring(monitorRect.height()) +
|
||||
L'_' +
|
||||
virtualDesktopId;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::wstring GenerateUniqueIdAllMonitorsArea(const std::wstring& virtualDesktopId)
|
||||
{
|
||||
std::wstring result{ ZonedWindowProperties::MultiMonitorDeviceID };
|
||||
|
||||
RECT combinedResolution = GetAllMonitorsCombinedRect<&MONITORINFO::rcMonitor>();
|
||||
|
||||
result += L'_';
|
||||
result += std::to_wstring(combinedResolution.right - combinedResolution.left);
|
||||
result += L'_';
|
||||
result += std::to_wstring(combinedResolution.bottom - combinedResolution.top);
|
||||
result += L'_';
|
||||
result += virtualDesktopId;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
struct ZoneWindow : public winrt::implements<ZoneWindow, IZoneWindow>
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -2,12 +2,6 @@
|
||||
#include "FancyZones.h"
|
||||
#include "lib/ZoneSet.h"
|
||||
|
||||
namespace ZoneWindowUtils
|
||||
{
|
||||
std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& devideId, const std::wstring& virtualDesktopId);
|
||||
std::wstring GenerateUniqueIdAllMonitorsArea(const std::wstring& virtualDesktopId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing single work area, which is defined by monitor and virtual desktop.
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <sstream>
|
||||
#include <complex>
|
||||
|
||||
#include <fancyzones/lib/FancyZonesDataTypes.h>
|
||||
|
||||
// Non-Localizable strings
|
||||
namespace NonLocalizable
|
||||
{
|
||||
@@ -40,7 +42,7 @@ namespace
|
||||
|
||||
namespace FancyZonesUtils
|
||||
{
|
||||
std::wstring ParseDeviceId(const std::wstring& deviceId)
|
||||
std::wstring TrimDeviceId(const std::wstring& deviceId)
|
||||
{
|
||||
// We're interested in the unique part between the first and last #'s
|
||||
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
||||
@@ -63,7 +65,95 @@ namespace FancyZonesUtils
|
||||
return defaultDeviceId;
|
||||
}
|
||||
}
|
||||
|
||||
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*);
|
||||
UINT GetDpiForMonitor(HMONITOR monitor) noexcept
|
||||
{
|
||||
@@ -457,6 +547,71 @@ namespace FancyZonesUtils
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& deviceId, const std::wstring& virtualDesktopId)
|
||||
{
|
||||
MONITORINFOEXW mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
if (!virtualDesktopId.empty() && GetMonitorInfo(monitor, &mi))
|
||||
{
|
||||
Rect const monitorRect(mi.rcMonitor);
|
||||
// Unique identifier format: <parsed-device-id>_<width>_<height>_<virtual-desktop-id>
|
||||
return TrimDeviceId(deviceId) +
|
||||
L'_' +
|
||||
std::to_wstring(monitorRect.width()) +
|
||||
L'_' +
|
||||
std::to_wstring(monitorRect.height()) +
|
||||
L'_' +
|
||||
virtualDesktopId;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::wstring GenerateUniqueIdAllMonitorsArea(const std::wstring& virtualDesktopId)
|
||||
{
|
||||
std::wstring result{ ZonedWindowProperties::MultiMonitorDeviceID };
|
||||
|
||||
RECT combinedResolution = GetAllMonitorsCombinedRect<&MONITORINFO::rcMonitor>();
|
||||
|
||||
result += L'_';
|
||||
result += std::to_wstring(combinedResolution.right - combinedResolution.left);
|
||||
result += L'_';
|
||||
result += std::to_wstring(combinedResolution.bottom - combinedResolution.top);
|
||||
result += L'_';
|
||||
result += virtualDesktopId;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<std::wstring> GenerateMonitorId(MONITORINFOEX mi, HMONITOR monitor, const GUID& virtualDesktopId)
|
||||
{
|
||||
DISPLAY_DEVICE displayDevice = { sizeof(displayDevice) };
|
||||
PCWSTR deviceId = nullptr;
|
||||
|
||||
bool validMonitor = true;
|
||||
if (EnumDisplayDevices(mi.szDevice, 0, &displayDevice, 1))
|
||||
{
|
||||
if (displayDevice.DeviceID[0] != L'\0')
|
||||
{
|
||||
deviceId = displayDevice.DeviceID;
|
||||
}
|
||||
}
|
||||
|
||||
if (!deviceId)
|
||||
{
|
||||
deviceId = GetSystemMetrics(SM_REMOTESESSION) ?
|
||||
L"\\\\?\\DISPLAY#REMOTEDISPLAY#" :
|
||||
L"\\\\?\\DISPLAY#LOCALDISPLAY#";
|
||||
}
|
||||
|
||||
wil::unique_cotaskmem_string vdId;
|
||||
if (SUCCEEDED(StringFromCLSID(virtualDesktopId, &vdId)))
|
||||
{
|
||||
return GenerateUniqueId(monitor, deviceId, vdId.get());
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector<RECT>& zoneRects) noexcept
|
||||
{
|
||||
using complex = std::complex<double>;
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
#include "gdiplus.h"
|
||||
#include <common/string_utils.h>
|
||||
|
||||
namespace FancyZonesDataTypes
|
||||
{
|
||||
struct DeviceIdData;
|
||||
}
|
||||
|
||||
namespace FancyZonesUtils
|
||||
{
|
||||
struct Rect
|
||||
@@ -131,6 +136,28 @@ namespace FancyZonesUtils
|
||||
return result;
|
||||
}
|
||||
|
||||
template<RECT MONITORINFO::*member>
|
||||
std::vector<std::pair<HMONITOR, MONITORINFOEX>> GetAllMonitorInfo()
|
||||
{
|
||||
using result_t = std::vector<std::pair<HMONITOR, MONITORINFOEX>>;
|
||||
result_t result;
|
||||
|
||||
auto enumMonitors = [](HMONITOR monitor, HDC hdc, LPRECT pRect, LPARAM param) -> BOOL {
|
||||
MONITORINFOEX mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
result_t& result = *reinterpret_cast<result_t*>(param);
|
||||
if (GetMonitorInfo(monitor, &mi))
|
||||
{
|
||||
result.push_back({ monitor, mi });
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
};
|
||||
|
||||
EnumDisplayMonitors(NULL, NULL, enumMonitors, reinterpret_cast<LPARAM>(&result));
|
||||
return result;
|
||||
}
|
||||
|
||||
template<RECT MONITORINFO::*member>
|
||||
RECT GetAllMonitorsCombinedRect()
|
||||
{
|
||||
@@ -157,8 +184,6 @@ namespace FancyZonesUtils
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring ParseDeviceId(const std::wstring& deviceId);
|
||||
|
||||
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
|
||||
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
|
||||
void SizeWindowToRect(HWND window, RECT rect) noexcept;
|
||||
@@ -174,6 +199,13 @@ namespace FancyZonesUtils
|
||||
void RestoreWindowOrigin(HWND window) noexcept;
|
||||
|
||||
bool IsValidGuid(const std::wstring& str);
|
||||
|
||||
std::wstring GenerateUniqueId(HMONITOR monitor, const std::wstring& devideId, const std::wstring& virtualDesktopId);
|
||||
std::wstring GenerateUniqueIdAllMonitorsArea(const std::wstring& virtualDesktopId);
|
||||
std::optional<std::wstring> GenerateMonitorId(MONITORINFOEX mi, HMONITOR monitor, const GUID& 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;
|
||||
|
||||
Reference in New Issue
Block a user