[FancyZones] Use a window pool for zone windows (#9625)

* Implemented and tested WindowPool

* Added mutex

* Add logs to WindowPool

* Fix a potential thread safety bug
This commit is contained in:
Ivan Stošić 2021-03-10 14:50:40 +01:00 committed by GitHub
parent e586a7ad64
commit 72da703f0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -25,6 +25,81 @@ namespace NonLocalizable
using namespace FancyZonesUtils; 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> struct ZoneWindow : public winrt::implements<ZoneWindow, IZoneWindow>
{ {
public: public:
@ -78,7 +153,7 @@ private:
winrt::com_ptr<IZoneWindowHost> m_host; winrt::com_ptr<IZoneWindowHost> m_host;
HMONITOR m_monitor{}; HMONITOR m_monitor{};
std::wstring m_uniqueId; // Parsed deviceId + resolution + virtualDesktopId 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{}; HWND m_windowMoveSize{};
winrt::com_ptr<IZoneSet> m_activeZoneSet; winrt::com_ptr<IZoneSet> m_activeZoneSet;
std::vector<winrt::com_ptr<IZoneSet>> m_zoneSets; std::vector<winrt::com_ptr<IZoneSet>> m_zoneSets;
@ -104,6 +179,7 @@ ZoneWindow::ZoneWindow(HINSTANCE hinstance)
ZoneWindow::~ZoneWindow() ZoneWindow::~ZoneWindow()
{ {
windowPool.FreeZoneWindow(m_window);
} }
bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, const std::wstring& parentUniqueId) bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, const std::wstring& parentUniqueId)
@ -130,21 +206,14 @@ bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monit
m_uniqueId = uniqueId; m_uniqueId = uniqueId;
InitializeZoneSets(parentUniqueId); InitializeZoneSets(parentUniqueId);
m_window = wil::unique_hwnd{ m_window = windowPool.NewZoneWindow(workAreaRect, hinstance, this);
CreateWindowExW(WS_EX_TOOLWINDOW, NonLocalizable::ToolWindowClassName, L"", WS_POPUP, workAreaRect.left(), workAreaRect.top(), workAreaRect.width(), workAreaRect.height(), nullptr, nullptr, hinstance, this)
};
if (!m_window) if (!m_window)
{ {
return false; return false;
} }
MakeWindowTransparent(m_window.get()); m_zoneWindowDrawing = std::make_unique<ZoneWindowDrawing>(m_window);
// 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());
return true; return true;
} }
@ -162,7 +231,7 @@ IFACEMETHODIMP ZoneWindow::MoveSizeUpdate(POINT const& ptScreen, bool dragEnable
{ {
bool redraw = false; bool redraw = false;
POINT ptClient = ptScreen; POINT ptClient = ptScreen;
MapWindowPoints(nullptr, m_window.get(), &ptClient, 1); MapWindowPoints(nullptr, m_window, &ptClient, 1);
if (dragEnabled) if (dragEnabled)
{ {
@ -212,8 +281,8 @@ IFACEMETHODIMP ZoneWindow::MoveSizeEnd(HWND window, POINT const& ptScreen) noexc
if (m_activeZoneSet) if (m_activeZoneSet)
{ {
POINT ptClient = ptScreen; POINT ptClient = ptScreen;
MapWindowPoints(nullptr, m_window.get(), &ptClient, 1); MapWindowPoints(nullptr, m_window, &ptClient, 1);
m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window.get(), m_highlightZone); m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window, m_highlightZone);
if (FancyZonesUtils::HasNoVisibleOwner(window)) if (FancyZonesUtils::HasNoVisibleOwner(window))
{ {
@ -238,7 +307,7 @@ ZoneWindow::MoveWindowIntoZoneByIndexSet(HWND window, const std::vector<size_t>&
{ {
if (m_activeZoneSet) if (m_activeZoneSet)
{ {
m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window.get(), indexSet); m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window, indexSet);
} }
} }
@ -247,7 +316,7 @@ ZoneWindow::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, boo
{ {
if (m_activeZoneSet) 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)) if (FancyZonesUtils::HasNoVisibleOwner(window))
{ {
@ -264,7 +333,7 @@ ZoneWindow::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode,
{ {
if (m_activeZoneSet) if (m_activeZoneSet)
{ {
if (m_activeZoneSet->MoveWindowIntoZoneByDirectionAndPosition(window, m_window.get(), vkCode, cycle)) if (m_activeZoneSet->MoveWindowIntoZoneByDirectionAndPosition(window, m_window, vkCode, cycle))
{ {
SaveWindowProcessToZoneIndex(window); SaveWindowProcessToZoneIndex(window);
return true; return true;
@ -278,7 +347,7 @@ ZoneWindow::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noexce
{ {
if (m_activeZoneSet) if (m_activeZoneSet)
{ {
if (m_activeZoneSet->ExtendWindowByDirectionAndPosition(window, m_window.get(), vkCode)) if (m_activeZoneSet->ExtendWindowByDirectionAndPosition(window, m_window, vkCode))
{ {
SaveWindowProcessToZoneIndex(window); SaveWindowProcessToZoneIndex(window);
return true; return true;
@ -294,7 +363,7 @@ ZoneWindow::CycleActiveZoneSet(DWORD wparam) noexcept
if (m_windowMoveSize) if (m_windowMoveSize)
{ {
InvalidateRect(m_window.get(), nullptr, true); InvalidateRect(m_window, nullptr, true);
} }
} }
@ -320,7 +389,7 @@ ZoneWindow::SaveWindowProcessToZoneIndex(HWND window) noexcept
IFACEMETHODIMP_(void) IFACEMETHODIMP_(void)
ZoneWindow::ShowZoneWindow() noexcept ZoneWindow::ShowZoneWindow() noexcept
{ {
auto window = m_window.get(); auto window = m_window;
if (!window) if (!window)
{ {
return; return;
@ -461,8 +530,8 @@ LRESULT ZoneWindow::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept
{ {
case WM_NCDESTROY: case WM_NCDESTROY:
{ {
::DefWindowProc(m_window.get(), message, wparam, lparam); ::DefWindowProc(m_window, message, wparam, lparam);
SetWindowLongPtr(m_window.get(), GWLP_USERDATA, 0); SetWindowLongPtr(m_window, GWLP_USERDATA, 0);
} }
break; break;
@ -477,7 +546,7 @@ LRESULT ZoneWindow::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept
default: default:
{ {
return DefWindowProc(m_window.get(), message, wparam, lparam); return DefWindowProc(m_window, message, wparam, lparam);
} }
} }
return 0; return 0;