Mouse Utils - Find My Mouse (#13916)

* Initial FindMyMouse implementation

* Proper enable/disable code

* Settings page

* Change FindMyMouse window name

* Add Oobe page.

* Add icons

* Change settings preview

* Fix mouse utilities aka.ms link spelling

* Remove right control exit behavior

* Remove dllmain boilerplate comments and code

* Add filters to vcxproj

* Add logging

* Add telemetry

* Add installer instructions

* Add dll to pipelines

* Fix Task Manager name for runner changing

* Add a description in dllmain

* Proper resource file creation

* Add reference of link to the docs

* Fix spellchecker errors

* Call DestroyWindow on correct thread

* Add attribution

* Proper ordering of module in Settings and Oobe

* Update Target Platform Version to 18362

* Fix project filters

* Add attribution to Community.md

* Lowercase "utilities"

* [Mouse utils] Adding icon (#13933)

* Adding images to docs folder

* Updated imagery

Co-authored-by: Laute <Niels.Laute@philips.com>

* Add settings deeplink

Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Laute <Niels.Laute@philips.com>
This commit is contained in:
Jaime Bernardo 2021-10-22 13:30:18 +01:00 committed by GitHub
parent dadb12a6e5
commit 8d383cba9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 1665 additions and 5 deletions

View File

@ -5,6 +5,7 @@ abcdef
abcdefgh
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abgr
abi
ABlocked
Abug
accctrl
@ -382,6 +383,7 @@ CXVIRTUALSCREEN
cxx
cxxopts
CYSMICON
CYVIRTUALSCREEN
cziplib
Dac
dacl
@ -493,6 +495,8 @@ DPICHANGED
DPolicy
DPopup
DPSAPI
DQTAT
DQTYPE
DRAWFRAME
drawingcolor
dreamsofameaningfullife
@ -788,6 +792,7 @@ hotspot
HPAINTBUFFER
hpj
hpp
HRAWINPUT
hread
HREDRAW
href
@ -807,6 +812,7 @@ Htmdid
html
htt
http
HTTRANSPARENT
hwb
HWINEVENTHOOK
hwnd
@ -840,6 +846,7 @@ ICollection
IColor
ICommand
IComparer
ICompositor
ICONERROR
ICONINFORMATION
IContext
@ -854,6 +861,7 @@ IDesktop
IDictionary
IDirectory
IDispatch
IDispatcher
IDisposable
idl
IDLIST
@ -941,6 +949,7 @@ INPUTHARDWARE
INPUTKEYBOARD
INPUTLANGCHANGED
INPUTMOUSE
INPUTSINK
INPUTTYPE
INSTALLDESKTOPSHORTCUT
INSTALLDIR
@ -1702,6 +1711,9 @@ Radiobuttons
RAII
RAlt
randyrants
RAWINPUT
RAWINPUTDEVICE
RAWINPUTHEADER
RAWPATH
rbegin
Rbp
@ -1786,6 +1798,7 @@ rgs
rhs
ricardosantos
Richtext
RIDEV
RIGHTSCROLLBAR
riid
riverar
@ -2165,7 +2178,9 @@ tsx
TYMED
typedef
TYPEKEY
TYPEKEYBOARD
TYPELIB
TYPEMOUSE
typename
typeof
typeparam
@ -2468,6 +2483,7 @@ XSmall
XStr
XToolset
xunit
XVIRTUALSCREEN
Yaml
YDiff
YIncrement
@ -2478,6 +2494,7 @@ YStr
YUY
yuyoyuppe
YUYV
YVIRTUALSCREEN
YVU
YVYU
ZEROINIT

View File

@ -170,6 +170,7 @@ build:
- 'modules\launcher\Wox.dll'
- 'modules\launcher\Wox.Infrastructure.dll'
- 'modules\launcher\Wox.Plugin.dll'
- 'modules\MouseUtils\FindMyMouse.dll'
- 'modules\PowerRename\PowerRenameExt.dll'
- 'modules\ShortcutGuide\ShortcutGuide\PowerToys.ShortcutGuide.exe'
- 'modules\ShortcutGuide\ShortcutGuideModuleInterface\ShortcutGuideModuleInterface.dll'

View File

@ -72,6 +72,10 @@ PowerToys Awake is a tool to keep your computer awake.
Color Picker is from Martin.
### [@oldnewthing](https://github.com/oldnewthing) - Raymond Chen
Find My Mouse is based on Raymond Chen's SuperSonar.
### Microsoft InVEST team
This amazing team helped PowerToys develop PowerToys Run and Keyboard manager as well as update our Settings to v2. @alekhyareddy28, @arjunbalgovind, @jyuwono @laviusmotileng-ms, @ryanbodrug-microsoft, @saahmedm, @somil55, @traies, @udit3333

View File

@ -372,6 +372,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plu
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.WindowsTerminal.UnitTests", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.UnitTests\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.UnitTests.csproj", "{4ED320BC-BA04-4D42-8D15-CBE62151F08B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MouseUtils", "MouseUtils", "{322566EF-20DC-43A6-B9F8-616AF942579A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FindMyMouse", "src\modules\MouseUtils\FindMyMouse\FindMyMouse.vcxproj", "{E94FD11C-0591-456F-899F-EFC0CA548336}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -981,6 +985,12 @@ Global
{4ED320BC-BA04-4D42-8D15-CBE62151F08B}.Release|x64.Build.0 = Release|x64
{4ED320BC-BA04-4D42-8D15-CBE62151F08B}.Release|x86.ActiveCfg = Release|Any CPU
{4ED320BC-BA04-4D42-8D15-CBE62151F08B}.Release|x86.Build.0 = Release|Any CPU
{E94FD11C-0591-456F-899F-EFC0CA548336}.Debug|x64.ActiveCfg = Debug|x64
{E94FD11C-0591-456F-899F-EFC0CA548336}.Debug|x64.Build.0 = Debug|x64
{E94FD11C-0591-456F-899F-EFC0CA548336}.Debug|x86.ActiveCfg = Debug|x64
{E94FD11C-0591-456F-899F-EFC0CA548336}.Release|x64.ActiveCfg = Release|x64
{E94FD11C-0591-456F-899F-EFC0CA548336}.Release|x64.Build.0 = Release|x64
{E94FD11C-0591-456F-899F-EFC0CA548336}.Release|x86.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1098,6 +1108,8 @@ Global
{F40C3397-1834-4530-B2D9-8F8B8456BCDF} = {2F305555-C296-497E-AC20-5FA1B237996A}
{A2D583F0-B70C-4462-B1F0-8E81AFB7BA85} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{4ED320BC-BA04-4D42-8D15-CBE62151F08B} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{322566EF-20DC-43A6-B9F8-616AF942579A} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{E94FD11C-0591-456F-899F-EFC0CA548336} = {322566EF-20DC-43A6-B9F8-616AF942579A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

View File

@ -19,7 +19,7 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
| [Awake](https://aka.ms/PowerToysOverview_Awake) | [Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) | [FancyZones](https://aka.ms/PowerToysOverview_FancyZones) |
| [File Explorer Add-ons](https://aka.ms/PowerToysOverview_FileExplorerAddOns) | [Image Resizer](https://aka.ms/PowerToysOverview_ImageResizer) | [Keyboard Manager](https://aka.ms/PowerToysOverview_KeyboardManager) |
| [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) |
| [Video Conference Mute (Experimental)](https://aka.ms/PowerToysOverview_VideoConference) | | |
| [Video Conference Mute (Experimental)](https://aka.ms/PowerToysOverview_VideoConference) | [Mouse utilities](https://aka.ms/PowerToysOverview_MouseUtilities) | |
## Installing and running Microsoft PowerToys

View File

@ -25,6 +25,7 @@
| PowerToysOverview_FileExplorerAddOns | https://docs.microsoft.com/windows/powertoys/file-explorer |
| PowerToysOverview_ImageResizer | https://docs.microsoft.com/windows/powertoys/image-resizer |
| PowerToysOverview_KeyboardManager | https://docs.microsoft.com/windows/powertoys/keyboard-manager |
| PowerToysOverview_MouseUtilities | https://docs.microsoft.com/windows/powertoys/mouse-utilities |
| PowerToysOverview_PowerRename | https://docs.microsoft.com/windows/powertoys/powerrename |
| PowerToysOverview_PowerToysRun | https://docs.microsoft.com/windows/powertoys/run |
| PowerToysOverview_ShortcutGuide | https://docs.microsoft.com/windows/powertoys/shortcut-guide |

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View File

@ -10,6 +10,7 @@
<?define ColorPickerProjectName="ColorPicker"?>
<?define VideoConferenceProjectName="VideoConference"?>
<?define AwakeProjectName="Awake"?>
<?define MouseUtilsProjectName="MouseUtils"?>
<?define RepoDir="$(var.ProjectDir)..\..\" ?>
<?define BinX32Dir="$(var.RepoDir)x86\$(var.Configuration)\" ?>
@ -270,6 +271,10 @@
<Directory Id="ColorPickerResourcesFolder" Name="Resources"/>
</Directory>
<!-- Mouse Utils -->
<Directory Id="MouseUtilsInstallFolder" Name="$(var.MouseUtilsProjectName)">
</Directory>
<!-- Launcher -->
<Directory Id="LauncherInstallFolder" Name="launcher">
<Directory Id="AssetsFolder" Name="Assets" />
@ -702,6 +707,13 @@
</Component>
</DirectoryRef>
<!-- MouseUtils -->
<DirectoryRef Id="MouseUtilsInstallFolder" FileSource="$(var.BinX64Dir)modules\$(var.MouseUtilsProjectName)">
<Component Id="Module_FindMyMouse" Guid="60D0E4AE-188F-4403-BF06-1465AACC1BC5" Win64="yes">
<File Source="$(var.BinX64Dir)modules\$(var.MouseUtilsProjectName)\FindMyMouse.dll" KeyPath="yes" />
</Component>
</DirectoryRef>
<!-- Shortcut guide -->
<DirectoryRef Id="ShortcutGuideModuleInterfaceInstallFolder" FileSource="$(var.ShortcutGuideModuleInterface)">
<Component Id="Module_ShortcutGuideModuleInterface" Guid="CBD0AC09-91D3-428E-B2B3-05745ADF3473" Win64="yes">
@ -903,21 +915,21 @@
</DirectoryRef>
<DirectoryRef Id="SettingsV2AssetsModulesInstallFolder" FileSource="$(var.BinX64Dir)Settings\Assets\Modules">
<Component Id="SettingsV2AssetsModules" Guid="A0B961A9-77D0-4223-88A9-E3B41BD9C329" Win64="yes">
<?foreach File in ColorPicker.png;FancyZones.png;Awake.png;ImageResizer.png;KBM.png;PowerLauncher.png;PowerPreview.png;PowerRename.png;PT.png;ShortcutGuide.png;VideoConference.png?>
<?foreach File in ColorPicker.png;FancyZones.png;Awake.png;ImageResizer.png;KBM.png;MouseUtils.png;PowerLauncher.png;PowerPreview.png;PowerRename.png;PT.png;ShortcutGuide.png;VideoConference.png?>
<File Id="SettingsV2AssetsModules_$(var.File)" Source="$(var.BinX64Dir)Settings\Assets\Modules\$(var.File)" />
<?endforeach?>
</Component>
</DirectoryRef>
<DirectoryRef Id="SettingsV2OOBEAssetsModulesInstallFolder" FileSource="$(var.BinX64Dir)Settings\Assets\Modules\OOBE">
<Component Id="SettingsV2OOBEAssetsModules" Guid="E2360A83-6694-4B33-B5F6-641A906359EE" Win64="yes">
<?foreach File in ColorPicker.gif;Awake.png;FancyZones.gif;FileExplorer.png;ImageResizer.gif;KBM.gif;PowerRename.gif;Run.gif;OOBEShortcutGuide.png;VideoConferenceMute.png;OOBEPTHero.png?>
<?foreach File in ColorPicker.gif;Awake.png;FancyZones.gif;FileExplorer.png;ImageResizer.gif;KBM.gif;MouseUtils.gif;PowerRename.gif;Run.gif;OOBEShortcutGuide.png;VideoConferenceMute.png;OOBEPTHero.png?>
<File Id="SettingsV2OOBEAssetsModules_$(var.File)" Source="$(var.BinX64Dir)Settings\Assets\Modules\OOBE\$(var.File)" />
<?endforeach?>
</Component>
</DirectoryRef>
<DirectoryRef Id="SettingsV2OOBEAssetsFluentIconsInstallFolder" FileSource="$(var.BinX64Dir)Settings\Assets\FluentIcons">
<Component Id="SettingsV2OOBEAssetsFluentIcons" Guid="6A380D5A-DA63-45B5-B68F-06D57CDD1B9C" Win64="yes">
<?foreach File in ColorPicker.png;FancyZones.png;Awake.png;FileExplorerPreview.png;ImageResizer.png;KeyboardManager.png;PowerRename.png;PowerToys.png;PowerToysRun.png;Settings.png;ShortcutGuide.png;VideoConferenceMute.png ?>
<?foreach File in ColorPicker.png;FancyZones.png;Awake.png;FileExplorerPreview.png;ImageResizer.png;KeyboardManager.png;MouseUtils.png;PowerRename.png;PowerToys.png;PowerToysRun.png;Settings.png;ShortcutGuide.png;VideoConferenceMute.png ?>
<File Id="SettingsV2OOBEAssetsFluentIcons_$(var.File)" Source="$(var.BinX64Dir)Settings\Assets\FluentIcons\FluentIcons$(var.File)" />
<?endforeach?>
</Component>
@ -1012,6 +1024,7 @@
<ComponentRef Id="Module_Awake_runtime_netstandard20"/>
<ComponentRef Id="Module_Awake_runtime_netcoreapp30"/>
<ComponentRef Id="Module_Awake_runtime_netcoreapp21"/>
<ComponentRef Id="Module_FindMyMouse"/>
<ComponentRef Id="SettingsV2" />
<ComponentRef Id="SettingsV2Assets" />
<ComponentRef Id="SettingsV2AssetsModules" />

View File

@ -19,6 +19,7 @@ namespace Microsoft.PowerToys.Common.UI
Run,
ImageResizer,
KBM,
MouseUtils,
PowerRename,
FileExplorer,
ShortcutGuide,
@ -43,6 +44,8 @@ namespace Microsoft.PowerToys.Common.UI
return "ImageResizer";
case SettingsWindow.KBM:
return "KBM";
case SettingsWindow.MouseUtils:
return "MouseUtils";
case SettingsWindow.PowerRename:
return "PowerRename";
case SettingsWindow.FileExplorer:

View File

@ -23,6 +23,7 @@ struct LogSettings
inline const static std::wstring shortcutGuideLogPath = L"ShortcutGuideLogs\\shortcut-guide-log.txt";
inline const static std::string keyboardManagerLoggerName = "keyboard-manager";
inline const static std::wstring keyboardManagerLogPath = L"Logs\\keyboard-manager-log.txt";
inline const static std::string findMyMouseLoggerName = "find-my-mouse";
inline const static int retention = 30;
std::wstring logLevel;
LogSettings();

View File

@ -0,0 +1,5 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 .\ resource.base.h resource.h FindMyMouse.base.rc FindMyMouse.rc" />
</Target>
</Project>

View File

@ -0,0 +1,40 @@
#include <windows.h>
#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

View File

@ -0,0 +1,814 @@
// FindMyMouse.cpp : Based on Raymond Chen's SuperSonar.cpp
//
#include "pch.h"
#include "FindMyMouse.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
#pragma region Super_Sonar_Base_Code
template<typename D>
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;
protected:
// Base class members you can access.
D* Shim() { return static_cast<D*>(this); }
LRESULT BaseWndProc(UINT message, WPARAM wParam, LPARAM lParam) noexcept;
HWND m_hwnd;
POINT m_sonarPos = ptNowhere;
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;
winrt::DispatcherQueueController m_dispatcherQueueController{ nullptr };
private:
static bool IsEqual(POINT const& p1, POINT const& p2)
{
return p1.x == p2.x && p1.y == p2.y;
}
static constexpr POINT ptNowhere = { -1, -1 };
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{};
DWORD m_lastKeyTime{};
static constexpr DWORD NoSonar = 0;
static constexpr DWORD SonarWaitingForMouseMove = 1;
DWORD m_sonarStart = NoSonar;
bool m_isSnoopingMouse = false;
private:
static constexpr auto className = L"FindMyMouse";
// Use the runner name for the Window title. Otherwise, since Find My Mouse has an actual visual, its Window name will be the one shown in Task Manager after being shown.
static constexpr auto windowTitle = L"PowerToys Runner";
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 StartSonar();
void StopSonar();
void UpdateMouseSnooping();
};
template<typename D>
bool SuperSonar<D>::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 = (HBRUSH)GetStockObject(NULL_BRUSH);
wc.lpszClassName = className;
if (!RegisterClassW(&wc))
{
return false;
}
}
m_hwndOwner = CreateWindow(L"static", nullptr, WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, hinst, nullptr);
DWORD exStyle = WS_EX_TRANSPARENT | WS_EX_LAYERED | Shim()->GetExtendedStyle();
return CreateWindowExW(exStyle, className, windowTitle, WS_POPUP, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, m_hwndOwner, nullptr, hinst, this) != nullptr;
}
template<typename D>
void SuperSonar<D>::Terminate()
{
auto dispatcherQueue = m_dispatcherQueueController.DispatcherQueue();
bool enqueueSucceeded = dispatcherQueue.TryEnqueue([=]() {
DestroyWindow(m_hwndOwner);
});
if (!enqueueSucceeded)
{
Logger::error("Couldn't enqueue message to destroy the sonar Window.");
}
}
template<typename D>
LRESULT SuperSonar<D>::s_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
SuperSonar* self;
if (message == WM_NCCREATE)
{
auto info = (LPCREATESTRUCT)lParam;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)info->lpCreateParams);
self = (SuperSonar*)info->lpCreateParams;
self->m_hwnd = hwnd;
}
else
{
self = (SuperSonar*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (self)
{
return self->Shim()->WndProc(message, wParam, lParam);
}
else
{
return DefWindowProc(hwnd, message, wParam, lParam);
}
}
template<typename D>
LRESULT SuperSonar<D>::BaseWndProc(UINT message, WPARAM wParam, LPARAM lParam) noexcept
{
switch (message)
{
case WM_CREATE:
return OnSonarCreate() ? 0 : -1;
case WM_DESTROY:
OnSonarDestroy();
break;
case WM_INPUT:
OnSonarInput(wParam, (HRAWINPUT)lParam);
break;
case WM_TIMER:
switch (wParam)
{
case TIMER_ID_TRACK:
OnMouseTimer();
break;
}
break;
case WM_NCHITTEST:
return HTTRANSPARENT;
}
return DefWindowProc(m_hwnd, message, wParam, lParam);
}
template<typename D>
BOOL SuperSonar<D>::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<typename D>
void SuperSonar<D>::OnSonarDestroy()
{
PostQuitMessage(0);
}
template<typename D>
void SuperSonar<D>::OnSonarInput(WPARAM flags, HRAWINPUT hInput)
{
RAWINPUT input;
UINT size = sizeof(input);
auto result = GetRawInputData(hInput, RID_INPUT, &input, &size, sizeof(RAWINPUTHEADER));
if ((int)result < sizeof(RAWINPUTHEADER))
{
return;
}
switch (input.header.dwType)
{
case RIM_TYPEKEYBOARD:
OnSonarKeyboardInput(input);
break;
case RIM_TYPEMOUSE:
OnSonarMouseInput(input);
break;
}
}
template<typename D>
void SuperSonar<D>::OnSonarKeyboardInput(RAWINPUT const& input)
{
if (input.data.keyboard.VKey != VK_CONTROL)
{
StopSonar();
return;
}
bool pressed = (input.data.keyboard.Flags & RI_KEY_BREAK) == 0;
bool rightCtrl = (input.data.keyboard.Flags & RI_KEY_E0) != 0;
// Deal with rightCtrl first.
if (rightCtrl)
{
/*
* SuperSonar originally exited when pressing right control after pressing left control twice.
* We take care of exiting FindMyMouse through module disabling in PowerToys settings instead.
if (m_sonarState == SonarState::ControlUp2)
{
Terminate();
}
*/
StopSonar();
return;
}
switch (m_sonarState)
{
case SonarState::Idle:
if (pressed)
{
m_sonarState = SonarState::ControlDown1;
m_lastKeyTime = GetTickCount();
m_lastKeyPos = {};
GetCursorPos(&m_lastKeyPos);
UpdateMouseSnooping();
}
break;
case SonarState::ControlDown1:
if (!pressed)
{
m_sonarState = SonarState::ControlUp1;
}
break;
case SonarState::ControlUp1:
case SonarState::ControlUp2:
if (pressed)
{
m_sonarState = SonarState::ControlDown2;
auto now = GetTickCount();
POINT ptCursor{};
if (GetCursorPos(&ptCursor) &&
now - m_lastKeyTime <= GetDoubleClickTime() &&
IsEqual(m_lastKeyPos, ptCursor))
{
StartSonar();
}
m_lastKeyTime = now;
m_lastKeyPos = ptCursor;
}
break;
case SonarState::ControlDown2:
if (!pressed)
{
m_sonarState = SonarState::ControlUp2;
}
break;
}
}
template<typename D>
void SuperSonar<D>::OnSonarMouseInput(RAWINPUT const& input)
{
if (input.data.mouse.usButtonFlags)
{
StopSonar();
}
else if (m_sonarStart != NoSonar)
{
OnMouseTimer();
}
}
template<typename D>
void SuperSonar<D>::StartSonar()
{
Logger::info("Focusing the sonar on the mouse cursor.");
Trace::MousePointerFocused();
// Cover the entire virtual screen.
SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN), GetSystemMetrics(SM_YVIRTUALSCREEN), GetSystemMetrics(SM_CXVIRTUALSCREEN), GetSystemMetrics(SM_CYVIRTUALSCREEN), 0);
m_sonarPos = ptNowhere;
OnMouseTimer();
UpdateMouseSnooping();
Shim()->SetSonarVisibility(true);
}
template<typename D>
void SuperSonar<D>::StopSonar()
{
if (m_sonarStart != NoSonar)
{
m_sonarStart = NoSonar;
Shim()->SetSonarVisibility(false);
KillTimer(m_hwnd, TIMER_ID_TRACK);
}
m_sonarState = SonarState::Idle;
UpdateMouseSnooping();
}
template<typename D>
void SuperSonar<D>::OnMouseTimer()
{
auto now = GetTickCount();
// 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<typename D>
void SuperSonar<D>::UpdateMouseSnooping()
{
bool wantSnoopingMouse = m_sonarStart != NoSonar || m_sonarState != SonarState::Idle;
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));
}
}
struct CompositionSpotlight : SuperSonar<CompositionSpotlight>
{
static constexpr UINT WM_OPACITY_ANIMATION_COMPLETED = WM_APP;
static constexpr float SonarRadiusFloat = static_cast<float>(SonarRadius);
DWORD GetExtendedStyle()
{
return WS_EX_NOREDIRECTIONBITMAP;
}
void AfterMoveSonar()
{
m_spotlight.Offset({ (float)m_sonarPos.x, (float)m_sonarPos.y, 0.0f });
}
LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam) noexcept
{
switch (message)
{
case WM_CREATE:
return OnCompositionCreate() && BaseWndProc(message, wParam, lParam);
case WM_OPACITY_ANIMATION_COMPLETED:
OnOpacityAnimationCompleted();
break;
}
return BaseWndProc(message, wParam, lParam);
}
void SetSonarVisibility(bool visible)
{
m_batch = m_compositor.GetCommitBatch(winrt::CompositionBatchTypes::Animation);
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);
if (visible)
{
ShowWindow(m_hwnd, SW_SHOWNOACTIVATE);
}
}
private:
bool OnCompositionCreate()
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<ABI::ICompositorDesktopInterop>()->CreateDesktopWindowTarget(m_hwnd, false, &target));
*winrt::put_abi(m_target) = target;
// Our composition tree:
//
// [root] ContainerVisual
// \ LayerVisual
// \[gray backdrop]
// [spotlight]
m_root = m_compositor.CreateContainerVisual();
m_root.RelativeSizeAdjustment({ 1.0f, 1.0f }); // fill the parent
m_root.Opacity(0.0f);
m_target.Root(m_root);
auto layer = m_compositor.CreateLayerVisual();
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_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_spotlight = m_compositor.CreateShapeVisual();
m_spotlight.Size({ SonarRadiusFloat * 2 * SonarZoomFactor, SonarRadiusFloat * 2 * SonarZoomFactor });
m_spotlight.AnchorPoint({ 0.5f, 0.5f });
m_spotlight.Shapes().Append(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 });
auto collection = m_compositor.CreateImplicitAnimationCollection();
collection.Insert(L"Opacity", 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.
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));
radiusExpression.Expression(expressionText);
m_circleGeometry.StartAnimation(L"Radius", radiusExpression);
return true;
}
catch (...)
{
return false;
}
void OnOpacityAnimationCompleted()
{
if (m_root.Opacity() < 0.01f)
{
ShowWindow(m_hwnd, SW_HIDE);
}
}
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 };
};
template<typename D>
struct GdiSonar : SuperSonar<D>
{
LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam) noexcept
{
switch (message)
{
case WM_CREATE:
SetLayeredWindowAttributes(this->m_hwnd, 0, 0, LWA_ALPHA);
break;
case WM_TIMER:
switch (wParam)
{
case TIMER_ID_FADE:
OnFadeTimer();
break;
}
break;
case WM_PAINT:
this->Shim()->OnPaint();
break;
}
return this->BaseWndProc(message, wParam, lParam);
}
void BeforeMoveSonar() { this->Shim()->InvalidateSonar(); }
void AfterMoveSonar() { this->Shim()->InvalidateSonar(); }
void SetSonarVisibility(bool visible)
{
m_alphaTarget = visible ? MaxAlpha : 0;
m_fadeStart = GetTickCount() - FadeFramePeriod;
SetTimer(this->m_hwnd, TIMER_ID_FADE, FadeFramePeriod, nullptr);
OnFadeTimer();
}
void OnFadeTimer()
{
auto now = GetTickCount();
auto step = (int)((now - m_fadeStart) * MaxAlpha / this->FadeDuration);
this->Shim()->InvalidateSonar();
if (m_alpha < m_alphaTarget)
{
m_alpha += step;
if (m_alpha > m_alphaTarget)
m_alpha = m_alphaTarget;
}
else if (m_alpha > m_alphaTarget)
{
m_alpha -= step;
if (m_alpha < m_alphaTarget)
m_alpha = m_alphaTarget;
}
SetLayeredWindowAttributes(this->m_hwnd, 0, (BYTE)m_alpha, LWA_ALPHA);
this->Shim()->InvalidateSonar();
if (m_alpha == m_alphaTarget)
{
KillTimer(this->m_hwnd, TIMER_ID_FADE);
if (m_alpha == 0)
{
ShowWindow(this->m_hwnd, SW_HIDE);
}
}
else
{
ShowWindow(this->m_hwnd, SW_SHOWNOACTIVATE);
}
}
protected:
int CurrentSonarRadius()
{
int range = MaxAlpha - m_alpha;
int radius = this->SonarRadius + this->SonarRadius * range * (this->SonarZoomFactor - 1) / MaxAlpha;
return radius;
}
private:
static constexpr DWORD FadeFramePeriod = 10;
static constexpr int MaxAlpha = SuperSonar<D>::FinalAlphaNumerator * 255 / SuperSonar<D>::FinalAlphaDenominator;
static constexpr DWORD TIMER_ID_FADE = 101;
private:
int m_alpha = 0;
int m_alphaTarget = 0;
DWORD m_fadeStart = 0;
};
struct GdiSpotlight : GdiSonar<GdiSpotlight>
{
void InvalidateSonar()
{
RECT rc;
auto radius = CurrentSonarRadius();
rc.left = this->m_sonarPos.x - radius;
rc.top = this->m_sonarPos.y - radius;
rc.right = this->m_sonarPos.x + radius;
rc.bottom = this->m_sonarPos.y + radius;
InvalidateRect(this->m_hwnd, &rc, FALSE);
}
void OnPaint()
{
PAINTSTRUCT ps;
BeginPaint(this->m_hwnd, &ps);
auto radius = CurrentSonarRadius();
auto spotlight = CreateRoundRectRgn(
this->m_sonarPos.x - radius, this->m_sonarPos.y - radius, this->m_sonarPos.x + radius, this->m_sonarPos.y + radius, radius * 2, radius * 2);
FillRgn(ps.hdc, spotlight, (HBRUSH)GetStockObject(WHITE_BRUSH));
Sleep(1000 / 60);
ExtSelectClipRgn(ps.hdc, spotlight, RGN_DIFF);
FillRect(ps.hdc, &ps.rcPaint, (HBRUSH)GetStockObject(BLACK_BRUSH));
DeleteObject(spotlight);
EndPaint(this->m_hwnd, &ps);
}
};
struct GdiCrosshairs : GdiSonar<GdiCrosshairs>
{
void InvalidateSonar()
{
RECT rc;
auto radius = CurrentSonarRadius();
GetClientRect(m_hwnd, &rc);
rc.left = m_sonarPos.x - radius;
rc.right = m_sonarPos.x + radius;
InvalidateRect(m_hwnd, &rc, FALSE);
GetClientRect(m_hwnd, &rc);
rc.top = m_sonarPos.y - radius;
rc.bottom = m_sonarPos.y + radius;
InvalidateRect(m_hwnd, &rc, FALSE);
}
void OnPaint()
{
PAINTSTRUCT ps;
BeginPaint(this->m_hwnd, &ps);
auto radius = CurrentSonarRadius();
RECT rc;
HBRUSH white = (HBRUSH)GetStockObject(WHITE_BRUSH);
rc.left = m_sonarPos.x - radius;
rc.top = ps.rcPaint.top;
rc.right = m_sonarPos.x + radius;
rc.bottom = ps.rcPaint.bottom;
FillRect(ps.hdc, &rc, white);
rc.left = ps.rcPaint.left;
rc.top = m_sonarPos.y - radius;
rc.right = ps.rcPaint.right;
rc.bottom = m_sonarPos.y + radius;
FillRect(ps.hdc, &rc, white);
HBRUSH black = (HBRUSH)GetStockObject(BLACK_BRUSH);
// Top left
rc.left = ps.rcPaint.left;
rc.top = ps.rcPaint.top;
rc.right = m_sonarPos.x - radius;
rc.bottom = m_sonarPos.y - radius;
FillRect(ps.hdc, &rc, black);
// Top right
rc.left = m_sonarPos.x + radius;
rc.top = ps.rcPaint.top;
rc.right = ps.rcPaint.right;
rc.bottom = m_sonarPos.y - radius;
FillRect(ps.hdc, &rc, black);
// Bottom left
rc.left = ps.rcPaint.left;
rc.top = m_sonarPos.y + radius;
rc.right = m_sonarPos.x - radius;
rc.bottom = ps.rcPaint.bottom;
FillRect(ps.hdc, &rc, black);
// Bottom right
rc.left = m_sonarPos.x + radius;
rc.top = m_sonarPos.y + radius;
rc.right = ps.rcPaint.right;
rc.bottom = ps.rcPaint.bottom;
FillRect(ps.hdc, &rc, black);
EndPaint(this->m_hwnd, &ps);
}
};
#pragma endregion Super_Sonar_Base_Code
#pragma region Super_Sonar_API
CompositionSpotlight* m_sonar = nullptr;
void FindMyMouseDisable()
{
if (m_sonar != nullptr)
{
Logger::info("Terminating a sonar instance.");
m_sonar->Terminate();
}
}
bool FindMyMouseIsEnabled()
{
return (m_sonar != nullptr);
}
// Based on SuperSonar's original wWinMain.
int FindMyMouseMain(HINSTANCE hinst)
{
Logger::info("Starting a sonar instance.");
if (m_sonar != nullptr)
{
Logger::error("A sonar instance was still working when trying to start a new one.");
return 0;
}
CompositionSpotlight sonar;
if (!sonar.Initialize(hinst))
{
Logger::error("Couldn't initialize a sonar instance.");
return 0;
}
m_sonar = &sonar;
Logger::info("Initialized the sonar instance.");
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Logger::info("Sonar message loop ended.");
m_sonar = nullptr;
return (int)msg.wParam;
}
#pragma endregion Super_Sonar_API

View File

@ -0,0 +1,6 @@
#pragma once
#include "pch.h"
int FindMyMouseMain(HINSTANCE hinst);
void FindMyMouseDisable();
bool FindMyMouseIsEnabled();

View File

@ -0,0 +1,146 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{e94fd11c-0591-456f-899f-efc0ca548336}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>FindMyMouse</RootNamespace>
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
<ProjectName>FindMyMouse</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\MouseUtils\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\MouseUtils\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(CIBuild)'!='true'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="FindMyMouse.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="Generated Files\resource.h" />
<None Include="resource.base.h" />
<ClInclude Include="trace.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="FindMyMouse.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="trace.cpp" />
<None Include="FindMyMouse.base.rc" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SetttingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\FindMyMouse.rc" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>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}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Generated Files">
<UniqueIdentifier>{875a08c6-f610-4667-bd0f-80171ed96072}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="trace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FindMyMouse.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="trace.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FindMyMouse.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Generated Files\resource.h">
<Filter>Generated Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="resource.base.h">
<Filter>Resource Files</Filter>
</None>
<None Include="FindMyMouse.base.rc">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\FindMyMouse.rc">
<Filter>Generated Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,148 @@
#include "pch.h"
#include <interface/powertoy_module_interface.h>
#include <common/SettingsAPI/settings_objects.h>
#include "trace.h"
#include "FindMyMouse.h"
#include <thread>
#include <common/utils/logger_helper.h>
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"FindMyMouse";
// Add a description that will we shown in the module settings page.
const static wchar_t* MODULE_DESC = L"Focus the mouse pointer";
// Implement the PowerToy Module Interface and all the required methods.
class FindMyMouse : public PowertoyModuleIface
{
private:
// The PowerToy state.
bool m_enabled = false;
// Load initial settings from the persisted values.
void init_settings();
public:
// Constructor
FindMyMouse()
{
LoggerHelpers::init_logger(MODULE_NAME, L"ModuleInterface", LogSettings::findMyMouseLoggerName);
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<HINSTANCE>(&__ImageBase);
// Create a Settings object.
PowerToysSettings::Settings settings(hinstance, get_name());
settings.set_description(MODULE_DESC);
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());
values.save_to_settings_file();
}
catch (std::exception&)
{
// Improper JSON.
}
}
// Enable the powertoy
virtual void enable()
{
m_enabled = true;
Trace::EnableFindMyMouse(true);
std::thread([]() { FindMyMouseMain(m_hModule); }).detach();
}
// Disable the powertoy
virtual void disable()
{
m_enabled = false;
Trace::EnableFindMyMouse(false);
FindMyMouseDisable();
}
// Returns if the powertoys is enabled
virtual bool is_enabled() override
{
return m_enabled;
}
};
// Load the settings file.
void FindMyMouse::init_settings()
{
try
{
// Load and parse the settings file for this PowerToy.
PowerToysSettings::PowerToyValues settings =
PowerToysSettings::PowerToyValues::load_from_settings_file(FindMyMouse::get_key());
}
catch (std::exception&)
{
// Error while loading from the settings file. Let default values stay as they are.
}
}
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
return new FindMyMouse();
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
</packages>

View File

@ -0,0 +1 @@
#include "pch.h"

View File

@ -0,0 +1,20 @@
#pragma once
#define COMPOSITION
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <strsafe.h>
#include <hIdUsage.h>
#ifdef COMPOSITION
#include <windows.ui.composition.interop.h>
#include <DispatcherQueue.h>
#include <winrt/Windows.System.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.UI.Composition.Desktop.h>
#endif
#include <winrt/Windows.Foundation.Collections.h>
#include <ProjectTelemetry.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/logger/logger.h>

View File

@ -0,0 +1,14 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by FindMyMouse.rc
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys FindMyMouse"
#define INTERNAL_NAME "FindMyMouse"
#define ORIGINAL_FILENAME "FindMyMouse.dll"
#define IDS_KEYBOARDMANAGER_ICON 1001
// Non-localizable
//////////////////////////////

View File

@ -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 FindMyMouse enabled or disabled
void Trace::EnableFindMyMouse(const bool enabled) noexcept
{
TraceLoggingWrite(
g_hProvider,
"FindMyMouse_EnableFindMyMouse",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
TraceLoggingBoolean(enabled, "Enabled"));
}
// Log that the user activated the module by focusing the mouse pointer
void Trace::MousePointerFocused() noexcept
{
TraceLoggingWrite(
g_hProvider,
"FindMyMouse_MousePointerFocused",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

View File

@ -0,0 +1,14 @@
#pragma once
class Trace
{
public:
static void RegisterProvider() noexcept;
static void UnregisterProvider() noexcept;
// Log if the user has FindMyMouse enabled or disabled
static void EnableFindMyMouse(const bool enabled) noexcept;
// Log that the user activated the module by focusing the mouse pointer
static void MousePointerFocused() noexcept;
};

View File

@ -147,7 +147,8 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
L"modules/PowerRename/PowerRenameExt.dll",
L"modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.dll",
L"modules/ColorPicker/ColorPicker.dll",
L"modules/Awake/AwakeModuleInterface.dll"
L"modules/Awake/AwakeModuleInterface.dll",
L"modules/MouseUtils/FindMyMouse.dll"
};
const auto VCM_PATH = L"modules/VideoConference/VideoConferenceModule.dll";

View File

@ -523,6 +523,8 @@ std::string ESettingsWindowNames_to_string(ESettingsWindowNames value)
return "ImageResizer";
case ESettingsWindowNames::KBM:
return "KBM";
case ESettingsWindowNames::MouseUtils:
return "MouseUtils";
case ESettingsWindowNames::PowerRename:
return "PowerRename";
case ESettingsWindowNames::FileExplorer:
@ -570,6 +572,10 @@ ESettingsWindowNames ESettingsWindowNames_from_string(std::string value)
{
return ESettingsWindowNames::KBM;
}
else if (value == "MouseUtils")
{
return ESettingsWindowNames::MouseUtils;
}
else if (value == "PowerRename")
{
return ESettingsWindowNames::PowerRename;

View File

@ -11,6 +11,7 @@ enum class ESettingsWindowNames
Run,
ImageResizer,
KBM,
MouseUtils,
PowerRename,
FileExplorer,
ShortcutGuide,

View File

@ -175,6 +175,22 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}
}
private bool findMyMouse = true;
[JsonPropertyName("FindMyMouse")]
public bool FindMyMouse
{
get => findMyMouse;
set
{
if (findMyMouse != value)
{
LogTelemetryEvent(value);
findMyMouse = value;
}
}
}
public string ToJsonString()
{
return JsonSerializer.Serialize(this);

View File

@ -0,0 +1,56 @@
// 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;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
{
public class MouseUtilsViewModel : Observable
{
private GeneralSettings GeneralSettingsConfig { get; set; }
public MouseUtilsViewModel(ISettingsRepository<GeneralSettings> settingsRepository, Func<string, int> ipcMSGCallBackFunc)
{
// To obtain the general settings configurations of PowerToys Settings.
if (settingsRepository == null)
{
throw new ArgumentNullException(nameof(settingsRepository));
}
GeneralSettingsConfig = settingsRepository.SettingsConfig;
_isFindMyMouseEnabled = GeneralSettingsConfig.Enabled.FindMyMouse;
// set the callback functions value to hangle outgoing IPC message.
SendConfigMSG = ipcMSGCallBackFunc;
}
public bool IsFindMyMouseEnabled
{
get => _isFindMyMouseEnabled;
set
{
if (_isFindMyMouseEnabled != value)
{
_isFindMyMouseEnabled = value;
GeneralSettingsConfig.Enabled.FindMyMouse = value;
OnPropertyChanged(nameof(IsFindMyMouseEnabled));
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
SendConfigMSG(outgoing.ToString());
// TODO: Implement when this module has properties.
// NotifyPropertyChanged();
}
}
}
private Func<string, int> SendConfigMSG { get; }
private bool _isFindMyMouseEnabled;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -155,6 +155,9 @@
<Compile Include="OOBE\Views\OobeKBM.xaml.cs">
<DependentUpon>OobeKBM.xaml</DependentUpon>
</Compile>
<Compile Include="OOBE\Views\OobeMouseUtils.xaml.cs">
<DependentUpon>OobeMouseUtils.xaml</DependentUpon>
</Compile>
<Compile Include="OOBE\Views\OobeOverview.xaml.cs">
<DependentUpon>OobeOverview.xaml</DependentUpon>
</Compile>
@ -192,6 +195,9 @@
<Compile Include="Views\KeyboardManagerPage.xaml.cs">
<DependentUpon>KeyboardManagerPage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\MouseUtilsPage.xaml.cs">
<DependentUpon>MouseUtilsPage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\PowerLauncherPage.xaml.cs">
<DependentUpon>PowerLauncherPage.xaml</DependentUpon>
</Compile>
@ -227,6 +233,7 @@
<Content Include="Assets\FluentIcons\FluentIconsFileExplorerPreview.png" />
<Content Include="Assets\FluentIcons\FluentIconsImageResizer.png" />
<Content Include="Assets\FluentIcons\FluentIconsKeyboardManager.png" />
<Content Include="Assets\FluentIcons\FluentIconsMouseUtils.png" />
<Content Include="Assets\FluentIcons\FluentIconsPowerRename.png" />
<Content Include="Assets\FluentIcons\FluentIconsPowerToys.png" />
<Content Include="Assets\FluentIcons\FluentIconsPowerToysRun.png" />
@ -239,12 +246,14 @@
<Content Include="Assets\Modules\FancyZones.png" />
<Content Include="Assets\Modules\ImageResizer.png" />
<Content Include="Assets\Modules\KBM.png" />
<Content Include="Assets\Modules\MouseUtils.png" />
<Content Include="Assets\Modules\OOBE\ColorPicker.gif" />
<Content Include="Assets\Modules\OOBE\Awake.png" />
<Content Include="Assets\Modules\OOBE\FancyZones.gif" />
<Content Include="Assets\Modules\OOBE\FileExplorer.png" />
<Content Include="Assets\Modules\OOBE\ImageResizer.gif" />
<Content Include="Assets\Modules\OOBE\KBM.gif" />
<Content Include="Assets\Modules\OOBE\MouseUtils.gif" />
<Content Include="Assets\Modules\OOBE\OOBEPTHero.png" />
<Content Include="Assets\Modules\OOBE\PowerRename.gif" />
<Content Include="Assets\Modules\OOBE\Run.gif" />
@ -365,6 +374,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="OOBE\Views\OobeMouseUtils.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="OOBE\Views\OobeOverview.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@ -429,6 +442,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\MouseUtilsPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\PowerLauncherPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View File

@ -13,6 +13,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Enums
FileExplorer,
ImageResizer,
KBM,
MouseUtils,
PowerRename,
Run,
ShortcutGuide,

View File

@ -0,0 +1,33 @@
<Page
x:Class="Microsoft.PowerToys.Settings.UI.OOBE.Views.OobeMouseUtils"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
xmlns:toolkitcontrols="using:Microsoft.Toolkit.Uwp.UI.Controls"
mc:Ignorable="d">
<controls:OOBEPageControl ModuleTitle="{x:Bind ViewModel.ModuleName}"
ModuleImageSource="{x:Bind ViewModel.PreviewImageSource}"
ModuleDescription="{x:Bind ViewModel.Description}">
<controls:OOBEPageControl.ModuleContent>
<StackPanel Orientation="Vertical">
<TextBlock x:Uid="Oobe_MouseUtils_FindMyMouse"
Style="{ThemeResource OobeSubtitleStyle}" />
<toolkitcontrols:MarkdownTextBlock Background="Transparent" x:Uid="Oobe_MouseUtils_FindMyMouse_Description" />
<StackPanel Orientation="Horizontal" Spacing="12" Margin="0,24,0,0">
<Button x:Uid="OOBE_Settings"
Click="SettingsLaunchButton_Click"/>
<HyperlinkButton NavigateUri="{x:Bind ViewModel.Link}" Style="{StaticResource TextButtonStyle}">
<TextBlock x:Uid="LearnMore_MouseUtils"
TextWrapping="Wrap" />
</HyperlinkButton>
</StackPanel>
</StackPanel>
</controls:OOBEPageControl.ModuleContent>
</controls:OOBEPageControl>
</Page>

View File

@ -0,0 +1,45 @@
// 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 Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
using Microsoft.PowerToys.Settings.UI.Views;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
{
public sealed partial class OobeMouseUtils : Page
{
public OobePowerToysModule ViewModel { get; set; }
public OobeMouseUtils()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModulesEnum.MouseUtils]);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(MouseUtilsPage));
}
ViewModel.LogOpeningSettingsEvent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ViewModel.LogOpeningModuleEvent();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
ViewModel.LogClosingModuleEvent();
}
}
}

View File

@ -143,6 +143,18 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
PreviewImageSource = "ms-appx:///Assets/Modules/OOBE/KBM.gif",
Link = "https://aka.ms/PowerToysOverview_KeyboardManager",
});
Modules.Insert((int)PowerToysModulesEnum.MouseUtils, new OobePowerToysModule()
{
ModuleName = loader.GetString("Oobe_MouseUtils"),
Tag = "MouseUtils",
IsNew = true,
Icon = "\uE962",
FluentIcon = "ms-appx:///Assets/FluentIcons/FluentIconsMouseUtils.png",
Image = "ms-appx:///Assets/Modules/MouseUtils.png",
Description = loader.GetString("Oobe_MouseUtils_Description"),
PreviewImageSource = "ms-appx:///Assets/Modules/OOBE/MouseUtils.gif",
Link = "https://aka.ms/PowerToysOverview_MouseUtilities", // TODO: Add correct link after it's been created.
});
Modules.Insert((int)PowerToysModulesEnum.PowerRename, new OobePowerToysModule()
{
ModuleName = loader.GetString("Oobe_PowerRename"),
@ -228,6 +240,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
case "FileExplorer": NavigationFrame.Navigate(typeof(OobeFileExplorer)); break;
case "ShortcutGuide": NavigationFrame.Navigate(typeof(OobeShortcutGuide)); break;
case "VideoConference": NavigationFrame.Navigate(typeof(OobeVideoConference)); break;
case "MouseUtils": NavigationFrame.Navigate(typeof(OobeMouseUtils)); break;
}
}

View File

@ -248,6 +248,10 @@
<value>Keyboard Manager</value>
<comment>Product name: Navigation view item name for Keyboard Manager</comment>
</data>
<data name="Shell_MouseUtilities.Content" xml:space="preserve">
<value>Mouse utilities</value>
<comment>Product name: Navigation view item name for Mouse utilities</comment>
</data>
<data name="Shell_NavigationMenu_Announce_Collapse" xml:space="preserve">
<value>Navigation closed</value>
<comment>Accessibility announcement when the navigation pane collapses</comment>
@ -1275,6 +1279,10 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<data name="Oobe_VideoConference_Description" xml:space="preserve">
<value>Video Conference Mute allows users to quickly mute the microphone and turn off the camera while on a conference call with a single keystroke, regardless of what application has focus on your computer.</value>
</data>
<data name="Oobe_MouseUtils_Description" xml:space="preserve">
<value>A collection of utilities to enhance your mouse.</value>
<comment>Mouse as in the hardware peripheral</comment>
</data>
<data name="Oobe_Overview_Description.Text" xml:space="preserve">
<value>Microsoft PowerToys is a set of utilities for power users to tune and streamline their Windows experience for greater productivity.
@ -1567,6 +1575,10 @@ From there, simply click on a Markdown file, PDF file or SVG icon in the File Ex
<value>Learn more about Keyboard Manager</value>
<comment>Keyboard Manager is a product name, do not loc</comment>
</data>
<data name="LearnMore_MouseUtils.Text" xml:space="preserve">
<value>Learn more about Mouse utilities</value>
<comment>Mouse utilities is a product name, do not loc</comment>
</data>
<data name="LearnMore_PowerPreview.Text" xml:space="preserve">
<value>Learn more about File Explorer add-ons</value>
<comment>File Explorer is a product name, do not loc</comment>
@ -1591,6 +1603,18 @@ From there, simply click on a Markdown file, PDF file or SVG icon in the File Ex
<value>File Explorer add-ons</value>
<comment>Do not localize this string</comment>
</data>
<data name="Oobe_MouseUtils" xml:space="preserve">
<value>Mouse utilities</value>
<comment>Mouse as in the hardware peripheral</comment>
</data>
<data name="Oobe_MouseUtils_FindMyMouse.Text" xml:space="preserve">
<value>Find My Mouse</value>
<comment>Mouse as in the hardware peripheral</comment>
</data>
<data name="Oobe_MouseUtils_FindMyMouse_Description.Text" xml:space="preserve">
<value>Click twice on the Left Control key to focus on your mouse.</value>
<comment>Mouse as in the hardware peripheral. Key as in a keyboard key</comment>
</data>
<data name="Launch_Run.Content" xml:space="preserve">
<value>Launch PowerToys Run</value>
</data>
@ -1621,6 +1645,9 @@ From there, simply click on a Markdown file, PDF file or SVG icon in the File Ex
<data name="ImageResizer.SecondaryLinksHeader" xml:space="preserve">
<value>Attribution</value>
</data>
<data name="MouseUtils.SecondaryLinksHeader" xml:space="preserve">
<value>Attribution</value>
</data>
<data name="PowerLauncher.SecondaryLinksHeader" xml:space="preserve">
<value>Attribution</value>
</data>
@ -1661,4 +1688,18 @@ From there, simply click on a Markdown file, PDF file or SVG icon in the File Ex
<data name="Awake_TimeBeforeAwake.Header" xml:space="preserve">
<value>Time before returning to the previous awakeness state</value>
</data>
<data name="MouseUtils.ModuleTitle" xml:space="preserve">
<value>Mouse utilities</value>
</data>
<data name="MouseUtils.ModuleDescription" xml:space="preserve">
<value>A collection of mouse utilities.</value>
</data>
<data name="MouseUtils_Enable_FindMyMouse.Header" xml:space="preserve">
<value>Enable Find My Mouse</value>
<comment>"Find My Mouse" is the name of the utility.</comment>
</data>
<data name="MouseUtils_Enable_FindMyMouse.Description" xml:space="preserve">
<value>Press the Left Control key twice to focus the mouse pointer.</value>
<comment>"Left Control" is a keyboard key.</comment>
</data>
</root>

View File

@ -0,0 +1,32 @@
<Page
x:Class="Microsoft.PowerToys.Settings.UI.Views.MouseUtilsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
AutomationProperties.LandmarkType="Main">
<controls:SettingsPageControl x:Uid="MouseUtils"
ModuleImageSource="ms-appx:///Assets/Modules/MouseUtils.png">
<controls:SettingsPageControl.ModuleContent>
<StackPanel Orientation="Vertical">
<controls:Setting x:Uid="MouseUtils_Enable_FindMyMouse" Icon="&#xF272;">
<controls:Setting.ActionContent>
<ToggleSwitch IsOn="{x:Bind ViewModel.IsFindMyMouseEnabled, Mode=TwoWay}" HorizontalAlignment="Right"/>
</controls:Setting.ActionContent>
</controls:Setting>
</StackPanel>
</controls:SettingsPageControl.ModuleContent>
<controls:SettingsPageControl.PrimaryLinks>
<controls:PageLink x:Uid="LearnMore_MouseUtils" Link="https://aka.ms/PowerToysOverview_MouseUtilities"/>
</controls:SettingsPageControl.PrimaryLinks>
<controls:SettingsPageControl.SecondaryLinks>
<controls:PageLink Text="Raymond Chen's Find My Mouse" Link="https://devblogs.microsoft.com/oldnewthing/author/oldnewthing"/>
</controls:SettingsPageControl.SecondaryLinks>
</controls:SettingsPageControl>
</Page>

View File

@ -0,0 +1,23 @@
// 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 Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels;
using Windows.UI.Xaml.Controls;
namespace Microsoft.PowerToys.Settings.UI.Views
{
public sealed partial class MouseUtilsPage : Page
{
private MouseUtilsViewModel ViewModel { get; set; }
public MouseUtilsPage()
{
var settingsUtils = new SettingsUtils();
ViewModel = new MouseUtilsViewModel(SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage);
DataContext = ViewModel;
InitializeComponent();
}
}
}

View File

@ -95,6 +95,14 @@
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>
<muxc:NavigationViewItem x:Uid="Shell_MouseUtilities"
helpers:NavHelper.NavigateTo="views:MouseUtilsPage">
<muxc:NavigationViewItem.Icon>
<BitmapIcon UriSource="ms-appx:///Assets/FluentIcons/FluentIconsMouseUtils.png"
ShowAsMonochrome="False" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>
<muxc:NavigationViewItem x:Uid="Shell_PowerRename"
helpers:NavHelper.NavigateTo="views:PowerRenamePage">
<muxc:NavigationViewItem.Icon>

View File

@ -68,6 +68,7 @@ namespace PowerToys.Settings
case "Run": app.StartupPage = typeof(Microsoft.PowerToys.Settings.UI.Views.PowerLauncherPage); break;
case "ImageResizer": app.StartupPage = typeof(Microsoft.PowerToys.Settings.UI.Views.ImageResizerPage); break;
case "KBM": app.StartupPage = typeof(Microsoft.PowerToys.Settings.UI.Views.KeyboardManagerPage); break;
case "MouseUtils": app.StartupPage = typeof(Microsoft.PowerToys.Settings.UI.Views.MouseUtilsPage); break;
case "PowerRename": app.StartupPage = typeof(Microsoft.PowerToys.Settings.UI.Views.PowerRenamePage); break;
case "FileExplorer": app.StartupPage = typeof(Microsoft.PowerToys.Settings.UI.Views.PowerPreviewPage); break;
case "ShortcutGuide": app.StartupPage = typeof(Microsoft.PowerToys.Settings.UI.Views.ShortcutGuidePage); break;