diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json
index eb08c34c9c..618e437b9c 100644
--- a/.pipelines/ESRPSigning_core.json
+++ b/.pipelines/ESRPSigning_core.json
@@ -95,6 +95,7 @@
"modules\\MouseUtils\\PowerToys.FindMyMouse.dll",
"modules\\MouseUtils\\PowerToys.MouseHighlighter.dll",
+ "modules\\MouseUtils\\PowerToys.MousePointerCrosshair.dll",
"modules\\PowerRename\\PowerToys.PowerRenameExt.dll",
"modules\\PowerRename\\PowerToys.PowerRenameUILib.dll",
diff --git a/PowerToys.sln b/PowerToys.sln
index 5cd578d81d..2e80669738 100644
--- a/PowerToys.sln
+++ b/PowerToys.sln
@@ -388,6 +388,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AlwaysOnTopModuleInterface"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Community.PowerToys.Run.Plugin.WebSearch", "src\modules\launcher\Plugins\Community.PowerToys.Run.Plugin.WebSearch\Community.PowerToys.Run.Plugin.WebSearch.csproj", "{9F94B303-5E21-4364-9362-64426F8DB932}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MousePointerCrosshair", "src\modules\MouseUtils\MousePointerCrosshair\MousePointerCrosshair.vcxproj", "{EAE14C0E-7A6B-45DA-9080-A7D8C077BA6E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -1045,6 +1047,12 @@ Global
{9F94B303-5E21-4364-9362-64426F8DB932}.Release|x64.ActiveCfg = Release|x64
{9F94B303-5E21-4364-9362-64426F8DB932}.Release|x64.Build.0 = Release|x64
{9F94B303-5E21-4364-9362-64426F8DB932}.Release|x86.ActiveCfg = Release|x64
+ {EAE14C0E-7A6B-45DA-9080-A7D8C077BA6E}.Debug|x64.ActiveCfg = Debug|x64
+ {EAE14C0E-7A6B-45DA-9080-A7D8C077BA6E}.Debug|x64.Build.0 = Debug|x64
+ {EAE14C0E-7A6B-45DA-9080-A7D8C077BA6E}.Debug|x86.ActiveCfg = Debug|x64
+ {EAE14C0E-7A6B-45DA-9080-A7D8C077BA6E}.Release|x64.ActiveCfg = Release|x64
+ {EAE14C0E-7A6B-45DA-9080-A7D8C077BA6E}.Release|x64.Build.0 = Release|x64
+ {EAE14C0E-7A6B-45DA-9080-A7D8C077BA6E}.Release|x86.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1172,6 +1180,7 @@ Global
{1DC3BE92-CE89-43FB-8110-9C043A2FE7A2} = {60CD2D4F-C3B9-4897-9821-FCA5098B41CE}
{48A0A19E-A0BE-4256-ACF8-CC3B80291AF9} = {60CD2D4F-C3B9-4897-9821-FCA5098B41CE}
{9F94B303-5E21-4364-9362-64426F8DB932} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
+ {EAE14C0E-7A6B-45DA-9080-A7D8C077BA6E} = {322566EF-20DC-43A6-B9F8-616AF942579A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs
index 3ea4274ea0..138fa8b3be 100644
--- a/installer/PowerToysSetup/Product.wxs
+++ b/installer/PowerToysSetup/Product.wxs
@@ -707,6 +707,9 @@
+
+
+
@@ -1045,6 +1048,7 @@
+
diff --git a/src/common/logger/logger_settings.h b/src/common/logger/logger_settings.h
index e45e79c042..a5be14ced5 100644
--- a/src/common/logger/logger_settings.h
+++ b/src/common/logger/logger_settings.h
@@ -27,6 +27,7 @@ struct LogSettings
inline const static std::wstring keyboardManagerLogPath = L"Logs\\keyboard-manager-log.txt";
inline const static std::string findMyMouseLoggerName = "find-my-mouse";
inline const static std::string mouseHighlighterLoggerName = "mouse-highlighter";
+ inline const static std::string mousePointerCrosshairLoggerName = "mouse-pointer-crosshair";
inline const static std::string powerRenameLoggerName = "powerrename";
inline const static std::string alwaysOnTopLoggerName = "always-on-top";
inline const static std::wstring alwaysOnTopLogPath = L"always-on-top-log.txt";
diff --git a/src/modules/MouseUtils/MousePointerCrosshair/InclusiveCrosshair.cpp b/src/modules/MouseUtils/MousePointerCrosshair/InclusiveCrosshair.cpp
new file mode 100644
index 0000000000..0c67e4e77d
--- /dev/null
+++ b/src/modules/MouseUtils/MousePointerCrosshair/InclusiveCrosshair.cpp
@@ -0,0 +1,452 @@
+// InclusiveCrosshair.cpp : Defines the entry point for the application.
+//
+
+#include "pch.h"
+#include "InclusiveCrosshair.h"
+#include "trace.h"
+
+#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 InclusiveCrosshair
+{
+ bool MyRegisterClass(HINSTANCE hInstance);
+ static InclusiveCrosshair* instance;
+ void Terminate();
+ void SwitchActivationMode();
+ void ApplySettings(InclusiveCrosshairSettings& settings, bool applyToRuntimeObjects);
+
+private:
+ enum class MouseButton
+ {
+ Left,
+ Right
+ };
+
+ void DestroyInclusiveCrosshair();
+ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) noexcept;
+ void StartDrawing();
+ void StopDrawing();
+ bool CreateInclusiveCrosshair();
+ void UpdateCrosshairPosition();
+ HHOOK m_mouseHook = NULL;
+ static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) noexcept;
+
+ static constexpr auto m_className = L"MousePointerCrosshair";
+ static constexpr auto m_windowTitle = L"PowerToys Mouse Pointer Crosshair";
+ 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_crosshair_border_layer{ nullptr };
+ winrt::LayerVisual m_crosshair_layer{ nullptr };
+ winrt::SpriteVisual m_left_crosshair_border{ nullptr };
+ winrt::SpriteVisual m_left_crosshair{ nullptr };
+ winrt::SpriteVisual m_right_crosshair_border{ nullptr };
+ winrt::SpriteVisual m_right_crosshair{ nullptr };
+ winrt::SpriteVisual m_top_crosshair_border{ nullptr };
+ winrt::SpriteVisual m_top_crosshair{ nullptr };
+ winrt::SpriteVisual m_bottom_crosshair_border{ nullptr };
+ winrt::SpriteVisual m_bottom_crosshair{ nullptr };
+
+ bool m_visible = false;
+ bool m_destroyed = false;
+
+ // Configurable Settings
+ winrt::Windows::UI::Color m_crosshair_border_color = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_BORDER_COLOR;
+ winrt::Windows::UI::Color m_crosshair_color = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_COLOR;
+ float m_crosshair_radius = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_RADIUS;
+ float m_crosshair_thickness = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_THICKNESS;
+ float m_crosshair_border_size = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_BORDER_SIZE;
+ float m_crosshair_opacity = max(0.f, min(1.f, (float)INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_OPACITY / 100.0f));
+};
+
+InclusiveCrosshair* InclusiveCrosshair::instance = nullptr;
+
+bool InclusiveCrosshair::CreateInclusiveCrosshair()
+{
+ try
+ {
+ // We need a dispatcher queue.
+ DispatcherQueueOptions options = {
+ 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()->CreateDesktopWindowTarget(m_hwnd, false, &target));
+ *winrt::put_abi(m_target) = target;
+
+ // Our composition tree:
+ //
+ // [root] ContainerVisual
+ // \ [crosshair border layer] LayerVisual
+ // \ [crosshair border sprites]
+ // [crosshair layer] LayerVisual
+ // \ [crosshair sprites]
+
+ m_root = m_compositor.CreateContainerVisual();
+ m_root.RelativeSizeAdjustment({ 1.0f, 1.0f });
+ m_target.Root(m_root);
+
+ m_root.Opacity(m_crosshair_opacity);
+
+ m_crosshair_border_layer = m_compositor.CreateLayerVisual();
+ m_crosshair_border_layer.RelativeSizeAdjustment({ 1.0f, 1.0f });
+ m_root.Children().InsertAtTop(m_crosshair_border_layer);
+ m_crosshair_border_layer.Opacity(1.0f);
+
+ m_crosshair_layer = m_compositor.CreateLayerVisual();
+ m_crosshair_layer.RelativeSizeAdjustment({ 1.0f, 1.0f });
+
+ // Create the crosshair sprites.
+ m_left_crosshair_border = m_compositor.CreateSpriteVisual();
+ m_left_crosshair_border.AnchorPoint({ 1.0f, 0.5f });
+ m_left_crosshair_border.Brush(m_compositor.CreateColorBrush(m_crosshair_border_color));
+ m_crosshair_border_layer.Children().InsertAtTop(m_left_crosshair_border);
+ m_left_crosshair = m_compositor.CreateSpriteVisual();
+ m_left_crosshair.AnchorPoint({ 1.0f, 0.5f });
+ m_left_crosshair.Brush(m_compositor.CreateColorBrush(m_crosshair_color));
+ m_crosshair_layer.Children().InsertAtTop(m_left_crosshair);
+
+ m_right_crosshair_border = m_compositor.CreateSpriteVisual();
+ m_right_crosshair_border.AnchorPoint({ 0.0f, 0.5f });
+ m_right_crosshair_border.Brush(m_compositor.CreateColorBrush(m_crosshair_border_color));
+ m_crosshair_border_layer.Children().InsertAtTop(m_right_crosshair_border);
+ m_right_crosshair = m_compositor.CreateSpriteVisual();
+ m_right_crosshair.AnchorPoint({ 0.0f, 0.5f });
+ m_right_crosshair.Brush(m_compositor.CreateColorBrush(m_crosshair_color));
+ m_crosshair_layer.Children().InsertAtTop(m_right_crosshair);
+
+ m_top_crosshair_border = m_compositor.CreateSpriteVisual();
+ m_top_crosshair_border.AnchorPoint({ 0.5f, 1.0f });
+ m_top_crosshair_border.Brush(m_compositor.CreateColorBrush(m_crosshair_border_color));
+ m_crosshair_border_layer.Children().InsertAtTop(m_top_crosshair_border);
+ m_top_crosshair = m_compositor.CreateSpriteVisual();
+ m_top_crosshair.AnchorPoint({ 0.5f, 1.0f });
+ m_top_crosshair.Brush(m_compositor.CreateColorBrush(m_crosshair_color));
+ m_crosshair_layer.Children().InsertAtTop(m_top_crosshair);
+
+ m_bottom_crosshair_border = m_compositor.CreateSpriteVisual();
+ m_bottom_crosshair_border.AnchorPoint({ 0.5f, 0.0f });
+ m_bottom_crosshair_border.Brush(m_compositor.CreateColorBrush(m_crosshair_border_color));
+ m_crosshair_border_layer.Children().InsertAtTop(m_bottom_crosshair_border);
+ m_bottom_crosshair = m_compositor.CreateSpriteVisual();
+ m_bottom_crosshair.AnchorPoint({ 0.5f, 0.0f });
+ m_bottom_crosshair.Brush(m_compositor.CreateColorBrush(m_crosshair_color));
+ m_crosshair_layer.Children().InsertAtTop(m_bottom_crosshair);
+
+ m_crosshair_border_layer.Children().InsertAtTop(m_crosshair_layer);
+ m_crosshair_layer.Opacity(1.0f);
+
+ UpdateCrosshairPosition();
+
+ return true;
+ }
+ catch (...)
+ {
+ return false;
+ }
+}
+
+void InclusiveCrosshair::UpdateCrosshairPosition()
+{
+ POINT ptCursor;
+
+ GetCursorPos(&ptCursor);
+
+ HMONITOR cursorMonitor = MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST);
+
+ if (cursorMonitor == NULL)
+ {
+ return;
+ }
+
+ MONITORINFO monitorInfo;
+ monitorInfo.cbSize = sizeof(monitorInfo);
+
+ if (!GetMonitorInfo(cursorMonitor, &monitorInfo))
+ {
+ return;
+ }
+
+ POINT ptMonitorUpperLeft;
+ ptMonitorUpperLeft.x = monitorInfo.rcMonitor.left;
+ ptMonitorUpperLeft.y = monitorInfo.rcMonitor.top;
+
+ POINT ptMonitorBottomRight;
+ ptMonitorBottomRight.x = monitorInfo.rcMonitor.right;
+ ptMonitorBottomRight.y = monitorInfo.rcMonitor.bottom;
+
+ // Convert everything to client coordinates.
+ ScreenToClient(m_hwnd, &ptCursor);
+ ScreenToClient(m_hwnd, &ptMonitorUpperLeft);
+ ScreenToClient(m_hwnd, &ptMonitorBottomRight);
+
+ // Position crosshair components around the mouse pointer.
+ m_left_crosshair_border.Offset({ (float)ptCursor.x - m_crosshair_radius + m_crosshair_border_size, (float)ptCursor.y, .0f });
+ m_left_crosshair_border.Size({ (float)ptCursor.x - (float)ptMonitorUpperLeft.x - m_crosshair_radius + m_crosshair_border_size, m_crosshair_thickness + m_crosshair_border_size * 2 });
+ m_left_crosshair.Offset({ (float)ptCursor.x - m_crosshair_radius, (float)ptCursor.y, .0f });
+ m_left_crosshair.Size({ (float)ptCursor.x - (float)ptMonitorUpperLeft.x - m_crosshair_radius, m_crosshair_thickness });
+
+ m_right_crosshair_border.Offset({ (float)ptCursor.x + m_crosshair_radius - m_crosshair_border_size, (float)ptCursor.y, .0f });
+ m_right_crosshair_border.Size({ (float)ptMonitorBottomRight.x - (float)ptCursor.x - m_crosshair_radius + m_crosshair_border_size, m_crosshair_thickness + m_crosshair_border_size * 2 });
+ m_right_crosshair.Offset({ (float)ptCursor.x + m_crosshair_radius, (float)ptCursor.y, .0f });
+ m_right_crosshair.Size({ (float)ptMonitorBottomRight.x - (float)ptCursor.x - m_crosshair_radius, m_crosshair_thickness });
+
+ m_top_crosshair_border.Offset({ (float)ptCursor.x, (float)ptCursor.y - m_crosshair_radius + m_crosshair_border_size, .0f });
+ m_top_crosshair_border.Size({ m_crosshair_thickness + m_crosshair_border_size * 2, (float)ptCursor.y - (float)ptMonitorUpperLeft.y - m_crosshair_radius + m_crosshair_border_size });
+ m_top_crosshair.Offset({ (float)ptCursor.x, (float)ptCursor.y - m_crosshair_radius, .0f });
+ m_top_crosshair.Size({ m_crosshair_thickness, (float)ptCursor.y - (float)ptMonitorUpperLeft.y - m_crosshair_radius });
+
+ m_bottom_crosshair_border.Offset({ (float)ptCursor.x, (float)ptCursor.y + m_crosshair_radius - m_crosshair_border_size, .0f });
+ m_bottom_crosshair_border.Size({ m_crosshair_thickness + m_crosshair_border_size * 2, (float)ptMonitorBottomRight.y - (float)ptCursor.y - m_crosshair_radius + m_crosshair_border_size });
+ m_bottom_crosshair.Offset({ (float)ptCursor.x, (float)ptCursor.y + m_crosshair_radius, .0f });
+ m_bottom_crosshair.Size({ m_crosshair_thickness, (float)ptMonitorBottomRight.y - (float)ptCursor.y - m_crosshair_radius });
+
+}
+
+LRESULT CALLBACK InclusiveCrosshair::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) noexcept
+{
+ if (nCode >= 0)
+ {
+ MSLLHOOKSTRUCT* hookData = (MSLLHOOKSTRUCT*)lParam;
+ if (wParam == WM_MOUSEMOVE) {
+ instance->UpdateCrosshairPosition();
+ }
+ }
+ return CallNextHookEx(0, nCode, wParam, lParam);
+}
+
+void InclusiveCrosshair::StartDrawing()
+{
+ Logger::info("Start drawing crosshairs.");
+ Trace::StartDrawingCrosshair();
+ m_visible = true;
+ SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN), GetSystemMetrics(SM_YVIRTUALSCREEN), GetSystemMetrics(SM_CXVIRTUALSCREEN), GetSystemMetrics(SM_CYVIRTUALSCREEN), 0);
+ ShowWindow(m_hwnd, SW_SHOWNOACTIVATE);
+ m_mouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, m_hinstance, 0);
+ UpdateCrosshairPosition();
+}
+
+void InclusiveCrosshair::StopDrawing()
+{
+ Logger::info("Stop drawing crosshairs.");
+ m_visible = false;
+ ShowWindow(m_hwnd, SW_HIDE);
+ UnhookWindowsHookEx(m_mouseHook);
+ m_mouseHook = NULL;
+}
+
+void InclusiveCrosshair::SwitchActivationMode()
+{
+ PostMessage(m_hwnd, WM_SWITCH_ACTIVATION_MODE, 0, 0);
+}
+
+void InclusiveCrosshair::ApplySettings(InclusiveCrosshairSettings& settings, bool applyToRunTimeObjects)
+{
+ m_crosshair_radius = (float)settings.crosshairRadius;
+ m_crosshair_thickness = (float)settings.crosshairThickness;
+ m_crosshair_color = settings.crosshairColor;
+ m_crosshair_opacity = max(0.f, min(1.f, (float)settings.crosshairOpacity / 100.0f));
+ m_crosshair_border_color = settings.crosshairBorderColor;
+ m_crosshair_border_size = (float)settings.crosshairBorderSize;
+
+ if (applyToRunTimeObjects)
+ {
+ // Runtime objects already created. Should update in the owner thread.
+ auto dispatcherQueue = m_dispatcherQueueController.DispatcherQueue();
+ InclusiveCrosshairSettings localSettings = settings;
+ bool enqueueSucceeded = dispatcherQueue.TryEnqueue([=]() {
+ if (!m_destroyed)
+ {
+ // Apply new settings to runtime composition objects.
+ m_left_crosshair.Brush().as().Color(m_crosshair_color);
+ m_right_crosshair.Brush().as().Color(m_crosshair_color);
+ m_top_crosshair.Brush().as().Color(m_crosshair_color);
+ m_bottom_crosshair.Brush().as().Color(m_crosshair_color);
+ m_left_crosshair_border.Brush().as().Color(m_crosshair_border_color);
+ m_right_crosshair_border.Brush().as().Color(m_crosshair_border_color);
+ m_top_crosshair_border.Brush().as().Color(m_crosshair_border_color);
+ m_bottom_crosshair_border.Brush().as().Color(m_crosshair_border_color);
+ m_root.Opacity(m_crosshair_opacity);
+ UpdateCrosshairPosition();
+ }
+ });
+ if (!enqueueSucceeded)
+ {
+ Logger::error("Couldn't enqueue message to update the crosshair settings.");
+ }
+ }
+}
+
+void InclusiveCrosshair::DestroyInclusiveCrosshair()
+{
+ StopDrawing();
+ PostQuitMessage(0);
+}
+
+LRESULT CALLBACK InclusiveCrosshair::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->CreateInclusiveCrosshair() ? 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->DestroyInclusiveCrosshair();
+ break;
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ return 0;
+}
+
+bool InclusiveCrosshair::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);
+ wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
+ 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;
+ return CreateWindowExW(exStyle, m_className, m_windowTitle, WS_POPUP, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, m_hwndOwner, nullptr, hInstance, nullptr) != nullptr;
+}
+
+void InclusiveCrosshair::Terminate()
+{
+ auto dispatcherQueue = m_dispatcherQueueController.DispatcherQueue();
+ bool enqueueSucceeded = dispatcherQueue.TryEnqueue([=]() {
+ m_destroyed = true;
+ DestroyWindow(m_hwndOwner);
+ });
+ if (!enqueueSucceeded)
+ {
+ Logger::error("Couldn't enqueue message to destroy the window.");
+ }
+}
+
+#pragma region InclusiveCrosshair_API
+
+void InclusiveCrosshairApplySettings(InclusiveCrosshairSettings& settings)
+{
+ if (InclusiveCrosshair::instance != nullptr)
+ {
+ Logger::info("Applying settings.");
+ InclusiveCrosshair::instance->ApplySettings(settings, true);
+ }
+}
+
+void InclusiveCrosshairSwitch()
+{
+ if (InclusiveCrosshair::instance != nullptr)
+ {
+ Logger::info("Switching activation mode.");
+ InclusiveCrosshair::instance->SwitchActivationMode();
+ }
+}
+
+void InclusiveCrosshairDisable()
+{
+ if (InclusiveCrosshair::instance != nullptr)
+ {
+ Logger::info("Terminating the crosshair instance.");
+ InclusiveCrosshair::instance->Terminate();
+ }
+}
+
+bool InclusiveCrosshairIsEnabled()
+{
+ return (InclusiveCrosshair::instance != nullptr);
+}
+
+int InclusiveCrosshairMain(HINSTANCE hInstance, InclusiveCrosshairSettings& settings)
+{
+ Logger::info("Starting a crosshair instance.");
+ if (InclusiveCrosshair::instance != nullptr)
+ {
+ Logger::error("A crosshair instance was still working when trying to start a new one.");
+ return 0;
+ }
+
+ // Perform application initialization:
+ InclusiveCrosshair crosshair;
+ InclusiveCrosshair::instance = &crosshair;
+ crosshair.ApplySettings(settings, false);
+ if (!crosshair.MyRegisterClass(hInstance))
+ {
+ Logger::error("Couldn't initialize a crosshair instance.");
+ InclusiveCrosshair::instance = nullptr;
+ return FALSE;
+ }
+ Logger::info("Initialized the crosshair instance.");
+
+ MSG msg;
+
+ // Main message loop:
+ while (GetMessage(&msg, nullptr, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ Logger::info("Crosshair message loop ended.");
+ InclusiveCrosshair::instance = nullptr;
+
+ return (int)msg.wParam;
+}
+
+#pragma endregion InclusiveCrosshair_API
diff --git a/src/modules/MouseUtils/MousePointerCrosshair/InclusiveCrosshair.h b/src/modules/MouseUtils/MousePointerCrosshair/InclusiveCrosshair.h
new file mode 100644
index 0000000000..086ef709b2
--- /dev/null
+++ b/src/modules/MouseUtils/MousePointerCrosshair/InclusiveCrosshair.h
@@ -0,0 +1,25 @@
+#pragma once
+#include "pch.h"
+
+constexpr int INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_OPACITY = 75;
+const winrt::Windows::UI::Color INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(255, 255, 0, 0);
+const winrt::Windows::UI::Color INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_BORDER_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(255, 255, 255, 255);
+constexpr int INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_RADIUS = 20;
+constexpr int INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_THICKNESS = 5;
+constexpr int INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_BORDER_SIZE = 1;
+
+struct InclusiveCrosshairSettings
+{
+ winrt::Windows::UI::Color crosshairColor = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_COLOR;
+ winrt::Windows::UI::Color crosshairBorderColor = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_BORDER_COLOR;
+ int crosshairRadius = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_RADIUS;
+ int crosshairThickness = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_THICKNESS;
+ int crosshairOpacity = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_OPACITY;
+ int crosshairBorderSize = INCLUSIVE_MOUSE_DEFAULT_CROSSHAIR_BORDER_SIZE;
+};
+
+int InclusiveCrosshairMain(HINSTANCE hinst, InclusiveCrosshairSettings& settings);
+void InclusiveCrosshairDisable();
+bool InclusiveCrosshairIsEnabled();
+void InclusiveCrosshairSwitch();
+void InclusiveCrosshairApplySettings(InclusiveCrosshairSettings& settings);
diff --git a/src/modules/MouseUtils/MousePointerCrosshair/MousePointerCrosshair.rc b/src/modules/MouseUtils/MousePointerCrosshair/MousePointerCrosshair.rc
new file mode 100644
index 0000000000..5fa3c8b90d
--- /dev/null
+++ b/src/modules/MouseUtils/MousePointerCrosshair/MousePointerCrosshair.rc
@@ -0,0 +1,40 @@
+#include
+#include "resource.h"
+#include "../../../common/version/version.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+#include "winres.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+1 VERSIONINFO
+FILEVERSION FILE_VERSION
+PRODUCTVERSION PRODUCT_VERSION
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+FILEFLAGS VS_FF_DEBUG
+#else
+FILEFLAGS 0x0L
+#endif
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_DLL
+FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
+ BEGIN
+ VALUE "CompanyName", COMPANY_NAME
+ VALUE "FileDescription", FILE_DESCRIPTION
+ VALUE "FileVersion", FILE_VERSION_STRING
+ VALUE "InternalName", INTERNAL_NAME
+ VALUE "LegalCopyright", COPYRIGHT_NOTE
+ VALUE "OriginalFilename", ORIGINAL_FILENAME
+ VALUE "ProductName", PRODUCT_NAME
+ VALUE "ProductVersion", PRODUCT_VERSION_STRING
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
+ END
+END
diff --git a/src/modules/MouseUtils/MousePointerCrosshair/MousePointerCrosshair.vcxproj b/src/modules/MouseUtils/MousePointerCrosshair/MousePointerCrosshair.vcxproj
new file mode 100644
index 0000000000..c1f783ca29
--- /dev/null
+++ b/src/modules/MouseUtils/MousePointerCrosshair/MousePointerCrosshair.vcxproj
@@ -0,0 +1,145 @@
+
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 15.0
+ {eae14c0e-7a6b-45da-9080-a7d8c077ba6e}
+ Win32Proj
+ MousePointerCrosshair
+ 10.0.18362.0
+ MousePointerCrosshair
+
+
+
+
+ DynamicLibrary
+ true
+ v142
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v142
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)$(Platform)\$(Configuration)\modules\MouseUtils\
+ PowerToys.MousePointerCrosshair
+
+
+ false
+ $(SolutionDir)$(Platform)\$(Configuration)\modules\MouseUtils\
+ PowerToys.MousePointerCrosshair
+
+
+
+ Level3
+ Disabled
+ true
+ _DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ MultiThreadedDebug
+ stdcpplatest
+
+
+ Windows
+ true
+ $(OutDir)$(TargetName)$(TargetExt)
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+ stdcpplatest
+
+
+ Windows
+ true
+ true
+ true
+ $(OutDir)$(TargetName)$(TargetExt)
+
+
+
+
+ $(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories)
+
+
+
+
+ Use
+ pch.h
+
+
+
+
+
+
+
+
+
+
+
+
+ Create
+
+
+
+
+
+
+
+
+
+
+
+ {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}
+
+
+ {6955446d-23f7-4023-9bb3-8657f904af99}
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/MouseUtils/MousePointerCrosshair/MousePointerCrosshair.vcxproj.filters b/src/modules/MouseUtils/MousePointerCrosshair/MousePointerCrosshair.vcxproj.filters
new file mode 100644
index 0000000000..208f1a0dda
--- /dev/null
+++ b/src/modules/MouseUtils/MousePointerCrosshair/MousePointerCrosshair.vcxproj.filters
@@ -0,0 +1,56 @@
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Resource Files
+
+
+ Header Files
+
+
+
+
+
+
+
+ {890924c4-f592-4e84-b140-4b07a607a224}
+
+
+ {a9a2fd9b-de66-43dc-99b2-56f0d1ddecda}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {b5d0d62e-0275-439b-a910-e7c5befad045}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+ {5e350f4d-b07a-4bb2-8e63-b2c527358234}
+ cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx
+
+
+
+
+ Resource Files
+
+
+
\ No newline at end of file
diff --git a/src/modules/MouseUtils/MousePointerCrosshair/dllmain.cpp b/src/modules/MouseUtils/MousePointerCrosshair/dllmain.cpp
new file mode 100644
index 0000000000..532069e396
--- /dev/null
+++ b/src/modules/MouseUtils/MousePointerCrosshair/dllmain.cpp
@@ -0,0 +1,312 @@
+#include "pch.h"
+#include
+#include
+#include "trace.h"
+#include "InclusiveCrosshair.h"
+#include "common/utils/color.h"
+
+// Non-Localizable strings
+namespace
+{
+ const wchar_t JSON_KEY_PROPERTIES[] = L"properties";
+ const wchar_t JSON_KEY_VALUE[] = L"value";
+ const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"activation_shortcut";
+ const wchar_t JSON_KEY_CROSSHAIR_COLOR[] = L"crosshair_color";
+ const wchar_t JSON_KEY_CROSSHAIR_OPACITY[] = L"crosshair_opacity";
+ const wchar_t JSON_KEY_CROSSHAIR_RADIUS[] = L"crosshair_radius";
+ const wchar_t JSON_KEY_CROSSHAIR_THICKNESS[] = L"crosshair_thickness";
+ const wchar_t JSON_KEY_CROSSHAIR_BORDER_COLOR[] = L"crosshair_border_color";
+ const wchar_t JSON_KEY_CROSSHAIR_BORDER_SIZE[] = L"crosshair_border_size";
+}
+
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+
+HMODULE m_hModule;
+
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+ m_hModule = hModule;
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ Trace::RegisterProvider();
+ break;
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ case DLL_PROCESS_DETACH:
+ Trace::UnregisterProvider();
+ break;
+ }
+ return TRUE;
+}
+
+// The PowerToy name that will be shown in the settings.
+const static wchar_t* MODULE_NAME = L"MousePointerCrosshair";
+// Add a description that will we shown in the module settings page.
+const static wchar_t* MODULE_DESC = L"";
+
+// Implement the PowerToy Module Interface and all the required methods.
+class MousePointerCrosshair : public PowertoyModuleIface
+{
+private:
+ // The PowerToy state.
+ bool m_enabled = false;
+
+ // Hotkey to invoke the module
+ HotkeyEx m_hotkey;
+
+ // Mouse Pointer Crosshair specific settings
+ InclusiveCrosshairSettings m_inclusiveCrosshairSettings;
+
+public:
+ // Constructor
+ MousePointerCrosshair()
+ {
+ LoggerHelpers::init_logger(MODULE_NAME, L"ModuleInterface", LogSettings::mousePointerCrosshairLoggerName);
+ init_settings();
+ };
+
+ // Destroy the powertoy and free memory
+ virtual void destroy() override
+ {
+ delete this;
+ }
+
+ // Return the localized display name of the powertoy
+ virtual const wchar_t* get_name() override
+ {
+ return MODULE_NAME;
+ }
+
+ // Return the non localized key of the powertoy, this will be cached by the runner
+ virtual const wchar_t* get_key() override
+ {
+ return MODULE_NAME;
+ }
+
+ // Return JSON with the configuration options.
+ virtual bool get_config(wchar_t* buffer, int* buffer_size) override
+ {
+ HINSTANCE hinstance = reinterpret_cast(&__ImageBase);
+
+ PowerToysSettings::Settings settings(hinstance, get_name());
+
+ return settings.serialize_to_buffer(buffer, buffer_size);
+ }
+
+ // Signal from the Settings editor to call a custom action.
+ // This can be used to spawn more complex editors.
+ virtual void call_custom_action(const wchar_t* action) override
+ {
+ }
+
+ // Called by the runner to pass the updated settings values as a serialized JSON.
+ virtual void set_config(const wchar_t* config) override
+ {
+ try
+ {
+ // Parse the input JSON string.
+ PowerToysSettings::PowerToyValues values =
+ PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
+
+ parse_settings(values);
+
+ InclusiveCrosshairApplySettings(m_inclusiveCrosshairSettings);
+ }
+ catch (std::exception&)
+ {
+ Logger::error("Invalid json when trying to parse Mouse Pointer Crosshair settings json.");
+ }
+ }
+
+ // Enable the powertoy
+ virtual void enable()
+ {
+ m_enabled = true;
+ Trace::EnableMousePointerCrosshair(true);
+ std::thread([=]() { InclusiveCrosshairMain(m_hModule, m_inclusiveCrosshairSettings); }).detach();
+ }
+
+ // Disable the powertoy
+ virtual void disable()
+ {
+ m_enabled = false;
+ Trace::EnableMousePointerCrosshair(false);
+ InclusiveCrosshairDisable();
+ }
+
+ // Returns if the powertoys is enabled
+ virtual bool is_enabled() override
+ {
+ return m_enabled;
+ }
+
+ // Returns whether the PowerToys should be enabled by default
+ virtual bool is_enabled_by_default() const override
+ {
+ return false;
+ }
+
+ virtual std::optional GetHotkeyEx() override
+ {
+ return m_hotkey;
+ }
+
+ virtual void OnHotkeyEx() override
+ {
+ InclusiveCrosshairSwitch();
+ }
+ // Load the settings file.
+ void init_settings()
+ {
+ try
+ {
+ // Load and parse the settings file for this PowerToy.
+ PowerToysSettings::PowerToyValues settings =
+ PowerToysSettings::PowerToyValues::load_from_settings_file(MousePointerCrosshair::get_key());
+ parse_settings(settings);
+ }
+ catch (std::exception&)
+ {
+ Logger::error("Invalid json when trying to load the Mouse Pointer Crosshair settings json from file.");
+ }
+ }
+
+ void parse_settings(PowerToysSettings::PowerToyValues& settings)
+ {
+ // TODO: refactor to use common/utils/json.h instead
+ auto settingsObject = settings.get_raw_json();
+ InclusiveCrosshairSettings inclusiveCrosshairSettings;
+ if (settingsObject.GetView().Size())
+ {
+ try
+ {
+ // Parse HotKey
+ auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ACTIVATION_SHORTCUT);
+ auto hotkey = PowerToysSettings::HotkeyObject::from_json(jsonPropertiesObject);
+ m_hotkey = HotkeyEx();
+ if (hotkey.win_pressed())
+ {
+ m_hotkey.modifiersMask |= MOD_WIN;
+ }
+
+ if (hotkey.ctrl_pressed())
+ {
+ m_hotkey.modifiersMask |= MOD_CONTROL;
+ }
+
+ if (hotkey.shift_pressed())
+ {
+ m_hotkey.modifiersMask |= MOD_SHIFT;
+ }
+
+ if (hotkey.alt_pressed())
+ {
+ m_hotkey.modifiersMask |= MOD_ALT;
+ }
+
+ m_hotkey.vkCode = hotkey.get_code();
+ }
+ catch (...)
+ {
+ Logger::warn("Failed to initialize Mouse Pointer Crosshair activation shortcut");
+ }
+ try
+ {
+ // Parse Opacity
+ auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIR_OPACITY);
+ inclusiveCrosshairSettings.crosshairOpacity = (uint8_t)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE);
+ }
+ catch (...)
+ {
+ Logger::warn("Failed to initialize Opacity from settings. Will use default value");
+ }
+ try
+ {
+ // Parse crosshair color
+ auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIR_COLOR);
+ auto crosshairColor = (std::wstring)jsonPropertiesObject.GetNamedString(JSON_KEY_VALUE);
+ uint8_t r, g, b;
+ if (!checkValidRGB(crosshairColor, &r, &g, &b))
+ {
+ Logger::error("Crosshair color RGB value is invalid. Will use default value");
+ }
+ else
+ {
+ inclusiveCrosshairSettings.crosshairColor = winrt::Windows::UI::ColorHelper::FromArgb(255, r, g, b);
+ }
+ }
+ catch (...)
+ {
+ Logger::warn("Failed to initialize crosshair color from settings. Will use default value");
+ }
+ try
+ {
+ // Parse Radius
+ auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIR_RADIUS);
+ inclusiveCrosshairSettings.crosshairRadius = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE);
+ }
+ catch (...)
+ {
+ Logger::warn("Failed to initialize Radius from settings. Will use default value");
+ }
+ try
+ {
+ // Parse Thickness
+ auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIR_THICKNESS);
+ inclusiveCrosshairSettings.crosshairThickness = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE);
+ }
+ catch (...)
+ {
+ Logger::warn("Failed to initialize Thickness from settings. Will use default value");
+ }
+ try
+ {
+ // Parse crosshair border color
+ auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIR_BORDER_COLOR);
+ auto crosshairBorderColor = (std::wstring)jsonPropertiesObject.GetNamedString(JSON_KEY_VALUE);
+ uint8_t r, g, b;
+ if (!checkValidRGB(crosshairBorderColor, &r, &g, &b))
+ {
+ Logger::error("Crosshair border color RGB value is invalid. Will use default value");
+ }
+ else
+ {
+ inclusiveCrosshairSettings.crosshairBorderColor = winrt::Windows::UI::ColorHelper::FromArgb(255, r, g, b);
+ }
+ }
+ catch (...)
+ {
+ Logger::warn("Failed to initialize crosshair border color from settings. Will use default value");
+ }
+ try
+ {
+ // Parse border size
+ auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIR_BORDER_SIZE);
+ inclusiveCrosshairSettings.crosshairBorderSize = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE);
+ }
+ catch (...)
+ {
+ Logger::warn("Failed to initialize border color from settings. Will use default value");
+ }
+ }
+ else
+ {
+ Logger::info("Mouse Pointer Crosshair settings are empty");
+ }
+ if (!m_hotkey.modifiersMask)
+ {
+ Logger::info("Mouse Pointer Crosshair is going to use default shortcut");
+ m_hotkey.modifiersMask = MOD_CONTROL | MOD_ALT;
+ m_hotkey.vkCode = 0x50; // P key
+ }
+ m_inclusiveCrosshairSettings = inclusiveCrosshairSettings;
+ }
+
+};
+
+extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
+{
+ return new MousePointerCrosshair();
+}
\ No newline at end of file
diff --git a/src/modules/MouseUtils/MousePointerCrosshair/packages.config b/src/modules/MouseUtils/MousePointerCrosshair/packages.config
new file mode 100644
index 0000000000..81f107b8bc
--- /dev/null
+++ b/src/modules/MouseUtils/MousePointerCrosshair/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/MouseUtils/MousePointerCrosshair/pch.cpp b/src/modules/MouseUtils/MousePointerCrosshair/pch.cpp
new file mode 100644
index 0000000000..1d9f38c57d
--- /dev/null
+++ b/src/modules/MouseUtils/MousePointerCrosshair/pch.cpp
@@ -0,0 +1 @@
+#include "pch.h"
diff --git a/src/modules/MouseUtils/MousePointerCrosshair/pch.h b/src/modules/MouseUtils/MousePointerCrosshair/pch.h
new file mode 100644
index 0000000000..5fc459cbc9
--- /dev/null
+++ b/src/modules/MouseUtils/MousePointerCrosshair/pch.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#define COMPOSITION
+#define WIN32_LEAN_AND_MEAN
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
diff --git a/src/modules/MouseUtils/MousePointerCrosshair/resource.h b/src/modules/MouseUtils/MousePointerCrosshair/resource.h
new file mode 100644
index 0000000000..e4ff6cf76a
--- /dev/null
+++ b/src/modules/MouseUtils/MousePointerCrosshair/resource.h
@@ -0,0 +1,13 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by MousePointerCrosshair.rc
+
+//////////////////////////////
+// Non-localizable
+
+#define FILE_DESCRIPTION "PowerToys MousePointerCrosshair"
+#define INTERNAL_NAME "MousePointerCrosshair"
+#define ORIGINAL_FILENAME "PowerToys.MousePointerCrosshair.dll"
+
+// Non-localizable
+//////////////////////////////
diff --git a/src/modules/MouseUtils/MousePointerCrosshair/trace.cpp b/src/modules/MouseUtils/MousePointerCrosshair/trace.cpp
new file mode 100644
index 0000000000..b4fc60c923
--- /dev/null
+++ b/src/modules/MouseUtils/MousePointerCrosshair/trace.cpp
@@ -0,0 +1,40 @@
+#include "pch.h"
+#include "trace.h"
+
+TRACELOGGING_DEFINE_PROVIDER(
+ g_hProvider,
+ "Microsoft.PowerToys",
+ // {38e8889b-9731-53f5-e901-e8a7c1753074}
+ (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
+ TraceLoggingOptionProjectTelemetry());
+
+void Trace::RegisterProvider() noexcept
+{
+ TraceLoggingRegister(g_hProvider);
+}
+
+void Trace::UnregisterProvider() noexcept
+{
+ TraceLoggingUnregister(g_hProvider);
+}
+
+// Log if the user has MousePointerCrosshair enabled or disabled
+void Trace::EnableMousePointerCrosshair(const bool enabled) noexcept
+{
+ TraceLoggingWrite(
+ g_hProvider,
+ "MousePointerCrosshair_EnableMousePointerCrosshair",
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
+ TraceLoggingBoolean(enabled, "Enabled"));
+}
+
+// Log that the user activated the module by having the crosshair be drawn
+void Trace::StartDrawingCrosshair() noexcept
+{
+ TraceLoggingWrite(
+ g_hProvider,
+ "MousePointerCrosshair_StartDrawingCrosshair",
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
+}
diff --git a/src/modules/MouseUtils/MousePointerCrosshair/trace.h b/src/modules/MouseUtils/MousePointerCrosshair/trace.h
new file mode 100644
index 0000000000..d12eabc44a
--- /dev/null
+++ b/src/modules/MouseUtils/MousePointerCrosshair/trace.h
@@ -0,0 +1,14 @@
+#pragma once
+
+class Trace
+{
+public:
+ static void RegisterProvider() noexcept;
+ static void UnregisterProvider() noexcept;
+
+ // Log if the user has MousePointerCrosshair enabled or disabled
+ static void EnableMousePointerCrosshair(const bool enabled) noexcept;
+
+ // Log that the user activated the module by having the crosshair be drawn
+ static void StartDrawingCrosshair() noexcept;
+};
diff --git a/src/runner/main.cpp b/src/runner/main.cpp
index 08b6816d59..f1b9a48960 100644
--- a/src/runner/main.cpp
+++ b/src/runner/main.cpp
@@ -151,7 +151,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
L"modules/MouseUtils/PowerToys.FindMyMouse.dll" ,
L"modules/MouseUtils/PowerToys.MouseHighlighter.dll",
L"modules/AlwaysOnTop/PowerToys.AlwaysOnTopModuleInterface.dll",
-
+ L"modules/MouseUtils/PowerToys.MousePointerCrosshair.dll",
};
const auto VCM_PATH = L"modules/VideoConference/PowerToys.VideoConferenceModule.dll";
if (const auto mf = LoadLibraryA("mf.dll"))
diff --git a/src/settings-ui/Settings.UI.Library/EnabledModules.cs b/src/settings-ui/Settings.UI.Library/EnabledModules.cs
index 09f0015277..3e074c2aae 100644
--- a/src/settings-ui/Settings.UI.Library/EnabledModules.cs
+++ b/src/settings-ui/Settings.UI.Library/EnabledModules.cs
@@ -223,6 +223,22 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}
}
+ private bool mousePointerCrosshair = true;
+
+ [JsonPropertyName("MousePointerCrosshair")]
+ public bool MousePointerCrosshair
+ {
+ get => mousePointerCrosshair;
+ set
+ {
+ if (mousePointerCrosshair != value)
+ {
+ LogTelemetryEvent(value);
+ mousePointerCrosshair = value;
+ }
+ }
+ }
+
public string ToJsonString()
{
return JsonSerializer.Serialize(this);
diff --git a/src/settings-ui/Settings.UI.Library/MousePointerCrosshairProperties.cs b/src/settings-ui/Settings.UI.Library/MousePointerCrosshairProperties.cs
new file mode 100644
index 0000000000..155add7396
--- /dev/null
+++ b/src/settings-ui/Settings.UI.Library/MousePointerCrosshairProperties.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization;
+
+namespace Microsoft.PowerToys.Settings.UI.Library
+{
+ public class MousePointerCrosshairProperties
+ {
+ [JsonPropertyName("activation_shortcut")]
+ public HotkeySettings ActivationShortcut { get; set; }
+
+ [JsonPropertyName("crosshair_color")]
+ public StringProperty CrosshairColor { get; set; }
+
+ [JsonPropertyName("crosshair_opacity")]
+ public IntProperty CrosshairOpacity { get; set; }
+
+ [JsonPropertyName("crosshair_radius")]
+ public IntProperty CrosshairRadius { get; set; }
+
+ [JsonPropertyName("crosshair_thickness")]
+ public IntProperty CrosshairThickness { get; set; }
+
+ [JsonPropertyName("crosshair_border_color")]
+ public StringProperty CrosshairBorderColor { get; set; }
+
+ [JsonPropertyName("crosshair_border_size")]
+ public IntProperty CrosshairBorderSize { get; set; }
+
+ public MousePointerCrosshairProperties()
+ {
+ ActivationShortcut = new HotkeySettings(false, true, true, false, 0x50); // Ctrl + Alt + P
+ CrosshairColor = new StringProperty("#FF0000");
+ CrosshairOpacity = new IntProperty(75);
+ CrosshairRadius = new IntProperty(20);
+ CrosshairThickness = new IntProperty(5);
+ CrosshairBorderColor = new StringProperty("#FFFFFF");
+ CrosshairBorderSize = new IntProperty(1);
+ }
+ }
+}
diff --git a/src/settings-ui/Settings.UI.Library/MousePointerCrosshairSettings.cs b/src/settings-ui/Settings.UI.Library/MousePointerCrosshairSettings.cs
new file mode 100644
index 0000000000..08c0aa2a43
--- /dev/null
+++ b/src/settings-ui/Settings.UI.Library/MousePointerCrosshairSettings.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization;
+using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
+
+namespace Microsoft.PowerToys.Settings.UI.Library
+{
+ public class MousePointerCrosshairSettings : BasePTModuleSettings, ISettingsConfig
+ {
+ public const string ModuleName = "MousePointerCrosshair";
+
+ [JsonPropertyName("properties")]
+ public MousePointerCrosshairProperties Properties { get; set; }
+
+ public MousePointerCrosshairSettings()
+ {
+ Name = ModuleName;
+ Properties = new MousePointerCrosshairProperties();
+ Version = "1.0";
+ }
+
+ public string GetModuleName()
+ {
+ return Name;
+ }
+
+ // This can be utilized in the future if the settings.json file is to be modified/deleted.
+ public bool UpgradeSettingsConfiguration()
+ {
+ return false;
+ }
+ }
+}
diff --git a/src/settings-ui/Settings.UI.Library/MousePointerCrosshairSettingsIPCMessage.cs b/src/settings-ui/Settings.UI.Library/MousePointerCrosshairSettingsIPCMessage.cs
new file mode 100644
index 0000000000..18fa5e21cb
--- /dev/null
+++ b/src/settings-ui/Settings.UI.Library/MousePointerCrosshairSettingsIPCMessage.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Microsoft.PowerToys.Settings.UI.Library
+{
+ public class MousePointerCrosshairSettingsIPCMessage
+ {
+ [JsonPropertyName("powertoys")]
+ public SndMousePointerCrosshairSettings Powertoys { get; set; }
+
+ public MousePointerCrosshairSettingsIPCMessage()
+ {
+ }
+
+ public MousePointerCrosshairSettingsIPCMessage(SndMousePointerCrosshairSettings settings)
+ {
+ this.Powertoys = settings;
+ }
+
+ public string ToJsonString()
+ {
+ return JsonSerializer.Serialize(this);
+ }
+ }
+}
diff --git a/src/settings-ui/Settings.UI.Library/SndMousePointerCrosshairSettings.cs b/src/settings-ui/Settings.UI.Library/SndMousePointerCrosshairSettings.cs
new file mode 100644
index 0000000000..18576b2c38
--- /dev/null
+++ b/src/settings-ui/Settings.UI.Library/SndMousePointerCrosshairSettings.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Microsoft.PowerToys.Settings.UI.Library
+{
+ public class SndMousePointerCrosshairSettings
+ {
+ [JsonPropertyName("MousePointerCrosshair")]
+ public MousePointerCrosshairSettings MousePointerCrosshair { get; set; }
+
+ public SndMousePointerCrosshairSettings()
+ {
+ }
+
+ public SndMousePointerCrosshairSettings(MousePointerCrosshairSettings settings)
+ {
+ MousePointerCrosshair = settings;
+ }
+
+ public string ToJsonString()
+ {
+ return JsonSerializer.Serialize(this);
+ }
+ }
+}
diff --git a/src/settings-ui/Settings.UI.Library/ViewModels/MouseUtilsViewModel.cs b/src/settings-ui/Settings.UI.Library/ViewModels/MouseUtilsViewModel.cs
index d2c9a6a864..befc0ddd36 100644
--- a/src/settings-ui/Settings.UI.Library/ViewModels/MouseUtilsViewModel.cs
+++ b/src/settings-ui/Settings.UI.Library/ViewModels/MouseUtilsViewModel.cs
@@ -19,7 +19,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
private MouseHighlighterSettings MouseHighlighterSettingsConfig { get; set; }
- public MouseUtilsViewModel(ISettingsUtils settingsUtils, ISettingsRepository settingsRepository, ISettingsRepository findMyMouseSettingsRepository, ISettingsRepository mouseHighlighterSettingsRepository, Func ipcMSGCallBackFunc)
+ private MousePointerCrosshairSettings MousePointerCrosshairSettingsConfig { get; set; }
+
+ public MouseUtilsViewModel(ISettingsUtils settingsUtils, ISettingsRepository settingsRepository, ISettingsRepository findMyMouseSettingsRepository, ISettingsRepository mouseHighlighterSettingsRepository, ISettingsRepository mousePointerCrosshairSettingsRepository, Func ipcMSGCallBackFunc)
{
SettingsUtils = settingsUtils;
@@ -35,6 +37,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
_isMouseHighlighterEnabled = GeneralSettingsConfig.Enabled.MouseHighlighter;
+ _isMousePointerCrosshairEnabled = GeneralSettingsConfig.Enabled.MousePointerCrosshair;
+
// To obtain the find my mouse settings, if the file exists.
// If not, to create a file with the default settings and to return the default configurations.
if (findMyMouseSettingsRepository == null)
@@ -73,6 +77,24 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
_highlightFadeDelayMs = MouseHighlighterSettingsConfig.Properties.HighlightFadeDelayMs.Value;
_highlightFadeDurationMs = MouseHighlighterSettingsConfig.Properties.HighlightFadeDurationMs.Value;
+ if (mousePointerCrosshairSettingsRepository == null)
+ {
+ throw new ArgumentNullException(nameof(mousePointerCrosshairSettingsRepository));
+ }
+
+ MousePointerCrosshairSettingsConfig = mousePointerCrosshairSettingsRepository.SettingsConfig;
+
+ string crosshairColor = MousePointerCrosshairSettingsConfig.Properties.CrosshairColor.Value;
+ _mousePointerCrosshairColor = !string.IsNullOrEmpty(crosshairColor) ? crosshairColor : "#FF0000";
+
+ string crosshairBorderColor = MousePointerCrosshairSettingsConfig.Properties.CrosshairBorderColor.Value;
+ _mousePointerCrosshairBorderColor = !string.IsNullOrEmpty(crosshairBorderColor) ? crosshairBorderColor : "#FFFFFF";
+
+ _mousePointerCrosshairOpacity = MousePointerCrosshairSettingsConfig.Properties.CrosshairOpacity.Value;
+ _mousePointerCrosshairRadius = MousePointerCrosshairSettingsConfig.Properties.CrosshairRadius.Value;
+ _mousePointerCrosshairThickness = MousePointerCrosshairSettingsConfig.Properties.CrosshairThickness.Value;
+ _mousePointerCrosshairBorderSize = MousePointerCrosshairSettingsConfig.Properties.CrosshairBorderSize.Value;
+
// set the callback functions value to handle outgoing IPC message.
SendConfigMSG = ipcMSGCallBackFunc;
}
@@ -398,6 +420,169 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
SettingsUtils.SaveSettings(MouseHighlighterSettingsConfig.ToJsonString(), MouseHighlighterSettings.ModuleName);
}
+ public bool IsMousePointerCrosshairEnabled
+ {
+ get => _isMousePointerCrosshairEnabled;
+ set
+ {
+ if (_isMousePointerCrosshairEnabled != value)
+ {
+ _isMousePointerCrosshairEnabled = value;
+
+ GeneralSettingsConfig.Enabled.MousePointerCrosshair = value;
+ OnPropertyChanged(nameof(_isMousePointerCrosshairEnabled));
+
+ OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
+ SendConfigMSG(outgoing.ToString());
+
+ NotifyMousePointerCrosshairPropertyChanged();
+ }
+ }
+ }
+
+ public HotkeySettings MousePointerCrosshairActivationShortcut
+ {
+ get
+ {
+ return MousePointerCrosshairSettingsConfig.Properties.ActivationShortcut;
+ }
+
+ set
+ {
+ if (MousePointerCrosshairSettingsConfig.Properties.ActivationShortcut != value)
+ {
+ MousePointerCrosshairSettingsConfig.Properties.ActivationShortcut = value;
+ NotifyMousePointerCrosshairPropertyChanged();
+ }
+ }
+ }
+
+ public string MousePointerCrosshairColor
+ {
+ get
+ {
+ return _mousePointerCrosshairColor;
+ }
+
+ set
+ {
+ // The fallback value is based on ToRGBHex's behavior, which returns
+ // #FFFFFF if any exceptions are encountered, e.g. from passing in a null value.
+ // This extra handling is added here to deal with FxCop warnings.
+ value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#FFFFFF";
+ if (!value.Equals(_mousePointerCrosshairColor, StringComparison.OrdinalIgnoreCase))
+ {
+ _mousePointerCrosshairColor = value;
+ MousePointerCrosshairSettingsConfig.Properties.CrosshairColor.Value = value;
+ NotifyMousePointerCrosshairPropertyChanged();
+ }
+ }
+ }
+
+ public int MousePointerCrosshairOpacity
+ {
+ get
+ {
+ return _mousePointerCrosshairOpacity;
+ }
+
+ set
+ {
+ if (value != _mousePointerCrosshairOpacity)
+ {
+ _mousePointerCrosshairOpacity = value;
+ MousePointerCrosshairSettingsConfig.Properties.CrosshairOpacity.Value = value;
+ NotifyMousePointerCrosshairPropertyChanged();
+ }
+ }
+ }
+
+ public int MousePointerCrosshairRadius
+ {
+ get
+ {
+ return _mousePointerCrosshairRadius;
+ }
+
+ set
+ {
+ if (value != _mousePointerCrosshairRadius)
+ {
+ _mousePointerCrosshairRadius = value;
+ MousePointerCrosshairSettingsConfig.Properties.CrosshairRadius.Value = value;
+ NotifyMousePointerCrosshairPropertyChanged();
+ }
+ }
+ }
+
+ public int MousePointerCrosshairThickness
+ {
+ get
+ {
+ return _mousePointerCrosshairThickness;
+ }
+
+ set
+ {
+ if (value != _mousePointerCrosshairThickness)
+ {
+ _mousePointerCrosshairThickness = value;
+ MousePointerCrosshairSettingsConfig.Properties.CrosshairThickness.Value = value;
+ NotifyMousePointerCrosshairPropertyChanged();
+ }
+ }
+ }
+
+ public string MousePointerCrosshairBorderColor
+ {
+ get
+ {
+ return _mousePointerCrosshairBorderColor;
+ }
+
+ set
+ {
+ // The fallback value is based on ToRGBHex's behavior, which returns
+ // #FFFFFF if any exceptions are encountered, e.g. from passing in a null value.
+ // This extra handling is added here to deal with FxCop warnings.
+ value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#FFFFFF";
+ if (!value.Equals(_mousePointerCrosshairBorderColor, StringComparison.OrdinalIgnoreCase))
+ {
+ _mousePointerCrosshairBorderColor = value;
+ MousePointerCrosshairSettingsConfig.Properties.CrosshairBorderColor.Value = value;
+ NotifyMousePointerCrosshairPropertyChanged();
+ }
+ }
+ }
+
+ public int MousePointerCrosshairBorderSize
+ {
+ get
+ {
+ return _mousePointerCrosshairBorderSize;
+ }
+
+ set
+ {
+ if (value != _mousePointerCrosshairBorderSize)
+ {
+ _mousePointerCrosshairBorderSize = value;
+ MousePointerCrosshairSettingsConfig.Properties.CrosshairBorderSize.Value = value;
+ NotifyMousePointerCrosshairPropertyChanged();
+ }
+ }
+ }
+
+ public void NotifyMousePointerCrosshairPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ OnPropertyChanged(propertyName);
+
+ SndMousePointerCrosshairSettings outsettings = new SndMousePointerCrosshairSettings(MousePointerCrosshairSettingsConfig);
+ SndModuleSettings ipcMessage = new SndModuleSettings(outsettings);
+ SendConfigMSG(ipcMessage.ToJsonString());
+ SettingsUtils.SaveSettings(MousePointerCrosshairSettingsConfig.ToJsonString(), MousePointerCrosshairSettings.ModuleName);
+ }
+
private Func SendConfigMSG { get; }
private bool _isFindMyMouseEnabled;
@@ -416,5 +601,13 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
private int _highlighterRadius;
private int _highlightFadeDelayMs;
private int _highlightFadeDurationMs;
+
+ private bool _isMousePointerCrosshairEnabled;
+ private string _mousePointerCrosshairColor;
+ private int _mousePointerCrosshairOpacity;
+ private int _mousePointerCrosshairRadius;
+ private int _mousePointerCrosshairThickness;
+ private string _mousePointerCrosshairBorderColor;
+ private int _mousePointerCrosshairBorderSize;
}
}
diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeMouseUtils.xaml b/src/settings-ui/Settings.UI/OOBE/Views/OobeMouseUtils.xaml
index 5ac7945097..5f1cca7e33 100644
--- a/src/settings-ui/Settings.UI/OOBE/Views/OobeMouseUtils.xaml
+++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeMouseUtils.xaml
@@ -22,6 +22,10 @@
Style="{ThemeResource OobeSubtitleStyle}" />
+
+
+
diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw
index 10ba89fa02..3c5ba8ac3c 100644
--- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw
+++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw
@@ -1651,6 +1651,14 @@ From there, simply click on a Markdown file, PDF file or SVG icon in the File Ex
Use a keyboard shortcut highlight left and right mouse clicks.
Mouse as in the hardware peripheral.
+
+ Mouse Pointer Crosshair
+ Mouse as in the hardware peripheral.
+
+
+ Draw a crosshair centered around the mouse pointer.
+ Mouse as in the hardware peripheral.
+
Launch PowerToys Run
@@ -1821,6 +1829,45 @@ From there, simply click on a Markdown file, PDF file or SVG icon in the File Ex
Duration of the disappear animation (ms)
ms = milliseconds
+
+ Mouse Pointer Crosshair
+ Refers to the utility name
+
+
+ Mouse Pointer Crosshair draws a crosshair centered on the mouse pointer.
+ "Mouse Pointer Crosshair" is the name of the utility. Mouse is the hardware mouse.
+
+
+ Enable Mouse Pointer Crosshair
+ "Mouse Pointer Crosshair" is the name of the utility.
+
+
+ Activation shortcut
+
+
+ Customize the shortcut to show/hide the crosshair
+
+
+ Crosshair color
+
+
+ Crosshair opacity (%)
+
+
+ Crosshair center radius (px)
+ px = pixels
+
+
+ Crosshair thickness (px)
+ px = pixels
+
+
+ Crosshair border color
+
+
+ Crosshair border size (px)
+ px = pixels
+
Custom colors
diff --git a/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml b/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml
index 5c8e792185..a8357bd147 100644
--- a/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml
+++ b/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml
@@ -175,7 +175,89 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml.cs b/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml.cs
index 83a525abd2..b36b134731 100644
--- a/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml.cs
+++ b/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml.cs
@@ -33,7 +33,14 @@ namespace Microsoft.PowerToys.Settings.UI.Views
}
var settingsUtils = new SettingsUtils();
- ViewModel = new MouseUtilsViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage);
+ ViewModel = new MouseUtilsViewModel(
+ settingsUtils,
+ SettingsRepository.GetInstance(settingsUtils),
+ SettingsRepository.GetInstance(settingsUtils),
+ SettingsRepository.GetInstance(settingsUtils),
+ SettingsRepository.GetInstance(settingsUtils),
+ ShellPage.SendDefaultIPCMessage);
+
DataContext = ViewModel;
InitializeComponent();
}