[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:
vldmr11080
2020-05-31 12:36:45 +02:00
committed by GitHub
parent 595b15fcd9
commit 2216cda2f1
14 changed files with 330 additions and 269 deletions

View File

@@ -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(&currentVirtualDesktopId))
{
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);