Skip to content

Commit

Permalink
[FindMyMouse]Add additional settings (#14590)
Browse files Browse the repository at this point in the history
* [FindMyMouse]Add additional settings

* Add setting for Spotlight Initial Zoom

* PR feedback: lowercase settings names

* PR feedback: settings descriptions

* PR feedback: change opacity to percentage

* PR feedback: increase maximum zoom factor

* PR feedback: group spotlight settings

* PR feedback: Expanders start collapsed initially

* Remove extra settings file save in dllmain

* PR feedback: change initial zoom description

* PR feedback: Add warning for photo sensitive users

* PR feedback: remove warning and add description instead

* PR feedback: size->factor in initial zoom description

* Feedback PR: remove opacity description

* PR feedback: remove photo sensitivity warning
  • Loading branch information
jaimecbernardo committed Nov 23, 2021
1 parent 8afac77 commit 46244e8
Show file tree
Hide file tree
Showing 10 changed files with 482 additions and 79 deletions.
1 change: 1 addition & 0 deletions PowerToys.sln
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "utils", "utils", "{B39DC643-4663-475E-B329-03F0C9918D48}"
ProjectSection(SolutionItems) = preProject
src\common\utils\appMutex.h = src\common\utils\appMutex.h
src\common\utils\color.h = src\common\utils\color.h
src\common\utils\com_object_factory.h = src\common\utils\com_object_factory.h
src\common\utils\elevation.h = src\common\utils\elevation.h
src\common\utils\EventLocker.h = src\common\utils\EventLocker.h
Expand Down
21 changes: 21 additions & 0 deletions src/common/utils/color.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

// helper function to get the RGB from a #FFFFFF string.
inline bool checkValidRGB(std::wstring_view hex, uint8_t* R, uint8_t* G, uint8_t* B)
{
if (hex.length() != 7)
return false;
hex = hex.substr(1, 6); // remove #
for (auto& c : hex)
{
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')))
{
return false;
}
}
if (swscanf_s(hex.data(), L"%2hhx%2hhx%2hhx", R, G, B) != 3)
{
return false;
}
return true;
}
136 changes: 101 additions & 35 deletions src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ namespace ABI
}
#endif

bool m_doNotActivateOnGameMode = true;

#pragma region Super_Sonar_Base_Code

template<typename D>
Expand Down Expand Up @@ -58,11 +56,13 @@ struct SuperSonar
// 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;

static constexpr int SonarRadius = 100;
static constexpr int SonarZoomFactor = 9;
static constexpr DWORD FadeDuration = 500;
static constexpr int FinalAlphaNumerator = 1;
static constexpr int FinalAlphaDenominator = 2;
bool m_destroyed = false;
bool m_doNotActivateOnGameMode = true;
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;
int m_finalAlphaNumerator = FIND_MY_MOUSE_DEFAULT_OVERLAY_OPACITY;
static constexpr int FinalAlphaDenominator = 100;
winrt::DispatcherQueueController m_dispatcherQueueController{ nullptr };

private:
Expand Down Expand Up @@ -150,6 +150,7 @@ void SuperSonar<D>::Terminate()
{
auto dispatcherQueue = m_dispatcherQueueController.DispatcherQueue();
bool enqueueSucceeded = dispatcherQueue.TryEnqueue([=]() {
m_destroyed = true;
DestroyWindow(m_hwndOwner);
});
if (!enqueueSucceeded)
Expand Down Expand Up @@ -457,7 +458,7 @@ void SuperSonar<D>::UpdateMouseSnooping()
struct CompositionSpotlight : SuperSonar<CompositionSpotlight>
{
static constexpr UINT WM_OPACITY_ANIMATION_COMPLETED = WM_APP;
static constexpr float SonarRadiusFloat = static_cast<float>(SonarRadius);
float m_sonarRadiusFloat = static_cast<float>(m_sonarRadius);

DWORD GetExtendedStyle()
{
Expand Down Expand Up @@ -489,7 +490,7 @@ struct CompositionSpotlight : SuperSonar<CompositionSpotlight>
m_batch.Completed([hwnd = m_hwnd](auto&&, auto&&) {
PostMessage(hwnd, WM_OPACITY_ANIMATION_COMPLETED, 0, 0);
});
m_root.Opacity(visible ? static_cast<float>(FinalAlphaNumerator) / FinalAlphaDenominator : 0.0f);
m_root.Opacity(visible ? static_cast<float>(m_finalAlphaNumerator) / FinalAlphaDenominator : 0.0f);
if (visible)
{
ShowWindow(m_hwnd, SW_SHOWNOACTIVATE);
Expand Down Expand Up @@ -531,38 +532,38 @@ struct CompositionSpotlight : SuperSonar<CompositionSpotlight>
layer.RelativeSizeAdjustment({ 1.0f, 1.0f }); // fill the parent
m_root.Children().InsertAtTop(layer);

auto backdrop = m_compositor.CreateSpriteVisual();
backdrop.RelativeSizeAdjustment({ 1.0f, 1.0f }); // fill the parent
backdrop.Brush(m_compositor.CreateColorBrush({ 255, 0, 0, 0 }));
layer.Children().InsertAtTop(backdrop);
m_backdrop = m_compositor.CreateSpriteVisual();
m_backdrop.RelativeSizeAdjustment({ 1.0f, 1.0f }); // fill the parent
m_backdrop.Brush(m_compositor.CreateColorBrush(m_backgroundColor));
layer.Children().InsertAtTop(m_backdrop);

m_circleGeometry = m_compositor.CreateEllipseGeometry(); // radius set via expression animation
auto circleShape = m_compositor.CreateSpriteShape(m_circleGeometry);
circleShape.FillBrush(m_compositor.CreateColorBrush({ 255, 255, 255, 255 }));
circleShape.Offset({ SonarRadiusFloat * SonarZoomFactor, SonarRadiusFloat * SonarZoomFactor });
m_circleShape = m_compositor.CreateSpriteShape(m_circleGeometry);
m_circleShape.FillBrush(m_compositor.CreateColorBrush(m_spotlightColor));
m_circleShape.Offset({ m_sonarRadiusFloat * m_sonarZoomFactor, m_sonarRadiusFloat * m_sonarZoomFactor });
m_spotlight = m_compositor.CreateShapeVisual();
m_spotlight.Size({ SonarRadiusFloat * 2 * SonarZoomFactor, SonarRadiusFloat * 2 * SonarZoomFactor });
m_spotlight.Size({ m_sonarRadiusFloat * 2 * m_sonarZoomFactor, m_sonarRadiusFloat * 2 * m_sonarZoomFactor });
m_spotlight.AnchorPoint({ 0.5f, 0.5f });
m_spotlight.Shapes().Append(circleShape);
m_spotlight.Shapes().Append(m_circleShape);

layer.Children().InsertAtTop(m_spotlight);

// Implicitly animate the alpha.
auto animation = m_compositor.CreateScalarKeyFrameAnimation();
animation.Target(L"Opacity");
animation.InsertExpressionKeyFrame(1.0f, L"this.FinalValue");
animation.Duration(std::chrono::milliseconds{ FadeDuration });
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", animation);
collection.Insert(L"Opacity", m_animation);
m_root.ImplicitAnimations(collection);

// Radius of spotlight shrinks as opacity increases.
// At opacity zero, it is SonarRadius * SonarZoomFactor.
// At maximum opacity, it is SonarRadius.
// At opacity zero, it is m_sonarRadius * SonarZoomFactor.
// At maximum opacity, it is m_sonarRadius.
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(%d, %d), Vector2(%d, %d), Root.Opacity * %d / %d)", SonarRadius * SonarZoomFactor, SonarRadius * SonarZoomFactor, SonarRadius, SonarRadius, FinalAlphaDenominator, FinalAlphaNumerator));
winrt::check_hresult(StringCchPrintfW(expressionText, ARRAYSIZE(expressionText), L"Lerp(Vector2(%d, %d), Vector2(%d, %d), Root.Opacity * %d / %d)", m_sonarRadius * m_sonarZoomFactor, m_sonarRadius * m_sonarZoomFactor, m_sonarRadius, m_sonarRadius, FinalAlphaDenominator, m_finalAlphaNumerator));
radiusExpression.Expression(expressionText);
m_circleGeometry.StartAnimation(L"Radius", radiusExpression);

Expand All @@ -581,13 +582,74 @@ struct CompositionSpotlight : SuperSonar<CompositionSpotlight>
}
}

public:
void ApplySettings(const FindMyMouseSettings& settings, bool applyToRuntimeObjects) {
if (!applyToRuntimeObjects)
{
// Runtime objects not created yet. Just update fields.
m_sonarRadius = settings.spotlightRadius;
m_sonarRadiusFloat = static_cast<float>(m_sonarRadius);
m_backgroundColor = settings.backgroundColor;
m_spotlightColor = settings.spotlightColor;
m_doNotActivateOnGameMode = settings.doNotActivateOnGameMode;
m_fadeDuration = settings.animationDurationMs > 0 ? settings.animationDurationMs : 1;
m_finalAlphaNumerator = settings.overlayOpacity;
m_sonarZoomFactor = settings.spotlightInitialZoom;
}
else
{
// Runtime objects already created. Should update in the owner thread.
auto dispatcherQueue = m_dispatcherQueueController.DispatcherQueue();
FindMyMouseSettings localSettings = settings;
bool enqueueSucceeded = dispatcherQueue.TryEnqueue([=]() {
if (!m_destroyed)
{
// Runtime objects not created yet. Just update fields.
m_sonarRadius = localSettings.spotlightRadius;
m_sonarRadiusFloat = static_cast<float>(m_sonarRadius);
m_backgroundColor = localSettings.backgroundColor;
m_spotlightColor = localSettings.spotlightColor;
m_doNotActivateOnGameMode = localSettings.doNotActivateOnGameMode;
m_fadeDuration = localSettings.animationDurationMs > 0 ? localSettings.animationDurationMs : 1;
m_finalAlphaNumerator = localSettings.overlayOpacity;
m_sonarZoomFactor = localSettings.spotlightInitialZoom;

// Apply new settings to runtime composition objects.
m_backdrop.Brush().as<winrt::CompositionColorBrush>().Color(m_backgroundColor);
m_circleShape.FillBrush().as<winrt::CompositionColorBrush>().Color(m_spotlightColor);
m_circleShape.Offset({ m_sonarRadiusFloat * m_sonarZoomFactor, m_sonarRadiusFloat * m_sonarZoomFactor });
m_spotlight.Size({ m_sonarRadiusFloat * 2 * m_sonarZoomFactor, m_sonarRadiusFloat * 2 * m_sonarZoomFactor });
m_animation.Duration(std::chrono::milliseconds{ m_fadeDuration });
m_circleGeometry.StopAnimation(L"Radius");

// Update animation
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(%d, %d), Vector2(%d, %d), Root.Opacity * %d / %d)", m_sonarRadius * m_sonarZoomFactor, m_sonarRadius * m_sonarZoomFactor, m_sonarRadius, m_sonarRadius, FinalAlphaDenominator, m_finalAlphaNumerator));
radiusExpression.Expression(expressionText);
m_circleGeometry.StartAnimation(L"Radius", radiusExpression);
}
});
if (!enqueueSucceeded)
{
Logger::error("Couldn't enqueue message to update the sonar settings.");
}
}
}

private:
winrt::Compositor m_compositor{ nullptr };
winrt::Desktop::DesktopWindowTarget m_target{ nullptr };
winrt::ContainerVisual m_root{ nullptr };
winrt::CompositionEllipseGeometry m_circleGeometry{ nullptr };
winrt::ShapeVisual m_spotlight{ nullptr };
winrt::CompositionCommitBatch m_batch{ nullptr };
winrt::SpriteVisual m_backdrop{ nullptr };
winrt::CompositionSpriteShape m_circleShape{ 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;
winrt::ScalarKeyFrameAnimation m_animation{ nullptr };
};

template<typename D>
Expand Down Expand Up @@ -631,7 +693,7 @@ struct GdiSonar : SuperSonar<D>
void OnFadeTimer()
{
auto now = GetTickCount();
auto step = (int)((now - m_fadeStart) * MaxAlpha / this->FadeDuration);
auto step = (int)((now - m_fadeStart) * MaxAlpha / this->m_fadeDuration);

this->Shim()->InvalidateSonar();
if (m_alpha < m_alphaTarget)
Expand Down Expand Up @@ -666,13 +728,13 @@ struct GdiSonar : SuperSonar<D>
int CurrentSonarRadius()
{
int range = MaxAlpha - m_alpha;
int radius = this->SonarRadius + this->SonarRadius * range * (this->SonarZoomFactor - 1) / MaxAlpha;
int radius = this->m_sonarRadius + this->m_sonarRadius * range * (this->m_sonarZoomFactor - 1) / MaxAlpha;
return radius;
}

private:
static constexpr DWORD FadeFramePeriod = 10;
static constexpr int MaxAlpha = SuperSonar<D>::FinalAlphaNumerator * 255 / SuperSonar<D>::FinalAlphaDenominator;
int MaxAlpha = SuperSonar<D>::m_finalAlphaNumerator * 255 / SuperSonar<D>::FinalAlphaDenominator;
static constexpr DWORD TIMER_ID_FADE = 101;

private:
Expand Down Expand Up @@ -792,6 +854,14 @@ struct GdiCrosshairs : GdiSonar<GdiCrosshairs>
#pragma region Super_Sonar_API

CompositionSpotlight* m_sonar = nullptr;
void FindMyMouseApplySettings(const FindMyMouseSettings& settings)
{
if (m_sonar != nullptr)
{
Logger::info("Applying settings.");
m_sonar->ApplySettings(settings, true);
}
}

void FindMyMouseDisable()
{
Expand All @@ -807,13 +877,8 @@ bool FindMyMouseIsEnabled()
return (m_sonar != nullptr);
}

void FindMyMouseSetDoNotActivateOnGameMode(bool doNotActivate)
{
m_doNotActivateOnGameMode = doNotActivate;
}

// Based on SuperSonar's original wWinMain.
int FindMyMouseMain(HINSTANCE hinst)
int FindMyMouseMain(HINSTANCE hinst, const FindMyMouseSettings& settings)
{
Logger::info("Starting a sonar instance.");
if (m_sonar != nullptr)
Expand All @@ -823,6 +888,7 @@ int FindMyMouseMain(HINSTANCE hinst)
}

CompositionSpotlight sonar;
sonar.ApplySettings(settings, false);
if (!sonar.Initialize(hinst))
{
Logger::error("Couldn't initialize a sonar instance.");
Expand Down
24 changes: 22 additions & 2 deletions src/modules/MouseUtils/FindMyMouse/FindMyMouse.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
#pragma once
#include "pch.h"
int FindMyMouseMain(HINSTANCE hinst);

constexpr bool FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE = true;
const winrt::Windows::UI::Color FIND_MY_MOUSE_DEFAULT_BACKGROUND_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(255, 0, 0, 0);
const winrt::Windows::UI::Color FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(255, 255, 255, 255);
constexpr int FIND_MY_MOUSE_DEFAULT_OVERLAY_OPACITY = 50;
constexpr int FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_RADIUS = 100;
constexpr int FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS = 500;
constexpr int FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM = 9;

struct FindMyMouseSettings
{
bool doNotActivateOnGameMode = FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE;
winrt::Windows::UI::Color backgroundColor = FIND_MY_MOUSE_DEFAULT_BACKGROUND_COLOR;
winrt::Windows::UI::Color spotlightColor = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_COLOR;
int overlayOpacity = FIND_MY_MOUSE_DEFAULT_OVERLAY_OPACITY;
int spotlightRadius = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_RADIUS;
int animationDurationMs = FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS;
int spotlightInitialZoom = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM;
};

int FindMyMouseMain(HINSTANCE hinst, const FindMyMouseSettings& settings);
void FindMyMouseDisable();
bool FindMyMouseIsEnabled();
void FindMyMouseSetDoNotActivateOnGameMode(bool doNotActivate);
void FindMyMouseApplySettings(const FindMyMouseSettings& settings);

0 comments on commit 46244e8

Please sign in to comment.