mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 19:57:57 +01:00
[FancyZones] Improve monitor work area handling (#3418)
* Initial design for improving handling of different engaged work areas in fancyzones. * Remove active device id check in ZoneWindow. * Remove concept of active device identifier in JSONHelpers. * Refactor interface description and add new method. * Simplify ZoneWindow initialization. * Default value for active ZoneWindow during move/size. * Add newline at the end of file. * Use COM pointers for ZoneWindow instead of passing raw ptr. * Solve few issues after merging with master. * Fix typo in documentation.
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
#include "lib/util.h"
|
||||
#include "trace.h"
|
||||
#include "VirtualDesktopUtils.h"
|
||||
#include "MonitorWorkAreaHandler.h"
|
||||
|
||||
#include <interface/win_hook_event_data.h>
|
||||
|
||||
@@ -25,19 +26,6 @@ enum class DisplayChangeType
|
||||
Initialization
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZonesCallback, IZoneWindowHost>
|
||||
{
|
||||
public:
|
||||
@@ -58,19 +46,19 @@ public:
|
||||
void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept
|
||||
{
|
||||
std::unique_lock writeLock(m_lock);
|
||||
m_windowMoveHandler.MoveSizeStart(window, monitor, ptScreen, m_zoneWindowMap);
|
||||
m_windowMoveHandler.MoveSizeStart(window, monitor, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(m_currentDesktopId));
|
||||
}
|
||||
|
||||
void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept
|
||||
{
|
||||
std::unique_lock writeLock(m_lock);
|
||||
m_windowMoveHandler.MoveSizeUpdate(monitor, ptScreen, m_zoneWindowMap);
|
||||
m_windowMoveHandler.MoveSizeUpdate(monitor, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(m_currentDesktopId));
|
||||
}
|
||||
|
||||
void MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept
|
||||
{
|
||||
std::unique_lock writeLock(m_lock);
|
||||
m_windowMoveHandler.MoveSizeEnd(window, ptScreen, m_zoneWindowMap);
|
||||
m_windowMoveHandler.MoveSizeEnd(window, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(m_currentDesktopId));
|
||||
}
|
||||
IFACEMETHODIMP_(void)
|
||||
HandleWinHookEvent(const WinHookEvent* data) noexcept
|
||||
@@ -153,18 +141,6 @@ public:
|
||||
const auto nB = (tmp & 0xFF);
|
||||
return RGB(nR, nG, nB);
|
||||
}
|
||||
IFACEMETHODIMP_(IZoneWindow*)
|
||||
GetParentZoneWindow(HMONITOR monitor) noexcept
|
||||
{
|
||||
//NOTE: as public method it's unsafe without lock, but it's called from AddZoneWindow through making ZoneWindow that causes deadlock
|
||||
//TODO: needs refactoring
|
||||
auto it = m_zoneWindowMap.find(monitor);
|
||||
if (it != m_zoneWindowMap.end())
|
||||
{
|
||||
return it->second.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
IFACEMETHODIMP_(int)
|
||||
GetZoneHighlightOpacity() noexcept
|
||||
{
|
||||
@@ -223,9 +199,6 @@ private:
|
||||
|
||||
void RegisterVirtualDesktopUpdates(std::vector<GUID>& ids) noexcept;
|
||||
|
||||
void RegisterNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept;
|
||||
bool IsNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept;
|
||||
|
||||
bool IsSplashScreen(HWND window);
|
||||
|
||||
void OnEditorExitEvent() noexcept;
|
||||
@@ -239,11 +212,11 @@ private:
|
||||
mutable std::shared_mutex m_lock;
|
||||
HWND m_window{};
|
||||
WindowMoveHandler m_windowMoveHandler;
|
||||
MonitorWorkAreaHandler m_workAreaHandler;
|
||||
|
||||
std::map<HMONITOR, winrt::com_ptr<IZoneWindow>> m_zoneWindowMap; // Map of monitor to ZoneWindow (one per monitor)
|
||||
winrt::com_ptr<IFancyZonesSettings> m_settings{};
|
||||
GUID m_currentVirtualDesktopId{}; // UUID of the current virtual desktop. Is GUID_NULL until first VD switch per session.
|
||||
std::unordered_map<GUID, std::vector<HMONITOR>> m_processedWorkAreas; // Work area is defined by monitor and virtual desktop id.
|
||||
GUID m_previousDesktopId{}; // UUID of previously active virtual desktop.
|
||||
GUID m_currentDesktopId{}; // UUID of the current virtual desktop.
|
||||
wil::unique_handle m_terminateEditorEvent; // Handle of FancyZonesEditor.exe we launch and wait on
|
||||
wil::unique_handle m_terminateVirtualDesktopTrackerEvent;
|
||||
|
||||
@@ -309,7 +282,7 @@ IFACEMETHODIMP_(void)
|
||||
FancyZones::Destroy() noexcept
|
||||
{
|
||||
std::unique_lock writeLock(m_lock);
|
||||
m_zoneWindowMap.clear();
|
||||
m_workAreaHandler.Clear();
|
||||
BufferedPaintUnInit();
|
||||
if (m_window)
|
||||
{
|
||||
@@ -346,18 +319,9 @@ FancyZones::WindowCreated(HWND window) noexcept
|
||||
|
||||
if (m_settings->GetSettings()->appLastZone_moveWindows && IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
|
||||
{
|
||||
for (const auto& [monitor, zoneWindow] : m_zoneWindowMap)
|
||||
auto zoneWindow = m_workAreaHandler.GetWorkArea(window);
|
||||
if (zoneWindow)
|
||||
{
|
||||
// WindowCreated is also invoked when a virtual desktop switch occurs, we need a way
|
||||
// to figure out when that happens to avoid moving windows that should not be moved.
|
||||
GUID windowDesktopId{};
|
||||
GUID zoneWindowDesktopId{};
|
||||
if (VirtualDesktopUtils::GetWindowDesktopId(window, &windowDesktopId) &&
|
||||
VirtualDesktopUtils::GetZoneWindowDesktopId(zoneWindow.get(), &zoneWindowDesktopId) &&
|
||||
(windowDesktopId != zoneWindowDesktopId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto activeZoneSet = zoneWindow->ActiveZoneSet();
|
||||
if (activeZoneSet)
|
||||
{
|
||||
@@ -371,9 +335,8 @@ FancyZones::WindowCreated(HWND window) noexcept
|
||||
!IsSplashScreen(window) &&
|
||||
!fancyZonesData.IsAnotherWindowOfApplicationInstanceZoned(window))
|
||||
{
|
||||
m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, monitor, zoneIndexSet, m_zoneWindowMap);
|
||||
m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, zoneWindow);
|
||||
fancyZonesData.UpdateProcessIdToHandleMap(window);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -468,8 +431,8 @@ void FancyZones::ToggleEditor() noexcept
|
||||
}
|
||||
|
||||
std::shared_lock readLock(m_lock);
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
if (iter == m_zoneWindowMap.end())
|
||||
auto zoneWindow = m_workAreaHandler.GetWorkArea(m_currentDesktopId, monitor);
|
||||
if (!zoneWindow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -482,8 +445,6 @@ void FancyZones::ToggleEditor() noexcept
|
||||
} })
|
||||
.wait();
|
||||
|
||||
auto zoneWindow = iter->second;
|
||||
|
||||
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
|
||||
fancyZonesData.CustomZoneSetsToJsonFile(ZoneWindowUtils::GetCustomZoneSetsTmpPath());
|
||||
|
||||
@@ -673,10 +634,11 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
|
||||
if (changeType == DisplayChangeType::VirtualDesktop ||
|
||||
changeType == DisplayChangeType::Initialization)
|
||||
{
|
||||
m_previousDesktopId = m_currentDesktopId;
|
||||
GUID currentVirtualDesktopId{};
|
||||
if (VirtualDesktopUtils::GetCurrentVirtualDesktopId(¤tVirtualDesktopId))
|
||||
{
|
||||
m_currentVirtualDesktopId = currentVirtualDesktopId;
|
||||
m_currentDesktopId = currentVirtualDesktopId;
|
||||
}
|
||||
if (changeType == DisplayChangeType::Initialization)
|
||||
{
|
||||
@@ -703,27 +665,30 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
|
||||
void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
|
||||
{
|
||||
std::unique_lock writeLock(m_lock);
|
||||
wil::unique_cotaskmem_string virtualDesktopId;
|
||||
if (SUCCEEDED_LOG(StringFromCLSID(m_currentVirtualDesktopId, &virtualDesktopId)))
|
||||
|
||||
if (m_workAreaHandler.IsNewWorkArea(m_currentDesktopId, monitor))
|
||||
{
|
||||
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId.get());
|
||||
JSONHelpers::FancyZonesDataInstance().SetActiveDeviceId(uniqueId);
|
||||
|
||||
const bool newWorkArea = IsNewWorkArea(m_currentVirtualDesktopId, monitor);
|
||||
// "Turning FLASHING_ZONE option off"
|
||||
//const bool flash = m_settings->GetSettings()->zoneSetChange_flashZones && newWorkArea;
|
||||
const bool flash = false;
|
||||
|
||||
auto zoneWindow = MakeZoneWindow(this, m_hinstance, monitor, uniqueId, flash, newWorkArea);
|
||||
if (zoneWindow)
|
||||
wil::unique_cotaskmem_string virtualDesktopId;
|
||||
if (SUCCEEDED(StringFromCLSID(m_currentDesktopId, &virtualDesktopId)))
|
||||
{
|
||||
m_zoneWindowMap[monitor] = std::move(zoneWindow);
|
||||
}
|
||||
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId.get());
|
||||
|
||||
if (newWorkArea)
|
||||
{
|
||||
RegisterNewWorkArea(m_currentVirtualDesktopId, monitor);
|
||||
JSONHelpers::FancyZonesDataInstance().SaveFancyZonesData();
|
||||
// "Turning FLASHING_ZONE option off"
|
||||
//const bool flash = m_settings->GetSettings()->zoneSetChange_flashZones;
|
||||
const bool flash = false;
|
||||
|
||||
std::wstring parentId{};
|
||||
auto parentArea = m_workAreaHandler.GetWorkArea(m_previousDesktopId, monitor);
|
||||
if (parentArea)
|
||||
{
|
||||
parentId = parentArea->UniqueId();
|
||||
}
|
||||
auto workArea = MakeZoneWindow(this, m_hinstance, monitor, uniqueId, parentId, flash);
|
||||
if (workArea)
|
||||
{
|
||||
m_workAreaHandler.AddWorkArea(m_currentDesktopId, monitor, workArea);
|
||||
JSONHelpers::FancyZonesDataInstance().SaveFancyZonesData();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -802,7 +767,11 @@ void FancyZones::UpdateWindowsPositions() noexcept
|
||||
|
||||
auto strongThis = reinterpret_cast<FancyZones*>(data);
|
||||
std::unique_lock writeLock(strongThis->m_lock);
|
||||
strongThis->m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, nullptr, indexSet, strongThis->m_zoneWindowMap);
|
||||
auto zoneWindow = strongThis->m_workAreaHandler.GetWorkArea(window);
|
||||
if (zoneWindow)
|
||||
{
|
||||
strongThis->m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, indexSet, zoneWindow);
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
};
|
||||
@@ -819,11 +788,10 @@ void FancyZones::CycleActiveZoneSet(DWORD vkCode) noexcept
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
if (iter != m_zoneWindowMap.end())
|
||||
auto zoneWindow = m_workAreaHandler.GetWorkArea(m_currentDesktopId, monitor);
|
||||
if (zoneWindow)
|
||||
{
|
||||
const auto& zoneWindowPtr = iter->second;
|
||||
zoneWindowPtr->CycleActiveZoneSet(vkCode);
|
||||
zoneWindow->CycleActiveZoneSet(vkCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -845,7 +813,7 @@ bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
|
||||
do
|
||||
{
|
||||
std::unique_lock writeLock(m_lock);
|
||||
if (m_windowMoveHandler.MoveWindowIntoZoneByDirection(*currMonitorInfo, window, vkCode, false /* cycle through zones */, m_zoneWindowMap))
|
||||
if (m_windowMoveHandler.MoveWindowIntoZoneByDirection(window, vkCode, false /* cycle through zones */, m_workAreaHandler.GetWorkArea(m_currentDesktopId, *currMonitorInfo)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -872,7 +840,7 @@ bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
|
||||
{
|
||||
// Single monitor environment.
|
||||
std::unique_lock writeLock(m_lock);
|
||||
return m_windowMoveHandler.MoveWindowIntoZoneByDirection(current, window, vkCode, true /* cycle through zones */, m_zoneWindowMap);
|
||||
return m_windowMoveHandler.MoveWindowIntoZoneByDirection(window, vkCode, true /* cycle through zones */, m_workAreaHandler.GetWorkArea(m_currentDesktopId, current));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -881,60 +849,24 @@ bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
|
||||
|
||||
void FancyZones::RegisterVirtualDesktopUpdates(std::vector<GUID>& ids) noexcept
|
||||
{
|
||||
std::unordered_set<GUID> activeVirtualDesktops(std::begin(ids), std::end(ids));
|
||||
std::unique_lock writeLock(m_lock);
|
||||
|
||||
std::vector<GUID> deleted{};
|
||||
m_workAreaHandler.RegisterUpdates(ids, deleted);
|
||||
|
||||
bool modified{ false };
|
||||
for (auto it = std::begin(m_processedWorkAreas); it != std::end(m_processedWorkAreas);)
|
||||
for (const auto& id : deleted)
|
||||
{
|
||||
auto iter = activeVirtualDesktops.find(it->first);
|
||||
if (iter == activeVirtualDesktops.end())
|
||||
wil::unique_cotaskmem_string virtualDesktopId;
|
||||
if (SUCCEEDED(StringFromCLSID(id, &virtualDesktopId)))
|
||||
{
|
||||
// if we couldn't find the GUID in currentVirtualDesktopIds, we must remove it from both m_processedWorkAreas and deviceInfoMap
|
||||
wil::unique_cotaskmem_string virtualDesktopId;
|
||||
if (SUCCEEDED_LOG(StringFromCLSID(it->first, &virtualDesktopId)))
|
||||
{
|
||||
modified |= JSONHelpers::FancyZonesDataInstance().RemoveDevicesByVirtualDesktopId(virtualDesktopId.get());
|
||||
}
|
||||
it = m_processedWorkAreas.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
activeVirtualDesktops.erase(it->first); // virtual desktop already in map, skip it
|
||||
++it;
|
||||
modified |= JSONHelpers::FancyZonesDataInstance().RemoveDevicesByVirtualDesktopId(virtualDesktopId.get());
|
||||
}
|
||||
}
|
||||
if (modified)
|
||||
{
|
||||
JSONHelpers::FancyZonesDataInstance().SaveFancyZonesData();
|
||||
}
|
||||
// register new virtual desktops, if any
|
||||
for (const auto& id : activeVirtualDesktops)
|
||||
{
|
||||
m_processedWorkAreas[id] = std::vector<HMONITOR>();
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::RegisterNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept
|
||||
{
|
||||
if (!m_processedWorkAreas.contains(virtualDesktopId))
|
||||
{
|
||||
m_processedWorkAreas[virtualDesktopId] = { monitor };
|
||||
}
|
||||
else
|
||||
{
|
||||
m_processedWorkAreas[virtualDesktopId].push_back(monitor);
|
||||
}
|
||||
}
|
||||
|
||||
bool FancyZones::IsNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept
|
||||
{
|
||||
auto it = m_processedWorkAreas.find(virtualDesktopId);
|
||||
if (it != m_processedWorkAreas.end())
|
||||
{
|
||||
// virtual desktop exists, check if it's processed on given monitor
|
||||
return std::find(it->second.begin(), it->second.end(), monitor) == it->second.end();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FancyZones::IsSplashScreen(HWND window)
|
||||
@@ -956,10 +888,10 @@ void FancyZones::OnEditorExitEvent() noexcept
|
||||
JSONHelpers::FancyZonesDataInstance().ParseDeletedCustomZoneSetsFromTmpFile(ZoneWindowUtils::GetCustomZoneSetsTmpPath());
|
||||
JSONHelpers::FancyZonesDataInstance().ParseCustomZoneSetFromTmpFile(ZoneWindowUtils::GetAppliedZoneSetTmpPath());
|
||||
JSONHelpers::FancyZonesDataInstance().SaveFancyZonesData();
|
||||
// Update zone sets for currently active work areas.
|
||||
for (auto& [monitor, zoneWindow] : m_zoneWindowMap)
|
||||
|
||||
for (auto workArea : m_workAreaHandler.GetAllWorkAreas())
|
||||
{
|
||||
zoneWindow->UpdateActiveZoneSet();
|
||||
workArea->UpdateActiveZoneSet();
|
||||
}
|
||||
if (m_settings->GetSettings()->zoneSetChange_moveWindows)
|
||||
{
|
||||
@@ -974,9 +906,8 @@ bool FancyZones::ProcessSnapHotkey() noexcept
|
||||
const HMONITOR monitor = MonitorFromWindow(GetForegroundWindow(), MONITOR_DEFAULTTONULL);
|
||||
if (monitor)
|
||||
{
|
||||
auto zoneWindow = m_zoneWindowMap.find(monitor);
|
||||
if (zoneWindow != m_zoneWindowMap.end() &&
|
||||
zoneWindow->second->ActiveZoneSet())
|
||||
auto zoneWindow = m_workAreaHandler.GetWorkArea(m_currentDesktopId, monitor);
|
||||
if (zoneWindow->ActiveZoneSet() != nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -1001,9 +932,10 @@ std::vector<std::pair<HMONITOR, RECT>> FancyZones::GetRawMonitorData() noexcept
|
||||
std::shared_lock readLock(m_lock);
|
||||
|
||||
std::vector<std::pair<HMONITOR, RECT>> monitorInfo;
|
||||
for (const auto& [monitor, window] : m_zoneWindowMap)
|
||||
const auto& activeWorkAreaMap = m_workAreaHandler.GetWorkAreasByDesktopId(m_currentDesktopId);
|
||||
for (const auto& [monitor, workArea] : activeWorkAreaMap)
|
||||
{
|
||||
if (window->ActiveZoneSet() != nullptr)
|
||||
if (workArea->ActiveZoneSet() != nullptr)
|
||||
{
|
||||
MONITORINFOEX mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
|
||||
Reference in New Issue
Block a user