mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-01-18 22:43:31 +08:00
[AlwaysOnTop] Improve performance and power consumption (#15993)
This commit is contained in:
parent
ddf96e28b8
commit
3e7b04891d
3
.github/actions/spell-check/expect.txt
vendored
3
.github/actions/spell-check/expect.txt
vendored
@ -488,6 +488,7 @@ DQTYPE
|
||||
DRAWFRAME
|
||||
drawingcolor
|
||||
dreamsofameaningfullife
|
||||
DRect
|
||||
drivedetectionwarning
|
||||
dshow
|
||||
dst
|
||||
@ -1194,6 +1195,7 @@ mdpreviewhandler
|
||||
MEDIASUBTYPE
|
||||
mediatype
|
||||
Melman
|
||||
memcmp
|
||||
memcpy
|
||||
memset
|
||||
MENUITEMINFO
|
||||
@ -2103,6 +2105,7 @@ uap
|
||||
udit
|
||||
Udp
|
||||
uefi
|
||||
UHash
|
||||
UIA
|
||||
Uid
|
||||
uint
|
||||
|
@ -1,6 +1,18 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "FrameDrawer.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
size_t D2DRectUHash(D2D1_SIZE_U rect)
|
||||
{
|
||||
using pod_repr_t = uint64_t;
|
||||
static_assert(sizeof(D2D1_SIZE_U) == sizeof(pod_repr_t));
|
||||
std::hash<pod_repr_t> hasher{};
|
||||
return hasher(*reinterpret_cast<const pod_repr_t*>(&rect));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<FrameDrawer> FrameDrawer::Create(HWND window)
|
||||
{
|
||||
auto self = std::make_unique<FrameDrawer>(window);
|
||||
@ -12,63 +24,55 @@ std::unique_ptr<FrameDrawer> FrameDrawer::Create(HWND window)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FrameDrawer::FrameDrawer(FrameDrawer&& other) :
|
||||
m_window(other.m_window),
|
||||
m_renderTarget(std::move(other.m_renderTarget)),
|
||||
m_sceneRect(std::move(other.m_sceneRect)),
|
||||
m_renderThread(std::move(m_renderThread))
|
||||
{
|
||||
}
|
||||
|
||||
FrameDrawer::FrameDrawer(HWND window) :
|
||||
m_window(window), m_renderTarget(nullptr)
|
||||
m_window(window)
|
||||
{
|
||||
}
|
||||
|
||||
FrameDrawer::~FrameDrawer()
|
||||
bool FrameDrawer::CreateRenderTargets(const RECT& clientRect)
|
||||
{
|
||||
m_abortThread = true;
|
||||
m_renderThread.join();
|
||||
HRESULT hr;
|
||||
|
||||
if (m_renderTarget)
|
||||
constexpr float DPI = 96.f; // Always using the default in DPI-aware mode
|
||||
const auto renderTargetProperties = D2D1::RenderTargetProperties(
|
||||
D2D1_RENDER_TARGET_TYPE_DEFAULT,
|
||||
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
|
||||
DPI,
|
||||
DPI);
|
||||
|
||||
const auto renderTargetSize = D2D1::SizeU(clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
|
||||
const auto rectHash = D2DRectUHash(renderTargetSize);
|
||||
if (m_renderTarget && rectHash == m_renderTargetSizeHash)
|
||||
{
|
||||
m_renderTarget->Release();
|
||||
// Already at the desired size -> do nothing
|
||||
return true;
|
||||
}
|
||||
|
||||
m_renderTarget = nullptr;
|
||||
|
||||
const auto hwndRenderTargetProperties = D2D1::HwndRenderTargetProperties(m_window, renderTargetSize, D2D1_PRESENT_OPTIONS_NONE);
|
||||
|
||||
hr = GetD2DFactory()->CreateHwndRenderTarget(renderTargetProperties, hwndRenderTargetProperties, m_renderTarget.put());
|
||||
|
||||
if (!SUCCEEDED(hr) || !m_renderTarget)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_renderTargetSizeHash = rectHash;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FrameDrawer::Init()
|
||||
{
|
||||
RECT clientRect;
|
||||
|
||||
// Obtain the size of the drawing area.
|
||||
if (!GetClientRect(m_window, &clientRect))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
// Create a Direct2D render target
|
||||
// We should always use the DPI value of 96 since we're running in DPI aware mode
|
||||
auto renderTargetProperties = D2D1::RenderTargetProperties(
|
||||
D2D1_RENDER_TARGET_TYPE_DEFAULT,
|
||||
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
|
||||
96.f,
|
||||
96.f);
|
||||
|
||||
auto renderTargetSize = D2D1::SizeU(clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
|
||||
auto hwndRenderTargetProperties = D2D1::HwndRenderTargetProperties(m_window, renderTargetSize);
|
||||
|
||||
hr = GetD2DFactory()->CreateHwndRenderTarget(renderTargetProperties, hwndRenderTargetProperties, &m_renderTarget);
|
||||
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_renderThread = std::thread([this]() { RenderLoop(); });
|
||||
|
||||
return true;
|
||||
return CreateRenderTargets(clientRect);
|
||||
}
|
||||
|
||||
void FrameDrawer::Hide()
|
||||
@ -79,19 +83,64 @@ void FrameDrawer::Hide()
|
||||
void FrameDrawer::Show()
|
||||
{
|
||||
ShowWindow(m_window, SW_SHOWNA);
|
||||
Render();
|
||||
}
|
||||
|
||||
void FrameDrawer::SetBorderRect(RECT windowRect, COLORREF color, float thickness)
|
||||
{
|
||||
std::unique_lock lock(m_mutex);
|
||||
|
||||
auto borderColor = ConvertColor(color);
|
||||
|
||||
m_sceneRect = DrawableRect{
|
||||
const auto newSceneRect = DrawableRect{
|
||||
.rect = ConvertRect(windowRect),
|
||||
.borderColor = borderColor,
|
||||
.borderColor = ConvertColor(color),
|
||||
.thickness = thickness
|
||||
};
|
||||
|
||||
const bool colorUpdated = std::memcmp(&m_sceneRect.borderColor, &newSceneRect.borderColor, sizeof(newSceneRect.borderColor));
|
||||
const bool thicknessUpdated = m_sceneRect.thickness != newSceneRect.thickness;
|
||||
const bool needsRedraw = colorUpdated || thicknessUpdated;
|
||||
|
||||
RECT clientRect;
|
||||
|
||||
if (!GetClientRect(m_window, &clientRect))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_sceneRect = newSceneRect;
|
||||
|
||||
const auto renderTargetSize = D2D1::SizeU(clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
|
||||
|
||||
const auto rectHash = D2DRectUHash(renderTargetSize);
|
||||
|
||||
const bool atTheDesiredSize = (rectHash == m_renderTargetSizeHash) && m_renderTarget;
|
||||
if (!atTheDesiredSize)
|
||||
{
|
||||
const bool resizeOk = m_renderTarget && SUCCEEDED(m_renderTarget->Resize(renderTargetSize));
|
||||
if (!resizeOk)
|
||||
{
|
||||
if (!CreateRenderTargets(clientRect))
|
||||
{
|
||||
Logger::error(L"Failed to create render targets");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_renderTargetSizeHash = rectHash;
|
||||
}
|
||||
}
|
||||
|
||||
if (colorUpdated)
|
||||
{
|
||||
m_borderBrush = nullptr;
|
||||
if (m_renderTarget)
|
||||
{
|
||||
m_renderTarget->CreateSolidColorBrush(m_sceneRect.borderColor, m_borderBrush.put());
|
||||
}
|
||||
}
|
||||
|
||||
if (!atTheDesiredSize || needsRedraw)
|
||||
{
|
||||
Render();
|
||||
}
|
||||
}
|
||||
|
||||
ID2D1Factory* FrameDrawer::GetD2DFactory()
|
||||
@ -127,46 +176,18 @@ D2D1_RECT_F FrameDrawer::ConvertRect(RECT rect)
|
||||
return D2D1::RectF((float)rect.left, (float)rect.top, (float)rect.right, (float)rect.bottom);
|
||||
}
|
||||
|
||||
FrameDrawer::RenderResult FrameDrawer::Render()
|
||||
void FrameDrawer::Render()
|
||||
{
|
||||
std::unique_lock lock(m_mutex);
|
||||
|
||||
if (!m_renderTarget)
|
||||
{
|
||||
return RenderResult::Failed;
|
||||
}
|
||||
|
||||
return;
|
||||
m_renderTarget->BeginDraw();
|
||||
|
||||
// Draw backdrop
|
||||
m_renderTarget->Clear(D2D1::ColorF(0.f, 0.f, 0.f, 0.f));
|
||||
|
||||
ID2D1SolidColorBrush* borderBrush = nullptr;
|
||||
m_renderTarget->CreateSolidColorBrush(m_sceneRect.borderColor, &borderBrush);
|
||||
|
||||
if (borderBrush)
|
||||
if (m_borderBrush)
|
||||
{
|
||||
m_renderTarget->DrawRectangle(m_sceneRect.rect, borderBrush, m_sceneRect.thickness);
|
||||
borderBrush->Release();
|
||||
m_renderTarget->DrawRectangle(m_sceneRect.rect, m_borderBrush.get(), m_sceneRect.thickness);
|
||||
}
|
||||
|
||||
// The lock must be released here, as EndDraw() will wait for vertical sync
|
||||
lock.unlock();
|
||||
|
||||
m_renderTarget->EndDraw();
|
||||
return RenderResult::Ok;
|
||||
}
|
||||
|
||||
void FrameDrawer::RenderLoop()
|
||||
{
|
||||
while (!m_abortThread)
|
||||
{
|
||||
auto result = Render();
|
||||
if (result == RenderResult::Failed)
|
||||
{
|
||||
Logger::error("Render failed");
|
||||
Hide();
|
||||
m_abortThread = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <d2d1.h>
|
||||
#include <winrt/base.h>
|
||||
#include <dwrite.h>
|
||||
|
||||
class FrameDrawer
|
||||
@ -10,8 +12,7 @@ public:
|
||||
static std::unique_ptr<FrameDrawer> Create(HWND window);
|
||||
|
||||
FrameDrawer(HWND window);
|
||||
FrameDrawer(FrameDrawer&& other);
|
||||
~FrameDrawer();
|
||||
FrameDrawer(FrameDrawer&& other) = default;
|
||||
|
||||
bool Init();
|
||||
|
||||
@ -20,6 +21,8 @@ public:
|
||||
void SetBorderRect(RECT windowRect, COLORREF color, float thickness);
|
||||
|
||||
private:
|
||||
bool CreateRenderTargets(const RECT& clientRect);
|
||||
|
||||
struct DrawableRect
|
||||
{
|
||||
D2D1_RECT_F rect;
|
||||
@ -27,25 +30,15 @@ private:
|
||||
float thickness;
|
||||
};
|
||||
|
||||
enum struct RenderResult
|
||||
{
|
||||
Ok,
|
||||
Failed,
|
||||
};
|
||||
|
||||
static ID2D1Factory* GetD2DFactory();
|
||||
static IDWriteFactory* GetWriteFactory();
|
||||
static D2D1_COLOR_F ConvertColor(COLORREF color);
|
||||
static D2D1_RECT_F ConvertRect(RECT rect);
|
||||
RenderResult Render();
|
||||
void RenderLoop();
|
||||
void Render();
|
||||
|
||||
HWND m_window = nullptr;
|
||||
ID2D1HwndRenderTarget* m_renderTarget = nullptr;
|
||||
|
||||
std::mutex m_mutex;
|
||||
DrawableRect m_sceneRect;
|
||||
|
||||
std::atomic<bool> m_abortThread = false;
|
||||
std::thread m_renderThread;
|
||||
size_t m_renderTargetSizeHash = {};
|
||||
winrt::com_ptr<ID2D1HwndRenderTarget> m_renderTarget;
|
||||
winrt::com_ptr<ID2D1SolidColorBrush> m_borderBrush;
|
||||
DrawableRect m_sceneRect = {};
|
||||
};
|
@ -31,18 +31,10 @@ std::optional<RECT> GetFrameRect(HWND window)
|
||||
}
|
||||
|
||||
WindowBorder::WindowBorder(HWND window) :
|
||||
SettingsObserver({SettingId::FrameColor, SettingId::FrameThickness, SettingId::FrameAccentColor }),
|
||||
m_window(nullptr),
|
||||
m_trackingWindow(window),
|
||||
m_frameDrawer(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
WindowBorder::WindowBorder(WindowBorder&& other) :
|
||||
SettingsObserver({ SettingId::FrameColor, SettingId::FrameThickness, SettingId::FrameAccentColor }),
|
||||
m_window(other.m_window),
|
||||
m_trackingWindow(other.m_trackingWindow),
|
||||
m_frameDrawer(std::move(other.m_frameDrawer))
|
||||
m_window(nullptr),
|
||||
m_trackingWindow(window),
|
||||
m_frameDrawer(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@ -72,6 +64,12 @@ std::unique_ptr<WindowBorder> WindowBorder::Create(HWND window, HINSTANCE hinsta
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr uint32_t REFRESH_BORDER_TIMER_ID = 123;
|
||||
constexpr uint32_t REFRESH_BORDER_INTERVAL = 100;
|
||||
}
|
||||
|
||||
bool WindowBorder::Init(HINSTANCE hinstance)
|
||||
{
|
||||
if (!m_trackingWindow)
|
||||
@ -136,6 +134,8 @@ bool WindowBorder::Init(HINSTANCE hinstance)
|
||||
|
||||
UpdateBorderProperties();
|
||||
m_frameDrawer->Show();
|
||||
m_timer_id = SetTimer(m_window, REFRESH_BORDER_TIMER_ID, REFRESH_BORDER_INTERVAL, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -154,7 +154,7 @@ void WindowBorder::UpdateBorderPosition() const
|
||||
}
|
||||
|
||||
RECT rect = rectOpt.value();
|
||||
SetWindowPos(m_window, m_trackingWindow, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOREDRAW);
|
||||
SetWindowPos(m_window, m_trackingWindow, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOREDRAW | SWP_NOACTIVATE);
|
||||
}
|
||||
|
||||
void WindowBorder::UpdateBorderProperties() const
|
||||
@ -170,7 +170,9 @@ void WindowBorder::UpdateBorderProperties() const
|
||||
return;
|
||||
}
|
||||
|
||||
RECT windowRect = windowRectOpt.value();
|
||||
const RECT windowRect = windowRectOpt.value();
|
||||
SetWindowPos(m_window, m_trackingWindow, windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, SWP_NOREDRAW | SWP_NOACTIVATE);
|
||||
|
||||
RECT frameRect{ 0, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top };
|
||||
|
||||
COLORREF color;
|
||||
@ -192,8 +194,22 @@ LRESULT WindowBorder::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexce
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_TIMER:
|
||||
{
|
||||
switch (wparam)
|
||||
{
|
||||
case REFRESH_BORDER_TIMER_ID:
|
||||
KillTimer(m_window, m_timer_id);
|
||||
m_timer_id = SetTimer(m_window, REFRESH_BORDER_TIMER_ID, REFRESH_BORDER_INTERVAL, nullptr);
|
||||
UpdateBorderPosition();
|
||||
UpdateBorderProperties();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_NCDESTROY:
|
||||
{
|
||||
KillTimer(m_window, m_timer_id);
|
||||
::DefWindowProc(m_window, message, wparam, lparam);
|
||||
SetWindowLongPtr(m_window, GWLP_USERDATA, 0);
|
||||
}
|
||||
@ -231,7 +247,7 @@ void WindowBorder::SettingsUpdate(SettingId id)
|
||||
UpdateBorderProperties();
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case SettingId::FrameColor:
|
||||
{
|
||||
UpdateBorderProperties();
|
||||
@ -246,5 +262,4 @@ void WindowBorder::SettingsUpdate(SettingId id)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ class FrameDrawer;
|
||||
class WindowBorder : public SettingsObserver
|
||||
{
|
||||
WindowBorder(HWND window);
|
||||
WindowBorder(WindowBorder&& other);
|
||||
|
||||
WindowBorder(WindowBorder&& other) = default;
|
||||
|
||||
public:
|
||||
static std::unique_ptr<WindowBorder> Create(HWND window, HINSTANCE hinstance);
|
||||
~WindowBorder();
|
||||
@ -32,8 +32,9 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
HWND m_window;
|
||||
HWND m_trackingWindow;
|
||||
UINT_PTR m_timer_id = {};
|
||||
HWND m_window = {};
|
||||
HWND m_trackingWindow = {};
|
||||
std::unique_ptr<FrameDrawer> m_frameDrawer;
|
||||
|
||||
LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept;
|
||||
|
@ -4,4 +4,7 @@
|
||||
#include <winrt/base.h>
|
||||
#include <wil/resource.h>
|
||||
#include <ProjectTelemetry.h>
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/logger/logger.h>
|
||||
|
||||
#include <functional>
|
||||
#include <array>
|
Loading…
Reference in New Issue
Block a user