From 3d619f1670112cb753764814b14208d460b6f17d Mon Sep 17 00:00:00 2001 From: PrzemyslawTusinski <61138537+PrzemyslawTusinski@users.noreply.github.com> Date: Wed, 27 May 2020 16:52:59 +0200 Subject: [PATCH] [FancyZones] Outlook new message restore placement bug (#2534) --- src/modules/fancyzones/lib/FancyZones.cpp | 30 ++++-- src/modules/fancyzones/lib/JsonHelpers.cpp | 114 +++++++++++++++++++-- src/modules/fancyzones/lib/JsonHelpers.h | 4 + 3 files changed, 131 insertions(+), 17 deletions(-) diff --git a/src/modules/fancyzones/lib/FancyZones.cpp b/src/modules/fancyzones/lib/FancyZones.cpp index 00cb4954c2..b0a5314505 100644 --- a/src/modules/fancyzones/lib/FancyZones.cpp +++ b/src/modules/fancyzones/lib/FancyZones.cpp @@ -60,13 +60,13 @@ public: std::unique_lock writeLock(m_lock); m_windowMoveHandler.MoveSizeStart(window, monitor, ptScreen, m_zoneWindowMap); } - + void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept { std::unique_lock writeLock(m_lock); m_windowMoveHandler.MoveSizeUpdate(monitor, ptScreen, m_zoneWindowMap); } - + void MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept { std::unique_lock writeLock(m_lock); @@ -114,7 +114,7 @@ public: ToggleEditor() noexcept; IFACEMETHODIMP_(void) SettingsChanged() noexcept; - + void WindowCreated(HWND window) noexcept; // IZoneWindowHost @@ -222,9 +222,12 @@ private: bool OnSnapHotkey(DWORD vkCode) noexcept; void RegisterVirtualDesktopUpdates(std::vector& ids) noexcept; + void RegisterNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept; bool IsNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept; + bool IsSplashScreen(HWND window); + void OnEditorExitEvent() noexcept; bool ProcessSnapHotkey() noexcept; @@ -340,6 +343,7 @@ IFACEMETHODIMP_(void) FancyZones::WindowCreated(HWND window) noexcept { std::shared_lock readLock(m_lock); + if (m_settings->GetSettings()->appLastZone_moveWindows && IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray)) { for (const auto& [monitor, zoneWindow] : m_zoneWindowMap) @@ -357,15 +361,18 @@ FancyZones::WindowCreated(HWND window) noexcept const auto activeZoneSet = zoneWindow->ActiveZoneSet(); if (activeZoneSet) { - const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance(); + auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance(); wil::unique_cotaskmem_string guidString; if (SUCCEEDED(StringFromCLSID(activeZoneSet->Id(), &guidString))) { std::vector zoneIndexSet = fancyZonesData.GetAppLastZoneIndexSet(window, zoneWindow->UniqueId(), guidString.get()); - if (zoneIndexSet.size()) + if (zoneIndexSet.size() && + !IsSplashScreen(window) && + !fancyZonesData.IsAnotherWindowOfApplicationInstanceZoned(window)) { m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, monitor, zoneIndexSet, m_zoneWindowMap); + fancyZonesData.UpdateProcessIdToHandleMap(window); break; } } @@ -662,7 +669,6 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa return 0; } - void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept { if (changeType == DisplayChangeType::VirtualDesktop || @@ -932,6 +938,18 @@ bool FancyZones::IsNewWorkArea(GUID virtualDesktopId, HMONITOR monitor) noexcept return true; } +bool FancyZones::IsSplashScreen(HWND window) +{ + wchar_t splashClassName[] = L"MsoSplash"; // shouldn't be localized + wchar_t className[MAX_PATH]; + if (GetClassName(window, className, MAX_PATH) == 0) + { + return false; + } + + return wcscmp(splashClassName, className) == 0; +} + void FancyZones::OnEditorExitEvent() noexcept { // Collect information about changes in zone layout after editor exited. diff --git a/src/modules/fancyzones/lib/JsonHelpers.cpp b/src/modules/fancyzones/lib/JsonHelpers.cpp index d4b595411b..03e6bb053d 100644 --- a/src/modules/fancyzones/lib/JsonHelpers.cpp +++ b/src/modules/fancyzones/lib/JsonHelpers.cpp @@ -382,6 +382,51 @@ namespace JSONHelpers SaveFancyZonesData(); } + bool FancyZonesData::IsAnotherWindowOfApplicationInstanceZoned(HWND window) const + { + std::scoped_lock lock{ dataLock }; + auto processPath = get_process_path(window); + if (!processPath.empty()) + { + auto history = appZoneHistoryMap.find(processPath); + if (history != appZoneHistoryMap.end()) + { + DWORD processId = 0; + GetWindowThreadProcessId(window, &processId); + + auto processIdIt = history->second.processIdToHandleMap.find(processId); + + if (processIdIt == history->second.processIdToHandleMap.end()) + { + return false; + } + else if (processIdIt->second != window && IsWindow(processIdIt->second)) + { + return true; + } + } + } + + return false; + } + + void FancyZonesData::UpdateProcessIdToHandleMap(HWND window) + { + std::scoped_lock lock{ dataLock }; + auto processPath = get_process_path(window); + if (!processPath.empty()) + { + auto history = appZoneHistoryMap.find(processPath); + if (history != appZoneHistoryMap.end()) + { + DWORD processId = 0; + GetWindowThreadProcessId(window, &processId); + + history->second.processIdToHandleMap[processId] = window; + } + } + } + std::vector FancyZonesData::GetAppLastZoneIndexSet(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const { std::scoped_lock lock{ dataLock }; @@ -411,9 +456,26 @@ namespace JSONHelpers auto history = appZoneHistoryMap.find(processPath); if (history != appZoneHistoryMap.end()) { - const auto& data = history->second; + auto& data = history->second; if (data.zoneSetUuid == zoneSetId && data.deviceId == deviceId) { + if (!IsAnotherWindowOfApplicationInstanceZoned(window)) + { + DWORD processId = 0; + GetWindowThreadProcessId(window, &processId); + + data.processIdToHandleMap.erase(processId); + } + + // if there is another instance placed don't erase history + for (auto placedWindow : data.processIdToHandleMap) + { + if (IsWindow(placedWindow.second)) + { + return false; + } + } + appZoneHistoryMap.erase(processPath); SaveFancyZonesData(); return true; @@ -427,13 +489,39 @@ namespace JSONHelpers bool FancyZonesData::SetAppLastZones(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, const std::vector& zoneIndexSet) { std::scoped_lock lock{ dataLock }; + + if (IsAnotherWindowOfApplicationInstanceZoned(window)) + { + return false; + } + auto processPath = get_process_path(window); if (processPath.empty()) { return false; } - appZoneHistoryMap[processPath] = AppZoneHistoryData{ .zoneSetUuid = zoneSetId, .deviceId = deviceId, .zoneIndexSet = zoneIndexSet }; + DWORD processId = 0; + GetWindowThreadProcessId(window, &processId); + + auto history = appZoneHistoryMap.find(processPath); + + if (history != appZoneHistoryMap.end()) + { + auto& data = history->second; + + for (auto placedWindow : data.processIdToHandleMap) + { + if (IsWindow(placedWindow.second) && processId != placedWindow.first) + { + return false; + } + } + } + + auto windowMap = appZoneHistoryMap[processPath].processIdToHandleMap; + windowMap[processId] = window; + appZoneHistoryMap[processPath] = AppZoneHistoryData{ .processIdToHandleMap = windowMap, .zoneSetUuid = zoneSetId, .deviceId = deviceId, .zoneIndexSet = zoneIndexSet }; SaveFancyZonesData(); return true; } @@ -735,18 +823,19 @@ namespace JSONHelpers switch (zoneSetData.type) { - case CustomLayoutType::Grid: { + case CustomLayoutType::Grid: + { int j = 5; GridLayoutInfo zoneSetInfo(GridLayoutInfo::Minimal{ .rows = data[j++], .columns = data[j++] }); - for (int row = 0; row < zoneSetInfo.rows(); row++, j+=2) + for (int row = 0; row < zoneSetInfo.rows(); row++, j += 2) { - zoneSetInfo.rowsPercents()[row] = data[j] * 256 + data[j+1]; + zoneSetInfo.rowsPercents()[row] = data[j] * 256 + data[j + 1]; } - for (int col = 0; col < zoneSetInfo.columns(); col++, j+=2) + for (int col = 0; col < zoneSetInfo.columns(); col++, j += 2) { - zoneSetInfo.columnsPercents()[col] = data[j] * 256 + data[j+1]; + zoneSetInfo.columnsPercents()[col] = data[j] * 256 + data[j + 1]; } for (int row = 0; row < zoneSetInfo.rows(); row++) @@ -759,7 +848,8 @@ namespace JSONHelpers zoneSetData.info = zoneSetInfo; break; } - case CustomLayoutType::Canvas: { + case CustomLayoutType::Canvas: + { CanvasLayoutInfo info; int j = 5; @@ -1071,7 +1161,8 @@ namespace JSONHelpers result.SetNamedValue(L"name", json::value(customZoneSet.data.name)); switch (customZoneSet.data.type) { - case CustomLayoutType::Canvas: { + case CustomLayoutType::Canvas: + { result.SetNamedValue(L"type", json::value(L"canvas")); CanvasLayoutInfo info = std::get(customZoneSet.data.info); @@ -1079,7 +1170,8 @@ namespace JSONHelpers break; } - case CustomLayoutType::Grid: { + case CustomLayoutType::Grid: + { result.SetNamedValue(L"type", json::value(L"grid")); GridLayoutInfo gridInfo = std::get(customZoneSet.data.info); @@ -1103,7 +1195,7 @@ namespace JSONHelpers { return std::nullopt; } - + result.data.name = customZoneSet.GetNamedString(L"name"); json::JsonObject infoJson = customZoneSet.GetNamedObject(L"info"); diff --git a/src/modules/fancyzones/lib/JsonHelpers.h b/src/modules/fancyzones/lib/JsonHelpers.h index 870ab066b0..ebd02baa5f 100644 --- a/src/modules/fancyzones/lib/JsonHelpers.h +++ b/src/modules/fancyzones/lib/JsonHelpers.h @@ -132,6 +132,8 @@ namespace JSONHelpers struct AppZoneHistoryData { + std::map processIdToHandleMap; // Maps process id(DWORD) of application to zoned window handle(HWND) + std::wstring zoneSetUuid; std::wstring deviceId; std::vector zoneIndexSet; @@ -242,6 +244,8 @@ namespace JSONHelpers void UpdatePrimaryDesktopData(const std::wstring& desktopId); void RemoveDeletedDesktops(const std::vector& activeDesktops); + bool IsAnotherWindowOfApplicationInstanceZoned(HWND window) const; + void UpdateProcessIdToHandleMap(HWND window); std::vector 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 std::vector& zoneIndexSet);