Skip to content

Commit

Permalink
[FancyZones] Use a window pool for zone windows (#9625)
Browse files Browse the repository at this point in the history
* Implemented and tested WindowPool

* Added mutex

* Add logs to WindowPool

* Fix a potential thread safety bug
  • Loading branch information
ivan100sic committed Mar 10, 2021
1 parent e586a7a commit 72da703
Showing 1 changed file with 91 additions and 22 deletions.
113 changes: 91 additions & 22 deletions src/modules/fancyzones/lib/ZoneWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,81 @@ namespace NonLocalizable

using namespace FancyZonesUtils;

struct ZoneWindow;

namespace
{
// The reason for using this class is the need to call ShowWindow(window, SW_SHOWNORMAL); on each
// newly created window for it to be displayed properly. The call sometimes has side effects when
// a fullscreen app is running, and happens when the resolution change event is triggered
// (e.g. when running some games).
// This class will serve as a pool of windows for which this call was already done.
class WindowPool
{
std::vector<HWND> m_pool;
std::mutex m_mutex;

HWND ExtractWindow()
{
std::unique_lock lock(m_mutex);

if (m_pool.empty())
{
return NULL;
}

HWND window = m_pool.back();
m_pool.pop_back();
return window;
}

public:

HWND NewZoneWindow(Rect position, HINSTANCE hinstance, ZoneWindow* owner)
{
HWND windowFromPool = ExtractWindow();
if (windowFromPool == NULL)
{
HWND window = CreateWindowExW(WS_EX_TOOLWINDOW, NonLocalizable::ToolWindowClassName, L"", WS_POPUP, position.left(), position.top(), position.width(), position.height(), nullptr, nullptr, hinstance, owner);
Logger::info("Creating new zone window, hWnd = {}", (void*)window);
MakeWindowTransparent(window);

// According to ShowWindow docs, we must call it with SW_SHOWNORMAL the first time
ShowWindow(window, SW_SHOWNORMAL);
ShowWindow(window, SW_HIDE);
return window;
}
else
{
Logger::info("Reusing zone window from pool, hWnd = {}", (void*)windowFromPool);
SetWindowLongPtrW(windowFromPool, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(owner));
MoveWindow(windowFromPool, position.left(), position.top(), position.width(), position.height(), TRUE);
return windowFromPool;
}
}

void FreeZoneWindow(HWND window)
{
Logger::info("Freeing zone window, hWnd = {}", (void*)window);
SetWindowLongPtrW(window, GWLP_USERDATA, 0);
ShowWindow(window, SW_HIDE);

std::unique_lock lock(m_mutex);
m_pool.push_back(window);
}

~WindowPool()
{
for (HWND window : m_pool)
{
DestroyWindow(window);
}
}
};

WindowPool windowPool;
}

struct ZoneWindow : public winrt::implements<ZoneWindow, IZoneWindow>
{
public:
Expand Down Expand Up @@ -78,7 +153,7 @@ struct ZoneWindow : public winrt::implements<ZoneWindow, IZoneWindow>
winrt::com_ptr<IZoneWindowHost> m_host;
HMONITOR m_monitor{};
std::wstring m_uniqueId; // Parsed deviceId + resolution + virtualDesktopId
wil::unique_hwnd m_window{}; // Hidden tool window used to represent current monitor desktop work area.
HWND m_window{}; // Hidden tool window used to represent current monitor desktop work area.
HWND m_windowMoveSize{};
winrt::com_ptr<IZoneSet> m_activeZoneSet;
std::vector<winrt::com_ptr<IZoneSet>> m_zoneSets;
Expand All @@ -104,6 +179,7 @@ ZoneWindow::ZoneWindow(HINSTANCE hinstance)

ZoneWindow::~ZoneWindow()
{
windowPool.FreeZoneWindow(m_window);
}

bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, const std::wstring& parentUniqueId)
Expand All @@ -130,21 +206,14 @@ bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monit
m_uniqueId = uniqueId;
InitializeZoneSets(parentUniqueId);

m_window = wil::unique_hwnd{
CreateWindowExW(WS_EX_TOOLWINDOW, NonLocalizable::ToolWindowClassName, L"", WS_POPUP, workAreaRect.left(), workAreaRect.top(), workAreaRect.width(), workAreaRect.height(), nullptr, nullptr, hinstance, this)
};
m_window = windowPool.NewZoneWindow(workAreaRect, hinstance, this);

if (!m_window)
{
return false;
}

MakeWindowTransparent(m_window.get());
// According to ShowWindow docs, we must call it with SW_SHOWNORMAL the first time
ShowWindow(m_window.get(), SW_SHOWNORMAL);
ShowWindow(m_window.get(), SW_HIDE);

m_zoneWindowDrawing = std::make_unique<ZoneWindowDrawing>(m_window.get());
m_zoneWindowDrawing = std::make_unique<ZoneWindowDrawing>(m_window);

return true;
}
Expand All @@ -162,7 +231,7 @@ IFACEMETHODIMP ZoneWindow::MoveSizeUpdate(POINT const& ptScreen, bool dragEnable
{
bool redraw = false;
POINT ptClient = ptScreen;
MapWindowPoints(nullptr, m_window.get(), &ptClient, 1);
MapWindowPoints(nullptr, m_window, &ptClient, 1);

if (dragEnabled)
{
Expand Down Expand Up @@ -212,8 +281,8 @@ IFACEMETHODIMP ZoneWindow::MoveSizeEnd(HWND window, POINT const& ptScreen) noexc
if (m_activeZoneSet)
{
POINT ptClient = ptScreen;
MapWindowPoints(nullptr, m_window.get(), &ptClient, 1);
m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window.get(), m_highlightZone);
MapWindowPoints(nullptr, m_window, &ptClient, 1);
m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window, m_highlightZone);

if (FancyZonesUtils::HasNoVisibleOwner(window))
{
Expand All @@ -238,7 +307,7 @@ ZoneWindow::MoveWindowIntoZoneByIndexSet(HWND window, const std::vector<size_t>&
{
if (m_activeZoneSet)
{
m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window.get(), indexSet);
m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window, indexSet);
}
}

Expand All @@ -247,7 +316,7 @@ ZoneWindow::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, boo
{
if (m_activeZoneSet)
{
if (m_activeZoneSet->MoveWindowIntoZoneByDirectionAndIndex(window, m_window.get(), vkCode, cycle))
if (m_activeZoneSet->MoveWindowIntoZoneByDirectionAndIndex(window, m_window, vkCode, cycle))
{
if (FancyZonesUtils::HasNoVisibleOwner(window))
{
Expand All @@ -264,7 +333,7 @@ ZoneWindow::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode,
{
if (m_activeZoneSet)
{
if (m_activeZoneSet->MoveWindowIntoZoneByDirectionAndPosition(window, m_window.get(), vkCode, cycle))
if (m_activeZoneSet->MoveWindowIntoZoneByDirectionAndPosition(window, m_window, vkCode, cycle))
{
SaveWindowProcessToZoneIndex(window);
return true;
Expand All @@ -278,7 +347,7 @@ ZoneWindow::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noexce
{
if (m_activeZoneSet)
{
if (m_activeZoneSet->ExtendWindowByDirectionAndPosition(window, m_window.get(), vkCode))
if (m_activeZoneSet->ExtendWindowByDirectionAndPosition(window, m_window, vkCode))
{
SaveWindowProcessToZoneIndex(window);
return true;
Expand All @@ -294,7 +363,7 @@ ZoneWindow::CycleActiveZoneSet(DWORD wparam) noexcept

if (m_windowMoveSize)
{
InvalidateRect(m_window.get(), nullptr, true);
InvalidateRect(m_window, nullptr, true);
}
}

Expand All @@ -320,7 +389,7 @@ ZoneWindow::SaveWindowProcessToZoneIndex(HWND window) noexcept
IFACEMETHODIMP_(void)
ZoneWindow::ShowZoneWindow() noexcept
{
auto window = m_window.get();
auto window = m_window;
if (!window)
{
return;
Expand Down Expand Up @@ -461,8 +530,8 @@ LRESULT ZoneWindow::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept
{
case WM_NCDESTROY:
{
::DefWindowProc(m_window.get(), message, wparam, lparam);
SetWindowLongPtr(m_window.get(), GWLP_USERDATA, 0);
::DefWindowProc(m_window, message, wparam, lparam);
SetWindowLongPtr(m_window, GWLP_USERDATA, 0);
}
break;

Expand All @@ -477,7 +546,7 @@ LRESULT ZoneWindow::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept

default:
{
return DefWindowProc(m_window.get(), message, wparam, lparam);
return DefWindowProc(m_window, message, wparam, lparam);
}
}
return 0;
Expand Down

0 comments on commit 72da703

Please sign in to comment.