mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-19 09:40:21 +01:00
Compare commits
2 Commits
async-cpp-
...
dev/vanzue
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
158ada435d | ||
|
|
52bf804f57 |
@@ -16,6 +16,11 @@
|
|||||||
#include <trace.h>
|
#include <trace.h>
|
||||||
#include <WinHookEventIDs.h>
|
#include <WinHookEventIDs.h>
|
||||||
|
|
||||||
|
#include <dwmapi.h>
|
||||||
|
#include <ScalingUtils.h>
|
||||||
|
#include <WindowCornersUtil.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
namespace NonLocalizable
|
namespace NonLocalizable
|
||||||
{
|
{
|
||||||
@@ -31,6 +36,14 @@ bool isExcluded(HWND window)
|
|||||||
return check_excluded_app(window, processPath, AlwaysOnTopSettings::settings().excludedApps);
|
return check_excluded_app(window, processPath, AlwaysOnTopSettings::settings().excludedApps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool TryGetExtendedFrameBounds(HWND window, RECT& rect) noexcept
|
||||||
|
{
|
||||||
|
return DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS, &rect, sizeof(rect)) == S_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AlwaysOnTop::AlwaysOnTop(bool useLLKH, DWORD mainThreadId) :
|
AlwaysOnTop::AlwaysOnTop(bool useLLKH, DWORD mainThreadId) :
|
||||||
SettingsObserver({SettingId::FrameEnabled, SettingId::Hotkey, SettingId::ExcludeApps}),
|
SettingsObserver({SettingId::FrameEnabled, SettingId::Hotkey, SettingId::ExcludeApps}),
|
||||||
m_hinstance(reinterpret_cast<HINSTANCE>(&__ImageBase)),
|
m_hinstance(reinterpret_cast<HINSTANCE>(&__ImageBase)),
|
||||||
@@ -48,6 +61,13 @@ AlwaysOnTop::AlwaysOnTop(bool useLLKH, DWORD mainThreadId) :
|
|||||||
AlwaysOnTopSettings::instance().InitFileWatcher();
|
AlwaysOnTopSettings::instance().InitFileWatcher();
|
||||||
AlwaysOnTopSettings::instance().LoadSettings();
|
AlwaysOnTopSettings::instance().LoadSettings();
|
||||||
|
|
||||||
|
m_dimOverlay = std::make_unique<DimOverlay>();
|
||||||
|
if (!m_dimOverlay->Initialize(m_hinstance))
|
||||||
|
{
|
||||||
|
Logger::warn("Failed to initialize AlwaysOnTop dim overlay.");
|
||||||
|
m_dimOverlay.reset();
|
||||||
|
}
|
||||||
|
|
||||||
RegisterHotkey();
|
RegisterHotkey();
|
||||||
RegisterLLKH();
|
RegisterLLKH();
|
||||||
|
|
||||||
@@ -124,6 +144,7 @@ void AlwaysOnTop::SettingsUpdate(SettingId id)
|
|||||||
iter.second = nullptr;
|
iter.second = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UpdateDimOverlay();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SettingId::ExcludeApps:
|
case SettingId::ExcludeApps:
|
||||||
@@ -142,6 +163,7 @@ void AlwaysOnTop::SettingsUpdate(SettingId id)
|
|||||||
{
|
{
|
||||||
m_topmostWindows.erase(window);
|
m_topmostWindows.erase(window);
|
||||||
}
|
}
|
||||||
|
UpdateDimOverlay();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -216,17 +238,72 @@ void AlwaysOnTop::ProcessCommand(HWND window)
|
|||||||
{
|
{
|
||||||
soundType = Sound::Type::On;
|
soundType = Sound::Type::On;
|
||||||
AssignBorder(window);
|
AssignBorder(window);
|
||||||
|
MinimizeOtherWindows(window);
|
||||||
|
|
||||||
Trace::AlwaysOnTop::PinWindow();
|
Trace::AlwaysOnTop::PinWindow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateDimOverlay();
|
||||||
|
|
||||||
if (AlwaysOnTopSettings::settings().enableSound)
|
if (AlwaysOnTopSettings::settings().enableSound)
|
||||||
{
|
{
|
||||||
m_sound.Play(soundType);
|
m_sound.Play(soundType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AlwaysOnTop::MinimizeOtherWindows(HWND pinnedWindow)
|
||||||
|
{
|
||||||
|
struct EnumState
|
||||||
|
{
|
||||||
|
AlwaysOnTop* self{};
|
||||||
|
HWND pinned{};
|
||||||
|
};
|
||||||
|
|
||||||
|
EnumState state{ this, pinnedWindow };
|
||||||
|
EnumWindows([](HWND window, LPARAM lparam) -> BOOL {
|
||||||
|
auto* state = reinterpret_cast<EnumState*>(lparam);
|
||||||
|
if (!state || !state->self)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window == state->pinned)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsWindowVisible(window) || IsIconic(window))
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window == state->self->m_window)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->self->m_dimOverlay && window == state->self->m_dimOverlay->Hwnd())
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto exStyle = GetWindowLongPtr(window, GWL_EXSTYLE);
|
||||||
|
if ((exStyle & WS_EX_TOOLWINDOW) == WS_EX_TOOLWINDOW)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state->self->m_virtualDesktopUtils.IsWindowOnCurrentDesktop(window))
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowWindow(window, SW_MINIMIZE);
|
||||||
|
return TRUE;
|
||||||
|
}, reinterpret_cast<LPARAM>(&state));
|
||||||
|
}
|
||||||
|
|
||||||
void AlwaysOnTop::StartTrackingTopmostWindows()
|
void AlwaysOnTop::StartTrackingTopmostWindows()
|
||||||
{
|
{
|
||||||
using result_t = std::vector<HWND>;
|
using result_t = std::vector<HWND>;
|
||||||
@@ -262,6 +339,7 @@ void AlwaysOnTop::StartTrackingTopmostWindows()
|
|||||||
AssignBorder(window);
|
AssignBorder(window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AlwaysOnTop::AssignBorder(HWND window)
|
bool AlwaysOnTop::AssignBorder(HWND window)
|
||||||
@@ -430,11 +508,18 @@ void AlwaysOnTop::UnpinAll()
|
|||||||
|
|
||||||
m_topmostWindows.clear();
|
m_topmostWindows.clear();
|
||||||
m_windowOriginalLayeredState.clear();
|
m_windowOriginalLayeredState.clear();
|
||||||
|
|
||||||
|
UpdateDimOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlwaysOnTop::CleanUp()
|
void AlwaysOnTop::CleanUp()
|
||||||
{
|
{
|
||||||
UnpinAll();
|
UnpinAll();
|
||||||
|
if (m_dimOverlay)
|
||||||
|
{
|
||||||
|
m_dimOverlay->Terminate();
|
||||||
|
m_dimOverlay.reset();
|
||||||
|
}
|
||||||
if (m_window)
|
if (m_window)
|
||||||
{
|
{
|
||||||
DestroyWindow(m_window);
|
DestroyWindow(m_window);
|
||||||
@@ -492,11 +577,13 @@ bool AlwaysOnTop::IsTracked(HWND window) const noexcept
|
|||||||
|
|
||||||
void AlwaysOnTop::HandleWinHookEvent(WinHookEvent* data) noexcept
|
void AlwaysOnTop::HandleWinHookEvent(WinHookEvent* data) noexcept
|
||||||
{
|
{
|
||||||
if (!AlwaysOnTopSettings::settings().enableFrame || !data->hwnd)
|
if (!data->hwnd)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool frameEnabled = AlwaysOnTopSettings::settings().enableFrame;
|
||||||
|
|
||||||
std::vector<HWND> toErase{};
|
std::vector<HWND> toErase{};
|
||||||
for (const auto& [window, border] : m_topmostWindows)
|
for (const auto& [window, border] : m_topmostWindows)
|
||||||
{
|
{
|
||||||
@@ -520,23 +607,29 @@ void AlwaysOnTop::HandleWinHookEvent(WinHookEvent* data) noexcept
|
|||||||
{
|
{
|
||||||
case EVENT_OBJECT_LOCATIONCHANGE:
|
case EVENT_OBJECT_LOCATIONCHANGE:
|
||||||
{
|
{
|
||||||
auto iter = m_topmostWindows.find(data->hwnd);
|
if (frameEnabled)
|
||||||
if (iter != m_topmostWindows.end())
|
|
||||||
{
|
{
|
||||||
const auto& border = iter->second;
|
auto iter = m_topmostWindows.find(data->hwnd);
|
||||||
if (border)
|
if (iter != m_topmostWindows.end())
|
||||||
{
|
{
|
||||||
border->UpdateBorderPosition();
|
const auto& border = iter->second;
|
||||||
|
if (border)
|
||||||
|
{
|
||||||
|
border->UpdateBorderPosition();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EVENT_SYSTEM_MINIMIZESTART:
|
case EVENT_SYSTEM_MINIMIZESTART:
|
||||||
{
|
{
|
||||||
auto iter = m_topmostWindows.find(data->hwnd);
|
if (frameEnabled)
|
||||||
if (iter != m_topmostWindows.end())
|
|
||||||
{
|
{
|
||||||
m_topmostWindows[data->hwnd] = nullptr;
|
auto iter = m_topmostWindows.find(data->hwnd);
|
||||||
|
if (iter != m_topmostWindows.end())
|
||||||
|
{
|
||||||
|
m_topmostWindows[data->hwnd] = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -546,20 +639,26 @@ void AlwaysOnTop::HandleWinHookEvent(WinHookEvent* data) noexcept
|
|||||||
if (iter != m_topmostWindows.end())
|
if (iter != m_topmostWindows.end())
|
||||||
{
|
{
|
||||||
// pin border again, in some cases topmost flag stops working: https://github.com/microsoft/PowerToys/issues/17332
|
// pin border again, in some cases topmost flag stops working: https://github.com/microsoft/PowerToys/issues/17332
|
||||||
PinTopmostWindow(data->hwnd);
|
PinTopmostWindow(data->hwnd);
|
||||||
AssignBorder(data->hwnd);
|
if (frameEnabled)
|
||||||
|
{
|
||||||
|
AssignBorder(data->hwnd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EVENT_SYSTEM_MOVESIZEEND:
|
case EVENT_SYSTEM_MOVESIZEEND:
|
||||||
{
|
{
|
||||||
auto iter = m_topmostWindows.find(data->hwnd);
|
if (frameEnabled)
|
||||||
if (iter != m_topmostWindows.end())
|
|
||||||
{
|
{
|
||||||
const auto& border = iter->second;
|
auto iter = m_topmostWindows.find(data->hwnd);
|
||||||
if (border)
|
if (iter != m_topmostWindows.end())
|
||||||
{
|
{
|
||||||
border->UpdateBorderPosition();
|
const auto& border = iter->second;
|
||||||
|
if (border)
|
||||||
|
{
|
||||||
|
border->UpdateBorderPosition();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -573,7 +672,10 @@ void AlwaysOnTop::HandleWinHookEvent(WinHookEvent* data) noexcept
|
|||||||
GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED_LEARN_MORE),
|
GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED_LEARN_MORE),
|
||||||
GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED_DIALOG_DONT_SHOW_AGAIN));
|
GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED_DIALOG_DONT_SHOW_AGAIN));
|
||||||
}
|
}
|
||||||
RefreshBorders();
|
if (frameEnabled)
|
||||||
|
{
|
||||||
|
RefreshBorders();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EVENT_OBJECT_FOCUS:
|
case EVENT_OBJECT_FOCUS:
|
||||||
@@ -593,6 +695,8 @@ void AlwaysOnTop::HandleWinHookEvent(WinHookEvent* data) noexcept
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateDimOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlwaysOnTop::RefreshBorders()
|
void AlwaysOnTop::RefreshBorders()
|
||||||
@@ -614,6 +718,97 @@ void AlwaysOnTop::RefreshBorders()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateDimOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlwaysOnTop::UpdateDimOverlay()
|
||||||
|
{
|
||||||
|
if (!m_dimOverlay)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PinnedEntry
|
||||||
|
{
|
||||||
|
HWND window = nullptr;
|
||||||
|
HWND border = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<DimOverlayHole> holes;
|
||||||
|
std::vector<PinnedEntry> pinnedEntries;
|
||||||
|
holes.reserve(m_topmostWindows.size());
|
||||||
|
pinnedEntries.reserve(m_topmostWindows.size());
|
||||||
|
|
||||||
|
for (const auto& [window, border] : m_topmostWindows)
|
||||||
|
{
|
||||||
|
if (!IsWindow(window) || !IsPinned(window))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsWindowVisible(window) || IsIconic(window))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_virtualDesktopUtils.IsWindowOnCurrentDesktop(window))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
RECT rect{};
|
||||||
|
if (!TryGetExtendedFrameBounds(window, rect))
|
||||||
|
{
|
||||||
|
if (!GetWindowRect(window, &rect))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int radius = WindowCornerUtils::CornersRadius(window);
|
||||||
|
if (radius > 0)
|
||||||
|
{
|
||||||
|
const float scale = ScalingUtils::ScalingFactor(window);
|
||||||
|
radius = static_cast<int>(std::lround(radius * scale));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
radius = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
holes.push_back({ rect, radius });
|
||||||
|
pinnedEntries.push_back({ window, border ? border->Hwnd() : nullptr });
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool visible = !holes.empty();
|
||||||
|
m_dimOverlay->Update(std::move(holes), visible);
|
||||||
|
|
||||||
|
if (!visible)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HWND overlayHwnd = m_dimOverlay->Hwnd();
|
||||||
|
if (!overlayHwnd)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetWindowPos(overlayHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||||
|
|
||||||
|
for (const auto& entry : pinnedEntries)
|
||||||
|
{
|
||||||
|
if (entry.border)
|
||||||
|
{
|
||||||
|
SetWindowPos(entry.border, overlayHwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||||
|
SetWindowPos(entry.window, entry.border, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetWindowPos(entry.window, overlayHwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HWND AlwaysOnTop::ResolveTransparencyTargetWindow(HWND window)
|
HWND AlwaysOnTop::ResolveTransparencyTargetWindow(HWND window)
|
||||||
@@ -776,4 +971,4 @@ void AlwaysOnTop::RestoreWindowAlpha(HWND window)
|
|||||||
SetWindowLong(window, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
|
SetWindowLong(window, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <Settings.h>
|
#include <Settings.h>
|
||||||
#include <SettingsObserver.h>
|
#include <SettingsObserver.h>
|
||||||
#include <Sound.h>
|
#include <Sound.h>
|
||||||
#include <VirtualDesktopUtils.h>
|
#include <VirtualDesktopUtils.h>
|
||||||
#include <WindowBorder.h>
|
#include <WindowBorder.h>
|
||||||
|
#include <DimOverlay.h>
|
||||||
|
|
||||||
#include <common/hooks/WinHookEvent.h>
|
#include <common/hooks/WinHookEvent.h>
|
||||||
#include <common/notifications/NotificationUtil.h>
|
#include <common/notifications/NotificationUtil.h>
|
||||||
@@ -60,6 +62,8 @@ private:
|
|||||||
COLORREF colorKey = 0;
|
COLORREF colorKey = 0;
|
||||||
};
|
};
|
||||||
std::map<HWND, WindowLayeredState> m_windowOriginalLayeredState{};
|
std::map<HWND, WindowLayeredState> m_windowOriginalLayeredState{};
|
||||||
|
|
||||||
|
std::unique_ptr<DimOverlay> m_dimOverlay;
|
||||||
|
|
||||||
HANDLE m_hPinEvent;
|
HANDLE m_hPinEvent;
|
||||||
HANDLE m_hTerminateEvent;
|
HANDLE m_hTerminateEvent;
|
||||||
@@ -80,6 +84,7 @@ private:
|
|||||||
void SubscribeToEvents();
|
void SubscribeToEvents();
|
||||||
|
|
||||||
void ProcessCommand(HWND window);
|
void ProcessCommand(HWND window);
|
||||||
|
void MinimizeOtherWindows(HWND pinnedWindow);
|
||||||
void StartTrackingTopmostWindows();
|
void StartTrackingTopmostWindows();
|
||||||
void UnpinAll();
|
void UnpinAll();
|
||||||
void CleanUp();
|
void CleanUp();
|
||||||
@@ -92,6 +97,7 @@ private:
|
|||||||
bool UnpinTopmostWindow(HWND window) const noexcept;
|
bool UnpinTopmostWindow(HWND window) const noexcept;
|
||||||
bool AssignBorder(HWND window);
|
bool AssignBorder(HWND window);
|
||||||
void RefreshBorders();
|
void RefreshBorders();
|
||||||
|
void UpdateDimOverlay();
|
||||||
|
|
||||||
// Transparency methods
|
// Transparency methods
|
||||||
HWND ResolveTransparencyTargetWindow(HWND window);
|
HWND ResolveTransparencyTargetWindow(HWND window);
|
||||||
|
|||||||
@@ -121,6 +121,7 @@
|
|||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="AlwaysOnTop.cpp" />
|
<ClCompile Include="AlwaysOnTop.cpp" />
|
||||||
|
<ClCompile Include="DimOverlay.cpp" />
|
||||||
<ClCompile Include="FrameDrawer.cpp" />
|
<ClCompile Include="FrameDrawer.cpp" />
|
||||||
<ClCompile Include="main.cpp" />
|
<ClCompile Include="main.cpp" />
|
||||||
<ClCompile Include="pch.cpp">
|
<ClCompile Include="pch.cpp">
|
||||||
@@ -136,6 +137,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="AlwaysOnTop.h" />
|
<ClInclude Include="AlwaysOnTop.h" />
|
||||||
|
<ClInclude Include="DimOverlay.h" />
|
||||||
<ClInclude Include="FrameDrawer.h" />
|
<ClInclude Include="FrameDrawer.h" />
|
||||||
<ClInclude Include="Generated Files/resource.h" />
|
<ClInclude Include="Generated Files/resource.h" />
|
||||||
<ClInclude Include="ModuleConstants.h" />
|
<ClInclude Include="ModuleConstants.h" />
|
||||||
@@ -198,4 +200,4 @@
|
|||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -30,6 +30,9 @@
|
|||||||
<ClCompile Include="AlwaysOnTop.cpp">
|
<ClCompile Include="AlwaysOnTop.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="DimOverlay.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="FrameDrawer.cpp">
|
<ClCompile Include="FrameDrawer.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -79,6 +82,9 @@
|
|||||||
<ClInclude Include="AlwaysOnTop.h">
|
<ClInclude Include="AlwaysOnTop.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="DimOverlay.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="FrameDrawer.h">
|
<ClInclude Include="FrameDrawer.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -130,4 +136,4 @@
|
|||||||
<Filter>Resource Files</Filter>
|
<Filter>Resource Files</Filter>
|
||||||
</Image>
|
</Image>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
383
src/modules/alwaysontop/AlwaysOnTop/DimOverlay.cpp
Normal file
383
src/modules/alwaysontop/AlwaysOnTop/DimOverlay.cpp
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "DimOverlay.h"
|
||||||
|
|
||||||
|
#include <common/utils/MsWindowsSettings.h>
|
||||||
|
|
||||||
|
#include <DispatcherQueue.h>
|
||||||
|
#include <dwmapi.h>
|
||||||
|
#include <windows.ui.composition.interop.h>
|
||||||
|
#include <winrt/Windows.Foundation.Collections.h>
|
||||||
|
#include <winrt/Windows.UI.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace winrt
|
||||||
|
{
|
||||||
|
using namespace winrt::Windows::System;
|
||||||
|
using namespace winrt::Windows::UI::Composition;
|
||||||
|
using namespace winrt::Windows::UI::Composition::Desktop;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ABI
|
||||||
|
{
|
||||||
|
using namespace ABI::Windows::System;
|
||||||
|
using namespace ABI::Windows::UI::Composition::Desktop;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr wchar_t OverlayWindowClassName[] = L"AlwaysOnTop_DimOverlay";
|
||||||
|
constexpr int FadeDurationMs = 200;
|
||||||
|
|
||||||
|
winrt::Windows::UI::Color GetDefaultDimColor()
|
||||||
|
{
|
||||||
|
return winrt::Windows::UI::ColorHelper::FromArgb(160, 32, 32, 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DimOverlay::~DimOverlay()
|
||||||
|
{
|
||||||
|
Terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DimOverlay::Initialize(HINSTANCE hinstance)
|
||||||
|
{
|
||||||
|
m_hinstance = hinstance;
|
||||||
|
return CreateWindowAndVisuals();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DimOverlay::Terminate()
|
||||||
|
{
|
||||||
|
if (m_destroyed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_destroyed = true;
|
||||||
|
|
||||||
|
if (m_hwnd)
|
||||||
|
{
|
||||||
|
DestroyWindow(m_hwnd);
|
||||||
|
m_hwnd = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_hwndOwner)
|
||||||
|
{
|
||||||
|
DestroyWindow(m_hwndOwner);
|
||||||
|
m_hwndOwner = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND DimOverlay::Hwnd() const noexcept
|
||||||
|
{
|
||||||
|
return m_hwnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DimOverlay::Update(std::vector<DimOverlayHole> holes, bool visible)
|
||||||
|
{
|
||||||
|
if (m_destroyed || !m_dispatcherQueueController)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dispatcherQueue = m_dispatcherQueueController.DispatcherQueue();
|
||||||
|
bool enqueueSucceeded = dispatcherQueue.TryEnqueue([this, holes = std::move(holes), visible]() {
|
||||||
|
if (m_destroyed || !m_hwnd)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_lastHoles = holes;
|
||||||
|
UpdateWindowBounds();
|
||||||
|
UpdateRegion(holes);
|
||||||
|
SetVisible(visible);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!enqueueSucceeded)
|
||||||
|
{
|
||||||
|
Logger::error("Couldn't enqueue message to update the dim overlay.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CALLBACK DimOverlay::WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
||||||
|
{
|
||||||
|
auto thisRef = reinterpret_cast<DimOverlay*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
|
||||||
|
if ((thisRef == nullptr) && (message == WM_CREATE))
|
||||||
|
{
|
||||||
|
const auto createStruct = reinterpret_cast<LPCREATESTRUCT>(lparam);
|
||||||
|
thisRef = static_cast<DimOverlay*>(createStruct->lpCreateParams);
|
||||||
|
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(thisRef));
|
||||||
|
}
|
||||||
|
|
||||||
|
return thisRef ? thisRef->MessageHandler(hwnd, message, wparam, lparam) : DefWindowProc(hwnd, message, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT DimOverlay::MessageHandler(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
||||||
|
{
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
case WM_DISPLAYCHANGE:
|
||||||
|
UpdateWindowBounds();
|
||||||
|
UpdateRegion(m_lastHoles);
|
||||||
|
break;
|
||||||
|
case WM_NCHITTEST:
|
||||||
|
return HTTRANSPARENT;
|
||||||
|
case WM_ERASEBKGND:
|
||||||
|
return TRUE;
|
||||||
|
case WM_NCDESTROY:
|
||||||
|
SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProc(hwnd, message, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DimOverlay::CreateWindowAndVisuals()
|
||||||
|
{
|
||||||
|
WNDCLASS wc{};
|
||||||
|
if (!GetClassInfoW(m_hinstance, OverlayWindowClassName, &wc))
|
||||||
|
{
|
||||||
|
wc.lpfnWndProc = DimOverlay::WndProc;
|
||||||
|
wc.hInstance = m_hinstance;
|
||||||
|
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||||
|
wc.hbrBackground = static_cast<HBRUSH>(GetStockObject(NULL_BRUSH));
|
||||||
|
wc.lpszClassName = OverlayWindowClassName;
|
||||||
|
|
||||||
|
if (!RegisterClassW(&wc))
|
||||||
|
{
|
||||||
|
Logger::error("Failed to register DimOverlay window class. GetLastError={}", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hwndOwner = CreateWindow(L"static", nullptr, WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, m_hinstance, nullptr);
|
||||||
|
if (!m_hwndOwner)
|
||||||
|
{
|
||||||
|
Logger::error("Failed to create DimOverlay owner window. GetLastError={}", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DWORD exStyle = WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOREDIRECTIONBITMAP |
|
||||||
|
WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_TOPMOST;
|
||||||
|
m_hwnd = CreateWindowExW(exStyle,
|
||||||
|
OverlayWindowClassName,
|
||||||
|
L"PowerToys Always On Top Dim Overlay",
|
||||||
|
WS_POPUP,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
0,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
0,
|
||||||
|
m_hwndOwner,
|
||||||
|
nullptr,
|
||||||
|
m_hinstance,
|
||||||
|
this);
|
||||||
|
if (!m_hwnd)
|
||||||
|
{
|
||||||
|
Logger::error("Failed to create DimOverlay window. GetLastError={}", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL excludeFromPeek = TRUE;
|
||||||
|
DwmSetWindowAttribute(m_hwnd, DWMWA_EXCLUDED_FROM_PEEK, &excludeFromPeek, sizeof(excludeFromPeek));
|
||||||
|
|
||||||
|
UpdateWindowBounds();
|
||||||
|
|
||||||
|
if (!EnsureDispatcherQueue())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EnsureCompositor())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DimOverlay::EnsureDispatcherQueue()
|
||||||
|
{
|
||||||
|
if (m_dispatcherQueueController)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatcherQueueOptions options = {
|
||||||
|
sizeof(DispatcherQueueOptions),
|
||||||
|
DQTYPE_THREAD_CURRENT,
|
||||||
|
DQTAT_COM_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
ABI::IDispatcherQueueController* controller = nullptr;
|
||||||
|
const HRESULT hr = CreateDispatcherQueueController(options, &controller);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Logger::error("Failed to create DispatcherQueueController for DimOverlay. HRESULT={:#x}", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*winrt::put_abi(m_dispatcherQueueController) = controller;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DimOverlay::EnsureCompositor()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_compositor = winrt::Compositor();
|
||||||
|
|
||||||
|
ABI::IDesktopWindowTarget* target = nullptr;
|
||||||
|
winrt::check_hresult(m_compositor.as<ABI::ICompositorDesktopInterop>()->CreateDesktopWindowTarget(m_hwnd, false, &target));
|
||||||
|
*winrt::put_abi(m_target) = target;
|
||||||
|
|
||||||
|
m_root = m_compositor.CreateContainerVisual();
|
||||||
|
m_root.RelativeSizeAdjustment({ 1.0f, 1.0f });
|
||||||
|
m_target.Root(m_root);
|
||||||
|
|
||||||
|
m_dim = m_compositor.CreateSpriteVisual();
|
||||||
|
m_dim.RelativeSizeAdjustment({ 1.0f, 1.0f });
|
||||||
|
m_dimBrush = m_compositor.CreateColorBrush(GetDefaultDimColor());
|
||||||
|
m_dim.Brush(m_dimBrush);
|
||||||
|
m_root.Children().InsertAtTop(m_dim);
|
||||||
|
|
||||||
|
m_root.Opacity(0.0f);
|
||||||
|
|
||||||
|
m_opacityAnimation = m_compositor.CreateScalarKeyFrameAnimation();
|
||||||
|
m_opacityAnimation.Target(L"Opacity");
|
||||||
|
m_opacityAnimation.InsertExpressionKeyFrame(1.0f, L"this.FinalValue");
|
||||||
|
BOOL animationsEnabled = GetAnimationsEnabled();
|
||||||
|
m_opacityAnimation.Duration(std::chrono::milliseconds{ animationsEnabled ? FadeDurationMs : 1 });
|
||||||
|
|
||||||
|
auto implicitAnimations = m_compositor.CreateImplicitAnimationCollection();
|
||||||
|
implicitAnimations.Insert(L"Opacity", m_opacityAnimation);
|
||||||
|
m_root.ImplicitAnimations(implicitAnimations);
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error& e)
|
||||||
|
{
|
||||||
|
Logger::error("Failed to create DimOverlay composition resources: {}", winrt::to_string(e.message()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DimOverlay::UpdateWindowBounds()
|
||||||
|
{
|
||||||
|
if (!m_hwnd)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RECT newBounds{};
|
||||||
|
newBounds.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
||||||
|
newBounds.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
||||||
|
newBounds.right = newBounds.left + GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
||||||
|
newBounds.bottom = newBounds.top + GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
||||||
|
|
||||||
|
if (EqualRect(&newBounds, &m_virtualBounds))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_virtualBounds = newBounds;
|
||||||
|
const int width = m_virtualBounds.right - m_virtualBounds.left;
|
||||||
|
const int height = m_virtualBounds.bottom - m_virtualBounds.top;
|
||||||
|
|
||||||
|
SetWindowPos(m_hwnd, nullptr, m_virtualBounds.left, m_virtualBounds.top, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DimOverlay::UpdateRegion(const std::vector<DimOverlayHole>& holes)
|
||||||
|
{
|
||||||
|
if (!m_hwnd)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int width = m_virtualBounds.right - m_virtualBounds.left;
|
||||||
|
const int height = m_virtualBounds.bottom - m_virtualBounds.top;
|
||||||
|
if (width <= 0 || height <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRGN baseRegion = CreateRectRgn(0, 0, width, height);
|
||||||
|
if (!baseRegion)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RECT bounds{ 0, 0, width, height };
|
||||||
|
for (const auto& hole : holes)
|
||||||
|
{
|
||||||
|
RECT local = hole.rect;
|
||||||
|
OffsetRect(&local, -m_virtualBounds.left, -m_virtualBounds.top);
|
||||||
|
if (!IntersectRect(&local, &local, &bounds))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (local.right <= local.left || local.bottom <= local.top)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int radius = (std::max)(0, hole.radius);
|
||||||
|
const int maxRadius = (std::min)((local.right - local.left) / 2, (local.bottom - local.top) / 2);
|
||||||
|
radius = (std::min)(radius, maxRadius);
|
||||||
|
HRGN holeRegion = nullptr;
|
||||||
|
if (radius > 0)
|
||||||
|
{
|
||||||
|
const int diameter = radius * 2;
|
||||||
|
holeRegion = CreateRoundRectRgn(local.left, local.top, local.right, local.bottom, diameter, diameter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
holeRegion = CreateRectRgn(local.left, local.top, local.right, local.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holeRegion)
|
||||||
|
{
|
||||||
|
CombineRgn(baseRegion, baseRegion, holeRegion, RGN_DIFF);
|
||||||
|
DeleteObject(holeRegion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SetWindowRgn(m_hwnd, baseRegion, TRUE) == 0)
|
||||||
|
{
|
||||||
|
DeleteObject(baseRegion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DimOverlay::SetVisible(bool visible)
|
||||||
|
{
|
||||||
|
if (m_visible == visible)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_root || !m_compositor)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_visible = visible;
|
||||||
|
|
||||||
|
if (visible)
|
||||||
|
{
|
||||||
|
ShowWindow(m_hwnd, SW_SHOWNOACTIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto batch = m_compositor.CreateScopedBatch(winrt::CompositionBatchTypes::Animation);
|
||||||
|
m_root.Opacity(visible ? 1.0f : 0.0f);
|
||||||
|
batch.Completed([hwnd = m_hwnd, visible](auto&&, auto&&) {
|
||||||
|
if (!visible)
|
||||||
|
{
|
||||||
|
ShowWindow(hwnd, SW_HIDE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
batch.End();
|
||||||
|
}
|
||||||
56
src/modules/alwaysontop/AlwaysOnTop/DimOverlay.h
Normal file
56
src/modules/alwaysontop/AlwaysOnTop/DimOverlay.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <winrt/Windows.System.h>
|
||||||
|
#include <winrt/Windows.UI.Composition.h>
|
||||||
|
#include <winrt/Windows.UI.Composition.Desktop.h>
|
||||||
|
|
||||||
|
struct DimOverlayHole
|
||||||
|
{
|
||||||
|
RECT rect{};
|
||||||
|
int radius = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DimOverlay
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DimOverlay() = default;
|
||||||
|
~DimOverlay();
|
||||||
|
|
||||||
|
bool Initialize(HINSTANCE hinstance);
|
||||||
|
void Terminate();
|
||||||
|
void Update(std::vector<DimOverlayHole> holes, bool visible);
|
||||||
|
|
||||||
|
HWND Hwnd() const noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) noexcept;
|
||||||
|
LRESULT MessageHandler(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) noexcept;
|
||||||
|
|
||||||
|
bool CreateWindowAndVisuals();
|
||||||
|
bool EnsureDispatcherQueue();
|
||||||
|
bool EnsureCompositor();
|
||||||
|
|
||||||
|
void UpdateWindowBounds();
|
||||||
|
void UpdateRegion(const std::vector<DimOverlayHole>& holes);
|
||||||
|
void SetVisible(bool visible);
|
||||||
|
|
||||||
|
HINSTANCE m_hinstance{};
|
||||||
|
HWND m_hwndOwner{};
|
||||||
|
HWND m_hwnd{};
|
||||||
|
RECT m_virtualBounds{};
|
||||||
|
bool m_visible = false;
|
||||||
|
bool m_destroyed = false;
|
||||||
|
|
||||||
|
winrt::Windows::System::DispatcherQueueController m_dispatcherQueueController{ nullptr };
|
||||||
|
winrt::Windows::UI::Composition::Compositor m_compositor{ nullptr };
|
||||||
|
winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget m_target{ nullptr };
|
||||||
|
winrt::Windows::UI::Composition::ContainerVisual m_root{ nullptr };
|
||||||
|
winrt::Windows::UI::Composition::SpriteVisual m_dim{ nullptr };
|
||||||
|
winrt::Windows::UI::Composition::CompositionColorBrush m_dimBrush{ nullptr };
|
||||||
|
winrt::Windows::UI::Composition::ScalarKeyFrameAnimation m_opacityAnimation{ nullptr };
|
||||||
|
|
||||||
|
std::vector<DimOverlayHole> m_lastHoles;
|
||||||
|
};
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include <SettingsObserver.h>
|
#include <SettingsObserver.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
class FrameDrawer;
|
class FrameDrawer;
|
||||||
|
|
||||||
class WindowBorder : public SettingsObserver
|
class WindowBorder : public SettingsObserver
|
||||||
@@ -15,6 +17,7 @@ public:
|
|||||||
|
|
||||||
void UpdateBorderPosition() const;
|
void UpdateBorderPosition() const;
|
||||||
void UpdateBorderProperties() const;
|
void UpdateBorderProperties() const;
|
||||||
|
HWND Hwnd() const noexcept { return m_window; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static LRESULT CALLBACK s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
static LRESULT CALLBACK s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
||||||
|
|||||||
Reference in New Issue
Block a user