From a8c99e9513ea8a4fa44ac996afe7262f8cd42be3 Mon Sep 17 00:00:00 2001 From: Mykhailo Pylyp Date: Mon, 26 Apr 2021 22:01:38 +0300 Subject: [PATCH] [KBM] Migrate Engine and Editor into separate processes (#10774) * Move KBM engine into separate process (#10672) * [KBM] Migrate KBM UI out of the runner (#10709) * Clean up keyboard hook handles (#10817) * [C++ common] Unhandled exception handler (#10821) * [KBM] Use icon in the KeyboardManagerEditor (#10845) * [KBM] Move resources from the Common project to the Editor. (#10844) * KBM Editor tests (#10858) * Rename engine executable (#10868) * clean up (#10870) * [KBM] Changed Editor and libraries output folders (#10871) * [KBM] New logs structure (#10872) * Add unhandled exception handling to the editor (#10874) * [KBM] Trace for edit keyboard window * Logging for XamlBridge message loop * [KBM] Added Editor and Engine to the installer (#10876) * Fix spelling * Interprocess communication logs, remove unnecessary windows message logs * [KBM] Separated telemetry for the engine and editor. (#10889) * [KBM] Editor test project (#10891) * Versions for the engine and the editor (#10897) * Add the editor's and the engine's executables to signing process (#10900) * [KBM editor] Run only one instance, exit when parent process exits (#10890) * [KBM] Force kill editor process to avoid XAML crash (#10907) * [KBM] Force kill editor process to avoid XAML crash * Fix event releasing Co-authored-by: mykhailopylyp <17161067+mykhailopylyp@users.noreply.github.com> * Make the editor dpi aware (#10908) * [KBM] KeyboardManagerCommon refactoring (#10909) * Do not start the process if it is already started (#10910) * logs * Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp * Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp * [KBM] Rename InitUnhandledExceptionHandler to make it explicit that is for x64 only. We will fix it properly when adding support for ARM64 and add a header with the proper conditional building. * [KBM] rename file/class/variables using camel case * [KBM] Rename "event_locker" -> "EventLocker" * [KBM] rename process_waiter Add a TODO comment * [KBM] rename methods Add TODO comment * [KBM] use uppercase for function names * [KBM] use uppercase for methos, lowercase for properties * [KBM] rename method, make methods private, formatting * [KBM] rename private variables * [KBM] use uppercase for function names * [KBM] Added support to run the editor stand-alone when built in debug mode * Update src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp * Check success of event creation, comment (#10947) * [KBM] code formatting (#10951) * [KBM] code formatting * Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp * [KBM] tracing * [KBM] Remappings not showing fix. (#10954) * removed mutex * retry loop for reading * retry on reading config once * log error Co-authored-by: Enrico Giordani Co-authored-by: Enrico Giordani Co-authored-by: Seraphima Zykova Co-authored-by: Enrico Giordani Co-authored-by: Enrico Giordani --- .github/actions/spell-check/expect.txt | 10 + .../ci/templates/build-powertoys-steps.yml | 3 +- .pipelines/pipeline.user.windows.yml | 2 + PowerToys.sln | 60 +- installer/PowerToysSetup/Product.wxs | 19 +- src/common/interop/interop.cpp | 4 - src/common/interop/shared_constants.h | 3 - src/common/logger/logger.h | 5 + src/common/utils/EventLocker.h | 61 ++ src/common/utils/EventWaiter.h | 78 ++ src/common/utils/ProcessWaiter.h | 32 + .../utils/UnhandledExceptionHandler_x64.h | 238 +++++ src/common/utils/logger_helper.h | 17 + src/common/utils/winapi_error.h | 6 + .../Keyboard.ico | Bin .../KeyboardManagerEditor.base.rc | 110 ++ .../KeyboardManagerEditor.cpp | 217 ++++ .../KeyboardManagerEditor.exe.manifest | 14 + .../KeyboardManagerEditor.h | 39 + .../KeyboardManagerEditor.vcxproj | 192 ++++ .../KeyboardManagerEditor.vcxproj.filters | 67 ++ .../KeyboardManagerEditor/LocProject.json | 14 + .../KeyboardManagerEditor/Resource.h | 24 + .../KeyboardManagerEditor/Resources.resx | 367 +++++++ .../packages.config | 0 .../{ui => KeyboardManagerEditor}/pch.cpp | 0 .../{ui => KeyboardManagerEditor}/pch.h | 64 +- .../KeyboardManagerEditor/resource.base.h | 14 + .../KeyboardManagerEditor/targetver.h | 6 + .../BufferValidationHelpers.cpp | 30 +- .../BufferValidationHelpers.h | 6 +- .../Dialog.cpp | 3 - .../Dialog.h | 0 .../EditKeyboardWindow.cpp | 90 +- .../EditKeyboardWindow.h | 20 +- .../EditShortcutsWindow.cpp | 76 +- .../EditShortcutsWindow.h | 20 +- .../KeyDropDownControl.cpp | 32 +- .../KeyDropDownControl.h | 10 +- .../KeyboardManagerEditorLibrary.vcxproj} | 196 ++-- ...boardManagerEditorLibrary.vcxproj.filters} | 205 ++-- .../KeyboardManagerEditorStrings.cpp | 50 + .../KeyboardManagerEditorStrings.h | 14 + .../LoadingAndSavingRemappingHelper.cpp | 10 +- .../LoadingAndSavingRemappingHelper.h | 3 +- .../ShortcutControl.cpp | 959 +++++++++--------- .../ShortcutControl.h | 117 +-- .../SingleKeyRemapControl.cpp | 717 ++++++------- .../SingleKeyRemapControl.h | 111 +- .../Styles.cpp | 0 .../Styles.h | 0 .../UIHelpers.cpp | 71 ++ .../KeyboardManagerEditorLibrary/UIHelpers.h | 32 + .../XamlBridge.cpp | 32 + .../XamlBridge.h | 1 - .../KeyboardManagerEditorLibrary/pch.cpp | 5 + .../KeyboardManagerEditorLibrary/pch.h | 39 + .../KeyboardManagerEditorLibrary/targetver.h | 6 + .../KeyboardManagerEditorLibrary/trace.cpp | 71 ++ .../trace.h | 11 +- .../BufferValidationTests.cpp | 3 +- .../KeyboardManagerEditorTest.rc} | 0 .../KeyboardManagerEditorTest.vcxproj | 83 ++ .../KeyboardManagerEditorTest.vcxproj.filters | 56 + .../LoadingAndSavingRemappingTests.cpp | 3 +- .../MockedInput.cpp | 0 .../MockedInput.h | 2 +- .../TestHelpers.cpp | 0 .../TestHelpers.h | 0 .../packages.config | 6 +- .../pch.cpp | 0 .../{test => KeyboardManagerEditorTest}/pch.h | 0 .../resource.h | 0 .../KeyboardManagerEngine/Keyboard.ico | Bin 0 -> 115571 bytes .../KeyboardManagerEngine.rc | 111 ++ .../KeyboardManagerEngine.vcxproj | 95 ++ .../KeyboardManagerEngine.vcxproj.filters | 47 + .../KeyboardManagerEngine/PropertySheet.props | 16 + .../KeyboardManagerEngine/main.cpp | 65 ++ .../KeyboardManagerEngine/packages.config | 4 + .../KeyboardManagerEngine/pch.cpp | 1 + .../KeyboardManagerEngine/pch.h | 10 + .../KeyboardManagerEngine/resource.h | 14 + .../KeyboardEventHandlers.cpp | 69 +- .../KeyboardEventHandlers.h | 33 + .../KeyboardManager.cpp | 167 +++ .../KeyboardManager.h | 48 + .../KeyboardManagerEngineLibrary.vcxproj | 74 ++ ...yboardManagerEngineLibrary.vcxproj.filters | 48 + .../packages.config | 4 + .../KeyboardManagerEngineLibrary/pch.cpp | 1 + .../KeyboardManagerEngineLibrary/pch.h | 11 + .../trace.cpp | 50 - .../KeyboardManagerEngineLibrary/trace.h | 17 + .../AppSpecificShortcutRemappingTests.cpp | 4 +- .../HelperTests.cpp} | 1 + .../KeyboardManagerEngineTest.vcxproj} | 26 +- ...KeyboardManagerEngineTest.vcxproj.filters} | 13 +- .../KeyboardManagerEngineTest/MockedInput.cpp | 163 +++ .../KeyboardManagerEngineTest/MockedInput.h | 61 ++ .../MockedInputSanityTests.cpp | 4 +- .../OSLevelShortcutRemappingTests.cpp | 4 +- .../SetKeyEventTests.cpp | 4 +- .../ShortcutTests.cpp | 1 + .../SingleKeyRemappingTests.cpp | 4 +- .../KeyboardManagerEngineTest/TestHelpers.cpp | 25 + .../KeyboardManagerEngineTest/TestHelpers.h | 16 + .../KeyboardManagerEngineTest/packages.config | 4 + .../KeyboardManagerEngineTest/pch.cpp | 1 + .../KeyboardManagerEngineTest/pch.h | 8 + .../KeyboardManagerEngineTest/resource.h | 13 + .../keyboardmanager/common/ErrorTypes.h | 27 + .../keyboardmanager/common/Helpers.cpp | 86 +- src/modules/keyboardmanager/common/Helpers.h | 48 - src/modules/keyboardmanager/common/Input.h | 30 + .../keyboardmanager/common/InputInterface.h | 23 +- .../common/KeyboardEventHandlers.cpp | 24 + .../common/KeyboardEventHandlers.h | 12 + .../common/KeyboardManagerCommon.vcxproj | 13 +- .../KeyboardManagerCommon.vcxproj.filters | 27 +- .../common/KeyboardManagerConstants.h | 16 +- .../common/KeyboardManagerState.cpp | 62 +- .../common/KeyboardManagerState.h | 7 - .../keyboardmanager/common/RemapShortcut.cpp | 2 - .../keyboardmanager/common/SettingsHelper.cpp | 194 ++++ .../keyboardmanager/common/SettingsHelper.h | 9 + .../keyboardmanager/common/Shortcut.cpp | 5 +- src/modules/keyboardmanager/common/Shortcut.h | 10 +- src/modules/keyboardmanager/dll/Input.cpp | 21 - src/modules/keyboardmanager/dll/Input.h | 17 - .../dll/KeyboardEventHandlers.h | 38 - .../dll/KeyboardManager.base.rc | 2 - .../dll/KeyboardManager.vcxproj | 12 +- .../dll/KeyboardManager.vcxproj.filters | 19 +- .../keyboardmanager/dll/Resources.resx | 178 ---- src/modules/keyboardmanager/dll/dllmain.cpp | 395 +------- src/modules/keyboardmanager/dll/trace.cpp | 30 + src/modules/keyboardmanager/dll/trace.h | 11 + src/modules/keyboardmanager/ui/UIHelpers.cpp | 38 - src/modules/keyboardmanager/ui/UIHelpers.h | 10 - .../ViewModels/KeyboardManagerViewModel.cs | 139 ++- 141 files changed, 5124 insertions(+), 2374 deletions(-) create mode 100644 src/common/utils/EventLocker.h create mode 100644 src/common/utils/EventWaiter.h create mode 100644 src/common/utils/ProcessWaiter.h create mode 100644 src/common/utils/UnhandledExceptionHandler_x64.h rename src/modules/keyboardmanager/{dll => KeyboardManagerEditor}/Keyboard.ico (100%) create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.base.rc create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.exe.manifest create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.h create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj.filters create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditor/LocProject.json create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditor/Resource.h create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx rename src/modules/keyboardmanager/{test => KeyboardManagerEditor}/packages.config (100%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditor}/pch.cpp (100%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditor}/pch.h (53%) create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditor/resource.base.h create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditor/targetver.h rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/BufferValidationHelpers.cpp (92%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/BufferValidationHelpers.h (86%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/Dialog.cpp (88%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/Dialog.h (100%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/EditKeyboardWindow.cpp (88%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/EditKeyboardWindow.h (81%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/EditShortcutsWindow.cpp (88%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/EditShortcutsWindow.h (81%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/KeyDropDownControl.cpp (96%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/KeyDropDownControl.h (98%) rename src/modules/keyboardmanager/{ui/KeyboardManagerUI.vcxproj => KeyboardManagerEditorLibrary/KeyboardManagerEditorLibrary.vcxproj} (68%) rename src/modules/keyboardmanager/{ui/KeyboardManagerUI.vcxproj.filters => KeyboardManagerEditorLibrary/KeyboardManagerEditorLibrary.vcxproj.filters} (79%) create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorStrings.cpp create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorStrings.h rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/LoadingAndSavingRemappingHelper.cpp (98%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/LoadingAndSavingRemappingHelper.h (97%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/ShortcutControl.cpp (92%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/ShortcutControl.h (90%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/SingleKeyRemapControl.cpp (94%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/SingleKeyRemapControl.h (94%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/Styles.cpp (100%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/Styles.h (100%) create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditorLibrary/UIHelpers.cpp create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditorLibrary/UIHelpers.h rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/XamlBridge.cpp (91%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorLibrary}/XamlBridge.h (95%) create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditorLibrary/pch.cpp create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditorLibrary/pch.h create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditorLibrary/targetver.h create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditorLibrary/trace.cpp rename src/modules/keyboardmanager/{common => KeyboardManagerEditorLibrary}/trace.h (67%) rename src/modules/keyboardmanager/{test => KeyboardManagerEditorTest}/BufferValidationTests.cpp (99%) rename src/modules/keyboardmanager/{test/KeyboardManagerTest.rc => KeyboardManagerEditorTest/KeyboardManagerEditorTest.rc} (100%) create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditorTest/KeyboardManagerEditorTest.vcxproj create mode 100644 src/modules/keyboardmanager/KeyboardManagerEditorTest/KeyboardManagerEditorTest.vcxproj.filters rename src/modules/keyboardmanager/{test => KeyboardManagerEditorTest}/LoadingAndSavingRemappingTests.cpp (99%) rename src/modules/keyboardmanager/{test => KeyboardManagerEditorTest}/MockedInput.cpp (100%) rename src/modules/keyboardmanager/{test => KeyboardManagerEditorTest}/MockedInput.h (97%) rename src/modules/keyboardmanager/{test => KeyboardManagerEditorTest}/TestHelpers.cpp (100%) rename src/modules/keyboardmanager/{test => KeyboardManagerEditorTest}/TestHelpers.h (100%) rename src/modules/keyboardmanager/{ui => KeyboardManagerEditorTest}/packages.config (98%) rename src/modules/keyboardmanager/{test => KeyboardManagerEditorTest}/pch.cpp (100%) rename src/modules/keyboardmanager/{test => KeyboardManagerEditorTest}/pch.h (100%) rename src/modules/keyboardmanager/{test => KeyboardManagerEditorTest}/resource.h (100%) create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngine/Keyboard.ico create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngine/KeyboardManagerEngine.rc create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngine/KeyboardManagerEngine.vcxproj create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngine/KeyboardManagerEngine.vcxproj.filters create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngine/PropertySheet.props create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngine/packages.config create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngine/pch.cpp create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngine/pch.h create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngine/resource.h rename src/modules/keyboardmanager/{dll => KeyboardManagerEngineLibrary}/KeyboardEventHandlers.cpp (93%) create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.cpp create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.h create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManagerEngineLibrary.vcxproj create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManagerEngineLibrary.vcxproj.filters create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineLibrary/packages.config create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineLibrary/pch.cpp create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineLibrary/pch.h rename src/modules/keyboardmanager/{common => KeyboardManagerEngineLibrary}/trace.cpp (55%) create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineLibrary/trace.h rename src/modules/keyboardmanager/{test => KeyboardManagerEngineTest}/AppSpecificShortcutRemappingTests.cpp (99%) rename src/modules/keyboardmanager/{test/KeyboardManagerHelperTests.cpp => KeyboardManagerEngineTest/HelperTests.cpp} (99%) rename src/modules/keyboardmanager/{test/KeyboardManagerTest.vcxproj => KeyboardManagerEngineTest/KeyboardManagerEngineTest.vcxproj} (81%) rename src/modules/keyboardmanager/{test/KeyboardManagerTest.vcxproj.filters => KeyboardManagerEngineTest/KeyboardManagerEngineTest.vcxproj.filters} (84%) create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineTest/MockedInput.cpp create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineTest/MockedInput.h rename src/modules/keyboardmanager/{test => KeyboardManagerEngineTest}/MockedInputSanityTests.cpp (92%) rename src/modules/keyboardmanager/{test => KeyboardManagerEngineTest}/OSLevelShortcutRemappingTests.cpp (99%) rename src/modules/keyboardmanager/{test => KeyboardManagerEngineTest}/SetKeyEventTests.cpp (94%) rename src/modules/keyboardmanager/{test => KeyboardManagerEngineTest}/ShortcutTests.cpp (99%) rename src/modules/keyboardmanager/{test => KeyboardManagerEngineTest}/SingleKeyRemappingTests.cpp (99%) create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineTest/TestHelpers.cpp create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineTest/TestHelpers.h create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineTest/packages.config create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineTest/pch.cpp create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineTest/pch.h create mode 100644 src/modules/keyboardmanager/KeyboardManagerEngineTest/resource.h create mode 100644 src/modules/keyboardmanager/common/ErrorTypes.h create mode 100644 src/modules/keyboardmanager/common/Input.h create mode 100644 src/modules/keyboardmanager/common/KeyboardEventHandlers.cpp create mode 100644 src/modules/keyboardmanager/common/KeyboardEventHandlers.h delete mode 100644 src/modules/keyboardmanager/common/RemapShortcut.cpp create mode 100644 src/modules/keyboardmanager/common/SettingsHelper.cpp create mode 100644 src/modules/keyboardmanager/common/SettingsHelper.h delete mode 100644 src/modules/keyboardmanager/dll/Input.cpp delete mode 100644 src/modules/keyboardmanager/dll/Input.h delete mode 100644 src/modules/keyboardmanager/dll/KeyboardEventHandlers.h create mode 100644 src/modules/keyboardmanager/dll/trace.cpp create mode 100644 src/modules/keyboardmanager/dll/trace.h delete mode 100644 src/modules/keyboardmanager/ui/UIHelpers.cpp delete mode 100644 src/modules/keyboardmanager/ui/UIHelpers.h diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 69f2fdc975..74c1f95a5b 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -5,6 +5,7 @@ abcdef abcdefgh abgr ABlocked +ABOUTBOX Abug accctrl Acceleratorkeys @@ -118,6 +119,7 @@ atlstr attr Attribs aumid +Aut AUTHN AUTOAPPEND autocomplete @@ -396,6 +398,7 @@ davidegiacometti Dayof dbdfc Dbg +Dbghelp DBLCLKS DBLEPSILON DCOM @@ -1117,6 +1120,7 @@ KEYBDINPUT keyboardeventhandlers keyboardmanager keyboardmanagercommon +KEYBOARDMANAGEREDITOR keyboardmanagerstate keyboardmanagerui keycode @@ -1510,6 +1514,7 @@ oldnewthing oldpath oldtheme oleaut +OleAut OLECHAR OLEDB OLIVEGREEN @@ -1737,6 +1742,7 @@ readme READMODE readonly READWRITE +REALTIME RECTDESTINATION RECTL rectp @@ -2008,6 +2014,7 @@ stdcall stdcpp stdcpplatest stdexcept +stdio stdin stdlib STDMETHODCALLTYPE @@ -2027,6 +2034,7 @@ Strikethrough Stringified stringify STRINGIZE +stringstream stringtable stringval Strmiids @@ -2179,6 +2187,7 @@ Tz UAC UAL uap +UCHAR udit UIA Uid @@ -2423,6 +2432,7 @@ wstr wstring wstringstream wsz +wtoi WTS WTSAT wu diff --git a/.pipelines/ci/templates/build-powertoys-steps.yml b/.pipelines/ci/templates/build-powertoys-steps.yml index a053bf883d..84847b5923 100644 --- a/.pipelines/ci/templates/build-powertoys-steps.yml +++ b/.pipelines/ci/templates/build-powertoys-steps.yml @@ -163,7 +163,8 @@ steps: configuration: '$(BuildConfiguration)' testSelector: 'testAssemblies' testAssemblyVer2: | - **\KeyboardManagerTest.dll + **\KeyboardManagerEngineTest.dll + **\KeyboardManagerEditorTest.dll **\UnitTests-CommonLib.dll **\PowerRenameUnitTests.dll **\powerpreviewTest.dll diff --git a/.pipelines/pipeline.user.windows.yml b/.pipelines/pipeline.user.windows.yml index e1ec657b3b..6d68405586 100644 --- a/.pipelines/pipeline.user.windows.yml +++ b/.pipelines/pipeline.user.windows.yml @@ -98,6 +98,8 @@ build: - 'modules\ImageResizer\ManagedTelemetry.dll' - 'modules\ImageResizer\Microsoft.PowerToys.Common.UI.dll' - 'modules\KeyboardManager\KeyboardManager.dll' + - 'modules\KeyboardManager\KeyboardManagerEditor\PowerToys.KeyboardManagerEditor.exe' + - 'modules\KeyboardManager\KeyboardManagerEngine\PowerToys.KeyboardManagerEngine.exe' - 'modules\launcher\Microsoft.PowerToys.Settings.UI.Lib.dll' - 'modules\launcher\ManagedCommon.dll' - 'modules\launcher\Microsoft.PowerToys.Common.UI.dll' diff --git a/PowerToys.sln b/PowerToys.sln index 21179a3f5d..79abcae51e 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -103,8 +103,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImageResizerExt", "src\modu EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageResizerUITest", "src\modules\imageresizer\tests\ImageResizerUITest.csproj", "{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerUI", "src\modules\keyboardmanager\ui\KeyboardManagerUI.vcxproj", "{EAF23649-EF6E-478B-980E-81FAD96CCA2A}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "action_runner", "src\action_runner\action_runner.vcxproj", "{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}" ProjectSection(ProjectDependencies) = postProject {17DA04DF-E393-4397-9CF0-84DABE11032E} = {17DA04DF-E393-4397-9CF0-84DABE11032E} @@ -210,8 +208,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerLauncher.Telemetry", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedTelemetry", "src\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj", "{5D00D290-4016-4CFE-9E41-1E7C724509BA}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerTest", "src\modules\keyboardmanager\test\KeyboardManagerTest.vcxproj", "{62173D9A-6724-4C00-A1C8-FB646480A9EC}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCommon", "src\common\ManagedCommon\ManagedCommon.csproj", "{4AED67B6-55FD-486F-B917-E543DEE2CB3C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Plugin.Program.UnitTests", "src\modules\launcher\Plugins\Microsoft.Plugin.Program.UnitTests\Microsoft.Plugin.Program.UnitTests.csproj", "{42851751-CBC8-45A6-97F5-7A0753F7B4D1}" @@ -285,14 +281,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "utils", "utils", "{B39DC643 src\common\utils\appMutex.h = src\common\utils\appMutex.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 + src\common\utils\EventWaiter.h = src\common\utils\EventWaiter.h src\common\utils\exec.h = src\common\utils\exec.h src\common\utils\json.h = src\common\utils\json.h src\common\utils\logger_helper.h = src\common\utils\logger_helper.h src\common\utils\os-detect.h = src\common\utils\os-detect.h src\common\utils\process_path.h = src\common\utils\process_path.h + src\common\utils\ProcessWaiter.h = src\common\utils\ProcessWaiter.h src\common\utils\resources.h = src\common\utils\resources.h src\common\utils\string_utils.h = src\common\utils\string_utils.h src\common\utils\timeutil.h = src\common\utils\timeutil.h + src\common\utils\UnhandledExceptionHandler_x64.h = src\common\utils\UnhandledExceptionHandler_x64.h src\common\utils\winapi_error.h = src\common\utils\winapi_error.h src\common\utils\window.h = src\common\utils\window.h EndProjectSection @@ -312,6 +312,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plu EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerToys.Settings", "src\settings-ui\PowerToys.Settings\PowerToys.Settings.csproj", "{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEngine", "src\modules\keyboardmanager\KeyboardManagerEngine\KeyboardManagerEngine.vcxproj", "{BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEngineLibrary", "src\modules\keyboardmanager\KeyboardManagerEngineLibrary\KeyboardManagerEngineLibrary.vcxproj", "{E496B7FC-1E99-4BAB-849B-0E8367040B02}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEngineTest", "src\modules\keyboardmanager\KeyboardManagerEngineTest\KeyboardManagerEngineTest.vcxproj", "{7F4B3A60-BC27-45A7-8000-68B0B6EA7466}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEditor", "src\modules\keyboardmanager\KeyboardManagerEditor\KeyboardManagerEditor.vcxproj", "{8DF78B53-200E-451F-9328-01EB907193AE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEditorLibrary", "src\modules\keyboardmanager\KeyboardManagerEditorLibrary\KeyboardManagerEditorLibrary.vcxproj", "{23D2070D-E4AD-4ADD-85A7-083D9C76AD49}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEditorTest", "src\modules\keyboardmanager\KeyboardManagerEditorTest\KeyboardManagerEditorTest.vcxproj", "{62173D9A-6724-4C00-A1C8-FB646480A9EC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -390,10 +402,6 @@ Global {E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Debug|x64.Build.0 = Debug|x64 {E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x64.ActiveCfg = Release|x64 {E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x64.Build.0 = Release|x64 - {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Debug|x64.ActiveCfg = Debug|x64 - {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Debug|x64.Build.0 = Debug|x64 - {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Release|x64.ActiveCfg = Release|x64 - {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Release|x64.Build.0 = Release|x64 {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x64.ActiveCfg = Debug|x64 {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x64.Build.0 = Debug|x64 {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x64.ActiveCfg = Release|x64 @@ -506,10 +514,6 @@ Global {5D00D290-4016-4CFE-9E41-1E7C724509BA}.Debug|x64.Build.0 = Debug|x64 {5D00D290-4016-4CFE-9E41-1E7C724509BA}.Release|x64.ActiveCfg = Release|x64 {5D00D290-4016-4CFE-9E41-1E7C724509BA}.Release|x64.Build.0 = Release|x64 - {62173D9A-6724-4C00-A1C8-FB646480A9EC}.Debug|x64.ActiveCfg = Debug|x64 - {62173D9A-6724-4C00-A1C8-FB646480A9EC}.Debug|x64.Build.0 = Debug|x64 - {62173D9A-6724-4C00-A1C8-FB646480A9EC}.Release|x64.ActiveCfg = Release|x64 - {62173D9A-6724-4C00-A1C8-FB646480A9EC}.Release|x64.Build.0 = Release|x64 {4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Debug|x64.ActiveCfg = Debug|x64 {4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Debug|x64.Build.0 = Debug|x64 {4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Release|x64.ActiveCfg = Release|x64 @@ -634,6 +638,30 @@ Global {6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Debug|x64.Build.0 = Debug|x64 {6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Release|x64.ActiveCfg = Release|x64 {6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Release|x64.Build.0 = Release|x64 + {BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}.Debug|x64.ActiveCfg = Debug|x64 + {BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}.Debug|x64.Build.0 = Debug|x64 + {BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}.Release|x64.ActiveCfg = Release|x64 + {BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}.Release|x64.Build.0 = Release|x64 + {E496B7FC-1E99-4BAB-849B-0E8367040B02}.Debug|x64.ActiveCfg = Debug|x64 + {E496B7FC-1E99-4BAB-849B-0E8367040B02}.Debug|x64.Build.0 = Debug|x64 + {E496B7FC-1E99-4BAB-849B-0E8367040B02}.Release|x64.ActiveCfg = Release|x64 + {E496B7FC-1E99-4BAB-849B-0E8367040B02}.Release|x64.Build.0 = Release|x64 + {7F4B3A60-BC27-45A7-8000-68B0B6EA7466}.Debug|x64.ActiveCfg = Debug|x64 + {7F4B3A60-BC27-45A7-8000-68B0B6EA7466}.Debug|x64.Build.0 = Debug|x64 + {7F4B3A60-BC27-45A7-8000-68B0B6EA7466}.Release|x64.ActiveCfg = Release|x64 + {7F4B3A60-BC27-45A7-8000-68B0B6EA7466}.Release|x64.Build.0 = Release|x64 + {8DF78B53-200E-451F-9328-01EB907193AE}.Debug|x64.ActiveCfg = Debug|x64 + {8DF78B53-200E-451F-9328-01EB907193AE}.Debug|x64.Build.0 = Debug|x64 + {8DF78B53-200E-451F-9328-01EB907193AE}.Release|x64.ActiveCfg = Release|x64 + {8DF78B53-200E-451F-9328-01EB907193AE}.Release|x64.Build.0 = Release|x64 + {23D2070D-E4AD-4ADD-85A7-083D9C76AD49}.Debug|x64.ActiveCfg = Debug|x64 + {23D2070D-E4AD-4ADD-85A7-083D9C76AD49}.Debug|x64.Build.0 = Debug|x64 + {23D2070D-E4AD-4ADD-85A7-083D9C76AD49}.Release|x64.ActiveCfg = Release|x64 + {23D2070D-E4AD-4ADD-85A7-083D9C76AD49}.Release|x64.Build.0 = Release|x64 + {62173D9A-6724-4C00-A1C8-FB646480A9EC}.Debug|x64.ActiveCfg = Debug|x64 + {62173D9A-6724-4C00-A1C8-FB646480A9EC}.Debug|x64.Build.0 = Debug|x64 + {62173D9A-6724-4C00-A1C8-FB646480A9EC}.Release|x64.ActiveCfg = Release|x64 + {62173D9A-6724-4C00-A1C8-FB646480A9EC}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -659,7 +687,6 @@ Global {2BE46397-4DFA-414C-9BD4-41E4BBF8CB34} = {6C7F47CC-2151-44A3-A546-41C70025132C} {0B43679E-EDFA-4DA0-AD30-F4628B308B1B} = {6C7F47CC-2151-44A3-A546-41C70025132C} {E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8} = {6C7F47CC-2151-44A3-A546-41C70025132C} - {EAF23649-EF6E-478B-980E-81FAD96CCA2A} = {38BDB927-829B-4C65-9CD9-93FB05D66D65} {17DA04DF-E393-4397-9CF0-84DABE11032E} = {1AFB6476-670D-4E80-A464-657E01DFF482} {38BDB927-829B-4C65-9CD9-93FB05D66D65} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {8AFFA899-0B73-49EC-8C50-0FADDA57B2FC} = {38BDB927-829B-4C65-9CD9-93FB05D66D65} @@ -691,7 +718,6 @@ Global {787B8AA6-CA93-4C84-96FE-DF31110AD1C4} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {08C8C05F-0362-41BC-818C-724572DF8B06} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68} {5D00D290-4016-4CFE-9E41-1E7C724509BA} = {1AFB6476-670D-4E80-A464-657E01DFF482} - {62173D9A-6724-4C00-A1C8-FB646480A9EC} = {38BDB927-829B-4C65-9CD9-93FB05D66D65} {4AED67B6-55FD-486F-B917-E543DEE2CB3C} = {1AFB6476-670D-4E80-A464-657E01DFF482} {42851751-CBC8-45A6-97F5-7A0753F7B4D1} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {1EF1EEF0-10F0-4F2E-8550-39B6D8044D3E} = {2F305555-C296-497E-AC20-5FA1B237996A} @@ -729,6 +755,12 @@ Global {4BABF3FE-3451-42FD-873F-3C332E18DCEF} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {0648DF05-5DDA-4BE1-B5F2-584926EBDB65} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A} = {C3081D9A-1586-441A-B5F4-ED815B3719C1} + {BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD} = {38BDB927-829B-4C65-9CD9-93FB05D66D65} + {E496B7FC-1E99-4BAB-849B-0E8367040B02} = {38BDB927-829B-4C65-9CD9-93FB05D66D65} + {7F4B3A60-BC27-45A7-8000-68B0B6EA7466} = {38BDB927-829B-4C65-9CD9-93FB05D66D65} + {8DF78B53-200E-451F-9328-01EB907193AE} = {38BDB927-829B-4C65-9CD9-93FB05D66D65} + {23D2070D-E4AD-4ADD-85A7-083D9C76AD49} = {38BDB927-829B-4C65-9CD9-93FB05D66D65} + {62173D9A-6724-4C00-A1C8-FB646480A9EC} = {38BDB927-829B-4C65-9CD9-93FB05D66D65} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 84690b52c7..c6996fbb90 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -212,7 +212,10 @@ - + + + + @@ -589,6 +592,18 @@ + + + + + + + + + + + + @@ -779,6 +794,8 @@ + + diff --git a/src/common/interop/interop.cpp b/src/common/interop/interop.cpp index bcf191401d..65be213234 100644 --- a/src/common/interop/interop.cpp +++ b/src/common/interop/interop.cpp @@ -155,9 +155,5 @@ public static String ^ ShowShortcutGuideSharedEvent() { return gcnew String(CommonSharedConstants::SHOW_SHORTCUT_GUIDE_SHARED_EVENT); } - - static String ^ KeyboardManagerConfigFileMutexName() { - return gcnew String(CommonSharedConstants::KEYBOARD_MANAGER_CONFIG_FILE_MUTEX_NAME); - } }; } diff --git a/src/common/interop/shared_constants.h b/src/common/interop/shared_constants.h index 3895d2ebd3..34476563b4 100644 --- a/src/common/interop/shared_constants.h +++ b/src/common/interop/shared_constants.h @@ -27,7 +27,4 @@ namespace CommonSharedConstants // Max DWORD for key code to disable keys. const int VK_DISABLED = 0x100; - - // Name of the mutex which controls access to the configuration file for Keyboard Manager - const wchar_t KEYBOARD_MANAGER_CONFIG_FILE_MUTEX_NAME[] = L"Local\\PowerToys_KeyboardManager_ConfigFileMutex"; } diff --git a/src/common/logger/logger.h b/src/common/logger/logger.h index c015e66d4e..04f71043ca 100644 --- a/src/common/logger/logger.h +++ b/src/common/logger/logger.h @@ -55,4 +55,9 @@ public: { logger->critical(fmt, args...); } + + static void flush() + { + logger->flush(); + } }; diff --git a/src/common/utils/EventLocker.h b/src/common/utils/EventLocker.h new file mode 100644 index 0000000000..01bd7b79c9 --- /dev/null +++ b/src/common/utils/EventLocker.h @@ -0,0 +1,61 @@ +#include +#include + +class EventLocker +{ +public: + EventLocker(HANDLE h) + { + eventHandle = h; + SetEvent(eventHandle); + } + + static std::optional Get(std::wstring eventName) + { + EventLocker locker(eventName); + if (!locker.eventHandle) + { + return {}; + } + + return locker; + } + + EventLocker(EventLocker& e) = delete; + EventLocker& operator=(EventLocker& e) = delete; + + EventLocker(EventLocker&& e) noexcept + { + this->eventHandle = e.eventHandle; + e.eventHandle = nullptr; + } + + EventLocker& operator=(EventLocker&& e) noexcept + { + this->eventHandle = e.eventHandle; + e.eventHandle = nullptr; + } + + ~EventLocker() + { + if (eventHandle) + { + ResetEvent(eventHandle); + CloseHandle(eventHandle); + eventHandle = nullptr; + } + } +private: + EventLocker(std::wstring eventName) + { + eventHandle = CreateEvent(nullptr, true, false, eventName.c_str()); + if (!eventHandle) + { + return; + } + + SetEvent(eventHandle); + } + + HANDLE eventHandle; +}; diff --git a/src/common/utils/EventWaiter.h b/src/common/utils/EventWaiter.h new file mode 100644 index 0000000000..304f475ade --- /dev/null +++ b/src/common/utils/EventWaiter.h @@ -0,0 +1,78 @@ +#include +#include +#include +#include + +class EventWaiter +{ +public: + EventWaiter() {} + EventWaiter(const std::wstring& name, std::function callback) + { + // Create localExitThreadEvent and localWaitingEvent for capturing. We can not capture 'this' as we implement move constructor. + auto localExitThreadEvent = exitThreadEvent = CreateEvent(nullptr, false, false, nullptr); + HANDLE localWaitingEvent = waitingEvent = CreateEvent(nullptr, false, false, name.c_str()); + std::thread([=]() { + HANDLE events[2] = { localWaitingEvent, localExitThreadEvent }; + while (true) + { + auto waitResult = WaitForMultipleObjects(2, events, false, INFINITE); + if (waitResult == WAIT_OBJECT_0 + 1) + { + break; + } + + if (waitResult == WAIT_FAILED) + { + callback(GetLastError()); + continue; + } + + if (waitResult == WAIT_OBJECT_0) + { + callback(ERROR_SUCCESS); + } + } + }).detach(); + } + + EventWaiter(EventWaiter&) = delete; + EventWaiter& operator=(EventWaiter&) = delete; + + EventWaiter(EventWaiter&& a) noexcept + { + this->exitThreadEvent = a.exitThreadEvent; + this->waitingEvent = a.waitingEvent; + + a.exitThreadEvent = nullptr; + a.waitingEvent = nullptr; + } + + EventWaiter& operator=(EventWaiter&& a) noexcept + { + this->exitThreadEvent = a.exitThreadEvent; + this->waitingEvent = a.waitingEvent; + + a.exitThreadEvent = nullptr; + a.waitingEvent = nullptr; + return *this; + } + + ~EventWaiter() + { + if (exitThreadEvent) + { + SetEvent(exitThreadEvent); + CloseHandle(exitThreadEvent); + } + + if (waitingEvent) + { + CloseHandle(waitingEvent); + } + } + +private: + HANDLE exitThreadEvent = nullptr; + HANDLE waitingEvent = nullptr; +}; \ No newline at end of file diff --git a/src/common/utils/ProcessWaiter.h b/src/common/utils/ProcessWaiter.h new file mode 100644 index 0000000000..badef9ffce --- /dev/null +++ b/src/common/utils/ProcessWaiter.h @@ -0,0 +1,32 @@ +#include +#include +#include +#include + +namespace ProcessWaiter +{ + void OnProcessTerminate(std::wstring parent_pid, std::function callback) + { + DWORD pid = std::stol(parent_pid); + std::thread([=]() { + HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid); + if (process != nullptr) + { + if (WaitForSingleObject(process, INFINITE) == WAIT_OBJECT_0) + { + CloseHandle(process); + callback(ERROR_SUCCESS); + } + else + { + CloseHandle(process); + callback(GetLastError()); + } + } + else + { + callback(GetLastError()); + } + }).detach(); + } +} diff --git a/src/common/utils/UnhandledExceptionHandler_x64.h b/src/common/utils/UnhandledExceptionHandler_x64.h new file mode 100644 index 0000000000..7aed1917c6 --- /dev/null +++ b/src/common/utils/UnhandledExceptionHandler_x64.h @@ -0,0 +1,238 @@ +#include +#include +#include +#include +#include "../logger/logger.h" + +static IMAGEHLP_SYMBOL64* pSymbol = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH * sizeof(TCHAR)); +static IMAGEHLP_LINE64 line; +static BOOLEAN processingException = FALSE; +static CHAR modulePath[MAX_PATH]; + +static inline const char* exceptionDescription(const DWORD& code) +{ + switch (code) + { + case EXCEPTION_ACCESS_VIOLATION: + return "EXCEPTION_ACCESS_VIOLATION"; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; + case EXCEPTION_BREAKPOINT: + return "EXCEPTION_BREAKPOINT"; + case EXCEPTION_DATATYPE_MISALIGNMENT: + return "EXCEPTION_DATATYPE_MISALIGNMENT"; + case EXCEPTION_FLT_DENORMAL_OPERAND: + return "EXCEPTION_FLT_DENORMAL_OPERAND"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; + case EXCEPTION_FLT_INEXACT_RESULT: + return "EXCEPTION_FLT_INEXACT_RESULT"; + case EXCEPTION_FLT_INVALID_OPERATION: + return "EXCEPTION_FLT_INVALID_OPERATION"; + case EXCEPTION_FLT_OVERFLOW: + return "EXCEPTION_FLT_OVERFLOW"; + case EXCEPTION_FLT_STACK_CHECK: + return "EXCEPTION_FLT_STACK_CHECK"; + case EXCEPTION_FLT_UNDERFLOW: + return "EXCEPTION_FLT_UNDERFLOW"; + case EXCEPTION_ILLEGAL_INSTRUCTION: + return "EXCEPTION_ILLEGAL_INSTRUCTION"; + case EXCEPTION_IN_PAGE_ERROR: + return "EXCEPTION_IN_PAGE_ERROR"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + return "EXCEPTION_INT_DIVIDE_BY_ZERO"; + case EXCEPTION_INT_OVERFLOW: + return "EXCEPTION_INT_OVERFLOW"; + case EXCEPTION_INVALID_DISPOSITION: + return "EXCEPTION_INVALID_DISPOSITION"; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + return "EXCEPTION_NONCONTINUABLE_EXCEPTION"; + case EXCEPTION_PRIV_INSTRUCTION: + return "EXCEPTION_PRIV_INSTRUCTION"; + case EXCEPTION_SINGLE_STEP: + return "EXCEPTION_SINGLE_STEP"; + case EXCEPTION_STACK_OVERFLOW: + return "EXCEPTION_STACK_OVERFLOW"; + default: + return "UNKNOWN EXCEPTION"; + } +} + +/* Returns the index of the last backslash in the file path */ +inline int GetFilenameStart(CHAR* path) +{ + int pos = 0; + int found = 0; + if (path != NULL) + { + while (path[pos] != '\0' && pos < MAX_PATH) + { + if (path[pos] == '\\') + { + found = pos + 1; + } + ++pos; + } + } + + return found; +} + +inline void LogStackTrace() +{ + BOOL result; + HANDLE thread; + HANDLE process; + CONTEXT context; + STACKFRAME64 stack; + ULONG frame; + DWORD64 dw64Displacement; + DWORD dwDisplacement; + + memset(&stack, 0, sizeof(STACKFRAME64)); + memset(pSymbol, '\0', sizeof(*pSymbol) + MAX_PATH); + memset(&modulePath[0], '\0', sizeof(modulePath)); + line.LineNumber = 0; + + try + { + RtlCaptureContext(&context); + } + catch (...) + { + Logger::error(L"Failed to capture context. {}", get_last_error_or_default(GetLastError())); + return; + } + + process = GetCurrentProcess(); + thread = GetCurrentThread(); + dw64Displacement = 0; + stack.AddrPC.Offset = context.Rip; + stack.AddrPC.Mode = AddrModeFlat; + stack.AddrStack.Offset = context.Rsp; + stack.AddrStack.Mode = AddrModeFlat; + stack.AddrFrame.Offset = context.Rbp; + stack.AddrFrame.Mode = AddrModeFlat; + + std::stringstream ss; + for (frame = 0;; frame++) + { + result = StackWalk64( + IMAGE_FILE_MACHINE_AMD64, + process, + thread, + &stack, + &context, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL); + + if (!result) + { + break; + } + + pSymbol->MaxNameLength = MAX_PATH; + pSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); + + if (!SymGetSymFromAddr64(process, stack.AddrPC.Offset, &dw64Displacement, pSymbol)) + { + Logger::error(L"Failed to get a symbol. {}", get_last_error_or_default(GetLastError())); + } + + line.LineNumber = 0; + SymGetLineFromAddr64(process, stack.AddrPC.Offset, &dwDisplacement, &line); + + DWORD64 moduleBase = SymGetModuleBase64(process, stack.AddrPC.Offset); + if (moduleBase) + { + if (!GetModuleFileNameA((HINSTANCE)moduleBase, modulePath, MAX_PATH)) + { + Logger::error(L"Failed to get a module path. {}", get_last_error_or_default(GetLastError())); + } + } + else + { + Logger::error(L"Failed to get a module. {}", get_last_error_or_default(GetLastError())); + } + + ss << std::string(modulePath).substr(GetFilenameStart(modulePath)) << "!" << pSymbol->Name << "(" << line.FileName << ":" << line.LineNumber << std::endl; + } + + Logger::error("STACK TRACE\r\n{}", ss.str()); + Logger::flush(); +} + +inline LONG WINAPI UnhandledExceptionHandler(PEXCEPTION_POINTERS info) +{ + if (!processingException) + { + bool headerLogged = false; + try + { + const char* exDescription = "Exception code not available"; + processingException = true; + if (info != NULL && info->ExceptionRecord != NULL && info->ExceptionRecord->ExceptionCode != NULL) + { + exDescription = exceptionDescription(info->ExceptionRecord->ExceptionCode); + } + + headerLogged = true; + Logger::error(exDescription); + LogStackTrace(); + } + catch (...) + { + Logger::error("Failed to log stack trace"); + Logger::flush(); + } + + processingException = false; + } + + return EXCEPTION_CONTINUE_SEARCH; +} + +/* Handler to trap abort() calls */ +inline void AbortHandler(int signal_number) +{ + Logger::error("--- ABORT"); + try + { + LogStackTrace(); + } + catch(...) + { + Logger::error("Failed to log stack trace on abort"); + Logger::flush(); + } +} + +inline void InitSymbols() +{ + // Preload symbols so they will be available in case of out-of-memory exception + SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + HANDLE process = GetCurrentProcess(); + if (!SymInitialize(process, NULL, TRUE)) + { + Logger::error(L"Failed to initialize symbol handler. {}", get_last_error_or_default(GetLastError())); + } +} + +inline void InitUnhandledExceptionHandler_x64(void) +{ + try + { + InitSymbols(); + // Global handler for unhandled exceptions + SetUnhandledExceptionFilter(UnhandledExceptionHandler); + // Handler for abort() + signal(SIGABRT, &AbortHandler); + } + catch(...) + { + Logger::error("Failed to init global unhandled exception handler"); + } +} diff --git a/src/common/utils/logger_helper.h b/src/common/utils/logger_helper.h index 4d9699224a..c2207525e3 100644 --- a/src/common/utils/logger_helper.h +++ b/src/common/utils/logger_helper.h @@ -2,6 +2,7 @@ #include #include +#include namespace LoggerHelpers { @@ -79,4 +80,20 @@ namespace LoggerHelpers return result; } + + inline void init_logger(std::wstring moduleName, std::wstring internalPath, std::string loggerName) + { + std::filesystem::path rootFolder(PTSettingsHelper::get_module_save_folder_location(moduleName)); + rootFolder.append(internalPath); + + auto currentFolder = rootFolder; + currentFolder.append(LogSettings::logPath); + currentFolder.append(get_product_version()); + + auto logsPath = currentFolder; + logsPath.append(L"log.txt"); + Logger::init(loggerName, logsPath.wstring(), PTSettingsHelper::get_log_settings_file_location()); + + delete_other_versions_log_folders(rootFolder.wstring(), currentFolder); + } } diff --git a/src/common/utils/winapi_error.h b/src/common/utils/winapi_error.h index 5a9dc4fc9c..b97d5c6450 100644 --- a/src/common/utils/winapi_error.h +++ b/src/common/utils/winapi_error.h @@ -22,6 +22,12 @@ inline std::optional get_last_error_message(const DWORD dw) return message; } +inline std::wstring get_last_error_or_default(const DWORD dw) +{ + auto message = get_last_error_message(dw); + return message.has_value() ? message.value() : L""; +} + inline void show_last_error_message(const wchar_t* functionName, DWORD dw, const wchar_t* errorTitle) { const auto system_message = get_last_error_message(dw); diff --git a/src/modules/keyboardmanager/dll/Keyboard.ico b/src/modules/keyboardmanager/KeyboardManagerEditor/Keyboard.ico similarity index 100% rename from src/modules/keyboardmanager/dll/Keyboard.ico rename to src/modules/keyboardmanager/KeyboardManagerEditor/Keyboard.ico diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.base.rc b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.base.rc new file mode 100644 index 0000000000..04a92d51f7 --- /dev/null +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.base.rc @@ -0,0 +1,110 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" +#include "../../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#ifndef APSTUDIO_INVOKED +#include "targetver.h" +#endif +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO 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" + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", "PowerToys Keyboard Manager Editor" + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", "PowerToys.KeyboardManagerEditor.exe" + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", "PowerToys.KeyboardManagerEditor.exe" + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#ifndef APSTUDIO_INVOKED\r\n" + "#include ""targetver.h""\r\n" + "#endif\r\n" + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + +IDS_KEYBOARDMANAGER_ICON ICON L"../Keyboard.ico" \ No newline at end of file diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp new file mode 100644 index 0000000000..51821f9cfe --- /dev/null +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp @@ -0,0 +1,217 @@ +// KeyboardManagerEditor.cpp : Defines the entry point for the application. +// + +#include "pch.h" +#include "KeyboardManagerEditor.h" + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +std::unique_ptr editor = nullptr; +const std::wstring instanceMutexName = L"Local\\PowerToys_KBMEditor_InstanceMutex"; + +int APIENTRY wWinMain(_In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPWSTR lpCmdLine, + _In_ int nCmdShow) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + + LoggerHelpers::init_logger(KeyboardManagerConstants::ModuleName, L"Editor", LogSettings::keyboardManagerLoggerName); + InitUnhandledExceptionHandler_x64(); + Trace::RegisterProvider(); + + auto mutex = CreateMutex(nullptr, true, instanceMutexName.c_str()); + if (mutex == nullptr) + { + Logger::error(L"Failed to create mutex. {}", get_last_error_or_default(GetLastError())); + } + else + { + Logger::trace(L"Created/Opened {} mutex", instanceMutexName); + } + + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + Logger::info(L"KBM editor instance is already running"); + return 0; + } + + int numArgs; + LPWSTR* cmdArgs = CommandLineToArgvW(GetCommandLineW(), &numArgs); + + KeyboardManagerEditorType type = KeyboardManagerEditorType::KeyEditor; + if (!IsDebuggerPresent()) + { + if (cmdArgs == nullptr) + { + Logger::error(L"Keyboard Manager Editor cannot start as a standalone application"); + return -1; + } + + if (numArgs < 2) + { + Logger::error(L"Invalid arguments on Keyboard Manager Editor start"); + return -1; + } + } + + if (numArgs > 1) + { + type = static_cast(_wtoi(cmdArgs[1])); + } + + if (numArgs == 3) + { + std::wstring pid = std::wstring(cmdArgs[2]); + Logger::trace(L"Editor started from the settings with pid {}", pid); + if (!pid.empty()) + { + auto mainThreadId = GetCurrentThreadId(); + ProcessWaiter::OnProcessTerminate(pid, [mainThreadId](int err) { + if (err != ERROR_SUCCESS) + { + Logger::error(L"Failed to wait for parent process exit. {}", get_last_error_or_default(err)); + } + + Logger::trace(L"Parent process exited. Exiting KeyboardManager editor"); + PostThreadMessage(mainThreadId, WM_QUIT, 0, 0); + }); + } + } + + editor = std::make_unique(hInstance); + if (!editor->StartLowLevelKeyboardHook()) + { + DWORD errorCode = GetLastError(); + show_last_error_message(L"SetWindowsHookEx", errorCode, L"PowerToys - Keyboard Manager Editor"); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Unable to start keyboard hook: {}", errorMessage.has_value() ? errorMessage.value() : L""); + Trace::Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"start_lowlevel_keyboard_hook.SetWindowsHookEx"); + + return -1; + } + + editor->OpenEditorWindow(type); + + editor = nullptr; + + Trace::UnregisterProvider(); + return 0; +} + +KeyboardManagerEditor::KeyboardManagerEditor(HINSTANCE hInst) : + hInstance(hInst) +{ + bool loadedSuccessful = SettingsHelper::LoadSettings(keyboardManagerState); + if (!loadedSuccessful) + { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + // retry once + SettingsHelper::LoadSettings(keyboardManagerState); + } + + StartLowLevelKeyboardHook(); +} + +KeyboardManagerEditor::~KeyboardManagerEditor() +{ + UnhookWindowsHookEx(hook); +} + +bool KeyboardManagerEditor::StartLowLevelKeyboardHook() +{ +#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED) + if (IsDebuggerPresent()) + { + return true; + } +#endif + + hook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyHookProc, GetModuleHandle(NULL), NULL); + return (hook != nullptr); +} + +void KeyboardManagerEditor::OpenEditorWindow(KeyboardManagerEditorType type) +{ + switch (type) + { + case KeyboardManagerEditorType::KeyEditor: + CreateEditKeyboardWindow(hInstance, keyboardManagerState); + break; + case KeyboardManagerEditorType::ShortcutEditor: + CreateEditShortcutsWindow(hInstance, keyboardManagerState); + } +} + +intptr_t KeyboardManagerEditor::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept +{ + // If the Detect Key Window is currently activated, then suppress the keyboard event + KeyboardManagerHelper::KeyboardHookDecision singleKeyRemapUIDetected = keyboardManagerState.DetectSingleRemapKeyUIBackend(data); + if (singleKeyRemapUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress) + { + return 1; + } + else if (singleKeyRemapUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook) + { + return 0; + } + + // If the Detect Shortcut Window from Remap Keys is currently activated, then suppress the keyboard event + KeyboardManagerHelper::KeyboardHookDecision remapKeyShortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data, true); + if (remapKeyShortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress) + { + return 1; + } + else if (remapKeyShortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook) + { + return 0; + } + + // If the Detect Shortcut Window is currently activated, then suppress the keyboard event + KeyboardManagerHelper::KeyboardHookDecision shortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data, false); + if (shortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress) + { + return 1; + } + else if (shortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook) + { + return 0; + } + + return 0; +} + +// Hook procedure definition +LRESULT KeyboardManagerEditor::KeyHookProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + LowlevelKeyboardEvent event; + if (nCode == HC_ACTION) + { + event.lParam = reinterpret_cast(lParam); + event.wParam = wParam; + if (editor->HandleKeyboardHookEvent(&event) == 1) + { + // Reset Num Lock whenever a NumLock key down event is suppressed since Num Lock key state change occurs before it is intercepted by low level hooks + if (event.lParam->vkCode == VK_NUMLOCK && (event.wParam == WM_KEYDOWN || event.wParam == WM_SYSKEYDOWN) && event.lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG) + { + KeyboardEventHandlers::SetNumLockToPreviousState(editor->GetInputHandler()); + } + return 1; + } + } + + return CallNextHookEx(hook, nCode, wParam, lParam); +} diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.exe.manifest b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.exe.manifest new file mode 100644 index 0000000000..e70a561c0f --- /dev/null +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + \ No newline at end of file diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.h b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.h new file mode 100644 index 0000000000..c69db93b68 --- /dev/null +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +enum class KeyboardManagerEditorType +{ + KeyEditor = 0, + ShortcutEditor, +}; + +class KeyboardManagerEditor +{ +public: + KeyboardManagerEditor(HINSTANCE hInstance); + ~KeyboardManagerEditor(); + + KeyboardManagerInput::Input& GetInputHandler() noexcept + { + return inputHandler; + } + + bool StartLowLevelKeyboardHook(); + void OpenEditorWindow(KeyboardManagerEditorType type); + + // Function called by the hook procedure to handle the events. This is the starting point function for remapping + intptr_t HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept; + +private: + static LRESULT CALLBACK KeyHookProc(int nCode, WPARAM wParam, LPARAM lParam); + + inline static HHOOK hook; + HINSTANCE hInstance; + + KeyboardManagerState keyboardManagerState; + + // Object of class which implements InputInterface. Required for calling library functions while enabling testing + KeyboardManagerInput::Input inputHandler; +}; diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj new file mode 100644 index 0000000000..8d193ce336 --- /dev/null +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj @@ -0,0 +1,192 @@ + + + + + + + Debug + x64 + + + Release + x64 + + + + + + true + Use + + + + + + pch.h + Level3 + false + true + stdcpplatest + /await %(AdditionalOptions) + _UNICODE;UNICODE;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + + + true + + + + + + _DEBUG;%(PreprocessorDefinitions) + Disabled + true + MultiThreadedDebug + + + true + + + + + NDEBUG;%(PreprocessorDefinitions) + MaxSpeed + false + MultiThreaded + true + true + + + true + true + true + + + + + 16.0 + Win32Proj + {8df78b53-200e-451f-9328-01eb907193ae} + KeyboardManagerEditor + true + 10.0.18362.0 + 10.0.18362.0 + + + + v142 + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ + $(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\$(MSBuildProjectName)\ + Unicode + Spectre + Application + PowerToys.$(MSBuildProjectName) + + + + true + true + + + false + true + false + + + + + + + + + + + + + + + + + ./;$(SolutionDir)src\modules\;$(SolutionDir)src\modules\KeyboardManager\KeyboardManagerEditorLibrary\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;./../common;./../;%(AdditionalIncludeDirectories) + + + Display.lib;shcore.lib;Dbghelp.lib;%(AdditionalDependencies) + $(SolutionDir)$(Platform)\$(ConfigurationName);%(AdditionalLibraryDirectories) + + + + + ./;$(SolutionDir)src\modules\;$(SolutionDir)src\modules\KeyboardManager\KeyboardManagerEditorLibrary\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;./../common;./../;%(AdditionalIncludeDirectories) + + + true + true + Display.lib;shcore.lib;Dbghelp.lib;%(AdditionalDependencies) + $(SolutionDir)$(Platform)\$(ConfigurationName);%(AdditionalLibraryDirectories) + + + + + + + + + + + + + Create + Create + + + + + + + + + {caba8dfb-823b-4bf2-93ac-3f31984150d9} + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {98537082-0fdb-40de-abd8-0dc5a4269bab} + + + {8affa899-0b73-49ec-8c50-0fadda57b2fc} + + + {23d2070d-e4ad-4add-85a7-083d9c76ad49} + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + \ No newline at end of file diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj.filters b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj.filters new file mode 100644 index 0000000000..b940b683fb --- /dev/null +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj.filters @@ -0,0 +1,67 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {904807de-a4f6-4c65-9399-a9c244580ca4} + + + + + Header Files + + + Header Files + + + Header Files + + + GeneratedFiles + + + + + Source Files + + + Source Files + + + + + GeneratedFiles + + + + + + Resource Files + + + Header Files + + + Resource Files + + + + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/LocProject.json b/src/modules/keyboardmanager/KeyboardManagerEditor/LocProject.json new file mode 100644 index 0000000000..a86ff2e861 --- /dev/null +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/LocProject.json @@ -0,0 +1,14 @@ +{ + "Projects": [ + { + "LanguageSet": "Azure_Languages", + "LocItems": [ + { + "SourceFile": "src\\modules\\keyboardmanager\\KeyboardManagerEditor\\Resources.resx", + "CopyOption": "LangIDOnName", + "OutputPath": "src\\modules\\keyboardmanager\\KeyboardManagerEditor" + } + ] + } + ] +} diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/Resource.h b/src/modules/keyboardmanager/KeyboardManagerEditor/Resource.h new file mode 100644 index 0000000000..17805695dc --- /dev/null +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/Resource.h @@ -0,0 +1,24 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by KeyboardManagerEditor.base.rc +// +#define IDC_MYICON 2 +#define IDD_KEYBOARDMANAGEREDITOR_DIALOG 102 +#define IDS_APP_TITLE 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDC_KEYBOARDMANAGEREDITOR 109 +#define IDR_MAINFRAME 128 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx b/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx new file mode 100644 index 0000000000..3c2cb3c6f5 --- /dev/null +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx @@ -0,0 +1,367 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + This feature requires Windows 10 version 1903 or higher + + + Keyboard Manager + + + Call to CreateWindow failed! + + + Error + + + Windows registration failed! + This refers to an application window + + + Error + + + Remap keys + + + Remap shortcuts + + + OK + + + Cancel + + + Continue Anyway + + + Key: + Key on a keyboard + + + Mapped To: + + + Shortcut: + + + Mapped To: + + + Target App: + + + The following keys have been reassigned and you won't be able to use them for their original assignment: + + + Some of the keys could not be remapped. Do you want to continue anyway? + + + Some of the shortcuts could not be remapped. Do you want to continue anyway? + + + Select the key you want to change (Key) and then the key or shortcut you want it to become (Mapped To). + + + For example, if you want to press A and get "Ctrl+C", key "A" would be your "Key" column and the shortcut "Ctrl+C" would be your "Mapped To" column. + + + Select the shortcut you want to change (Shortcut) and then the key or shortcut you want it to invoke (Mapped To). + + + For example, if you want to press "Ctrl+C" and get "Alt" only on Microsoft Edge, "Ctrl+C" would be your "Shortcut" column, the key "Alt" would be your "Mapped To" column, and "MSEdge" would be your "Target App" column. If no target app is entered, it will apply globally. The name must be the process name and not the app name. + + + Remapping successful + + + Some remappings were not applied + + + Cannot remap a key more than once + Key on a keyboard + + + Cannot remap a key to itself + Key on a keyboard + + + Cannot remap this key as it conflicts with another remapped key + Key on a keyboard + + + Cannot remap a shortcut more than once for the same target app + + + Cannot remap a shortcut to itself + + + Cannot remap this shortcut as it conflicts with another remapped shortcut + + + Cannot remap from/to Win L + Win refers to Windows as in the OS + + + Cannot remap from/to Ctrl Alt Del + + + Failed to save the remappings + + + Shortcut must start with a modifier key + Key on a keyboard + + + Shortcut cannot contain a repeated modifier + + + Shortcut must have atleast 2 keys + Key on a keyboard + + + Shortcut must contain an action key + Key on a keyboard + + + Shortcut cannot have more than one action key + Key on a keyboard + + + Shortcuts can only have up to 2 modifier keys + Key on a keyboard + + + Unexpected error + + + Type + As in type a key + + + Press a key on selected keyboard: + Key on a keyboard + + + Press the keys in shortcut: + Key on a keyboard + + + Key Pressed: + Key on a keyboard + + + Keys Pressed: + Key on a keyboard + + + Hold Enter to continue + + + Hold Esc to discard + + + All Apps + + + Remapping successful + + + Some remappings were not applied + + + Cannot remap a key more than once + Key on a keyboard + + + Cannot remap a key to itself + Key on a keyboard + + + Cannot remap this key as it conflicts with another remapped key + Key on a keyboard + + + Cannot remap a shortcut more than once for the same target app + + + Cannot remap a shortcut to itself + + + Cannot remap this shortcut as it conflicts with another remapped shortcut + + + Cannot remap from/to Win L + Win refers to Windows as in the OS + + + Cannot remap from/to Ctrl Alt Del + + + Failed to save the remappings + + + Shortcut must start with a modifier key + Key on a keyboard + + + Shortcut cannot contain a repeated modifier + + + Shortcut must have atleast 2 keys + Key on a keyboard + + + Shortcut must contain an action key + Key on a keyboard + + + Shortcut cannot have more than one action key + Key on a keyboard + + + Shortcuts can only have up to 2 modifier keys + Key on a keyboard + + + Unexpected error + + + Key + Key on a keyboard + + + Add Key Remap + Key on a keyboard + + + Add Shortcut Remapping + + + Delete Remapping + + + Row + + + Disable can not be an action or a modifier key + Key on a keyboard + + \ No newline at end of file diff --git a/src/modules/keyboardmanager/test/packages.config b/src/modules/keyboardmanager/KeyboardManagerEditor/packages.config similarity index 100% rename from src/modules/keyboardmanager/test/packages.config rename to src/modules/keyboardmanager/KeyboardManagerEditor/packages.config diff --git a/src/modules/keyboardmanager/ui/pch.cpp b/src/modules/keyboardmanager/KeyboardManagerEditor/pch.cpp similarity index 100% rename from src/modules/keyboardmanager/ui/pch.cpp rename to src/modules/keyboardmanager/KeyboardManagerEditor/pch.cpp diff --git a/src/modules/keyboardmanager/ui/pch.h b/src/modules/keyboardmanager/KeyboardManagerEditor/pch.h similarity index 53% rename from src/modules/keyboardmanager/ui/pch.h rename to src/modules/keyboardmanager/KeyboardManagerEditor/pch.h index b5134f6a61..7572389c74 100644 --- a/src/modules/keyboardmanager/ui/pch.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/pch.h @@ -1,30 +1,36 @@ -#pragma once -// Do not define WIN32_LEAN_AND_MEAN as WinUI doesn't work when it is defined -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "winrt/Windows.Foundation.h" -#include "winrt/Windows.Foundation.Numerics.h" -#include "winrt/Windows.UI.Xaml.Controls.Primitives.h" -#include "winrt/Windows.UI.Text.h" -#include "winrt/Windows.UI.Core.h" -#include -#include -#include - -using namespace winrt; -using namespace Windows::UI; -using namespace Windows::UI::Composition; -using namespace Windows::UI::Xaml::Hosting; -using namespace Windows::Foundation::Numerics; -using namespace Windows::UI::Xaml; +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#include +#include +#include + +#include + +#include +#include +#include + +#pragma push_macro("GetCurrentTime") +#undef GetCurrentTime +#include +#include +#include +#include +#include +#pragma pop_macro("GetCurrentTime") + +#include +#include + +#include + +using namespace winrt; +using namespace Windows::UI; +using namespace Windows::UI::Composition; +using namespace Windows::UI::Xaml::Hosting; +using namespace Windows::Foundation::Numerics; +using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml::Controls; \ No newline at end of file diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/resource.base.h b/src/modules/keyboardmanager/KeyboardManagerEditor/resource.base.h new file mode 100644 index 0000000000..d641bb0c7e --- /dev/null +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/resource.base.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by KeyboardManagerEditor.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys KeyboardManagerEditor" +#define INTERNAL_NAME "KeyboardManagerEditor" +#define ORIGINAL_FILENAME "PowerToys.KeyboardManagerEditor.exe" +#define IDS_KEYBOARDMANAGER_ICON 1001 + +// Non-localizable +////////////////////////////// diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/targetver.h b/src/modules/keyboardmanager/KeyboardManagerEditor/targetver.h new file mode 100644 index 0000000000..bf75e0895e --- /dev/null +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/targetver.h @@ -0,0 +1,6 @@ +#pragma once + +// // Including SDKDDKVer.h defines the highest available Windows platform. +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. +#include diff --git a/src/modules/keyboardmanager/ui/BufferValidationHelpers.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp similarity index 92% rename from src/modules/keyboardmanager/ui/BufferValidationHelpers.cpp rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp index 0ee4eff695..5eedae75a9 100644 --- a/src/modules/keyboardmanager/ui/BufferValidationHelpers.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp @@ -1,8 +1,11 @@ #include "pch.h" #include "BufferValidationHelpers.h" -#include + #include -#include + +#include +#include +#include namespace BufferValidationHelpers { @@ -82,9 +85,9 @@ namespace BufferValidationHelpers // warn and reset the drop down errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier; } - // If it is the last drop down else if (dropDownIndex == dropDownCount - 1) { + // If it is the last drop down // If last drop down and a modifier is selected: add a new drop down (max drop down count should be enforced) if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode) && dropDownCount < KeyboardManagerConstants::MaxShortcutSize) { @@ -94,21 +97,21 @@ namespace BufferValidationHelpers // warn and reset the drop down errorType = KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier; } - // If not, add a new drop down else { + // If not, add a new drop down dropDownAction = BufferValidationHelpers::DropDownAction::AddDropDown; } } - // If last drop down and a modifier is selected but there are already max drop downs: warn the user else if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode) && dropDownCount >= KeyboardManagerConstants::MaxShortcutSize) { + // If last drop down and a modifier is selected but there are already max drop downs: warn the user // warn and reset the drop down errorType = KeyboardManagerHelper::ErrorType::ShortcutMaxShortcutSizeOneActionKey; } - // If None is selected but it's the last index: warn else if (selectedKeyCode == 0) { + // If None is selected but it's the last index: warn // If it is a hybrid control and there are 2 drop downs then deletion is allowed if (isHybridControl && dropDownCount == KeyboardManagerConstants::MinShortcutSize) { @@ -122,16 +125,16 @@ namespace BufferValidationHelpers errorType = KeyboardManagerHelper::ErrorType::ShortcutOneActionKey; } } - // Disable can not be selected if one modifier key has already been selected else if (selectedKeyCode == CommonSharedConstants::VK_DISABLED && dropDownIndex) { + // Disable can not be selected if one modifier key has already been selected errorType = KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey; } // If none of the above, then the action key will be set } - // If it is not the last drop down else { + // If it is not the last drop down if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode)) { // If it matched any of the previous modifiers then reset that drop down @@ -142,9 +145,9 @@ namespace BufferValidationHelpers } // If not, the modifier key will be set } - // If None is selected and there are more than 2 drop downs else if (selectedKeyCode == 0 && dropDownCount > KeyboardManagerConstants::MinShortcutSize) { + // If None is selected and there are more than 2 drop downs // set delete drop down flag dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown; // do not delete the drop down now since there may be some other error which would cause the drop down to be invalid after removal @@ -164,14 +167,15 @@ namespace BufferValidationHelpers errorType = KeyboardManagerHelper::ErrorType::ShortcutAtleast2Keys; } } - // Allow selection of VK_DISABLE only in first dropdown else if (selectedKeyCode == CommonSharedConstants::VK_DISABLED && dropDownIndex) { + // Allow selection of VK_DISABLE only in first dropdown errorType = KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey; } - // If the user tries to set an action key check if all drop down menus after this are empty if it is not the first key. If it is a hybrid control, this can be done even on the first key else if (dropDownIndex != 0 || isHybridControl) { + // If the user tries to set an action key check if all drop down menus after this are empty if it is not the first key. + // If it is a hybrid control, this can be done even on the first key bool isClear = true; for (int i = dropDownIndex + 1; i < (int)dropDownCount; i++) { @@ -192,9 +196,9 @@ namespace BufferValidationHelpers errorType = KeyboardManagerHelper::ErrorType::ShortcutNotMoreThanOneActionKey; } } - // If there an action key is chosen on the first drop down and there are more than one drop down menus else { + // If there an action key is chosen on the first drop down and there are more than one drop down menus // warn and reset the drop down errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier; } @@ -217,7 +221,7 @@ namespace BufferValidationHelpers // Convert app name to lower case std::transform(appName.begin(), appName.end(), appName.begin(), towlower); - std::wstring lowercaseDefAppName = KeyboardManagerConstants::DefaultAppName; + std::wstring lowercaseDefAppName = KeyboardManagerEditorStrings::DefaultAppName; std::transform(lowercaseDefAppName.begin(), lowercaseDefAppName.end(), lowercaseDefAppName.begin(), towlower); if (appName == lowercaseDefAppName) { diff --git a/src/modules/keyboardmanager/ui/BufferValidationHelpers.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.h similarity index 86% rename from src/modules/keyboardmanager/ui/BufferValidationHelpers.h rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.h index 3c28a292ee..6714439d6c 100644 --- a/src/modules/keyboardmanager/ui/BufferValidationHelpers.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.h @@ -1,8 +1,6 @@ #pragma once -#include "keyboardmanager/common/Helpers.h" -#include -#include -#include "keyboardmanager/common/Shortcut.h" + +#include namespace BufferValidationHelpers { diff --git a/src/modules/keyboardmanager/ui/Dialog.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/Dialog.cpp similarity index 88% rename from src/modules/keyboardmanager/ui/Dialog.cpp rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/Dialog.cpp index 86d23bfa68..3ded9a2a7e 100644 --- a/src/modules/keyboardmanager/ui/Dialog.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/Dialog.cpp @@ -1,8 +1,5 @@ #include "pch.h" #include "Dialog.h" -#include -#include "keyboardmanager/dll/Generated Files/resource.h" -#include using namespace winrt::Windows::Foundation; diff --git a/src/modules/keyboardmanager/ui/Dialog.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/Dialog.h similarity index 100% rename from src/modules/keyboardmanager/ui/Dialog.h rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/Dialog.h diff --git a/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp similarity index 88% rename from src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp index a006bcd26c..3e40b68d41 100644 --- a/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp @@ -1,20 +1,25 @@ #include "pch.h" + +#include + +#include +#include +#include +#include + +#include +#include + #include "EditKeyboardWindow.h" +#include "ErrorTypes.h" #include "SingleKeyRemapControl.h" #include "KeyDropDownControl.h" #include "XamlBridge.h" -#include -#include -#include -#include -#include #include "Styles.h" #include "Dialog.h" -#include -#include -#include "keyboardmanager/common/KeyboardManagerState.h" #include "LoadingAndSavingRemappingHelper.h" #include "UIHelpers.h" +#include using namespace winrt::Windows::Foundation; @@ -22,11 +27,14 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND, UINT, WPARAM, LPARAM); // This Hwnd will be the window handler for the Xaml Island: A child window that contains Xaml. HWND hWndXamlIslandEditKeyboardWindow = nullptr; + // This variable is used to check if window registration has been done to avoid repeated registration leading to an error. bool isEditKeyboardWindowRegistrationCompleted = false; + // Holds the native window handle of EditKeyboard Window. HWND hwndEditKeyboardNativeWindow = nullptr; std::mutex editKeyboardWindowMutex; + // Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure static XamlBridge* xamlBridgePtr = nullptr; @@ -52,6 +60,7 @@ static IAsyncOperation OrphanKeysConfirmationDialog( orphanKeyString.append(state.keyboardMap.GetKeyName(k)); orphanKeyString.append(L", "); } + orphanKeyString = orphanKeyString.substr(0, max(0, orphanKeyString.length() - 2)); orphanKeysBlock.Text(winrt::hstring(orphanKeyString)); orphanKeysBlock.TextWrapping(TextWrapping::Wrap); @@ -84,13 +93,21 @@ static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, Xa co_return; } } + ApplyRemappings(); } // Function to create the Edit Keyboard Window -void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState) +inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState) { - Logger::trace("Creating Remap keys window"); + Logger::trace("CreateEditKeyboardWindowImpl()"); + auto locker = EventLocker::Get(KeyboardManagerConstants::EditorWindowEventName.c_str()); + if (!locker.has_value()) + { + Logger::error(L"Failed to lock event {}. {}", KeyboardManagerConstants::EditorWindowEventName, get_last_error_or_default(GetLastError())); + } + + Logger::trace(L"Signaled {} event to suspend the KBM engine", KeyboardManagerConstants::EditorWindowEventName); // Window Registration const wchar_t szWindowClass[] = L"EditKeyboardWindow"; @@ -109,6 +126,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan 48, 48, LR_DEFAULTCOLOR); + if (RegisterClassEx(&windowClass) == NULL) { MessageBox(NULL, GET_RESOURCE_STRING(IDS_REGISTERCLASSFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_REGISTERCLASSFAILED_ERRORTITLE).c_str(), NULL); @@ -139,11 +157,13 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan NULL, hInst, NULL); + if (_hWndEditKeyboardWindow == NULL) { MessageBox(NULL, GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORTITLE).c_str(), NULL); return; } + // Ensures the window is in foreground on first startup. If this is not done, the window appears behind because the thread is not on the foreground. if (_hWndEditKeyboardWindow) { @@ -157,8 +177,10 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan // Create the xaml bridge object XamlBridge xamlBridge(_hWndEditKeyboardWindow); + // DesktopSource needs to be declared before the RelativePanel xamlContainer object to avoid errors winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource; + // Create the desktop window xaml source object and set its content hWndXamlIslandEditKeyboardWindow = xamlBridge.InitDesktopWindowsXamlSource(desktopSource); @@ -205,7 +227,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan TextBlock originalKeyRemapHeader; originalKeyRemapHeader.Text(GET_RESOURCE_STRING(IDS_EDITKEYBOARD_SOURCEHEADER)); originalKeyRemapHeader.FontWeight(Text::FontWeights::Bold()); - StackPanel originalKeyHeaderContainer = KeyboardManagerHelper::GetWrapped(originalKeyRemapHeader, KeyboardManagerConstants::RemapTableDropDownWidth + KeyboardManagerConstants::TableArrowColWidth).as(); + StackPanel originalKeyHeaderContainer = UIHelpers::GetWrapped(originalKeyRemapHeader, KeyboardManagerConstants::RemapTableDropDownWidth + KeyboardManagerConstants::TableArrowColWidth).as(); // Second header textblock in the header row of the keys remap table TextBlock newKeyRemapHeader; @@ -220,11 +242,14 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan // Store handle of edit keyboard window SingleKeyRemapControl::EditKeyboardWindowHandle = _hWndEditKeyboardWindow; + // Store keyboard manager state SingleKeyRemapControl::keyboardManagerState = &keyboardManagerState; KeyDropDownControl::keyboardManagerState = &keyboardManagerState; + // Clear the single key remap buffer SingleKeyRemapControl::singleKeyRemapBuffer.clear(); + // Vector to store dynamically allocated control objects to avoid early destruction std::vector>> keyboardRemapControlObjects; @@ -251,13 +276,8 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan header.SetLeftOf(applyButton, cancelButton); auto ApplyRemappings = [&keyboardManagerState, _hWndEditKeyboardWindow]() { - // Disable the remappings while the remapping table is updated - keyboardManagerState.RemappingsDisabledWrapper( - [&keyboardManagerState]() { - LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(keyboardManagerState, SingleKeyRemapControl::singleKeyRemapBuffer, true); - // Save the updated shortcuts remaps to file. - bool saveResult = keyboardManagerState.SaveConfigToFile(); - }); + LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(keyboardManagerState, SingleKeyRemapControl::singleKeyRemapBuffer, true); + bool saveResult = keyboardManagerState.SaveConfigToFile(); PostMessage(_hWndEditKeyboardWindow, WM_CLOSE, 0, 0); }; @@ -352,9 +372,40 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan xamlBridge.ClearXamlIslands(); } +void CreateEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState) +{ + // Move implementation into the separate method so resources get destroyed correctly + CreateEditKeyboardWindowImpl(hInst, keyboardManagerState); + + // Calling ClearXamlIslands() outside of the message loop is not enough to prevent + // Microsoft.UI.XAML.dll from crashing during deinitialization, see https://github.com/microsoft/PowerToys/issues/10906 + Logger::trace("Terminating process {}", GetCurrentProcessId()); + Logger::flush(); + TerminateProcess(GetCurrentProcess(), 0); +} + +inline std::wstring getMessage(UINT messageCode) +{ + switch (messageCode) + { + case WM_SIZE: + return L"WM_SIZE"; + case WM_NCDESTROY: + return L"WM_NCDESTROY"; + default: + return L""; + } +} + LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam) { RECT rcClient; + auto message = getMessage(messageCode); + if (message != L"") + { + Logger::trace(L"EditKeyboardWindowProc() messageCode={}", getMessage(messageCode)); + } + switch (messageCode) { // Resize the XAML window whenever the parent window is painted or resized @@ -387,6 +438,7 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wPar PostQuitMessage(0); break; } + return DefWindowProc(hWnd, messageCode, wParam, lParam); break; } @@ -406,6 +458,7 @@ bool CheckEditKeyboardWindowActive() { ShowWindow(hwndEditKeyboardNativeWindow, SW_RESTORE); } + // If there is an already existing window no need to create a new open bring it on foreground. SetForegroundWindow(hwndEditKeyboardNativeWindow); result = true; @@ -420,6 +473,7 @@ void CloseActiveEditKeyboardWindow() std::unique_lock hwndLock(editKeyboardWindowMutex); if (hwndEditKeyboardNativeWindow != nullptr) { + Logger::trace("CloseActiveEditKeyboardWindow()"); PostMessage(hwndEditKeyboardNativeWindow, WM_CLOSE, 0, 0); } } diff --git a/src/modules/keyboardmanager/ui/EditKeyboardWindow.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.h similarity index 81% rename from src/modules/keyboardmanager/ui/EditKeyboardWindow.h rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.h index 260b0f78ab..9084cdee2f 100644 --- a/src/modules/keyboardmanager/ui/EditKeyboardWindow.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.h @@ -1,11 +1,11 @@ -#pragma once -class KeyboardManagerState; - -// Function to create the Edit Keyboard Window -void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState); - -// Function to check if there is already a window active if yes bring to foreground -bool CheckEditKeyboardWindowActive(); - -// Function to close any active Edit Keyboard window +#pragma once +class KeyboardManagerState; + +// Function to create the Edit Keyboard Window +void CreateEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState); + +// Function to check if there is already a window active if yes bring to foreground +bool CheckEditKeyboardWindowActive(); + +// Function to close any active Edit Keyboard window void CloseActiveEditKeyboardWindow(); \ No newline at end of file diff --git a/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp similarity index 88% rename from src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp index 8f42e9cd47..c42f8d3870 100644 --- a/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp @@ -1,18 +1,20 @@ #include "pch.h" #include "EditShortcutsWindow.h" -#include "ShortcutControl.h" -#include "KeyDropDownControl.h" -#include "XamlBridge.h" -#include -#include -#include -#include -#include "Styles.h" -#include "Dialog.h" -#include -#include -#include "LoadingAndSavingRemappingHelper.h" -#include "UIHelpers.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace winrt::Windows::Foundation; @@ -20,11 +22,14 @@ LRESULT CALLBACK EditShortcutsWindowProc(HWND, UINT, WPARAM, LPARAM); // This Hwnd will be the window handler for the Xaml Island: A child window that contains Xaml. HWND hWndXamlIslandEditShortcutsWindow = nullptr; + // This variable is used to check if window registration has been done to avoid repeated registration leading to an error. bool isEditShortcutsWindowRegistrationCompleted = false; + // Holds the native window handle of EditShortcuts Window. HWND hwndEditShortcutsNativeWindow = nullptr; std::mutex editShortcutsWindowMutex; + // Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure static XamlBridge* xamlBridgePtr = nullptr; @@ -46,9 +51,16 @@ static IAsyncAction OnClickAccept( } // Function to create the Edit Shortcuts Window -void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState) +inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState) { - Logger::trace("Creating Remap shortcuts window"); + Logger::trace("CreateEditShortcutsWindowImpl()"); + auto locker = EventLocker::Get(KeyboardManagerConstants::EditorWindowEventName.c_str()); + if (!locker.has_value()) + { + Logger::error(L"Failed to lock event {}. {}", KeyboardManagerConstants::EditorWindowEventName, get_last_error_or_default(GetLastError())); + } + + Logger::trace(L"Signaled {} event to suspend the KBM engine", KeyboardManagerConstants::EditorWindowEventName); // Window Registration const wchar_t szWindowClass[] = L"EditShortcutsWindow"; @@ -98,11 +110,13 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa NULL, hInst, NULL); + if (_hWndEditShortcutsWindow == NULL) { MessageBox(NULL, GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORTITLE).c_str(), NULL); return; } + // Ensures the window is in foreground on first startup. If this is not done, the window appears behind because the thread is not on the foreground. if (_hWndEditShortcutsWindow) { @@ -116,8 +130,10 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa // Create the xaml bridge object XamlBridge xamlBridge(_hWndEditShortcutsWindow); + // DesktopSource needs to be declared before the RelativePanel xamlContainer object to avoid errors winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource; + // Create the desktop window xaml source object and set its content hWndXamlIslandEditShortcutsWindow = xamlBridge.InitDesktopWindowsXamlSource(desktopSource); @@ -179,19 +195,22 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa StackPanel tableHeader = StackPanel(); tableHeader.Orientation(Orientation::Horizontal); tableHeader.Margin({ 10, 0, 0, 10 }); - auto originalShortcutContainer = KeyboardManagerHelper::GetWrapped(originalShortcutHeader, KeyboardManagerConstants::ShortcutOriginColumnWidth + (double)KeyboardManagerConstants::ShortcutArrowColumnWidth); + auto originalShortcutContainer = UIHelpers::GetWrapped(originalShortcutHeader, KeyboardManagerConstants::ShortcutOriginColumnWidth + (double)KeyboardManagerConstants::ShortcutArrowColumnWidth); tableHeader.Children().Append(originalShortcutContainer.as()); - auto newShortcutHeaderContainer = KeyboardManagerHelper::GetWrapped(newShortcutHeader, KeyboardManagerConstants::ShortcutTargetColumnWidth); + auto newShortcutHeaderContainer = UIHelpers::GetWrapped(newShortcutHeader, KeyboardManagerConstants::ShortcutTargetColumnWidth); tableHeader.Children().Append(newShortcutHeaderContainer.as()); tableHeader.Children().Append(targetAppHeader); // Store handle of edit shortcuts window - ShortcutControl::EditShortcutsWindowHandle = _hWndEditShortcutsWindow; + ShortcutControl::editShortcutsWindowHandle = _hWndEditShortcutsWindow; + // Store keyboard manager state ShortcutControl::keyboardManagerState = &keyboardManagerState; KeyDropDownControl::keyboardManagerState = &keyboardManagerState; + // Clear the shortcut remap buffer ShortcutControl::shortcutRemapBuffer.clear(); + // Vector to store dynamically allocated control objects to avoid early destruction std::vector>> keyboardRemapControlObjects; @@ -231,13 +250,8 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa header.SetLeftOf(applyButton, cancelButton); auto ApplyRemappings = [&keyboardManagerState, _hWndEditShortcutsWindow]() { - // Disable the remappings while the remapping table is updated - keyboardManagerState.RemappingsDisabledWrapper( - [&keyboardManagerState]() { - LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(keyboardManagerState, ShortcutControl::shortcutRemapBuffer, true); - // Save the updated key remaps to file. - bool saveResult = keyboardManagerState.SaveConfigToFile(); - }); + LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(keyboardManagerState, ShortcutControl::shortcutRemapBuffer, true); + bool saveResult = keyboardManagerState.SaveConfigToFile(); PostMessage(_hWndEditShortcutsWindow, WM_CLOSE, 0, 0); }; @@ -332,6 +346,18 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa xamlBridge.ClearXamlIslands(); } +void CreateEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState) +{ + // Move implementation into the separate method so resources get destroyed correctly + CreateEditShortcutsWindowImpl(hInst, keyboardManagerState); + + // Calling ClearXamlIslands() outside of the message loop is not enough to prevent + // Microsoft.UI.XAML.dll from crashing during deinitialization, see https://github.com/microsoft/PowerToys/issues/10906 + Logger::trace("Terminating process {}", GetCurrentProcessId()); + Logger::flush(); + TerminateProcess(GetCurrentProcess(), 0); +} + LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam) { RECT rcClient; diff --git a/src/modules/keyboardmanager/ui/EditShortcutsWindow.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.h similarity index 81% rename from src/modules/keyboardmanager/ui/EditShortcutsWindow.h rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.h index d333134354..3f8823612f 100644 --- a/src/modules/keyboardmanager/ui/EditShortcutsWindow.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.h @@ -1,11 +1,11 @@ -#pragma once -class KeyboardManagerState; - -// Function to create the Edit Shortcuts Window -void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState); - -// Function to check if there is already a window active if yes bring to foreground -bool CheckEditShortcutsWindowActive(); - -// Function to close any active Edit Shortcuts window +#pragma once +class KeyboardManagerState; + +// Function to create the Edit Shortcuts Window +void CreateEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState); + +// Function to check if there is already a window active if yes bring to foreground +bool CheckEditShortcutsWindowActive(); + +// Function to close any active Edit Shortcuts window void CloseActiveEditShortcutsWindow(); \ No newline at end of file diff --git a/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp similarity index 96% rename from src/modules/keyboardmanager/ui/KeyDropDownControl.cpp rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp index 431bd72211..9378ac940c 100644 --- a/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp @@ -1,11 +1,13 @@ #include "pch.h" #include "KeyDropDownControl.h" -#include "keyboardmanager/common/Helpers.h" -#include -#include "BufferValidationHelpers.h" + #include -#include -#include +#include + +#include +#include +#include +#include // Initialized to null KeyboardManagerState* KeyDropDownControl::keyboardManagerState = nullptr; @@ -15,7 +17,9 @@ DWORD KeyDropDownControl::GetSelectedValue(ComboBox comboBox) { auto dataContext = comboBox.SelectedValue(); if (!dataContext) + { return -1; + } auto value = winrt::unbox_value(dataContext); return stoi(std::wstring(value)); @@ -53,11 +57,13 @@ void KeyDropDownControl::SetDefaultProperties(bool isShortcut, bool renderDisabl { dropDown.as().Width(KeyboardManagerConstants::ShortcutTableDropDownWidth); } + dropDown.as().MaxDropDownHeight(KeyboardManagerConstants::TableDropDownHeight); + // Initialise layout attribute previousLayout = GetKeyboardLayout(0); dropDown.as().SelectedValuePath(L"DataContext"); - dropDown.as().ItemsSource(KeyboardManagerHelper::ToBoxValue(GetKeyList(isShortcut, renderDisable))); + dropDown.as().ItemsSource(UIHelpers::ToBoxValue(GetKeyList(isShortcut, renderDisable))); // drop down open handler - to reload the items with the latest layout dropDown.as().DropDownOpened([&, isShortcut](winrt::Windows::Foundation::IInspectable const& sender, auto args) { @@ -74,6 +80,7 @@ void KeyDropDownControl::SetDefaultProperties(bool isShortcut, bool renderDisabl style.Setters().Append(Setter(Windows::UI::Xaml::Controls::Control::TabNavigationProperty(), winrt::box_value(Windows::UI::Xaml::Input::KeyboardNavigationMode::Cycle))); warningFlyout.as().FlyoutPresenterStyle(style); dropDown.as().ContextFlyout().SetAttachedFlyout((FrameworkElement)dropDown.as(), warningFlyout.as()); + // To set the accessible name of the combo-box (by default index 1) SetAccessibleNameForComboBox(dropDown.as(), 1); } @@ -94,7 +101,7 @@ void KeyDropDownControl::CheckAndUpdateKeyboardLayout(ComboBox currentDropDown, // Check if the layout has changed if (previousLayout != layout) { - currentDropDown.ItemsSource(KeyboardManagerHelper::ToBoxValue(GetKeyList(isShortcut, renderDisable))); + currentDropDown.ItemsSource(UIHelpers::ToBoxValue(GetKeyList(isShortcut, renderDisable))); previousLayout = layout; } } @@ -112,13 +119,14 @@ void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row, ComboBox currentDropDown = sender.as(); int selectedKeyCode = GetSelectedValue(currentDropDown); + // Validate current remap selection KeyboardManagerHelper::ErrorType errorType = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(rowIndex, colIndex, selectedKeyCode, singleKeyRemapBuffer); // If there is an error set the warning flyout if (errorType != KeyboardManagerHelper::ErrorType::NoError) { - SetDropDownError(currentDropDown, KeyboardManagerHelper::GetErrorMessage(errorType)); + SetDropDownError(currentDropDown, KeyboardManagerEditorStrings::GetErrorMessage(errorType)); } }; @@ -185,7 +193,7 @@ std::pair KeyDropDownControl::ValidateSho // If the remapping is invalid display an error message if (validationResult.first != KeyboardManagerHelper::ErrorType::NoError) { - SetDropDownError(currentDropDown, KeyboardManagerHelper::GetErrorMessage(validationResult.first)); + SetDropDownError(currentDropDown, KeyboardManagerEditorStrings::GetErrorMessage(validationResult.first)); } // Handle None case if there are no other errors @@ -199,6 +207,7 @@ std::pair KeyDropDownControl::ValidateSho } parent.Children().RemoveAt(dropDownIndex); + // delete drop down control object from the vector so that it can be destructed keyDropDownControlObjects.erase(keyDropDownControlObjects.begin() + dropDownIndex); parent.UpdateLayout(); @@ -251,10 +260,11 @@ void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row, shortcutRemapBuffer[validationResult.second].first[colIndex] = tempShortcut; } } + if (targetApp != nullptr) { std::wstring newText = targetApp.Text().c_str(); - std::wstring lowercaseDefAppName = KeyboardManagerConstants::DefaultAppName; + std::wstring lowercaseDefAppName = KeyboardManagerEditorStrings::DefaultAppName; std::transform(newText.begin(), newText.end(), newText.begin(), towlower); std::transform(lowercaseDefAppName.begin(), lowercaseDefAppName.end(), lowercaseDefAppName.begin(), towlower); if (newText == lowercaseDefAppName) @@ -372,6 +382,7 @@ void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, StackPanel tabl { // Delete the existing drop down menus parent.Children().Clear(); + // Remove references to the old drop down objects to destroy them keyDropDownControlObjects.clear(); std::vector shortcutKeyCodes = shortcut.GetKeyCodes(); @@ -397,6 +408,7 @@ void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, StackPanel tabl } } } + parent.UpdateLayout(); } diff --git a/src/modules/keyboardmanager/ui/KeyDropDownControl.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.h similarity index 98% rename from src/modules/keyboardmanager/ui/KeyDropDownControl.h rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.h index 64b153c76d..fe96df979f 100644 --- a/src/modules/keyboardmanager/ui/KeyDropDownControl.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.h @@ -1,6 +1,7 @@ #pragma once -#include -#include + +#include + class KeyboardManagerState; namespace winrt::Windows @@ -30,12 +31,16 @@ class KeyDropDownControl private: // Stores the drop down combo box winrt::Windows::Foundation::IInspectable dropDown; + // Stores the previous layout HKL previousLayout = 0; + // Stores the flyout warning message winrt::Windows::Foundation::IInspectable warningMessage; + // Stores the flyout attached to the current drop down winrt::Windows::Foundation::IInspectable warningFlyout; + // Stores whether a key to shortcut warning has to be ignored bool ignoreKeyToShortcutWarning; @@ -50,6 +55,7 @@ private: // Function to set accessible name for combobox static void SetAccessibleNameForComboBox(ComboBox dropDown, int index); + public: // Pointer to the keyboard manager state static KeyboardManagerState* keyboardManagerState; diff --git a/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorLibrary.vcxproj similarity index 68% rename from src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorLibrary.vcxproj index ce0bd7a51b..6aad7a4aa8 100644 --- a/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorLibrary.vcxproj @@ -1,96 +1,102 @@ - - - - - 15.0 - {EAF23649-EF6E-478B-980E-81FAD96CCA2A} - Win32Proj - KeyboardManagerUI - True - KeyboardManagerUI - true - 10.0.18362.0 - - - - StaticLibrary - - - - - - - - - - - - - - - $(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\ - true - - - - Unicode - Spectre - true - 4002 - $(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories) - - - windowsapp.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - - - - - - - Create - - - - - - - - - - - - - - - - - - - - - - - - {98537082-0fdb-40de-abd8-0dc5a4269bab} - - - - - - - - - - - - - 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}. - - - - + + + + + 16.0 + Win32Proj + {23d2070d-e4ad-4add-85a7-083d9c76ad49} + KeyboardManagerEditorLibrary + true + 10.0.18362.0 + 10.0.18362.0 + + + + StaticLibrary + $(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\ + + + + + + + + + + + + + Level3 + ./;$(SolutionDir)src\modules\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;./../common;./../;%(AdditionalIncludeDirectories) + true + _LIB;%(PreprocessorDefinitions) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + + + + + + + + + + + + + + {caba8dfb-823b-4bf2-93ac-3f31984150d9} + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {98537082-0fdb-40de-abd8-0dc5a4269bab} + + + {8affa899-0b73-49ec-8c50-0fadda57b2fc} + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj.filters b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorLibrary.vcxproj.filters similarity index 79% rename from src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj.filters rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorLibrary.vcxproj.filters index c23ccf4a3e..3b20a68fe7 100644 --- a/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj.filters +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorLibrary.vcxproj.filters @@ -1,96 +1,111 @@ - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - {7ccc5562-a9e1-4a3a-9f23-bdfee9ed5776} - cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx - - - {80d1fd84-2f25-463b-9fc7-ab7e7e9529c0} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {7bd580d1-f340-4817-9893-e5cbfd20cf54} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + \ No newline at end of file diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorStrings.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorStrings.cpp new file mode 100644 index 0000000000..e5bcc3eb1c --- /dev/null +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorStrings.cpp @@ -0,0 +1,50 @@ +#include "pch.h" +#include "KeyboardManagerEditorStrings.h" + +// Function to return the error message +winrt::hstring KeyboardManagerEditorStrings::GetErrorMessage(KeyboardManagerHelper::ErrorType errorType) +{ + using namespace KeyboardManagerHelper; + + switch (errorType) + { + case ErrorType::NoError: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_REMAPSUCCESSFUL).c_str(); + case ErrorType::SameKeyPreviouslyMapped: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SAMEKEYPREVIOUSLYMAPPED).c_str(); + case ErrorType::MapToSameKey: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_MAPPEDTOSAMEKEY).c_str(); + case ErrorType::ConflictingModifierKey: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_CONFLICTINGMODIFIERKEY).c_str(); + case ErrorType::SameShortcutPreviouslyMapped: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SAMESHORTCUTPREVIOUSLYMAPPED).c_str(); + case ErrorType::MapToSameShortcut: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_MAPTOSAMESHORTCUT).c_str(); + case ErrorType::ConflictingModifierShortcut: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_CONFLICTINGMODIFIERSHORTCUT).c_str(); + case ErrorType::WinL: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_WINL).c_str(); + case ErrorType::CtrlAltDel: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_CTRLALTDEL).c_str(); + case ErrorType::RemapUnsuccessful: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_REMAPUNSUCCESSFUL).c_str(); + case ErrorType::SaveFailed: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SAVEFAILED).c_str(); + case ErrorType::ShortcutStartWithModifier: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTSTARTWITHMODIFIER).c_str(); + case ErrorType::ShortcutCannotHaveRepeatedModifier: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTNOREPEATEDMODIFIER).c_str(); + case ErrorType::ShortcutAtleast2Keys: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTATLEAST2KEYS).c_str(); + case ErrorType::ShortcutOneActionKey: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTONEACTIONKEY).c_str(); + case ErrorType::ShortcutNotMoreThanOneActionKey: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTMAXONEACTIONKEY).c_str(); + case ErrorType::ShortcutMaxShortcutSizeOneActionKey: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_MAXSHORTCUTSIZE).c_str(); + case ErrorType::ShortcutDisableAsActionKey: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_DISABLEASACTIONKEY).c_str(); + default: + return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_DEFAULT).c_str(); + } +} diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorStrings.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorStrings.h new file mode 100644 index 0000000000..708ae051f4 --- /dev/null +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorStrings.h @@ -0,0 +1,14 @@ +#pragma once + +#include "pch.h" + +#include + +namespace KeyboardManagerEditorStrings +{ + // String constant for the default app name in Remap shortcuts + inline const std::wstring DefaultAppName = GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_ALLAPPS); + + // Function to return the error message + winrt::hstring GetErrorMessage(KeyboardManagerHelper::ErrorType errorType); +} \ No newline at end of file diff --git a/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/LoadingAndSavingRemappingHelper.cpp similarity index 98% rename from src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/LoadingAndSavingRemappingHelper.cpp index f6d4ca508b..06b244cb19 100644 --- a/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/LoadingAndSavingRemappingHelper.cpp @@ -1,9 +1,13 @@ #include "pch.h" #include "LoadingAndSavingRemappingHelper.h" + #include + #include -#include -#include +#include +#include + +#include namespace LoadingAndSavingRemappingHelper { @@ -40,6 +44,7 @@ namespace LoadingAndSavingRemappingHelper isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful; } } + return isSuccess; } @@ -173,6 +178,7 @@ namespace LoadingAndSavingRemappingHelper DWORD successfulOSLevelShortcutToKeyRemapCount = 0; DWORD successfulAppSpecificShortcutToShortcutRemapCount = 0; DWORD successfulAppSpecificShortcutToKeyRemapCount = 0; + // Save the shortcuts that are valid and report if any of them were invalid for (int i = 0; i < remappings.size(); i++) { diff --git a/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/LoadingAndSavingRemappingHelper.h similarity index 97% rename from src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.h rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/LoadingAndSavingRemappingHelper.h index d715145eae..d5beff5189 100644 --- a/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/LoadingAndSavingRemappingHelper.h @@ -1,7 +1,6 @@ #pragma once -#include + #include -#include class KeyboardManagerState; diff --git a/src/modules/keyboardmanager/ui/ShortcutControl.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp similarity index 92% rename from src/modules/keyboardmanager/ui/ShortcutControl.cpp rename to src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp index 0a5b13ff22..16e5d157e5 100644 --- a/src/modules/keyboardmanager/ui/ShortcutControl.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp @@ -1,477 +1,482 @@ -#include "pch.h" -#include "ShortcutControl.h" -#include "KeyDropDownControl.h" -#include "keyboardmanager/common/KeyboardManagerState.h" -#include "keyboardmanager/common/Helpers.h" -#include "keyboardmanager/dll/Generated Files/resource.h" -#include - -//Both static members are initialized to null -HWND ShortcutControl::EditShortcutsWindowHandle = nullptr; -KeyboardManagerState* ShortcutControl::keyboardManagerState = nullptr; -// Initialized as new vector -RemapBuffer ShortcutControl::shortcutRemapBuffer; - -ShortcutControl::ShortcutControl(StackPanel table, StackPanel row, const int colIndex, TextBox targetApp) -{ - shortcutDropDownStackPanel = StackPanel(); - typeShortcut = Button(); - shortcutControlLayout = StackPanel(); - bool isHybridControl = colIndex == 1 ? true : false; - - shortcutDropDownStackPanel.as().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing); - shortcutDropDownStackPanel.as().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal); - - typeShortcut.as