2021-11-22 13:31:31 +00:00
|
|
|
// MouseHighlighter.cpp : Defines the entry point for the application.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "pch.h"
|
|
|
|
#include "MouseHighlighter.h"
|
|
|
|
#include "trace.h"
|
2025-06-27 14:11:39 +08:00
|
|
|
#include <cmath>
|
2025-08-12 10:55:24 +08:00
|
|
|
#include <algorithm>
|
2021-11-22 13:31:31 +00:00
|
|
|
|
|
|
|
#ifdef COMPOSITION
|
|
|
|
namespace winrt
|
|
|
|
{
|
|
|
|
using namespace winrt::Windows::System;
|
|
|
|
using namespace winrt::Windows::UI::Composition;
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace ABI
|
|
|
|
{
|
|
|
|
using namespace ABI::Windows::System;
|
|
|
|
using namespace ABI::Windows::UI::Composition::Desktop;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct Highlighter
|
|
|
|
{
|
|
|
|
bool MyRegisterClass(HINSTANCE hInstance);
|
|
|
|
static Highlighter* instance;
|
|
|
|
void Terminate();
|
|
|
|
void SwitchActivationMode();
|
|
|
|
void ApplySettings(MouseHighlighterSettings settings);
|
|
|
|
|
|
|
|
private:
|
|
|
|
enum class MouseButton
|
|
|
|
{
|
|
|
|
Left,
|
2023-07-26 23:48:00 +09:00
|
|
|
Right,
|
|
|
|
None
|
2021-11-22 13:31:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
void DestroyHighlighter();
|
|
|
|
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) noexcept;
|
|
|
|
void StartDrawing();
|
|
|
|
void StopDrawing();
|
|
|
|
bool CreateHighlighter();
|
|
|
|
void AddDrawingPoint(MouseButton button);
|
|
|
|
void UpdateDrawingPointPosition(MouseButton button);
|
|
|
|
void StartDrawingPointFading(MouseButton button);
|
2025-06-27 14:11:39 +08:00
|
|
|
void ClearDrawingPoint();
|
2021-11-22 13:31:31 +00:00
|
|
|
void ClearDrawing();
|
2023-08-06 17:16:56 +02:00
|
|
|
void BringToFront();
|
2021-11-22 13:31:31 +00:00
|
|
|
HHOOK m_mouseHook = NULL;
|
|
|
|
static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) noexcept;
|
2025-08-12 10:55:24 +08:00
|
|
|
// Helpers for spotlight overlay
|
|
|
|
float GetDpiScale() const;
|
|
|
|
void UpdateSpotlightMask(float cx, float cy, float radius, bool show);
|
2021-11-22 13:31:31 +00:00
|
|
|
|
|
|
|
static constexpr auto m_className = L"MouseHighlighter";
|
2021-12-09 11:08:53 +00:00
|
|
|
static constexpr auto m_windowTitle = L"PowerToys Mouse Highlighter";
|
2021-11-22 13:31:31 +00:00
|
|
|
HWND m_hwndOwner = NULL;
|
|
|
|
HWND m_hwnd = NULL;
|
|
|
|
HINSTANCE m_hinstance = NULL;
|
|
|
|
static constexpr DWORD WM_SWITCH_ACTIVATION_MODE = WM_APP;
|
|
|
|
|
|
|
|
winrt::DispatcherQueueController m_dispatcherQueueController{ nullptr };
|
|
|
|
winrt::Compositor m_compositor{ nullptr };
|
|
|
|
winrt::Desktop::DesktopWindowTarget m_target{ nullptr };
|
|
|
|
winrt::ContainerVisual m_root{ nullptr };
|
|
|
|
winrt::LayerVisual m_layer{ nullptr };
|
|
|
|
winrt::ShapeVisual m_shape{ nullptr };
|
|
|
|
|
|
|
|
winrt::CompositionSpriteShape m_leftPointer{ nullptr };
|
|
|
|
winrt::CompositionSpriteShape m_rightPointer{ nullptr };
|
2023-07-26 23:48:00 +09:00
|
|
|
winrt::CompositionSpriteShape m_alwaysPointer{ nullptr };
|
2025-08-12 10:55:24 +08:00
|
|
|
// Spotlight overlay (mask with soft feathered edge)
|
|
|
|
winrt::SpriteVisual m_overlay{ nullptr };
|
|
|
|
winrt::CompositionMaskBrush m_spotlightMask{ nullptr };
|
|
|
|
winrt::CompositionRadialGradientBrush m_spotlightMaskGradient{ nullptr };
|
|
|
|
winrt::CompositionColorBrush m_spotlightSource{ nullptr };
|
|
|
|
winrt::CompositionColorGradientStop m_maskStopCenter{ nullptr };
|
|
|
|
winrt::CompositionColorGradientStop m_maskStopInner{ nullptr };
|
|
|
|
winrt::CompositionColorGradientStop m_maskStopOuter{ nullptr };
|
2023-07-26 23:48:00 +09:00
|
|
|
|
|
|
|
bool m_leftPointerEnabled = true;
|
|
|
|
bool m_rightPointerEnabled = true;
|
|
|
|
bool m_alwaysPointerEnabled = true;
|
2025-06-27 14:11:39 +08:00
|
|
|
bool m_spotlightMode = false;
|
2023-07-26 23:48:00 +09:00
|
|
|
|
2021-11-22 13:31:31 +00:00
|
|
|
bool m_leftButtonPressed = false;
|
|
|
|
bool m_rightButtonPressed = false;
|
2023-08-06 17:16:56 +02:00
|
|
|
UINT_PTR m_timer_id = 0;
|
2021-11-22 13:31:31 +00:00
|
|
|
|
|
|
|
bool m_visible = false;
|
|
|
|
|
|
|
|
// Possible configurable settings
|
|
|
|
float m_radius = MOUSE_HIGHLIGHTER_DEFAULT_RADIUS;
|
|
|
|
|
|
|
|
int m_fadeDelay_ms = MOUSE_HIGHLIGHTER_DEFAULT_DELAY_MS;
|
|
|
|
int m_fadeDuration_ms = MOUSE_HIGHLIGHTER_DEFAULT_DURATION_MS;
|
|
|
|
|
|
|
|
winrt::Windows::UI::Color m_leftClickColor = MOUSE_HIGHLIGHTER_DEFAULT_LEFT_BUTTON_COLOR;
|
|
|
|
winrt::Windows::UI::Color m_rightClickColor = MOUSE_HIGHLIGHTER_DEFAULT_RIGHT_BUTTON_COLOR;
|
2023-07-26 23:48:00 +09:00
|
|
|
winrt::Windows::UI::Color m_alwaysColor = MOUSE_HIGHLIGHTER_DEFAULT_ALWAYS_COLOR;
|
2021-11-22 13:31:31 +00:00
|
|
|
};
|
2023-08-06 17:16:56 +02:00
|
|
|
static const uint32_t BRING_TO_FRONT_TIMER_ID = 123;
|
2021-11-22 13:31:31 +00:00
|
|
|
Highlighter* Highlighter::instance = nullptr;
|
|
|
|
|
|
|
|
bool Highlighter::CreateHighlighter()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// We need a dispatcher queue.
|
2025-06-27 14:11:39 +08:00
|
|
|
DispatcherQueueOptions options = {
|
2021-11-22 13:31:31 +00:00
|
|
|
sizeof(options),
|
|
|
|
DQTYPE_THREAD_CURRENT,
|
|
|
|
DQTAT_COM_ASTA,
|
|
|
|
};
|
|
|
|
ABI::IDispatcherQueueController* controller;
|
|
|
|
winrt::check_hresult(CreateDispatcherQueueController(options, &controller));
|
|
|
|
*winrt::put_abi(m_dispatcherQueueController) = controller;
|
|
|
|
|
|
|
|
// Create the compositor for our window.
|
|
|
|
m_compositor = winrt::Compositor();
|
|
|
|
ABI::IDesktopWindowTarget* target;
|
|
|
|
winrt::check_hresult(m_compositor.as<ABI::ICompositorDesktopInterop>()->CreateDesktopWindowTarget(m_hwnd, false, &target));
|
|
|
|
*winrt::put_abi(m_target) = target;
|
|
|
|
|
|
|
|
// Create visual root
|
|
|
|
m_root = m_compositor.CreateContainerVisual();
|
|
|
|
m_root.RelativeSizeAdjustment({ 1.0f, 1.0f });
|
|
|
|
m_target.Root(m_root);
|
|
|
|
|
|
|
|
// Create the shapes container visual and add it to root.
|
|
|
|
m_shape = m_compositor.CreateShapeVisual();
|
|
|
|
m_shape.RelativeSizeAdjustment({ 1.0f, 1.0f });
|
|
|
|
m_root.Children().InsertAtTop(m_shape);
|
|
|
|
|
2025-08-12 10:55:24 +08:00
|
|
|
// Create spotlight overlay (soft feather, DPI-aware)
|
|
|
|
m_overlay = m_compositor.CreateSpriteVisual();
|
|
|
|
m_overlay.RelativeSizeAdjustment({ 1.0f, 1.0f });
|
|
|
|
m_spotlightSource = m_compositor.CreateColorBrush(m_alwaysColor);
|
|
|
|
m_spotlightMaskGradient = m_compositor.CreateRadialGradientBrush();
|
|
|
|
m_spotlightMaskGradient.MappingMode(winrt::CompositionMappingMode::Absolute);
|
|
|
|
// Center region fully transparent
|
|
|
|
m_maskStopCenter = m_compositor.CreateColorGradientStop();
|
|
|
|
m_maskStopCenter.Offset(0.0f);
|
|
|
|
m_maskStopCenter.Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0));
|
|
|
|
// Inner edge of feather (still transparent)
|
|
|
|
m_maskStopInner = m_compositor.CreateColorGradientStop();
|
|
|
|
m_maskStopInner.Offset(0.995f); // will be updated per-radius
|
|
|
|
m_maskStopInner.Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0));
|
|
|
|
// Outer edge (opaque mask -> overlay visible)
|
|
|
|
m_maskStopOuter = m_compositor.CreateColorGradientStop();
|
|
|
|
m_maskStopOuter.Offset(1.0f);
|
|
|
|
m_maskStopOuter.Color(winrt::Windows::UI::ColorHelper::FromArgb(255, 255, 255, 255));
|
|
|
|
m_spotlightMaskGradient.ColorStops().Append(m_maskStopCenter);
|
|
|
|
m_spotlightMaskGradient.ColorStops().Append(m_maskStopInner);
|
|
|
|
m_spotlightMaskGradient.ColorStops().Append(m_maskStopOuter);
|
|
|
|
|
|
|
|
m_spotlightMask = m_compositor.CreateMaskBrush();
|
|
|
|
m_spotlightMask.Source(m_spotlightSource);
|
|
|
|
m_spotlightMask.Mask(m_spotlightMaskGradient);
|
|
|
|
m_overlay.Brush(m_spotlightMask);
|
|
|
|
m_overlay.IsVisible(false);
|
|
|
|
m_root.Children().InsertAtTop(m_overlay);
|
|
|
|
|
2021-11-22 13:31:31 +00:00
|
|
|
return true;
|
2025-06-27 14:11:39 +08:00
|
|
|
}
|
|
|
|
catch (...)
|
2021-11-22 13:31:31 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Highlighter::AddDrawingPoint(MouseButton button)
|
|
|
|
{
|
2025-06-27 14:11:39 +08:00
|
|
|
if (!m_compositor)
|
|
|
|
return;
|
|
|
|
|
2021-11-22 13:31:31 +00:00
|
|
|
POINT pt;
|
2023-08-06 17:16:56 +02:00
|
|
|
|
2021-11-22 13:31:31 +00:00
|
|
|
// Applies DPIs.
|
|
|
|
GetCursorPos(&pt);
|
|
|
|
|
|
|
|
// Converts to client area of the Windows.
|
|
|
|
ScreenToClient(m_hwnd, &pt);
|
|
|
|
|
|
|
|
// Create circle and add it.
|
|
|
|
auto circleGeometry = m_compositor.CreateEllipseGeometry();
|
|
|
|
circleGeometry.Radius({ m_radius, m_radius });
|
2025-06-27 14:11:39 +08:00
|
|
|
|
2021-11-22 13:31:31 +00:00
|
|
|
auto circleShape = m_compositor.CreateSpriteShape(circleGeometry);
|
2023-07-26 23:48:00 +09:00
|
|
|
circleShape.Offset({ static_cast<float>(pt.x), static_cast<float>(pt.y) });
|
2021-11-22 13:31:31 +00:00
|
|
|
if (button == MouseButton::Left)
|
|
|
|
{
|
|
|
|
circleShape.FillBrush(m_compositor.CreateColorBrush(m_leftClickColor));
|
|
|
|
m_leftPointer = circleShape;
|
|
|
|
}
|
2023-07-26 23:48:00 +09:00
|
|
|
else if (button == MouseButton::Right)
|
2021-11-22 13:31:31 +00:00
|
|
|
{
|
|
|
|
circleShape.FillBrush(m_compositor.CreateColorBrush(m_rightClickColor));
|
|
|
|
m_rightPointer = circleShape;
|
|
|
|
}
|
2023-07-26 23:48:00 +09:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// always
|
2025-06-27 14:11:39 +08:00
|
|
|
if (m_spotlightMode)
|
|
|
|
{
|
2025-08-12 10:55:24 +08:00
|
|
|
UpdateSpotlightMask(static_cast<float>(pt.x), static_cast<float>(pt.y), m_radius, true);
|
|
|
|
return;
|
2025-06-27 14:11:39 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
circleShape.FillBrush(m_compositor.CreateColorBrush(m_alwaysColor));
|
|
|
|
m_alwaysPointer = circleShape;
|
|
|
|
}
|
2023-07-26 23:48:00 +09:00
|
|
|
}
|
2025-06-27 14:11:39 +08:00
|
|
|
|
2021-11-22 13:31:31 +00:00
|
|
|
m_shape.Shapes().Append(circleShape);
|
|
|
|
|
|
|
|
// TODO: We're leaking shapes for long drawing sessions.
|
|
|
|
// Perhaps add a task to the Dispatcher every X circles to clean up.
|
|
|
|
|
|
|
|
// Get back on top in case other Window is now the topmost.
|
Updates for check-spelling v0.0.25 (#40386)
## Summary of the Pull Request
- #39572 updated check-spelling but ignored:
> 🐣 Breaking Changes
[Code Scanning action requires a Code Scanning
Ruleset](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset)
If you use SARIF reporting, then instead of the workflow yielding an ❌
when it fails, it will rely on [github-advanced-security
🤖](https://github.com/apps/github-advanced-security) to report the
failure. You will need to adjust your checks for PRs.
This means that check-spelling hasn't been properly doing its job 😦.
I'm sorry, I should have pushed a thing to this repo earlier,...
Anyway, as with most refreshes, this comes with a number of fixes, some
are fixes for typos that snuck in before the 0.0.25 upgrade, some are
for things that snuck in after, some are based on new rules in
spell-check-this, and some are hand written patterns based on running
through this repository a few times.
About the 🐣 **breaking change**: someone needs to create a ruleset for
this repository (see [Code Scanning action requires a Code Scanning
Ruleset: Sample ruleset
](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset#sample-ruleset)).
The alternative to adding a ruleset is to change the condition to not
use sarif for this repository. In general, I think the github
integration from sarif is prettier/more helpful, so I think that it's
the better choice.
You can see an example of it working in:
- https://github.com/check-spelling-sandbox/PowerToys/pull/23
---------
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
2025-07-08 18:16:52 -04:00
|
|
|
// HACK: Draw with 1 pixel off. Otherwise, Windows glitches the task bar transparency when a transparent window fill the whole screen.
|
2022-04-14 13:21:02 +01:00
|
|
|
SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, 0);
|
2021-11-22 13:31:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Highlighter::UpdateDrawingPointPosition(MouseButton button)
|
|
|
|
{
|
|
|
|
POINT pt;
|
|
|
|
|
|
|
|
// Applies DPIs.
|
|
|
|
GetCursorPos(&pt);
|
|
|
|
|
|
|
|
// Converts to client area of the Windows.
|
|
|
|
ScreenToClient(m_hwnd, &pt);
|
|
|
|
|
|
|
|
if (button == MouseButton::Left)
|
|
|
|
{
|
2023-07-26 23:48:00 +09:00
|
|
|
m_leftPointer.Offset({ static_cast<float>(pt.x), static_cast<float>(pt.y) });
|
|
|
|
}
|
|
|
|
else if (button == MouseButton::Right)
|
|
|
|
{
|
|
|
|
m_rightPointer.Offset({ static_cast<float>(pt.x), static_cast<float>(pt.y) });
|
2021-11-22 13:31:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2025-08-12 10:55:24 +08:00
|
|
|
// always / spotlight idle
|
2025-06-27 14:11:39 +08:00
|
|
|
if (m_spotlightMode)
|
|
|
|
{
|
2025-08-12 10:55:24 +08:00
|
|
|
UpdateSpotlightMask(static_cast<float>(pt.x), static_cast<float>(pt.y), m_radius, true);
|
2025-06-27 14:11:39 +08:00
|
|
|
}
|
2025-08-12 10:55:24 +08:00
|
|
|
else if (m_alwaysPointer)
|
2025-06-27 14:11:39 +08:00
|
|
|
{
|
2025-08-12 10:55:24 +08:00
|
|
|
m_alwaysPointer.Offset({ static_cast<float>(pt.x), static_cast<float>(pt.y) });
|
2025-06-27 14:11:39 +08:00
|
|
|
}
|
2021-11-22 13:31:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
void Highlighter::StartDrawingPointFading(MouseButton button)
|
|
|
|
{
|
|
|
|
winrt::Windows::UI::Composition::CompositionSpriteShape circleShape{ nullptr };
|
|
|
|
if (button == MouseButton::Left)
|
|
|
|
{
|
|
|
|
circleShape = m_leftPointer;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-07-26 23:48:00 +09:00
|
|
|
// right
|
2021-11-22 13:31:31 +00:00
|
|
|
circleShape = m_rightPointer;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto brushColor = circleShape.FillBrush().as<winrt::Windows::UI::Composition::CompositionColorBrush>().Color();
|
|
|
|
|
|
|
|
// Animate opacity to simulate a fade away effect.
|
|
|
|
auto animation = m_compositor.CreateColorKeyFrameAnimation();
|
|
|
|
animation.InsertKeyFrame(1, winrt::Windows::UI::ColorHelper::FromArgb(0, brushColor.R, brushColor.G, brushColor.B));
|
|
|
|
using timeSpan = std::chrono::duration<int, std::ratio<1, 1000>>;
|
2023-10-24 12:11:29 +02:00
|
|
|
// HACK: If user sets these durations to 0, the fade won't work. Setting them to 1ms instead to avoid this.
|
|
|
|
if (m_fadeDuration_ms == 0)
|
|
|
|
{
|
|
|
|
m_fadeDuration_ms = 1;
|
|
|
|
}
|
|
|
|
if (m_fadeDelay_ms == 0)
|
|
|
|
{
|
|
|
|
m_fadeDelay_ms = 1;
|
|
|
|
}
|
2021-11-22 13:31:31 +00:00
|
|
|
std::chrono::milliseconds duration(m_fadeDuration_ms);
|
|
|
|
std::chrono::milliseconds delay(m_fadeDelay_ms);
|
|
|
|
animation.Duration(timeSpan(duration));
|
|
|
|
animation.DelayTime(timeSpan(delay));
|
|
|
|
|
|
|
|
circleShape.FillBrush().StartAnimation(L"Color", animation);
|
|
|
|
}
|
|
|
|
|
2025-06-27 14:11:39 +08:00
|
|
|
void Highlighter::ClearDrawingPoint()
|
2023-07-26 23:48:00 +09:00
|
|
|
{
|
2025-06-27 14:11:39 +08:00
|
|
|
if (m_spotlightMode)
|
2025-02-18 23:21:03 +00:00
|
|
|
{
|
2025-08-12 10:55:24 +08:00
|
|
|
if (m_overlay)
|
2025-06-27 14:11:39 +08:00
|
|
|
{
|
2025-08-12 10:55:24 +08:00
|
|
|
m_overlay.IsVisible(false);
|
2025-06-27 14:11:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (m_alwaysPointer)
|
|
|
|
{
|
|
|
|
m_alwaysPointer.FillBrush().as<winrt::Windows::UI::Composition::CompositionColorBrush>().Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0));
|
|
|
|
}
|
2025-02-18 23:21:03 +00:00
|
|
|
}
|
2023-07-26 23:48:00 +09:00
|
|
|
}
|
2021-11-22 13:31:31 +00:00
|
|
|
|
|
|
|
void Highlighter::ClearDrawing()
|
|
|
|
{
|
2022-12-20 19:29:05 +00:00
|
|
|
if (nullptr == m_shape || nullptr == m_shape.Shapes())
|
|
|
|
{
|
|
|
|
// Guard against m_shape not being initialized.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-11-22 13:31:31 +00:00
|
|
|
m_shape.Shapes().Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
LRESULT CALLBACK Highlighter::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) noexcept
|
|
|
|
{
|
|
|
|
if (nCode >= 0)
|
|
|
|
{
|
2023-02-08 11:01:35 +00:00
|
|
|
MSLLHOOKSTRUCT* hookData = reinterpret_cast<MSLLHOOKSTRUCT*>(lParam);
|
2021-11-22 13:31:31 +00:00
|
|
|
switch (wParam)
|
|
|
|
{
|
|
|
|
case WM_LBUTTONDOWN:
|
2023-07-26 23:48:00 +09:00
|
|
|
if (instance->m_leftPointerEnabled)
|
|
|
|
{
|
|
|
|
if (instance->m_alwaysPointerEnabled && !instance->m_rightButtonPressed)
|
|
|
|
{
|
|
|
|
// Clear AlwaysPointer only when it's enabled and RightPointer is not active
|
2025-06-27 14:11:39 +08:00
|
|
|
instance->ClearDrawingPoint();
|
2023-07-26 23:48:00 +09:00
|
|
|
}
|
2025-02-18 23:21:03 +00:00
|
|
|
if (instance->m_leftButtonPressed)
|
|
|
|
{
|
|
|
|
// There might be a stray point from the user releasing the mouse button on an elevated window, which wasn't caught by us.
|
|
|
|
instance->StartDrawingPointFading(MouseButton::Left);
|
|
|
|
}
|
2025-06-27 14:11:39 +08:00
|
|
|
|
2023-07-26 23:48:00 +09:00
|
|
|
instance->AddDrawingPoint(MouseButton::Left);
|
|
|
|
instance->m_leftButtonPressed = true;
|
2023-08-06 17:16:56 +02:00
|
|
|
// start a timer for the scenario, when the user clicks a pinned window which has no focus.
|
|
|
|
// after we drow the highlighting circle the pinned window will jump in front of us,
|
|
|
|
// we have to bring our window back to topmost position
|
|
|
|
if (instance->m_timer_id == 0)
|
|
|
|
{
|
|
|
|
instance->m_timer_id = SetTimer(instance->m_hwnd, BRING_TO_FRONT_TIMER_ID, 10, nullptr);
|
|
|
|
}
|
2023-07-26 23:48:00 +09:00
|
|
|
}
|
2021-11-22 13:31:31 +00:00
|
|
|
break;
|
|
|
|
case WM_RBUTTONDOWN:
|
2023-07-26 23:48:00 +09:00
|
|
|
if (instance->m_rightPointerEnabled)
|
|
|
|
{
|
|
|
|
if (instance->m_alwaysPointerEnabled && !instance->m_leftButtonPressed)
|
|
|
|
{
|
|
|
|
// Clear AlwaysPointer only when it's enabled and LeftPointer is not active
|
2025-06-27 14:11:39 +08:00
|
|
|
instance->ClearDrawingPoint();
|
2023-07-26 23:48:00 +09:00
|
|
|
}
|
2025-02-18 23:21:03 +00:00
|
|
|
if (instance->m_rightButtonPressed)
|
|
|
|
{
|
|
|
|
// There might be a stray point from the user releasing the mouse button on an elevated window, which wasn't caught by us.
|
|
|
|
instance->StartDrawingPointFading(MouseButton::Right);
|
|
|
|
}
|
2023-07-26 23:48:00 +09:00
|
|
|
instance->AddDrawingPoint(MouseButton::Right);
|
|
|
|
instance->m_rightButtonPressed = true;
|
2023-08-06 17:16:56 +02:00
|
|
|
// same as for the left button, start a timer for reposition ourselves to topmost position
|
|
|
|
if (instance->m_timer_id == 0)
|
|
|
|
{
|
|
|
|
instance->m_timer_id = SetTimer(instance->m_hwnd, BRING_TO_FRONT_TIMER_ID, 10, nullptr);
|
|
|
|
}
|
2023-07-26 23:48:00 +09:00
|
|
|
}
|
2021-11-22 13:31:31 +00:00
|
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
|
|
if (instance->m_leftButtonPressed)
|
|
|
|
{
|
|
|
|
instance->UpdateDrawingPointPosition(MouseButton::Left);
|
|
|
|
}
|
|
|
|
if (instance->m_rightButtonPressed)
|
|
|
|
{
|
|
|
|
instance->UpdateDrawingPointPosition(MouseButton::Right);
|
|
|
|
}
|
2023-07-26 23:48:00 +09:00
|
|
|
if (instance->m_alwaysPointerEnabled && !instance->m_leftButtonPressed && !instance->m_rightButtonPressed)
|
|
|
|
{
|
|
|
|
instance->UpdateDrawingPointPosition(MouseButton::None);
|
|
|
|
}
|
2021-11-22 13:31:31 +00:00
|
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
|
|
if (instance->m_leftButtonPressed)
|
|
|
|
{
|
|
|
|
instance->StartDrawingPointFading(MouseButton::Left);
|
|
|
|
instance->m_leftButtonPressed = false;
|
2023-07-26 23:48:00 +09:00
|
|
|
if (instance->m_alwaysPointerEnabled && !instance->m_rightButtonPressed)
|
|
|
|
{
|
|
|
|
// Add AlwaysPointer only when it's enabled and RightPointer is not active
|
|
|
|
instance->AddDrawingPoint(MouseButton::None);
|
|
|
|
}
|
2021-11-22 13:31:31 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_RBUTTONUP:
|
|
|
|
if (instance->m_rightButtonPressed)
|
|
|
|
{
|
|
|
|
instance->StartDrawingPointFading(MouseButton::Right);
|
|
|
|
instance->m_rightButtonPressed = false;
|
2023-07-26 23:48:00 +09:00
|
|
|
if (instance->m_alwaysPointerEnabled && !instance->m_leftButtonPressed)
|
|
|
|
{
|
|
|
|
// Add AlwaysPointer only when it's enabled and LeftPointer is not active
|
|
|
|
instance->AddDrawingPoint(MouseButton::None);
|
|
|
|
}
|
2021-11-22 13:31:31 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return CallNextHookEx(0, nCode, wParam, lParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Highlighter::StartDrawing()
|
|
|
|
{
|
|
|
|
Logger::info("Starting draw mode.");
|
|
|
|
Trace::StartHighlightingSession();
|
2025-06-27 14:11:39 +08:00
|
|
|
|
|
|
|
if (m_spotlightMode && m_alwaysColor.A != 0)
|
|
|
|
{
|
|
|
|
Trace::StartSpotlightSession();
|
|
|
|
}
|
|
|
|
|
2021-11-22 13:31:31 +00:00
|
|
|
m_visible = true;
|
2022-03-17 17:25:15 +00:00
|
|
|
|
Updates for check-spelling v0.0.25 (#40386)
## Summary of the Pull Request
- #39572 updated check-spelling but ignored:
> 🐣 Breaking Changes
[Code Scanning action requires a Code Scanning
Ruleset](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset)
If you use SARIF reporting, then instead of the workflow yielding an ❌
when it fails, it will rely on [github-advanced-security
🤖](https://github.com/apps/github-advanced-security) to report the
failure. You will need to adjust your checks for PRs.
This means that check-spelling hasn't been properly doing its job 😦.
I'm sorry, I should have pushed a thing to this repo earlier,...
Anyway, as with most refreshes, this comes with a number of fixes, some
are fixes for typos that snuck in before the 0.0.25 upgrade, some are
for things that snuck in after, some are based on new rules in
spell-check-this, and some are hand written patterns based on running
through this repository a few times.
About the 🐣 **breaking change**: someone needs to create a ruleset for
this repository (see [Code Scanning action requires a Code Scanning
Ruleset: Sample ruleset
](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset#sample-ruleset)).
The alternative to adding a ruleset is to change the condition to not
use sarif for this repository. In general, I think the github
integration from sarif is prettier/more helpful, so I think that it's
the better choice.
You can see an example of it working in:
- https://github.com/check-spelling-sandbox/PowerToys/pull/23
---------
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
2025-07-08 18:16:52 -04:00
|
|
|
// HACK: Draw with 1 pixel off. Otherwise, Windows glitches the task bar transparency when a transparent window fill the whole screen.
|
2022-04-14 13:21:02 +01:00
|
|
|
SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, 0);
|
2021-11-22 13:31:31 +00:00
|
|
|
ClearDrawing();
|
|
|
|
ShowWindow(m_hwnd, SW_SHOWNOACTIVATE);
|
2025-06-27 14:11:39 +08:00
|
|
|
|
|
|
|
instance->AddDrawingPoint(Highlighter::MouseButton::None);
|
|
|
|
|
2021-11-22 13:31:31 +00:00
|
|
|
m_mouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, m_hinstance, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Highlighter::StopDrawing()
|
|
|
|
{
|
|
|
|
Logger::info("Stopping draw mode.");
|
|
|
|
m_visible = false;
|
|
|
|
m_leftButtonPressed = false;
|
|
|
|
m_rightButtonPressed = false;
|
|
|
|
m_leftPointer = nullptr;
|
|
|
|
m_rightPointer = nullptr;
|
2023-07-26 23:48:00 +09:00
|
|
|
m_alwaysPointer = nullptr;
|
2025-08-12 10:55:24 +08:00
|
|
|
if (m_overlay)
|
|
|
|
{
|
|
|
|
m_overlay.IsVisible(false);
|
|
|
|
}
|
2021-11-22 13:31:31 +00:00
|
|
|
ShowWindow(m_hwnd, SW_HIDE);
|
|
|
|
UnhookWindowsHookEx(m_mouseHook);
|
|
|
|
ClearDrawing();
|
|
|
|
m_mouseHook = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Highlighter::SwitchActivationMode()
|
|
|
|
{
|
|
|
|
PostMessage(m_hwnd, WM_SWITCH_ACTIVATION_MODE, 0, 0);
|
|
|
|
}
|
|
|
|
|
2025-06-27 14:11:39 +08:00
|
|
|
void Highlighter::ApplySettings(MouseHighlighterSettings settings)
|
|
|
|
{
|
2023-02-08 11:01:35 +00:00
|
|
|
m_radius = static_cast<float>(settings.radius);
|
2021-11-22 13:31:31 +00:00
|
|
|
m_fadeDelay_ms = settings.fadeDelayMs;
|
|
|
|
m_fadeDuration_ms = settings.fadeDurationMs;
|
|
|
|
m_leftClickColor = settings.leftButtonColor;
|
|
|
|
m_rightClickColor = settings.rightButtonColor;
|
2023-07-26 23:48:00 +09:00
|
|
|
m_alwaysColor = settings.alwaysColor;
|
|
|
|
m_leftPointerEnabled = settings.leftButtonColor.A != 0;
|
|
|
|
m_rightPointerEnabled = settings.rightButtonColor.A != 0;
|
|
|
|
m_alwaysPointerEnabled = settings.alwaysColor.A != 0;
|
2025-06-27 14:11:39 +08:00
|
|
|
m_spotlightMode = settings.spotlightMode && settings.alwaysColor.A != 0;
|
|
|
|
|
|
|
|
if (m_spotlightMode)
|
|
|
|
{
|
|
|
|
m_leftPointerEnabled = false;
|
|
|
|
m_rightPointerEnabled = false;
|
|
|
|
}
|
|
|
|
|
2025-08-12 10:55:24 +08:00
|
|
|
// Keep spotlight overlay color updated
|
|
|
|
if (m_spotlightSource)
|
|
|
|
{
|
|
|
|
m_spotlightSource.Color(m_alwaysColor);
|
|
|
|
}
|
|
|
|
if (!m_spotlightMode && m_overlay)
|
|
|
|
{
|
|
|
|
m_overlay.IsVisible(false);
|
|
|
|
}
|
|
|
|
|
2025-06-27 14:11:39 +08:00
|
|
|
if (instance->m_visible)
|
|
|
|
{
|
|
|
|
instance->StopDrawing();
|
|
|
|
instance->StartDrawing();
|
|
|
|
}
|
2021-11-22 13:31:31 +00:00
|
|
|
}
|
|
|
|
|
2025-06-27 14:11:39 +08:00
|
|
|
void Highlighter::BringToFront()
|
|
|
|
{
|
Updates for check-spelling v0.0.25 (#40386)
## Summary of the Pull Request
- #39572 updated check-spelling but ignored:
> 🐣 Breaking Changes
[Code Scanning action requires a Code Scanning
Ruleset](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset)
If you use SARIF reporting, then instead of the workflow yielding an ❌
when it fails, it will rely on [github-advanced-security
🤖](https://github.com/apps/github-advanced-security) to report the
failure. You will need to adjust your checks for PRs.
This means that check-spelling hasn't been properly doing its job 😦.
I'm sorry, I should have pushed a thing to this repo earlier,...
Anyway, as with most refreshes, this comes with a number of fixes, some
are fixes for typos that snuck in before the 0.0.25 upgrade, some are
for things that snuck in after, some are based on new rules in
spell-check-this, and some are hand written patterns based on running
through this repository a few times.
About the 🐣 **breaking change**: someone needs to create a ruleset for
this repository (see [Code Scanning action requires a Code Scanning
Ruleset: Sample ruleset
](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset#sample-ruleset)).
The alternative to adding a ruleset is to change the condition to not
use sarif for this repository. In general, I think the github
integration from sarif is prettier/more helpful, so I think that it's
the better choice.
You can see an example of it working in:
- https://github.com/check-spelling-sandbox/PowerToys/pull/23
---------
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
2025-07-08 18:16:52 -04:00
|
|
|
// HACK: Draw with 1 pixel off. Otherwise, Windows glitches the task bar transparency when a transparent window fill the whole screen.
|
2023-08-06 17:16:56 +02:00
|
|
|
SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, 0);
|
|
|
|
}
|
|
|
|
|
2021-11-22 13:31:31 +00:00
|
|
|
void Highlighter::DestroyHighlighter()
|
|
|
|
{
|
|
|
|
StopDrawing();
|
|
|
|
PostQuitMessage(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
LRESULT CALLBACK Highlighter::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) noexcept
|
|
|
|
{
|
|
|
|
switch (message)
|
|
|
|
{
|
|
|
|
case WM_NCCREATE:
|
|
|
|
instance->m_hwnd = hWnd;
|
|
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
|
|
case WM_CREATE:
|
|
|
|
return instance->CreateHighlighter() ? 0 : -1;
|
|
|
|
case WM_NCHITTEST:
|
|
|
|
return HTTRANSPARENT;
|
|
|
|
case WM_SWITCH_ACTIVATION_MODE:
|
|
|
|
if (instance->m_visible)
|
|
|
|
{
|
|
|
|
instance->StopDrawing();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
instance->StartDrawing();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
|
|
instance->DestroyHighlighter();
|
|
|
|
break;
|
2023-08-06 17:16:56 +02:00
|
|
|
case WM_TIMER:
|
|
|
|
{
|
|
|
|
switch (wParam)
|
|
|
|
{
|
|
|
|
// when the bring-to-front-timer expires (every 10 ms), we are repositioning our window to topmost Z order position
|
|
|
|
// As we experience that it takes 0-30 ms that the pinned window hides our window,
|
|
|
|
// we await 5 timer ticks (50 ms together) and then we stop the timer.
|
|
|
|
// If we would use a timer with a 50 ms period, there would be a flickering on the UI, as in most of the cases
|
|
|
|
// the pinned window hides our window in a few milliseconds.
|
|
|
|
case BRING_TO_FRONT_TIMER_ID:
|
|
|
|
static int fireCount = 0;
|
|
|
|
if (fireCount++ >= 4)
|
|
|
|
{
|
|
|
|
KillTimer(instance->m_hwnd, instance->m_timer_id);
|
|
|
|
instance->m_timer_id = 0;
|
|
|
|
fireCount = 0;
|
|
|
|
}
|
|
|
|
instance->BringToFront();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2021-11-22 13:31:31 +00:00
|
|
|
default:
|
|
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Highlighter::MyRegisterClass(HINSTANCE hInstance)
|
|
|
|
{
|
|
|
|
WNDCLASS wc{};
|
|
|
|
|
|
|
|
m_hinstance = hInstance;
|
|
|
|
|
|
|
|
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
|
|
|
if (!GetClassInfoW(hInstance, m_className, &wc))
|
|
|
|
{
|
|
|
|
wc.lpfnWndProc = WndProc;
|
|
|
|
wc.hInstance = hInstance;
|
|
|
|
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
|
|
|
|
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
2023-02-08 11:01:35 +00:00
|
|
|
wc.hbrBackground = static_cast<HBRUSH>(GetStockObject(NULL_BRUSH));
|
2021-11-22 13:31:31 +00:00
|
|
|
wc.lpszClassName = m_className;
|
|
|
|
|
|
|
|
if (!RegisterClassW(&wc))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_hwndOwner = CreateWindow(L"static", nullptr, WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, hInstance, nullptr);
|
|
|
|
|
|
|
|
DWORD exStyle = WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOREDIRECTIONBITMAP | WS_EX_TOOLWINDOW;
|
2025-06-27 14:11:39 +08:00
|
|
|
return CreateWindowExW(exStyle, m_className, m_windowTitle, WS_POPUP, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, m_hwndOwner, nullptr, hInstance, nullptr) != nullptr;
|
2021-11-22 13:31:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Highlighter::Terminate()
|
|
|
|
{
|
|
|
|
auto dispatcherQueue = m_dispatcherQueueController.DispatcherQueue();
|
|
|
|
bool enqueueSucceeded = dispatcherQueue.TryEnqueue([=]() {
|
|
|
|
DestroyWindow(m_hwndOwner);
|
|
|
|
});
|
|
|
|
if (!enqueueSucceeded)
|
|
|
|
{
|
|
|
|
Logger::error("Couldn't enqueue message to destroy the window.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-08-12 10:55:24 +08:00
|
|
|
float Highlighter::GetDpiScale() const
|
|
|
|
{
|
|
|
|
return static_cast<float>(GetDpiForWindow(m_hwnd)) / 96.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update spotlight radial mask center/radius with DPI-aware feather
|
|
|
|
void Highlighter::UpdateSpotlightMask(float cx, float cy, float radius, bool show)
|
|
|
|
{
|
|
|
|
if (!m_spotlightMaskGradient)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_spotlightMaskGradient.EllipseCenter({ cx, cy });
|
|
|
|
m_spotlightMaskGradient.EllipseRadius({ radius, radius });
|
|
|
|
|
|
|
|
const float dpiScale = GetDpiScale();
|
|
|
|
// Target a very fine edge: ~1 physical pixel, convert to DIPs: 1 / dpiScale
|
|
|
|
const float featherDip = 1.0f / (dpiScale > 0.0f ? dpiScale : 1.0f);
|
|
|
|
const float safeRadius = (std::max)(radius, 1.0f);
|
|
|
|
const float featherRel = (std::min)(0.25f, featherDip / safeRadius);
|
|
|
|
|
|
|
|
if (m_maskStopInner)
|
|
|
|
{
|
|
|
|
m_maskStopInner.Offset((std::max)(0.0f, 1.0f - featherRel));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_spotlightSource)
|
|
|
|
{
|
|
|
|
m_spotlightSource.Color(m_alwaysColor);
|
|
|
|
}
|
|
|
|
if (m_overlay)
|
|
|
|
{
|
|
|
|
m_overlay.IsVisible(show);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-22 13:31:31 +00:00
|
|
|
#pragma region MouseHighlighter_API
|
|
|
|
|
|
|
|
void MouseHighlighterApplySettings(MouseHighlighterSettings settings)
|
|
|
|
{
|
|
|
|
if (Highlighter::instance != nullptr)
|
|
|
|
{
|
|
|
|
Logger::info("Applying settings.");
|
|
|
|
Highlighter::instance->ApplySettings(settings);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MouseHighlighterSwitch()
|
|
|
|
{
|
|
|
|
if (Highlighter::instance != nullptr)
|
|
|
|
{
|
|
|
|
Logger::info("Switching activation mode.");
|
|
|
|
Highlighter::instance->SwitchActivationMode();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MouseHighlighterDisable()
|
|
|
|
{
|
|
|
|
if (Highlighter::instance != nullptr)
|
|
|
|
{
|
|
|
|
Logger::info("Terminating the highlighter instance.");
|
|
|
|
Highlighter::instance->Terminate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MouseHighlighterIsEnabled()
|
|
|
|
{
|
|
|
|
return (Highlighter::instance != nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
int MouseHighlighterMain(HINSTANCE hInstance, MouseHighlighterSettings settings)
|
|
|
|
{
|
|
|
|
Logger::info("Starting a highlighter instance.");
|
|
|
|
if (Highlighter::instance != nullptr)
|
|
|
|
{
|
|
|
|
Logger::error("A highlighter instance was still working when trying to start a new one.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform application initialization:
|
|
|
|
Highlighter highlighter;
|
|
|
|
Highlighter::instance = &highlighter;
|
|
|
|
highlighter.ApplySettings(settings);
|
|
|
|
if (!highlighter.MyRegisterClass(hInstance))
|
|
|
|
{
|
|
|
|
Logger::error("Couldn't initialize a highlighter instance.");
|
|
|
|
Highlighter::instance = nullptr;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
Logger::info("Initialized the highlighter instance.");
|
|
|
|
|
2023-08-08 18:01:23 +02:00
|
|
|
if (settings.autoActivate)
|
|
|
|
{
|
|
|
|
highlighter.SwitchActivationMode();
|
|
|
|
}
|
|
|
|
|
2021-11-22 13:31:31 +00:00
|
|
|
MSG msg;
|
|
|
|
|
|
|
|
// Main message loop:
|
|
|
|
while (GetMessage(&msg, nullptr, 0, 0))
|
|
|
|
{
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
DispatchMessage(&msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
Logger::info("Mouse highlighter message loop ended.");
|
|
|
|
Highlighter::instance = nullptr;
|
|
|
|
|
|
|
|
return (int)msg.wParam;
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma endregion MouseHighlighter_API
|