// FindMyMouse.cpp : Based on Raymond Chen's SuperSonar.cpp // #include "pch.h" #include "FindMyMouse.h" #include "WinHookEventIDs.h" #include "trace.h" #include "common/utils/game_mode.h" #include "common/utils/process_path.h" #include "common/utils/excluded_apps.h" #include "common/utils/MsWindowsSettings.h" #include #include #include #include #include #include #include #include #include #include namespace winrt { using namespace winrt::Windows::System; } namespace muxc = winrt::Microsoft::UI::Composition; namespace muxx = winrt::Microsoft::UI::Xaml; namespace muxxc = winrt::Microsoft::UI::Xaml::Controls; namespace muxxh = winrt::Microsoft::UI::Xaml::Hosting; #pragma region Super_Sonar_Base_Code template struct SuperSonar { bool Initialize(HINSTANCE hinst); void Terminate(); protected: // You are expected to override these, as appropriate. DWORD GetExtendedStyle() { return 0; } LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam) noexcept { return BaseWndProc(message, wParam, lParam); } void BeforeMoveSonar() {} void AfterMoveSonar() {} void SetSonarVisibility(bool visible) = delete; void UpdateMouseSnooping(); bool IsForegroundAppExcluded(); protected: // Base class members you can access. D* Shim() { return static_cast(this); } LRESULT BaseWndProc(UINT message, WPARAM wParam, LPARAM lParam) noexcept; HWND m_hwnd{}; POINT m_sonarPos = ptNowhere; // Only consider double left control click if at least 100ms passed between the clicks, to avoid keyboards that might be sending rapid clicks. // At actual check, time a fifth of the current double click setting might be used instead to take into account users who might have low values. static const int MIN_DOUBLE_CLICK_TIME = 100; bool m_destroyed = false; FindMyMouseActivationMethod m_activationMethod = FIND_MY_MOUSE_DEFAULT_ACTIVATION_METHOD; bool m_includeWinKey = FIND_MY_MOUSE_DEFAULT_INCLUDE_WIN_KEY; bool m_doNotActivateOnGameMode = FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE; int m_sonarRadius = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_RADIUS; int m_sonarZoomFactor = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM; DWORD m_fadeDuration = FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS; std::vector m_excludedApps; int m_shakeMinimumDistance = FIND_MY_MOUSE_DEFAULT_SHAKE_MINIMUM_DISTANCE; winrt::Microsoft::UI::Dispatching::DispatcherQueueController m_dispatcherQueueController{ nullptr }; // Don't consider movements started past these milliseconds to detect shaking. int m_shakeIntervalMs = FIND_MY_MOUSE_DEFAULT_SHAKE_INTERVAL_MS; // By which factor must travelled distance be than the diagonal of the rectangle containing the movements. (value in percent) int m_shakeFactor = FIND_MY_MOUSE_DEFAULT_SHAKE_FACTOR; private: // Save the mouse movement that occurred in any direction. struct PointerRecentMovement { POINT diff; ULONGLONG tick; }; std::vector m_movementHistory; // Raw Input may give relative or absolute values. Need to take each case into account. bool m_seenAnAbsoluteMousePosition = false; POINT m_lastAbsolutePosition = { 0, 0 }; static inline byte GetSign(LONG const& num) { if (num > 0) return 1; if (num < 0) return -1; return 0; } static bool IsEqual(POINT const& p1, POINT const& p2) { return p1.x == p2.x && p1.y == p2.y; } static constexpr POINT ptNowhere = { LONG_MIN, LONG_MIN }; static constexpr DWORD TIMER_ID_TRACK = 100; static constexpr DWORD IdlePeriod = 1000; // Activate sonar: Hit LeftControl twice. enum class SonarState { Idle, ControlDown1, ControlUp1, ControlDown2, ControlUp2, }; HWND m_hwndOwner{}; SonarState m_sonarState = SonarState::Idle; POINT m_lastKeyPos{}; ULONGLONG m_lastKeyTime{}; static constexpr DWORD NoSonar = 0; static constexpr DWORD SonarWaitingForMouseMove = 1; ULONGLONG m_sonarStart = NoSonar; bool m_isSnoopingMouse = false; private: static constexpr auto className = L"FindMyMouse"; static constexpr auto windowTitle = L"PowerToys Find My Mouse"; static LRESULT CALLBACK s_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); BOOL OnSonarCreate(); void OnSonarDestroy(); void OnSonarInput(WPARAM flags, HRAWINPUT hInput); void OnSonarKeyboardInput(RAWINPUT const& input); void OnSonarMouseInput(RAWINPUT const& input); void OnMouseTimer(); void DetectShake(); bool KeyboardInputCanActivate(); void StartSonar(FindMyMouseActivationMethod activationMethod); void StopSonar(); }; template bool SuperSonar::Initialize(HINSTANCE hinst) { SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); WNDCLASS wc{}; if (!GetClassInfoW(hinst, className, &wc)) { wc.lpfnWndProc = s_WndProc; wc.hInstance = hinst; wc.hIcon = LoadIcon(hinst, IDI_APPLICATION); wc.hCursor = LoadCursor(nullptr, IDC_ARROW); wc.hbrBackground = static_cast(GetStockObject(NULL_BRUSH)); wc.lpszClassName = className; if (!RegisterClassW(&wc)) { Logger::error("RegisterClassW failed. GetLastError={}", GetLastError()); return false; } } // else: class already registered m_hwndOwner = CreateWindow(L"static", nullptr, WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, hinst, nullptr); if (!m_hwndOwner) { Logger::error("Failed to create owner window. GetLastError={}", GetLastError()); return false; } DWORD exStyle = WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOOLWINDOW | Shim()->GetExtendedStyle(); HWND created = CreateWindowExW(exStyle, className, windowTitle, WS_POPUP, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, m_hwndOwner, nullptr, hinst, this); if (!created) { Logger::error("CreateWindowExW failed. GetLastError={}", GetLastError()); return false; } return true; } template void SuperSonar::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 sonar Window."); } } template LRESULT SuperSonar::s_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { SuperSonar* self; if (message == WM_NCCREATE) { auto info = reinterpret_cast(lParam); SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(info->lpCreateParams)); self = static_cast(info->lpCreateParams); self->m_hwnd = hwnd; } else { self = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); } if (self) { return self->Shim()->WndProc(message, wParam, lParam); } else { return DefWindowProc(hwnd, message, wParam, lParam); } } template LRESULT SuperSonar::BaseWndProc(UINT message, WPARAM wParam, LPARAM lParam) noexcept { switch (message) { case WM_CREATE: if (!OnSonarCreate()) return -1; UpdateMouseSnooping(); return 0; case WM_DESTROY: OnSonarDestroy(); break; case WM_INPUT: OnSonarInput(wParam, reinterpret_cast(lParam)); break; case WM_TIMER: switch (wParam) { case TIMER_ID_TRACK: OnMouseTimer(); break; } break; case WM_NCHITTEST: return HTTRANSPARENT; } if (message == WM_PRIV_SHORTCUT) { if (m_sonarStart == NoSonar) { StartSonar(FindMyMouseActivationMethod::Shortcut); } else { StopSonar(); } } return DefWindowProc(m_hwnd, message, wParam, lParam); } template BOOL SuperSonar::OnSonarCreate() { RAWINPUTDEVICE keyboard{}; keyboard.usUsagePage = HID_USAGE_PAGE_GENERIC; keyboard.usUsage = HID_USAGE_GENERIC_KEYBOARD; keyboard.dwFlags = RIDEV_INPUTSINK; keyboard.hwndTarget = m_hwnd; return RegisterRawInputDevices(&keyboard, 1, sizeof(keyboard)); } template void SuperSonar::OnSonarDestroy() { PostQuitMessage(0); } template void SuperSonar::OnSonarInput(WPARAM flags, HRAWINPUT hInput) { RAWINPUT input; UINT size = sizeof(input); auto result = GetRawInputData(hInput, RID_INPUT, &input, &size, sizeof(RAWINPUTHEADER)); if (result < sizeof(RAWINPUTHEADER)) { return; } switch (input.header.dwType) { case RIM_TYPEKEYBOARD: OnSonarKeyboardInput(input); break; case RIM_TYPEMOUSE: OnSonarMouseInput(input); break; } } template void SuperSonar::OnSonarKeyboardInput(RAWINPUT const& input) { // Don't stop the sonar when the shortcut is released if (m_activationMethod == FindMyMouseActivationMethod::Shortcut && (input.data.keyboard.Flags & RI_KEY_BREAK) != 0) { return; } if ((m_activationMethod != FindMyMouseActivationMethod::DoubleRightControlKey && m_activationMethod != FindMyMouseActivationMethod::DoubleLeftControlKey) || input.data.keyboard.VKey != VK_CONTROL) { StopSonar(); return; } bool pressed = (input.data.keyboard.Flags & RI_KEY_BREAK) == 0; bool leftCtrlPressed = (input.data.keyboard.Flags & RI_KEY_E0) == 0; bool rightCtrlPressed = (input.data.keyboard.Flags & RI_KEY_E0) != 0; if ((m_activationMethod == FindMyMouseActivationMethod::DoubleRightControlKey && !rightCtrlPressed) || (m_activationMethod == FindMyMouseActivationMethod::DoubleLeftControlKey && !leftCtrlPressed)) { StopSonar(); return; } switch (m_sonarState) { case SonarState::Idle: if (pressed) { m_sonarState = SonarState::ControlDown1; m_lastKeyTime = GetTickCount64(); m_lastKeyPos = {}; GetCursorPos(&m_lastKeyPos); UpdateMouseSnooping(); } break; case SonarState::ControlDown1: if (!pressed) { m_sonarState = SonarState::ControlUp1; } break; case SonarState::ControlUp1: if (pressed && KeyboardInputCanActivate()) { auto now = GetTickCount64(); auto doubleClickInterval = now - m_lastKeyTime; POINT ptCursor{}; auto doubleClickTimeSetting = GetDoubleClickTime(); if (GetCursorPos(&ptCursor) && doubleClickInterval >= min(MIN_DOUBLE_CLICK_TIME, doubleClickTimeSetting / 5) && doubleClickInterval <= doubleClickTimeSetting && IsEqual(m_lastKeyPos, ptCursor)) { m_sonarState = SonarState::ControlDown2; StartSonar(m_activationMethod); } else { m_sonarState = SonarState::ControlDown1; m_lastKeyTime = GetTickCount64(); m_lastKeyPos = {}; GetCursorPos(&m_lastKeyPos); UpdateMouseSnooping(); } m_lastKeyTime = now; m_lastKeyPos = ptCursor; } break; case SonarState::ControlUp2: // Also deactivate sonar with left control. if (pressed) { StopSonar(); } break; case SonarState::ControlDown2: if (!pressed) { m_sonarState = SonarState::ControlUp2; } break; } } // Shaking detection algorithm is: Has distance travelled been much greater than the diagonal of the rectangle containing the movement? template void SuperSonar::DetectShake() { ULONGLONG shakeStartTick = GetTickCount64() - m_shakeIntervalMs; // Prune the story of movements for those movements that started too long ago. std::erase_if(m_movementHistory, [shakeStartTick](const PointerRecentMovement& movement) { return movement.tick < shakeStartTick; }); double distanceTravelled = 0; LONGLONG currentX = 0, minX = 0, maxX = 0; LONGLONG currentY = 0, minY = 0, maxY = 0; for (const PointerRecentMovement& movement : m_movementHistory) { currentX += movement.diff.x; currentY += movement.diff.y; distanceTravelled += sqrt(static_cast(movement.diff.x) * movement.diff.x + static_cast(movement.diff.y) * movement.diff.y); // Pythagorean theorem minX = min(currentX, minX); maxX = max(currentX, maxX); minY = min(currentY, minY); maxY = max(currentY, maxY); } if (distanceTravelled < m_shakeMinimumDistance) { return; } // Size of the rectangle that the pointer moved in. double rectangleWidth = static_cast(maxX) - minX; double rectangleHeight = static_cast(maxY) - minY; double diagonal = sqrt(rectangleWidth * rectangleWidth + rectangleHeight * rectangleHeight); if (diagonal > 0 && distanceTravelled / diagonal > (m_shakeFactor / 100.f)) { m_movementHistory.clear(); StartSonar(m_activationMethod); } } template bool SuperSonar::KeyboardInputCanActivate() { return !m_includeWinKey || (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000); } template void SuperSonar::OnSonarMouseInput(RAWINPUT const& input) { if (m_activationMethod == FindMyMouseActivationMethod::ShakeMouse) { LONG relativeX = 0; LONG relativeY = 0; if ((input.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) == MOUSE_MOVE_ABSOLUTE && (input.data.mouse.lLastX != 0 || input.data.mouse.lLastY != 0)) { // Getting absolute mouse coordinates. Likely inside a VM / RDP session. if (m_seenAnAbsoluteMousePosition) { relativeX = input.data.mouse.lLastX - m_lastAbsolutePosition.x; relativeY = input.data.mouse.lLastY - m_lastAbsolutePosition.y; m_lastAbsolutePosition.x = input.data.mouse.lLastX; m_lastAbsolutePosition.y = input.data.mouse.lLastY; } m_seenAnAbsoluteMousePosition = true; } else { relativeX = input.data.mouse.lLastX; relativeY = input.data.mouse.lLastY; } if (m_movementHistory.size() > 0) { PointerRecentMovement& lastMovement = m_movementHistory.back(); // If the pointer is still moving in the same direction, just add to that movement instead of adding a new movement. // This helps in keeping the list of movements smaller even in cases where a high number of messages is sent. if (GetSign(lastMovement.diff.x) == GetSign(relativeX) && GetSign(lastMovement.diff.y) == GetSign(relativeY)) { lastMovement.diff.x += relativeX; lastMovement.diff.y += relativeY; } else { m_movementHistory.push_back({ .diff = { .x = relativeX, .y = relativeY }, .tick = GetTickCount64() }); // Mouse movement changed directions. Take the opportunity do detect shake. DetectShake(); } } else { m_movementHistory.push_back({ .diff = { .x = relativeX, .y = relativeY }, .tick = GetTickCount64() }); } } if (input.data.mouse.usButtonFlags) { StopSonar(); } else if (m_sonarStart != NoSonar) { OnMouseTimer(); } } template void SuperSonar::StartSonar(FindMyMouseActivationMethod activationMethod) { // Don't activate if game mode is on. if (m_doNotActivateOnGameMode && detect_game_mode()) { return; } if (IsForegroundAppExcluded()) { return; } Trace::MousePointerFocused(static_cast(activationMethod)); // Cover the entire virtual screen. // HACK: Draw with 1 pixel off. Otherwise, Windows glitches the task bar transparency when a transparent window fill the whole screen. SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, 0); m_sonarPos = ptNowhere; OnMouseTimer(); UpdateMouseSnooping(); Shim()->SetSonarVisibility(true); } template void SuperSonar::StopSonar() { if (m_sonarStart != NoSonar) { m_sonarStart = NoSonar; Shim()->SetSonarVisibility(false); KillTimer(m_hwnd, TIMER_ID_TRACK); } m_sonarState = SonarState::Idle; UpdateMouseSnooping(); } template void SuperSonar::OnMouseTimer() { auto now = GetTickCount64(); // If mouse has moved, then reset the sonar timer. POINT ptCursor{}; if (!GetCursorPos(&ptCursor)) { // We are no longer the active desktop - done. StopSonar(); return; } ScreenToClient(m_hwnd, &ptCursor); if (IsEqual(m_sonarPos, ptCursor)) { // Mouse is stationary. if (m_sonarStart != SonarWaitingForMouseMove && now - m_sonarStart >= IdlePeriod) { StopSonar(); return; } } else { // Mouse has moved. if (IsEqual(m_sonarPos, ptNowhere)) { // Initial call, mark sonar as active but waiting for first mouse-move. now = SonarWaitingForMouseMove; } SetTimer(m_hwnd, TIMER_ID_TRACK, IdlePeriod, nullptr); Shim()->BeforeMoveSonar(); m_sonarPos = ptCursor; m_sonarStart = now; Shim()->AfterMoveSonar(); } } template void SuperSonar::UpdateMouseSnooping() { bool wantSnoopingMouse = m_sonarStart != NoSonar || m_sonarState != SonarState::Idle || m_activationMethod == FindMyMouseActivationMethod::ShakeMouse; if (m_isSnoopingMouse != wantSnoopingMouse) { m_isSnoopingMouse = wantSnoopingMouse; RAWINPUTDEVICE mouse{}; mouse.usUsagePage = HID_USAGE_PAGE_GENERIC; mouse.usUsage = HID_USAGE_GENERIC_MOUSE; if (wantSnoopingMouse) { mouse.dwFlags = RIDEV_INPUTSINK; mouse.hwndTarget = m_hwnd; } else { mouse.dwFlags = RIDEV_REMOVE; mouse.hwndTarget = nullptr; } RegisterRawInputDevices(&mouse, 1, sizeof(mouse)); } } template bool SuperSonar::IsForegroundAppExcluded() { if (m_excludedApps.size() < 1) { return false; } if (HWND foregroundApp{ GetForegroundWindow() }) { auto processPath = get_process_path(foregroundApp); CharUpperBuffW(processPath.data(), static_cast(processPath.length())); return check_excluded_app(foregroundApp, processPath, m_excludedApps); } else { return false; } } struct CompositionSpotlight : SuperSonar { static constexpr UINT WM_OPACITY_ANIMATION_COMPLETED = WM_APP; float m_sonarRadiusFloat = static_cast(m_sonarRadius); DWORD GetExtendedStyle() { // Remove WS_EX_NOREDIRECTIONBITMAP for Composition/XAML to allow DWM redirection. return 0; } void AfterMoveSonar() { const float scale = static_cast(m_surface.XamlRoot().RasterizationScale()); // Move gradient center if (m_spotlightMaskGradient) { m_spotlightMaskGradient.EllipseCenter({ static_cast(m_sonarPos.x) / scale, static_cast(m_sonarPos.y) / scale }); } // Move spotlight visual (color fill) below masked backdrop if (m_spotlight) { m_spotlight.Offset({ static_cast(m_sonarPos.x) / scale, static_cast(m_sonarPos.y) / scale, 0.0f }); } } LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam) noexcept { switch (message) { case WM_CREATE: if (!OnCompositionCreate()) return -1; return BaseWndProc(message, wParam, lParam); case WM_OPACITY_ANIMATION_COMPLETED: OnOpacityAnimationCompleted(); break; case WM_SIZE: UpdateIslandSize(); break; } return BaseWndProc(message, wParam, lParam); } void SetSonarVisibility(bool visible) { m_batch = m_compositor.GetCommitBatch(muxc::CompositionBatchTypes::Animation); BOOL isEnabledAnimations = GetAnimationsEnabled(); m_animation.Duration(std::chrono::milliseconds{ isEnabledAnimations ? m_fadeDuration : 1 }); m_batch.Completed([hwnd = m_hwnd](auto&&, auto&&) { PostMessage(hwnd, WM_OPACITY_ANIMATION_COMPLETED, 0, 0); }); m_root.Opacity(visible ? 1.0f : 0.0f); if (visible) { ShowWindow(m_hwnd, SW_SHOWNOACTIVATE); } } HWND GetHwnd() noexcept { return m_hwnd; } private: bool OnCompositionCreate() try { // Creating composition resources // Ensure a DispatcherQueue bound to this thread (required by WinAppSDK composition/XAML) if (!m_dispatcherQueueController) { // Ensure COM is initialized try { winrt::init_apartment(winrt::apartment_type::single_threaded); // COM STA initialized } catch (const winrt::hresult_error& e) { Logger::error("Failed to initialize COM apartment: {}", winrt::to_string(e.message())); return false; } try { m_dispatcherQueueController = winrt::Microsoft::UI::Dispatching::DispatcherQueueController::CreateOnCurrentThread(); // DispatcherQueueController created } catch (const winrt::hresult_error& e) { Logger::error("Failed to create DispatcherQueueController: {}", winrt::to_string(e.message())); return false; } } // 1) Create a XAML island and attach it to this HWND try { m_island = winrt::Microsoft::UI::Xaml::Hosting::DesktopWindowXamlSource{}; auto windowId = winrt::Microsoft::UI::GetWindowIdFromWindow(m_hwnd); m_island.Initialize(windowId); // Xaml source initialized } catch (const winrt::hresult_error& e) { Logger::error("Failed to create XAML island: {}", winrt::to_string(e.message())); return false; } UpdateIslandSize(); // Island size set // 2) Create a XAML container to host the Composition child visual m_surface = winrt::Microsoft::UI::Xaml::Controls::Grid{}; // A transparent background keeps hit-testing consistent vs. null brush m_surface.Background(winrt::Microsoft::UI::Xaml::Media::SolidColorBrush{ winrt::Microsoft::UI::Colors::Transparent() }); m_surface.HorizontalAlignment(muxx::HorizontalAlignment::Stretch); m_surface.VerticalAlignment(muxx::VerticalAlignment::Stretch); m_island.Content(m_surface); // 3) Get the compositor from the XAML visual tree (pure MUXC path) try { auto elementVisual = winrt::Microsoft::UI::Xaml::Hosting::ElementCompositionPreview::GetElementVisual(m_surface); m_compositor = elementVisual.Compositor(); // Compositor acquired } catch (const winrt::hresult_error& e) { Logger::error("Failed to get compositor: {}", winrt::to_string(e.message())); return false; } // 4) Build the composition tree // // [root] ContainerVisual (fills host) // \ LayerVisual // \ [backdrop dim * radial gradient mask (hole)] m_root = m_compositor.CreateContainerVisual(); m_root.RelativeSizeAdjustment({ 1.0f, 1.0f }); m_root.Opacity(0.0f); // Insert our root as a hand-in Visual under the XAML element winrt::Microsoft::UI::Xaml::Hosting::ElementCompositionPreview::SetElementChildVisual(m_surface, m_root); auto layer = m_compositor.CreateLayerVisual(); layer.RelativeSizeAdjustment({ 1.0f, 1.0f }); m_root.Children().InsertAtTop(layer); const float scale = static_cast(m_surface.XamlRoot().RasterizationScale()); const float rDip = m_sonarRadiusFloat / scale; const float zoom = static_cast(m_sonarZoomFactor); // Spotlight shape (below backdrop, visible through hole) m_circleGeometry = m_compositor.CreateEllipseGeometry(); m_circleShape = m_compositor.CreateSpriteShape(m_circleGeometry); m_circleShape.FillBrush(m_compositor.CreateColorBrush(m_spotlightColor)); m_circleShape.Offset({ rDip * zoom, rDip * zoom }); m_spotlight = m_compositor.CreateShapeVisual(); m_spotlight.Size({ rDip * 2 * zoom, rDip * 2 * zoom }); m_spotlight.AnchorPoint({ 0.5f, 0.5f }); m_spotlight.Shapes().Append(m_circleShape); layer.Children().InsertAtTop(m_spotlight); // Dim color (source) m_dimColorBrush = m_compositor.CreateColorBrush(m_backgroundColor); // Radial gradient mask (center transparent, outer opaque) // Fixed feather width: 1px for radius < 300, 2px for radius >= 300 const float featherPixels = (m_sonarRadius >= 300) ? 2.0f : 1.0f; const float featherOffset = 1.0f - featherPixels / (rDip * zoom); m_spotlightMaskGradient = m_compositor.CreateRadialGradientBrush(); m_spotlightMaskGradient.MappingMode(muxc::CompositionMappingMode::Absolute); m_maskStopCenter = m_compositor.CreateColorGradientStop(); m_maskStopCenter.Offset(0.0f); m_maskStopCenter.Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0)); m_maskStopInner = m_compositor.CreateColorGradientStop(); m_maskStopInner.Offset(featherOffset); m_maskStopInner.Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0)); 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_spotlightMaskGradient.EllipseCenter({ rDip * zoom, rDip * zoom }); m_spotlightMaskGradient.EllipseRadius({ rDip * zoom, rDip * zoom }); m_maskBrush = m_compositor.CreateMaskBrush(); m_maskBrush.Source(m_dimColorBrush); m_maskBrush.Mask(m_spotlightMaskGradient); m_backdrop = m_compositor.CreateSpriteVisual(); m_backdrop.RelativeSizeAdjustment({ 1.0f, 1.0f }); m_backdrop.Brush(m_maskBrush); layer.Children().InsertAtTop(m_backdrop); // 5) Implicit opacity animation on the root m_animation = m_compositor.CreateScalarKeyFrameAnimation(); m_animation.Target(L"Opacity"); m_animation.InsertExpressionKeyFrame(1.0f, L"this.FinalValue"); m_animation.Duration(std::chrono::milliseconds{ m_fadeDuration }); auto collection = m_compositor.CreateImplicitAnimationCollection(); collection.Insert(L"Opacity", m_animation); m_root.ImplicitAnimations(collection); // 6) Spotlight radius shrinks as opacity increases (expression animation) SetupRadiusAnimations(rDip * zoom, rDip, featherPixels); // Composition created successfully return true; } catch (const winrt::hresult_error& e) { Logger::error("Failed to create FindMyMouse visual: {}", winrt::to_string(e.message())); return false; } void OnOpacityAnimationCompleted() { if (m_root.Opacity() < 0.01f) { ShowWindow(m_hwnd, SW_HIDE); } } // Helper to setup radius and feather expression animations void SetupRadiusAnimations(float startRadiusDip, float endRadiusDip, float featherPixels) { // Radius expression: shrinks from startRadiusDip to endRadiusDip as opacity goes 0->1 auto radiusExpression = m_compositor.CreateExpressionAnimation(); radiusExpression.SetReferenceParameter(L"Root", m_root); wchar_t expressionText[256]; winrt::check_hresult(StringCchPrintfW( expressionText, ARRAYSIZE(expressionText), L"Lerp(Vector2(%.1f, %.1f), Vector2(%.1f, %.1f), Root.Opacity)", startRadiusDip, startRadiusDip, endRadiusDip, endRadiusDip)); radiusExpression.Expression(expressionText); m_spotlightMaskGradient.StartAnimation(L"EllipseRadius", radiusExpression); // Feather expression: maintains fixed pixel width as radius changes auto featherExpression = m_compositor.CreateExpressionAnimation(); featherExpression.SetReferenceParameter(L"Root", m_root); wchar_t featherExpressionText[256]; winrt::check_hresult(StringCchPrintfW( featherExpressionText, ARRAYSIZE(featherExpressionText), L"1.0f - %.1ff / Lerp(%.1ff, %.1ff, Root.Opacity)", featherPixels, startRadiusDip, endRadiusDip)); featherExpression.Expression(featherExpressionText); m_maskStopInner.StartAnimation(L"Offset", featherExpression); // Circle geometry radius for visual consistency if (m_circleGeometry) { auto radiusExpression2 = m_compositor.CreateExpressionAnimation(); radiusExpression2.SetReferenceParameter(L"Root", m_root); radiusExpression2.Expression(expressionText); m_circleGeometry.StartAnimation(L"Radius", radiusExpression2); } } void UpdateIslandSize() { if (!m_island) return; RECT rc{}; if (!GetClientRect(m_hwnd, &rc)) return; const int width = rc.right - rc.left; const int height = rc.bottom - rc.top; auto bridge = m_island.SiteBridge(); bridge.MoveAndResize(winrt::Windows::Graphics::RectInt32{ 0, 0, width, height }); } public: void ApplySettings(const FindMyMouseSettings& settings, bool applyToRuntimeObjects) { if (!applyToRuntimeObjects) { m_sonarRadius = settings.spotlightRadius; m_sonarRadiusFloat = static_cast(m_sonarRadius); m_backgroundColor = settings.backgroundColor; m_spotlightColor = settings.spotlightColor; m_activationMethod = settings.activationMethod; m_includeWinKey = settings.includeWinKey; m_doNotActivateOnGameMode = settings.doNotActivateOnGameMode; m_fadeDuration = settings.animationDurationMs > 0 ? settings.animationDurationMs : 1; m_sonarZoomFactor = settings.spotlightInitialZoom; m_excludedApps = settings.excludedApps; m_shakeMinimumDistance = settings.shakeMinimumDistance; m_shakeIntervalMs = settings.shakeIntervalMs; m_shakeFactor = settings.shakeFactor; } else { if (m_dispatcherQueueController == nullptr) { Logger::warn("Tried accessing the dispatch queue controller before it was initialized."); return; } auto dispatcherQueue = m_dispatcherQueueController.DispatcherQueue(); FindMyMouseSettings localSettings = settings; bool enqueueSucceeded = dispatcherQueue.TryEnqueue([=]() { if (!m_destroyed) { m_sonarRadius = localSettings.spotlightRadius; m_sonarRadiusFloat = static_cast(m_sonarRadius); m_backgroundColor = localSettings.backgroundColor; m_spotlightColor = localSettings.spotlightColor; m_activationMethod = localSettings.activationMethod; m_includeWinKey = localSettings.includeWinKey; m_doNotActivateOnGameMode = localSettings.doNotActivateOnGameMode; m_fadeDuration = localSettings.animationDurationMs > 0 ? localSettings.animationDurationMs : 1; m_sonarZoomFactor = localSettings.spotlightInitialZoom; m_excludedApps = localSettings.excludedApps; m_shakeMinimumDistance = localSettings.shakeMinimumDistance; m_shakeIntervalMs = localSettings.shakeIntervalMs; m_shakeFactor = localSettings.shakeFactor; UpdateMouseSnooping(); // For the shake mouse activation method // Apply new settings to runtime composition objects. if (m_dimColorBrush) { m_dimColorBrush.Color(m_backgroundColor); } if (m_circleShape) { if (auto brush = m_circleShape.FillBrush().try_as()) { brush.Color(m_spotlightColor); } } const float scale = static_cast(m_surface.XamlRoot().RasterizationScale()); const float rDip = m_sonarRadiusFloat / scale; const float zoom = static_cast(m_sonarZoomFactor); const float featherPixels = (m_sonarRadius >= 300) ? 2.0f : 1.0f; const float startRadiusDip = rDip * zoom; m_spotlightMaskGradient.StopAnimation(L"EllipseRadius"); m_maskStopInner.StopAnimation(L"Offset"); if (m_circleGeometry) { m_circleGeometry.StopAnimation(L"Radius"); } m_spotlightMaskGradient.EllipseCenter({ startRadiusDip, startRadiusDip }); if (m_spotlight) { m_spotlight.Size({ rDip * 2 * zoom, rDip * 2 * zoom }); m_circleShape.Offset({ startRadiusDip, startRadiusDip }); } SetupRadiusAnimations(startRadiusDip, rDip, featherPixels); } }); if (!enqueueSucceeded) { Logger::error("Couldn't enqueue message to update the sonar settings."); } } } private: muxc::Compositor m_compositor{ nullptr }; muxxh::DesktopWindowXamlSource m_island{ nullptr }; muxxc::Grid m_surface{ nullptr }; muxc::ContainerVisual m_root{ nullptr }; muxc::CompositionCommitBatch m_batch{ nullptr }; muxc::SpriteVisual m_backdrop{ nullptr }; // Spotlight shape visuals muxc::CompositionEllipseGeometry m_circleGeometry{ nullptr }; muxc::ShapeVisual m_spotlight{ nullptr }; muxc::CompositionSpriteShape m_circleShape{ nullptr }; // Radial gradient mask components muxc::CompositionMaskBrush m_maskBrush{ nullptr }; muxc::CompositionColorBrush m_dimColorBrush{ nullptr }; muxc::CompositionRadialGradientBrush m_spotlightMaskGradient{ nullptr }; muxc::CompositionColorGradientStop m_maskStopCenter{ nullptr }; muxc::CompositionColorGradientStop m_maskStopInner{ nullptr }; muxc::CompositionColorGradientStop m_maskStopOuter{ nullptr }; winrt::Windows::UI::Color m_backgroundColor = FIND_MY_MOUSE_DEFAULT_BACKGROUND_COLOR; winrt::Windows::UI::Color m_spotlightColor = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_COLOR; muxc::ScalarKeyFrameAnimation m_animation{ nullptr }; }; #pragma endregion Super_Sonar_Base_Code #pragma region Super_Sonar_API CompositionSpotlight* m_sonar = nullptr; void FindMyMouseApplySettings(const FindMyMouseSettings& settings) { if (m_sonar != nullptr) { m_sonar->ApplySettings(settings, true); } } void FindMyMouseDisable() { if (m_sonar != nullptr) { m_sonar->Terminate(); } } bool FindMyMouseIsEnabled() { return (m_sonar != nullptr); } // Based on SuperSonar's original wWinMain. int FindMyMouseMain(HINSTANCE hinst, const FindMyMouseSettings& settings) { if (m_sonar != nullptr) { Logger::error("A sonar instance was still working when trying to start a new one."); return 0; } CompositionSpotlight sonar; sonar.ApplySettings(settings, false); if (!sonar.Initialize(hinst)) { Logger::error("Couldn't initialize a sonar instance."); return 0; } m_sonar = &sonar; InitializeWinhookEventIds(); MSG msg; // Main message loop: while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } m_sonar = nullptr; return (int)msg.wParam; } HWND GetSonarHwnd() noexcept { if (m_sonar != nullptr) { return m_sonar->GetHwnd(); } return nullptr; } #pragma endregion Super_Sonar_API