From 83f062542778a14d5dd260d0018d3db1f6c84af6 Mon Sep 17 00:00:00 2001 From: Davide Giacometti Date: Mon, 4 Sep 2023 17:58:37 +0200 Subject: [PATCH] [FindMyMouse]Support different activation modes (#28096) * multiple activation modes for find my mouse * use custom shortcut --- .../MouseUtils/FindMyMouse/FindMyMouse.cpp | 55 +++++++++++--- .../MouseUtils/FindMyMouse/FindMyMouse.h | 11 ++- .../FindMyMouse/FindMyMouse.vcxproj | 2 + .../FindMyMouse/FindMyMouse.vcxproj.filters | 6 ++ .../FindMyMouse/WinHookEventIDs.cpp | 14 ++++ .../MouseUtils/FindMyMouse/WinHookEventIDs.h | 5 ++ .../MouseUtils/FindMyMouse/dllmain.cpp | 76 ++++++++++++++++++- .../FindMyMouseProperties.cs | 6 ++ .../FindMyMouseSettings.cs | 13 +++- ...ouseActivationIntToVisibilityConverter.cs} | 16 ++-- .../SettingsXAML/Views/MouseUtilsPage.xaml | 14 +++- .../Settings.UI/Strings/en-us/Resources.resw | 21 ++++- .../ViewModels/MouseUtilsViewModel.cs | 19 ++++- 13 files changed, 222 insertions(+), 36 deletions(-) create mode 100644 src/modules/MouseUtils/FindMyMouse/WinHookEventIDs.cpp create mode 100644 src/modules/MouseUtils/FindMyMouse/WinHookEventIDs.h rename src/settings-ui/Settings.UI/Converters/{FindMyMouseActivationShakeMouseIntToVisibilityConverter.cs => FindMyMouseActivationIntToVisibilityConverter.cs} (58%) diff --git a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp index b1d1bf4f18..f5f3ff2e00 100644 --- a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp +++ b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp @@ -2,6 +2,7 @@ // #include "pch.h" #include "FindMyMouse.h" +#include "WinHookEventIDs.h" #include "trace.h" #include "common/utils/game_mode.h" #include "common/utils/process_path.h" @@ -246,6 +247,18 @@ LRESULT SuperSonar::BaseWndProc(UINT message, WPARAM wParam, LPARAM lParam) n return HTTRANSPARENT; } + if (message == WM_PRIV_SHORTCUT) + { + if (m_sonarStart == NoSonar) + { + StartSonar(); + } + else + { + StopSonar(); + } + } + return DefWindowProc(m_hwnd, message, wParam, lParam); } @@ -291,26 +304,27 @@ void SuperSonar::OnSonarInput(WPARAM flags, HRAWINPUT hInput) template void SuperSonar::OnSonarKeyboardInput(RAWINPUT const& input) { - if ( m_activationMethod != FindMyMouseActivationMethod::DoubleControlKey || input.data.keyboard.VKey != VK_CONTROL) + // 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 rightCtrl = (input.data.keyboard.Flags & RI_KEY_E0) != 0; - // Deal with rightCtrl first. - if (rightCtrl) + 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)) { - /* - * 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; } @@ -644,6 +658,11 @@ struct CompositionSpotlight : SuperSonar } } + HWND GetHwnd() noexcept + { + return m_hwnd; + } + private: bool OnCompositionCreate() try @@ -1057,6 +1076,8 @@ int FindMyMouseMain(HINSTANCE hinst, const FindMyMouseSettings& settings) m_sonar = &sonar; Logger::info("Initialized the sonar instance."); + InitializeWinhookEventIds(); + MSG msg; // Main message loop: @@ -1072,4 +1093,14 @@ int FindMyMouseMain(HINSTANCE hinst, const FindMyMouseSettings& settings) return (int)msg.wParam; } +HWND GetSonarHwnd() noexcept +{ + if (m_sonar != nullptr) + { + return m_sonar->GetHwnd(); + } + + return nullptr; +} + #pragma endregion Super_Sonar_API diff --git a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.h b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.h index bf70ee3279..149e52842b 100644 --- a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.h +++ b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.h @@ -3,9 +3,11 @@ enum struct FindMyMouseActivationMethod : int { - DoubleControlKey = 0, - ShakeMouse = 1, - EnumElements = 2, // number of elements in the enum, not counting this + DoubleLeftControlKey = 0, + DoubleRightControlKey = 1, + ShakeMouse = 2, + Shortcut = 3, + EnumElements = 4, // number of elements in the enum, not counting this }; constexpr bool FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE = true; @@ -15,7 +17,7 @@ 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; -constexpr FindMyMouseActivationMethod FIND_MY_MOUSE_DEFAULT_ACTIVATION_METHOD = FindMyMouseActivationMethod::DoubleControlKey; +constexpr FindMyMouseActivationMethod FIND_MY_MOUSE_DEFAULT_ACTIVATION_METHOD = FindMyMouseActivationMethod::DoubleLeftControlKey; constexpr int FIND_MY_MOUSE_DEFAULT_SHAKE_MINIMUM_DISTANCE = 1000; struct FindMyMouseSettings @@ -36,3 +38,4 @@ int FindMyMouseMain(HINSTANCE hinst, const FindMyMouseSettings& settings); void FindMyMouseDisable(); bool FindMyMouseIsEnabled(); void FindMyMouseApplySettings(const FindMyMouseSettings& settings); +HWND GetSonarHwnd() noexcept; \ No newline at end of file diff --git a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.vcxproj b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.vcxproj index 28ff78ab9a..8c60095541 100644 --- a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.vcxproj +++ b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.vcxproj @@ -93,6 +93,7 @@ + @@ -101,6 +102,7 @@ Create + diff --git a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.vcxproj.filters b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.vcxproj.filters index 5bd41642ef..4a2f070986 100644 --- a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.vcxproj.filters +++ b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.vcxproj.filters @@ -30,6 +30,9 @@ Source Files + + Source Files + @@ -44,6 +47,9 @@ Resource Files + + Header Files + diff --git a/src/modules/MouseUtils/FindMyMouse/WinHookEventIDs.cpp b/src/modules/MouseUtils/FindMyMouse/WinHookEventIDs.cpp new file mode 100644 index 0000000000..76bfbdc861 --- /dev/null +++ b/src/modules/MouseUtils/FindMyMouse/WinHookEventIDs.cpp @@ -0,0 +1,14 @@ +#include "pch.h" + +#include "WinHookEventIDs.h" + +UINT WM_PRIV_SHORTCUT; + +std::once_flag init_flag; + +void InitializeWinhookEventIds() +{ + std::call_once(init_flag, [&] { + WM_PRIV_SHORTCUT = RegisterWindowMessage(L"{1365FFC7-A44E-4171-9692-A3EEF378AE60}"); + }); +} diff --git a/src/modules/MouseUtils/FindMyMouse/WinHookEventIDs.h b/src/modules/MouseUtils/FindMyMouse/WinHookEventIDs.h new file mode 100644 index 0000000000..47596969fe --- /dev/null +++ b/src/modules/MouseUtils/FindMyMouse/WinHookEventIDs.h @@ -0,0 +1,5 @@ +#pragma once + +extern UINT WM_PRIV_SHORTCUT; // Shortcut is pressed + +void InitializeWinhookEventIds(); \ No newline at end of file diff --git a/src/modules/MouseUtils/FindMyMouse/dllmain.cpp b/src/modules/MouseUtils/FindMyMouse/dllmain.cpp index f29526b335..2d1aa55acc 100644 --- a/src/modules/MouseUtils/FindMyMouse/dllmain.cpp +++ b/src/modules/MouseUtils/FindMyMouse/dllmain.cpp @@ -3,6 +3,7 @@ #include #include "trace.h" #include "FindMyMouse.h" +#include "WinHookEventIDs.h" #include #include #include @@ -22,6 +23,7 @@ namespace const wchar_t JSON_KEY_SPOTLIGHT_INITIAL_ZOOM[] = L"spotlight_initial_zoom"; const wchar_t JSON_KEY_EXCLUDED_APPS[] = L"excluded_apps"; const wchar_t JSON_KEY_SHAKING_MINIMUM_DISTANCE[] = L"shaking_minimum_distance"; + const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"activation_shortcut"; } extern "C" IMAGE_DOS_HEADER __ImageBase; @@ -58,6 +60,9 @@ private: // The PowerToy state. bool m_enabled = false; + // Hotkey to invoke the module + HotkeyEx m_hotkey; + // Find My Mouse specific settings FindMyMouseSettings m_findMyMouseSettings; @@ -157,6 +162,27 @@ public: { return m_enabled; } + + virtual std::optional GetHotkeyEx() override + { + Logger::trace("GetHotkeyEx()"); + if (m_findMyMouseSettings.activationMethod == FindMyMouseActivationMethod::Shortcut) + { + return m_hotkey; + } + + return std::nullopt; + } + + virtual void OnHotkeyEx() override + { + Logger::trace("OnHotkeyEx()"); + HWND hwnd = GetSonarHwnd(); + if (hwnd != nullptr) + { + PostMessageW(hwnd, WM_PRIV_SHORTCUT, NULL, NULL); + } + } }; // Load the settings file. @@ -188,7 +214,15 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); if (value < static_cast(FindMyMouseActivationMethod::EnumElements) && value >= 0) { - findMyMouseSettings.activationMethod = static_cast(value); + std::wstring version = (std::wstring)settingsObject.GetNamedString(L"version"); + if (version == L"1.0" && value == 1) + { + findMyMouseSettings.activationMethod = FindMyMouseActivationMethod::ShakeMouse; + } + else + { + findMyMouseSettings.activationMethod = static_cast(value); + } } else { @@ -362,6 +396,46 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) { Logger::warn("Failed to initialize Shaking Minimum Distance from settings. Will use default value"); } + + try + { + // Parse HotKey + auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ACTIVATION_SHORTCUT); + auto hotkey = PowerToysSettings::HotkeyObject::from_json(jsonPropertiesObject); + m_hotkey = HotkeyEx(); + if (hotkey.win_pressed()) + { + m_hotkey.modifiersMask |= MOD_WIN; + } + + if (hotkey.ctrl_pressed()) + { + m_hotkey.modifiersMask |= MOD_CONTROL; + } + + if (hotkey.shift_pressed()) + { + m_hotkey.modifiersMask |= MOD_SHIFT; + } + + if (hotkey.alt_pressed()) + { + m_hotkey.modifiersMask |= MOD_ALT; + } + + m_hotkey.vkCode = static_cast(hotkey.get_code()); + } + catch (...) + { + Logger::warn("Failed to initialize Activation Shortcut from settings. Will use default value"); + } + + if (!m_hotkey.modifiersMask) + { + Logger::info("Using default Activation Shortcut"); + m_hotkey.modifiersMask = MOD_SHIFT | MOD_WIN; + m_hotkey.vkCode = 0x46; // F key + } } else { diff --git a/src/settings-ui/Settings.UI.Library/FindMyMouseProperties.cs b/src/settings-ui/Settings.UI.Library/FindMyMouseProperties.cs index d30b9307b8..ba97a590d7 100644 --- a/src/settings-ui/Settings.UI.Library/FindMyMouseProperties.cs +++ b/src/settings-ui/Settings.UI.Library/FindMyMouseProperties.cs @@ -8,9 +8,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library { public class FindMyMouseProperties { + public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x46); + [JsonPropertyName("activation_method")] public IntProperty ActivationMethod { get; set; } + [JsonPropertyName("activation_shortcut")] + public HotkeySettings ActivationShortcut { get; set; } + [JsonPropertyName("do_not_activate_on_game_mode")] public BoolProperty DoNotActivateOnGameMode { get; set; } @@ -41,6 +46,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library public FindMyMouseProperties() { ActivationMethod = new IntProperty(0); + ActivationShortcut = DefaultActivationShortcut; DoNotActivateOnGameMode = new BoolProperty(true); BackgroundColor = new StringProperty("#000000"); SpotlightColor = new StringProperty("#FFFFFF"); diff --git a/src/settings-ui/Settings.UI.Library/FindMyMouseSettings.cs b/src/settings-ui/Settings.UI.Library/FindMyMouseSettings.cs index cd65372cdd..921bd62992 100644 --- a/src/settings-ui/Settings.UI.Library/FindMyMouseSettings.cs +++ b/src/settings-ui/Settings.UI.Library/FindMyMouseSettings.cs @@ -18,7 +18,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { Name = ModuleName; Properties = new FindMyMouseProperties(); - Version = "1.0"; + Version = "1.1"; } public string GetModuleName() @@ -29,6 +29,17 @@ namespace Microsoft.PowerToys.Settings.UI.Library // This can be utilized in the future if the settings.json file is to be modified/deleted. public bool UpgradeSettingsConfiguration() { + if (Version == "1.0") + { + if (Properties.ActivationMethod.Value == 1) + { + Properties.ActivationMethod = new IntProperty(2); + } + + Version = "1.1"; + return true; + } + return false; } } diff --git a/src/settings-ui/Settings.UI/Converters/FindMyMouseActivationShakeMouseIntToVisibilityConverter.cs b/src/settings-ui/Settings.UI/Converters/FindMyMouseActivationIntToVisibilityConverter.cs similarity index 58% rename from src/settings-ui/Settings.UI/Converters/FindMyMouseActivationShakeMouseIntToVisibilityConverter.cs rename to src/settings-ui/Settings.UI/Converters/FindMyMouseActivationIntToVisibilityConverter.cs index d4539fe5dc..080de07713 100644 --- a/src/settings-ui/Settings.UI/Converters/FindMyMouseActivationShakeMouseIntToVisibilityConverter.cs +++ b/src/settings-ui/Settings.UI/Converters/FindMyMouseActivationIntToVisibilityConverter.cs @@ -3,26 +3,20 @@ // See the LICENSE file in the project root for more information. using System; +using System.Globalization; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Data; namespace Microsoft.PowerToys.Settings.UI.Converters { - public sealed class FindMyMouseActivationShakeMouseIntToVisibilityConverter : IValueConverter + public sealed class FindMyMouseActivationIntToVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { - var activationShake = (int)value; + var selectedActivation = (int)value; + var expectedActivation = int.Parse(parameter as string, CultureInfo.InvariantCulture); - // Assumes 1 is the index for the shake mouse option in the activation method combo box - if (activationShake == 1) - { - return Visibility.Visible; - } - else - { - return Visibility.Collapsed; - } + return selectedActivation == expectedActivation ? Visibility.Visible : Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, string language) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml index 77ee711c07..4db60c56e4 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml @@ -11,7 +11,7 @@ AutomationProperties.LandmarkType="Main" mc:Ignorable="d"> - + + + + Visibility="{x:Bind Mode=OneWay, Path=ViewModel.FindMyMouseActivationMethod, Converter={StaticResource FindMyMouseActivationIntToVisibilityConverter}, ConverterParameter=2}"> + + + +