Skip to content

Commit

Permalink
[FancyZones] Outlook new message restore placement bug (#2534)
Browse files Browse the repository at this point in the history
  • Loading branch information
PrzemyslawTusinski committed May 27, 2020
1 parent 3265549 commit 3d619f1
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 17 deletions.
30 changes: 24 additions & 6 deletions src/modules/fancyzones/lib/FancyZones.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZone
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);
Expand Down Expand Up @@ -114,7 +114,7 @@ struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZone
ToggleEditor() noexcept;
IFACEMETHODIMP_(void)
SettingsChanged() noexcept;

void WindowCreated(HWND window) noexcept;

// IZoneWindowHost
Expand Down Expand Up @@ -222,9 +222,12 @@ struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZone
bool OnSnapHotkey(DWORD vkCode) noexcept;

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;
bool ProcessSnapHotkey() noexcept;

Expand Down Expand Up @@ -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)
Expand All @@ -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<int> 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;
}
}
Expand Down Expand Up @@ -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 ||
Expand Down Expand Up @@ -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.
Expand Down
114 changes: 103 additions & 11 deletions src/modules/fancyzones/lib/JsonHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int> FancyZonesData::GetAppLastZoneIndexSet(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const
{
std::scoped_lock lock{ dataLock };
Expand Down Expand Up @@ -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;
Expand All @@ -427,13 +489,39 @@ namespace JSONHelpers
bool FancyZonesData::SetAppLastZones(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, const std::vector<int>& 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;
}
Expand Down Expand Up @@ -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++)
Expand All @@ -759,7 +848,8 @@ namespace JSONHelpers
zoneSetData.info = zoneSetInfo;
break;
}
case CustomLayoutType::Canvas: {
case CustomLayoutType::Canvas:
{
CanvasLayoutInfo info;

int j = 5;
Expand Down Expand Up @@ -1071,15 +1161,17 @@ 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<CanvasLayoutInfo>(customZoneSet.data.info);
result.SetNamedValue(L"info", CanvasLayoutInfo::ToJson(info));

break;
}
case CustomLayoutType::Grid: {
case CustomLayoutType::Grid:
{
result.SetNamedValue(L"type", json::value(L"grid"));

GridLayoutInfo gridInfo = std::get<GridLayoutInfo>(customZoneSet.data.info);
Expand All @@ -1103,7 +1195,7 @@ namespace JSONHelpers
{
return std::nullopt;
}

result.data.name = customZoneSet.GetNamedString(L"name");

json::JsonObject infoJson = customZoneSet.GetNamedObject(L"info");
Expand Down
4 changes: 4 additions & 0 deletions src/modules/fancyzones/lib/JsonHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ namespace JSONHelpers

struct AppZoneHistoryData
{
std::map<DWORD, HWND> processIdToHandleMap; // Maps process id(DWORD) of application to zoned window handle(HWND)

std::wstring zoneSetUuid;
std::wstring deviceId;
std::vector<int> zoneIndexSet;
Expand Down Expand Up @@ -242,6 +244,8 @@ namespace JSONHelpers
void UpdatePrimaryDesktopData(const std::wstring& desktopId);
void RemoveDeletedDesktops(const std::vector<std::wstring>& activeDesktops);

bool IsAnotherWindowOfApplicationInstanceZoned(HWND window) const;
void UpdateProcessIdToHandleMap(HWND window);
std::vector<int> 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<int>& zoneIndexSet);
Expand Down

0 comments on commit 3d619f1

Please sign in to comment.