mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-06-07 09:28:03 +08:00
[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 <enricogior@users.noreply.github.com> Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com> Co-authored-by: Seraphima Zykova <zykovas91@gmail.com> Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com> Co-authored-by: Enrico Giordani <enrico.giordani@gmail.com>
This commit is contained in:
parent
e9a0b58796
commit
a8c99e9513
10
.github/actions/spell-check/expect.txt
vendored
10
.github/actions/spell-check/expect.txt
vendored
@ -5,6 +5,7 @@ abcdef
|
|||||||
abcdefgh
|
abcdefgh
|
||||||
abgr
|
abgr
|
||||||
ABlocked
|
ABlocked
|
||||||
|
ABOUTBOX
|
||||||
Abug
|
Abug
|
||||||
accctrl
|
accctrl
|
||||||
Acceleratorkeys
|
Acceleratorkeys
|
||||||
@ -118,6 +119,7 @@ atlstr
|
|||||||
attr
|
attr
|
||||||
Attribs
|
Attribs
|
||||||
aumid
|
aumid
|
||||||
|
Aut
|
||||||
AUTHN
|
AUTHN
|
||||||
AUTOAPPEND
|
AUTOAPPEND
|
||||||
autocomplete
|
autocomplete
|
||||||
@ -396,6 +398,7 @@ davidegiacometti
|
|||||||
Dayof
|
Dayof
|
||||||
dbdfc
|
dbdfc
|
||||||
Dbg
|
Dbg
|
||||||
|
Dbghelp
|
||||||
DBLCLKS
|
DBLCLKS
|
||||||
DBLEPSILON
|
DBLEPSILON
|
||||||
DCOM
|
DCOM
|
||||||
@ -1117,6 +1120,7 @@ KEYBDINPUT
|
|||||||
keyboardeventhandlers
|
keyboardeventhandlers
|
||||||
keyboardmanager
|
keyboardmanager
|
||||||
keyboardmanagercommon
|
keyboardmanagercommon
|
||||||
|
KEYBOARDMANAGEREDITOR
|
||||||
keyboardmanagerstate
|
keyboardmanagerstate
|
||||||
keyboardmanagerui
|
keyboardmanagerui
|
||||||
keycode
|
keycode
|
||||||
@ -1510,6 +1514,7 @@ oldnewthing
|
|||||||
oldpath
|
oldpath
|
||||||
oldtheme
|
oldtheme
|
||||||
oleaut
|
oleaut
|
||||||
|
OleAut
|
||||||
OLECHAR
|
OLECHAR
|
||||||
OLEDB
|
OLEDB
|
||||||
OLIVEGREEN
|
OLIVEGREEN
|
||||||
@ -1737,6 +1742,7 @@ readme
|
|||||||
READMODE
|
READMODE
|
||||||
readonly
|
readonly
|
||||||
READWRITE
|
READWRITE
|
||||||
|
REALTIME
|
||||||
RECTDESTINATION
|
RECTDESTINATION
|
||||||
RECTL
|
RECTL
|
||||||
rectp
|
rectp
|
||||||
@ -2008,6 +2014,7 @@ stdcall
|
|||||||
stdcpp
|
stdcpp
|
||||||
stdcpplatest
|
stdcpplatest
|
||||||
stdexcept
|
stdexcept
|
||||||
|
stdio
|
||||||
stdin
|
stdin
|
||||||
stdlib
|
stdlib
|
||||||
STDMETHODCALLTYPE
|
STDMETHODCALLTYPE
|
||||||
@ -2027,6 +2034,7 @@ Strikethrough
|
|||||||
Stringified
|
Stringified
|
||||||
stringify
|
stringify
|
||||||
STRINGIZE
|
STRINGIZE
|
||||||
|
stringstream
|
||||||
stringtable
|
stringtable
|
||||||
stringval
|
stringval
|
||||||
Strmiids
|
Strmiids
|
||||||
@ -2179,6 +2187,7 @@ Tz
|
|||||||
UAC
|
UAC
|
||||||
UAL
|
UAL
|
||||||
uap
|
uap
|
||||||
|
UCHAR
|
||||||
udit
|
udit
|
||||||
UIA
|
UIA
|
||||||
Uid
|
Uid
|
||||||
@ -2423,6 +2432,7 @@ wstr
|
|||||||
wstring
|
wstring
|
||||||
wstringstream
|
wstringstream
|
||||||
wsz
|
wsz
|
||||||
|
wtoi
|
||||||
WTS
|
WTS
|
||||||
WTSAT
|
WTSAT
|
||||||
wu
|
wu
|
||||||
|
@ -163,7 +163,8 @@ steps:
|
|||||||
configuration: '$(BuildConfiguration)'
|
configuration: '$(BuildConfiguration)'
|
||||||
testSelector: 'testAssemblies'
|
testSelector: 'testAssemblies'
|
||||||
testAssemblyVer2: |
|
testAssemblyVer2: |
|
||||||
**\KeyboardManagerTest.dll
|
**\KeyboardManagerEngineTest.dll
|
||||||
|
**\KeyboardManagerEditorTest.dll
|
||||||
**\UnitTests-CommonLib.dll
|
**\UnitTests-CommonLib.dll
|
||||||
**\PowerRenameUnitTests.dll
|
**\PowerRenameUnitTests.dll
|
||||||
**\powerpreviewTest.dll
|
**\powerpreviewTest.dll
|
||||||
|
@ -98,6 +98,8 @@ build:
|
|||||||
- 'modules\ImageResizer\ManagedTelemetry.dll'
|
- 'modules\ImageResizer\ManagedTelemetry.dll'
|
||||||
- 'modules\ImageResizer\Microsoft.PowerToys.Common.UI.dll'
|
- 'modules\ImageResizer\Microsoft.PowerToys.Common.UI.dll'
|
||||||
- 'modules\KeyboardManager\KeyboardManager.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\Microsoft.PowerToys.Settings.UI.Lib.dll'
|
||||||
- 'modules\launcher\ManagedCommon.dll'
|
- 'modules\launcher\ManagedCommon.dll'
|
||||||
- 'modules\launcher\Microsoft.PowerToys.Common.UI.dll'
|
- 'modules\launcher\Microsoft.PowerToys.Common.UI.dll'
|
||||||
|
@ -103,8 +103,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImageResizerExt", "src\modu
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageResizerUITest", "src\modules\imageresizer\tests\ImageResizerUITest.csproj", "{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageResizerUITest", "src\modules\imageresizer\tests\ImageResizerUITest.csproj", "{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}"
|
||||||
EndProject
|
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}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "action_runner", "src\action_runner\action_runner.vcxproj", "{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}"
|
||||||
ProjectSection(ProjectDependencies) = postProject
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {17DA04DF-E393-4397-9CF0-84DABE11032E}
|
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {17DA04DF-E393-4397-9CF0-84DABE11032E}
|
||||||
@ -210,8 +208,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerLauncher.Telemetry", "
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedTelemetry", "src\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj", "{5D00D290-4016-4CFE-9E41-1E7C724509BA}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedTelemetry", "src\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj", "{5D00D290-4016-4CFE-9E41-1E7C724509BA}"
|
||||||
EndProject
|
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}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCommon", "src\common\ManagedCommon\ManagedCommon.csproj", "{4AED67B6-55FD-486F-B917-E543DEE2CB3C}"
|
||||||
EndProject
|
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}"
|
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\appMutex.h = src\common\utils\appMutex.h
|
||||||
src\common\utils\com_object_factory.h = src\common\utils\com_object_factory.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\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\exec.h = src\common\utils\exec.h
|
||||||
src\common\utils\json.h = src\common\utils\json.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\logger_helper.h = src\common\utils\logger_helper.h
|
||||||
src\common\utils\os-detect.h = src\common\utils\os-detect.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\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\resources.h = src\common\utils\resources.h
|
||||||
src\common\utils\string_utils.h = src\common\utils\string_utils.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\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\winapi_error.h = src\common\utils\winapi_error.h
|
||||||
src\common\utils\window.h = src\common\utils\window.h
|
src\common\utils\window.h = src\common\utils\window.h
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
@ -312,6 +312,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plu
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerToys.Settings", "src\settings-ui\PowerToys.Settings\PowerToys.Settings.csproj", "{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerToys.Settings", "src\settings-ui\PowerToys.Settings\PowerToys.Settings.csproj", "{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}"
|
||||||
EndProject
|
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
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x64 = Debug|x64
|
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}.Debug|x64.Build.0 = Debug|x64
|
||||||
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x64.ActiveCfg = Release|x64
|
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x64.ActiveCfg = Release|x64
|
||||||
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x64.Build.0 = 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.ActiveCfg = Debug|x64
|
||||||
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x64.Build.0 = Debug|x64
|
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x64.Build.0 = Debug|x64
|
||||||
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x64.ActiveCfg = Release|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}.Debug|x64.Build.0 = Debug|x64
|
||||||
{5D00D290-4016-4CFE-9E41-1E7C724509BA}.Release|x64.ActiveCfg = Release|x64
|
{5D00D290-4016-4CFE-9E41-1E7C724509BA}.Release|x64.ActiveCfg = Release|x64
|
||||||
{5D00D290-4016-4CFE-9E41-1E7C724509BA}.Release|x64.Build.0 = 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.ActiveCfg = Debug|x64
|
||||||
{4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Debug|x64.Build.0 = Debug|x64
|
{4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Debug|x64.Build.0 = Debug|x64
|
||||||
{4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Release|x64.ActiveCfg = Release|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}.Debug|x64.Build.0 = Debug|x64
|
||||||
{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Release|x64.ActiveCfg = Release|x64
|
{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Release|x64.ActiveCfg = Release|x64
|
||||||
{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Release|x64.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -659,7 +687,6 @@ Global
|
|||||||
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34} = {6C7F47CC-2151-44A3-A546-41C70025132C}
|
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34} = {6C7F47CC-2151-44A3-A546-41C70025132C}
|
||||||
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B} = {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}
|
{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}
|
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||||
{38BDB927-829B-4C65-9CD9-93FB05D66D65} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
{38BDB927-829B-4C65-9CD9-93FB05D66D65} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||||
{8AFFA899-0B73-49EC-8C50-0FADDA57B2FC} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
|
{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}
|
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
|
||||||
{08C8C05F-0362-41BC-818C-724572DF8B06} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
|
{08C8C05F-0362-41BC-818C-724572DF8B06} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
|
||||||
{5D00D290-4016-4CFE-9E41-1E7C724509BA} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
{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}
|
{4AED67B6-55FD-486F-B917-E543DEE2CB3C} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||||
{42851751-CBC8-45A6-97F5-7A0753F7B4D1} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
|
{42851751-CBC8-45A6-97F5-7A0753F7B4D1} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
|
||||||
{1EF1EEF0-10F0-4F2E-8550-39B6D8044D3E} = {2F305555-C296-497E-AC20-5FA1B237996A}
|
{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}
|
{4BABF3FE-3451-42FD-873F-3C332E18DCEF} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
|
||||||
{0648DF05-5DDA-4BE1-B5F2-584926EBDB65} = {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}
|
{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
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||||
|
@ -212,7 +212,10 @@
|
|||||||
<Directory Id="ShortcutGuideInstallFolder" Name="$(var.ShortcutGuideProjectName)"/>
|
<Directory Id="ShortcutGuideInstallFolder" Name="$(var.ShortcutGuideProjectName)"/>
|
||||||
<Directory Id="FileExplorerPreviewInstallFolder" Name="FileExplorerPreview" />
|
<Directory Id="FileExplorerPreviewInstallFolder" Name="FileExplorerPreview" />
|
||||||
<Directory Id="FancyZonesInstallFolder" Name="$(var.FancyZonesProjectName)" />
|
<Directory Id="FancyZonesInstallFolder" Name="$(var.FancyZonesProjectName)" />
|
||||||
<Directory Id="KeyboardManagerInstallFolder" Name="$(var.KeyboardManagerProjectName)" />
|
<Directory Id="KeyboardManagerInstallFolder" Name="$(var.KeyboardManagerProjectName)">
|
||||||
|
<Directory Id="KeyboardManagerEditorInstallFolder" Name="KeyboardManagerEditor" />
|
||||||
|
<Directory Id="KeyboardManagerEngineInstallFolder" Name="KeyboardManagerEngine" />
|
||||||
|
</Directory>
|
||||||
<Directory Id="ColorPickerInstallFolder" Name="$(var.ColorPickerProjectName)">
|
<Directory Id="ColorPickerInstallFolder" Name="$(var.ColorPickerProjectName)">
|
||||||
<Directory Id="ColorPickerResourcesFolder" Name="Resources"/>
|
<Directory Id="ColorPickerResourcesFolder" Name="Resources"/>
|
||||||
</Directory>
|
</Directory>
|
||||||
@ -589,6 +592,18 @@
|
|||||||
</Component>
|
</Component>
|
||||||
</DirectoryRef>
|
</DirectoryRef>
|
||||||
|
|
||||||
|
<DirectoryRef Id="KeyboardManagerEditorInstallFolder" FileSource="$(var.BinX64Dir)modules\$(var.KeyboardManagerProjectName)\KeyboardManagerEditor">
|
||||||
|
<Component Id="Module_KeyboardManager_Editor" Guid="1240F1B8-17FE-4D68-B9AF-91882B0B1933" Win64="yes">
|
||||||
|
<File Source="$(var.BinX64Dir)modules\$(var.KeyboardManagerProjectName)\KeyboardManagerEditor\PowerToys.KeyboardManagerEditor.exe" />
|
||||||
|
</Component>
|
||||||
|
</DirectoryRef>
|
||||||
|
|
||||||
|
<DirectoryRef Id="KeyboardManagerEngineInstallFolder" FileSource="$(var.BinX64Dir)modules\$(var.KeyboardManagerProjectName)\KeyboardManagerEngine">
|
||||||
|
<Component Id="Module_KeyboardManager_Engine" Guid="14DBAA38-B98D-431F-9439-8EDE1C0670DB" Win64="yes">
|
||||||
|
<File Source="$(var.BinX64Dir)modules\$(var.KeyboardManagerProjectName)\KeyboardManagerEngine\PowerToys.KeyboardManagerEngine.exe" />
|
||||||
|
</Component>
|
||||||
|
</DirectoryRef>
|
||||||
|
|
||||||
<DirectoryRef Id="ColorPickerInstallFolder" FileSource="$(var.BinX64Dir)modules\$(var.ColorPickerProjectName)">
|
<DirectoryRef Id="ColorPickerInstallFolder" FileSource="$(var.BinX64Dir)modules\$(var.ColorPickerProjectName)">
|
||||||
<Component Id="Module_ColorPicker" Guid="8A52A69E-37B2-4BEA-9D73-77763066052F" Win64="yes">
|
<Component Id="Module_ColorPicker" Guid="8A52A69E-37B2-4BEA-9D73-77763066052F" Win64="yes">
|
||||||
<?foreach File in ColorPicker.dll;System.IO.Abstractions.dll;ColorPickerUI.exe;ColorPickerUI.dll;ColorPickerUI.deps.json;ColorPickerUI.runtimeconfig.json;Microsoft.PowerToys.Settings.UI.Lib.dll;PowerToysInterop.dll;System.Text.Json.dll;ManagedTelemetry.dll;ManagedCommon.dll;ControlzEx.dll;Microsoft.Xaml.Behaviors.dll;ModernWpf.Controls.dll;ModernWpf.dll;System.ComponentModel.Composition.dll;Microsoft.PowerToys.Common.UI.dll?>
|
<?foreach File in ColorPicker.dll;System.IO.Abstractions.dll;ColorPickerUI.exe;ColorPickerUI.dll;ColorPickerUI.deps.json;ColorPickerUI.runtimeconfig.json;Microsoft.PowerToys.Settings.UI.Lib.dll;PowerToysInterop.dll;System.Text.Json.dll;ManagedTelemetry.dll;ManagedCommon.dll;ControlzEx.dll;Microsoft.Xaml.Behaviors.dll;ModernWpf.Controls.dll;ModernWpf.dll;System.ComponentModel.Composition.dll;Microsoft.PowerToys.Common.UI.dll?>
|
||||||
@ -779,6 +794,8 @@
|
|||||||
<ComponentRef Id="Module_PowerPreview" />
|
<ComponentRef Id="Module_PowerPreview" />
|
||||||
<ComponentRef Id="Module_PowerPreview_PerUserRegistry" />
|
<ComponentRef Id="Module_PowerPreview_PerUserRegistry" />
|
||||||
<ComponentRef Id="Module_KeyboardManager" />
|
<ComponentRef Id="Module_KeyboardManager" />
|
||||||
|
<ComponentRef Id="Module_KeyboardManager_Editor" />
|
||||||
|
<ComponentRef Id="Module_KeyboardManager_Engine" />
|
||||||
<ComponentRef Id="Module_ColorPicker" />
|
<ComponentRef Id="Module_ColorPicker" />
|
||||||
<ComponentRef Id="Module_ColorPicker_Resources"/>
|
<ComponentRef Id="Module_ColorPicker_Resources"/>
|
||||||
<ComponentRef Id="SettingsV2" />
|
<ComponentRef Id="SettingsV2" />
|
||||||
|
@ -155,9 +155,5 @@ public
|
|||||||
static String ^ ShowShortcutGuideSharedEvent() {
|
static String ^ ShowShortcutGuideSharedEvent() {
|
||||||
return gcnew String(CommonSharedConstants::SHOW_SHORTCUT_GUIDE_SHARED_EVENT);
|
return gcnew String(CommonSharedConstants::SHOW_SHORTCUT_GUIDE_SHARED_EVENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static String ^ KeyboardManagerConfigFileMutexName() {
|
|
||||||
return gcnew String(CommonSharedConstants::KEYBOARD_MANAGER_CONFIG_FILE_MUTEX_NAME);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,4 @@ namespace CommonSharedConstants
|
|||||||
|
|
||||||
// Max DWORD for key code to disable keys.
|
// Max DWORD for key code to disable keys.
|
||||||
const int VK_DISABLED = 0x100;
|
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";
|
|
||||||
}
|
}
|
||||||
|
@ -55,4 +55,9 @@ public:
|
|||||||
{
|
{
|
||||||
logger->critical(fmt, args...);
|
logger->critical(fmt, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void flush()
|
||||||
|
{
|
||||||
|
logger->flush();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
61
src/common/utils/EventLocker.h
Normal file
61
src/common/utils/EventLocker.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class EventLocker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EventLocker(HANDLE h)
|
||||||
|
{
|
||||||
|
eventHandle = h;
|
||||||
|
SetEvent(eventHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::optional<EventLocker> 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;
|
||||||
|
};
|
78
src/common/utils/EventWaiter.h
Normal file
78
src/common/utils/EventWaiter.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
#include <string>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
class EventWaiter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EventWaiter() {}
|
||||||
|
EventWaiter(const std::wstring& name, std::function<void(DWORD)> 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;
|
||||||
|
};
|
32
src/common/utils/ProcessWaiter.h
Normal file
32
src/common/utils/ProcessWaiter.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace ProcessWaiter
|
||||||
|
{
|
||||||
|
void OnProcessTerminate(std::wstring parent_pid, std::function<void(DWORD)> 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();
|
||||||
|
}
|
||||||
|
}
|
238
src/common/utils/UnhandledExceptionHandler_x64.h
Normal file
238
src/common/utils/UnhandledExceptionHandler_x64.h
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
#include <Windows.h>
|
||||||
|
#include <DbgHelp.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#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");
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <common/version/version.h>
|
#include <common/version/version.h>
|
||||||
|
#include <common/SettingsAPI/settings_helpers.h>
|
||||||
|
|
||||||
namespace LoggerHelpers
|
namespace LoggerHelpers
|
||||||
{
|
{
|
||||||
@ -79,4 +80,20 @@ namespace LoggerHelpers
|
|||||||
|
|
||||||
return result;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,12 @@ inline std::optional<std::wstring> get_last_error_message(const DWORD dw)
|
|||||||
return message;
|
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)
|
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);
|
const auto system_message = get_last_error_message(dw);
|
||||||
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
@ -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"
|
@ -0,0 +1,217 @@
|
|||||||
|
// KeyboardManagerEditor.cpp : Defines the entry point for the application.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "pch.h"
|
||||||
|
#include "KeyboardManagerEditor.h"
|
||||||
|
|
||||||
|
#include <common/utils/winapi_error.h>
|
||||||
|
#include <common/utils/logger_helper.h>
|
||||||
|
#include <common/utils/UnhandledExceptionHandler_x64.h>
|
||||||
|
|
||||||
|
#include <trace.h>
|
||||||
|
|
||||||
|
#include <KeyboardEventHandlers.h>
|
||||||
|
#include <KeyboardManagerState.h>
|
||||||
|
#include <SettingsHelper.h>
|
||||||
|
|
||||||
|
#include <EditKeyboardWindow.h>
|
||||||
|
#include <EditShortcutsWindow.h>
|
||||||
|
#include <common/utils/ProcessWaiter.h>
|
||||||
|
|
||||||
|
std::unique_ptr<KeyboardManagerEditor> 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<KeyboardManagerEditorType>(_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<KeyboardManagerEditor>(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<KBDLLHOOKSTRUCT*>(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);
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings>
|
||||||
|
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- Required for C++ XAML Islands. More details at https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/host-standard-control-with-xaml-islands-cpp#create-a-desktop-application-project -->
|
||||||
|
<maxversiontested Id="10.0.18362.0"/>
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
</assembly>
|
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <KeyboardManagerState.h>
|
||||||
|
#include <Input.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
@ -0,0 +1,192 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
|
<!-- Project configurations -->
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<!-- Props that should be disabled while building on CI server -->
|
||||||
|
<ItemDefinitionGroup Condition="'$(CIBuild)'!='true'">
|
||||||
|
<ClCompile>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<!-- C++ source compile-specific things for all configurations -->
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<ConformanceMode>false</ConformanceMode>
|
||||||
|
<TreatWarningAsError>true</TreatWarningAsError>
|
||||||
|
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||||
|
<AdditionalOptions>/await %(AdditionalOptions)</AdditionalOptions>
|
||||||
|
<PreprocessorDefinitions>_UNICODE;UNICODE;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
</Link>
|
||||||
|
<Lib>
|
||||||
|
<TreatLibWarningAsErrors>true</TreatLibWarningAsErrors>
|
||||||
|
</Lib>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<!-- C++ source compile-specific things for Debug/Release configurations -->
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<SDLCheck>false</SDLCheck>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<!-- Global props -->
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>16.0</VCProjectVersion>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<ProjectGuid>{8df78b53-200e-451f-9328-01eb907193ae}</ProjectGuid>
|
||||||
|
<RootNamespace>KeyboardManagerEditor</RootNamespace>
|
||||||
|
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
|
||||||
|
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
|
||||||
|
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<!-- Props that are constant for both Debug and Release configurations -->
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||||
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\$(MSBuildProjectName)\</OutDir>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
<SpectreMitigation>Spectre</SpectreMitigation>
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<TargetName>PowerToys.$(MSBuildProjectName)</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<!-- Debug/Release props -->
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\modules\KeyboardManager\KeyboardManagerEditorLibrary\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;./../common;./../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<AdditionalDependencies>Display.lib;shcore.lib;Dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(ConfigurationName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\modules\KeyboardManager\KeyboardManagerEditorLibrary\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;./../common;./../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<AdditionalDependencies>Display.lib;shcore.lib;Dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(ConfigurationName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="Generated Files\resource.h" />
|
||||||
|
<ClInclude Include="KeyboardManagerEditor.h" />
|
||||||
|
<ClInclude Include="pch.h" />
|
||||||
|
<None Include="resource.base.h" />
|
||||||
|
<ClInclude Include="targetver.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="KeyboardManagerEditor.cpp" />
|
||||||
|
<ClCompile Include="pch.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||||
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ResourceCompile Include="Generated Files\KeyboardManagerEditor.rc" />
|
||||||
|
<None Include="KeyboardManagerEditor.base.rc" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\common\Display\Display.vcxproj">
|
||||||
|
<Project>{caba8dfb-823b-4bf2-93ac-3f31984150d9}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
|
||||||
|
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
|
||||||
|
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\common\KeyboardManagerCommon.vcxproj">
|
||||||
|
<Project>{8affa899-0b73-49ec-8c50-0fadda57b2fc}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\KeyboardManagerEditorLibrary\KeyboardManagerEditorLibrary.vcxproj">
|
||||||
|
<Project>{23d2070d-e4ad-4add-85a7-083d9c76ad49}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Manifest Include="KeyboardManagerEditor.exe.manifest" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Resources.resx" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Image Include="Keyboard.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
</Target>
|
||||||
|
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
|
||||||
|
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h KeyboardManagerEditor.base.rc KeyboardManagerEditor.rc" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
|
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files">
|
||||||
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
|
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Resource Files">
|
||||||
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="GeneratedFiles">
|
||||||
|
<UniqueIdentifier>{904807de-a4f6-4c65-9399-a9c244580ca4}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="targetver.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="KeyboardManagerEditor.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="pch.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="Generated Files\resource.h">
|
||||||
|
<Filter>GeneratedFiles</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="KeyboardManagerEditor.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="pch.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ResourceCompile Include="Generated Files\KeyboardManagerEditor.rc">
|
||||||
|
<Filter>GeneratedFiles</Filter>
|
||||||
|
</ResourceCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<None Include="Resources.resx">
|
||||||
|
<Filter>Resource Files</Filter>
|
||||||
|
</None>
|
||||||
|
<None Include="resource.base.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</None>
|
||||||
|
<None Include="KeyboardManagerEditor.base.rc">
|
||||||
|
<Filter>Resource Files</Filter>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Manifest Include="KeyboardManagerEditor.exe.manifest" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Image Include="Keyboard.ico">
|
||||||
|
<Filter>Resource Files</Filter>
|
||||||
|
</Image>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"Projects": [
|
||||||
|
{
|
||||||
|
"LanguageSet": "Azure_Languages",
|
||||||
|
"LocItems": [
|
||||||
|
{
|
||||||
|
"SourceFile": "src\\modules\\keyboardmanager\\KeyboardManagerEditor\\Resources.resx",
|
||||||
|
"CopyOption": "LangIDOnName",
|
||||||
|
"OutputPath": "src\\modules\\keyboardmanager\\KeyboardManagerEditor"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
24
src/modules/keyboardmanager/KeyboardManagerEditor/Resource.h
Normal file
24
src/modules/keyboardmanager/KeyboardManagerEditor/Resource.h
Normal file
@ -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
|
367
src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx
Normal file
367
src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="Settings_Description" xml:space="preserve">
|
||||||
|
<value>This feature requires Windows 10 version 1903 or higher</value>
|
||||||
|
</data>
|
||||||
|
<data name="KeyboardManager" xml:space="preserve">
|
||||||
|
<value>Keyboard Manager</value>
|
||||||
|
</data>
|
||||||
|
<data name="CreateWindowFailed_ErrorMessage" xml:space="preserve">
|
||||||
|
<value>Call to CreateWindow failed!</value>
|
||||||
|
</data>
|
||||||
|
<data name="CreateWindowFailed_ErrorTitle" xml:space="preserve">
|
||||||
|
<value>Error</value>
|
||||||
|
</data>
|
||||||
|
<data name="RegisterClassFailed_ErrorMessage" xml:space="preserve">
|
||||||
|
<value>Windows registration failed!</value>
|
||||||
|
<comment>This refers to an application window</comment>
|
||||||
|
</data>
|
||||||
|
<data name="RegisterClassFailed_ErrorTitle" xml:space="preserve">
|
||||||
|
<value>Error</value>
|
||||||
|
</data>
|
||||||
|
<data name="EditKeyboard_WindowName" xml:space="preserve">
|
||||||
|
<value>Remap keys</value>
|
||||||
|
</data>
|
||||||
|
<data name="EditShortcuts_WindowName" xml:space="preserve">
|
||||||
|
<value>Remap shortcuts</value>
|
||||||
|
</data>
|
||||||
|
<data name="Ok_Button" xml:space="preserve">
|
||||||
|
<value>OK</value>
|
||||||
|
</data>
|
||||||
|
<data name="Cancel_Button" xml:space="preserve">
|
||||||
|
<value>Cancel</value>
|
||||||
|
</data>
|
||||||
|
<data name="Continue_Button" xml:space="preserve">
|
||||||
|
<value>Continue Anyway</value>
|
||||||
|
</data>
|
||||||
|
<data name="EditKeyboard_SourceHeader" xml:space="preserve">
|
||||||
|
<value>Key:</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="EditKeyboard_TargetHeader" xml:space="preserve">
|
||||||
|
<value>Mapped To:</value>
|
||||||
|
</data>
|
||||||
|
<data name="EditShortcuts_SourceHeader" xml:space="preserve">
|
||||||
|
<value>Shortcut:</value>
|
||||||
|
</data>
|
||||||
|
<data name="EditShortcuts_TargetHeader" xml:space="preserve">
|
||||||
|
<value>Mapped To:</value>
|
||||||
|
</data>
|
||||||
|
<data name="EditShortcuts_TargetAppHeader" xml:space="preserve">
|
||||||
|
<value>Target App:</value>
|
||||||
|
</data>
|
||||||
|
<data name="EditKeyboard_OrphanedDialogTitle" xml:space="preserve">
|
||||||
|
<value>The following keys have been reassigned and you won't be able to use them for their original assignment:</value>
|
||||||
|
</data>
|
||||||
|
<data name="EditKeyboard_PartialConfirmationDialogTitle" xml:space="preserve">
|
||||||
|
<value>Some of the keys could not be remapped. Do you want to continue anyway?</value>
|
||||||
|
</data>
|
||||||
|
<data name="EditShortcuts_PartialConfirmationDialogTitle" xml:space="preserve">
|
||||||
|
<value>Some of the shortcuts could not be remapped. Do you want to continue anyway?</value>
|
||||||
|
</data>
|
||||||
|
<data name="EditKeyboard_Info" xml:space="preserve">
|
||||||
|
<value>Select the key you want to change (Key) and then the key or shortcut you want it to become (Mapped To).</value>
|
||||||
|
</data>
|
||||||
|
<data name="EditKeyboard_InfoExample" xml:space="preserve">
|
||||||
|
<value>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.</value>
|
||||||
|
</data>
|
||||||
|
<data name="EditShortcuts_Info" xml:space="preserve">
|
||||||
|
<value>Select the shortcut you want to change (Shortcut) and then the key or shortcut you want it to invoke (Mapped To).</value>
|
||||||
|
</data>
|
||||||
|
<data name="EditShortcuts_InfoExample" xml:space="preserve">
|
||||||
|
<value>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.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_RemapSuccessful" xml:space="preserve">
|
||||||
|
<value>Remapping successful</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_RemapUnsuccessful" xml:space="preserve">
|
||||||
|
<value>Some remappings were not applied</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_SameKeyPreviouslyMapped" xml:space="preserve">
|
||||||
|
<value>Cannot remap a key more than once</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_MappedToSameKey" xml:space="preserve">
|
||||||
|
<value>Cannot remap a key to itself</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_ConflictingModifierKey" xml:space="preserve">
|
||||||
|
<value>Cannot remap this key as it conflicts with another remapped key</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_SameShortcutPreviouslyMapped" xml:space="preserve">
|
||||||
|
<value>Cannot remap a shortcut more than once for the same target app</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_MapToSameShortcut" xml:space="preserve">
|
||||||
|
<value>Cannot remap a shortcut to itself</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_ConflictingModifierShortcut" xml:space="preserve">
|
||||||
|
<value>Cannot remap this shortcut as it conflicts with another remapped shortcut</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_WinL" xml:space="preserve">
|
||||||
|
<value>Cannot remap from/to Win L</value>
|
||||||
|
<comment>Win refers to Windows as in the OS</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_CtrlAltDel" xml:space="preserve">
|
||||||
|
<value>Cannot remap from/to Ctrl Alt Del</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_SaveFailed" xml:space="preserve">
|
||||||
|
<value>Failed to save the remappings</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_ShortcutStartWithModifier" xml:space="preserve">
|
||||||
|
<value>Shortcut must start with a modifier key</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_ShortcutNoRepeatedModifier" xml:space="preserve">
|
||||||
|
<value>Shortcut cannot contain a repeated modifier</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_ShortcutAtleast2Keys" xml:space="preserve">
|
||||||
|
<value>Shortcut must have atleast 2 keys</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_ShortcutOneActionKey" xml:space="preserve">
|
||||||
|
<value>Shortcut must contain an action key</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_ShortcutMaxOneActionKey" xml:space="preserve">
|
||||||
|
<value>Shortcut cannot have more than one action key</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_MaxShortcutSize" xml:space="preserve">
|
||||||
|
<value>Shortcuts can only have up to 2 modifier keys</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_Default" xml:space="preserve">
|
||||||
|
<value>Unexpected error</value>
|
||||||
|
</data>
|
||||||
|
<data name="Type_Button" xml:space="preserve">
|
||||||
|
<value>Type</value>
|
||||||
|
<comment>As in type a key</comment>
|
||||||
|
</data>
|
||||||
|
<data name="TypeKey_Title" xml:space="preserve">
|
||||||
|
<value>Press a key on selected keyboard:</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="TypeShortcut_Title" xml:space="preserve">
|
||||||
|
<value>Press the keys in shortcut:</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="TypeKey_Header" xml:space="preserve">
|
||||||
|
<value>Key Pressed:</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="TypeShortcut_Header" xml:space="preserve">
|
||||||
|
<value>Keys Pressed:</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Type_HoldEnter" xml:space="preserve">
|
||||||
|
<value>Hold Enter to continue</value>
|
||||||
|
</data>
|
||||||
|
<data name="Type_HoldEsc" xml:space="preserve">
|
||||||
|
<value>Hold Esc to discard</value>
|
||||||
|
</data>
|
||||||
|
<data name="EditShortcuts_AllApps" xml:space="preserve">
|
||||||
|
<value>All Apps</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_RemapSuccessful" xml:space="preserve">
|
||||||
|
<value>Remapping successful</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_RemapUnsuccessful" xml:space="preserve">
|
||||||
|
<value>Some remappings were not applied</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_SameKeyPreviouslyMapped" xml:space="preserve">
|
||||||
|
<value>Cannot remap a key more than once</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_MappedToSameKey" xml:space="preserve">
|
||||||
|
<value>Cannot remap a key to itself</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_ConflictingModifierKey" xml:space="preserve">
|
||||||
|
<value>Cannot remap this key as it conflicts with another remapped key</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_SameShortcutPreviouslyMapped" xml:space="preserve">
|
||||||
|
<value>Cannot remap a shortcut more than once for the same target app</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_MapToSameShortcut" xml:space="preserve">
|
||||||
|
<value>Cannot remap a shortcut to itself</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_ConflictingModifierShortcut" xml:space="preserve">
|
||||||
|
<value>Cannot remap this shortcut as it conflicts with another remapped shortcut</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_WinL" xml:space="preserve">
|
||||||
|
<value>Cannot remap from/to Win L</value>
|
||||||
|
<comment>Win refers to Windows as in the OS</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_CtrlAltDel" xml:space="preserve">
|
||||||
|
<value>Cannot remap from/to Ctrl Alt Del</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_SaveFailed" xml:space="preserve">
|
||||||
|
<value>Failed to save the remappings</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_ShortcutStartWithModifier" xml:space="preserve">
|
||||||
|
<value>Shortcut must start with a modifier key</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_ShortcutNoRepeatedModifier" xml:space="preserve">
|
||||||
|
<value>Shortcut cannot contain a repeated modifier</value>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_ShortcutAtleast2Keys" xml:space="preserve">
|
||||||
|
<value>Shortcut must have atleast 2 keys</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_ShortcutOneActionKey" xml:space="preserve">
|
||||||
|
<value>Shortcut must contain an action key</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_ShortcutMaxOneActionKey" xml:space="preserve">
|
||||||
|
<value>Shortcut cannot have more than one action key</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_MaxShortcutSize" xml:space="preserve">
|
||||||
|
<value>Shortcuts can only have up to 2 modifier keys</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="ErrorMessage_Default" xml:space="preserve">
|
||||||
|
<value>Unexpected error</value>
|
||||||
|
</data>
|
||||||
|
<data name="Key_DropDown_Combobox" xml:space="preserve">
|
||||||
|
<value>Key</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Add_Key_Remap_Button" xml:space="preserve">
|
||||||
|
<value>Add Key Remap</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
<data name="Add_Shortcut_Button" xml:space="preserve">
|
||||||
|
<value>Add Shortcut Remapping</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete_Remapping_Button" xml:space="preserve">
|
||||||
|
<value>Delete Remapping</value>
|
||||||
|
</data>
|
||||||
|
<data name="AutomationProperties_Row" xml:space="preserve">
|
||||||
|
<value>Row </value>
|
||||||
|
</data>
|
||||||
|
<data name="ERRORMESSAGE_DISABLEASACTIONKEY" xml:space="preserve">
|
||||||
|
<value>Disable can not be an action or a modifier key</value>
|
||||||
|
<comment>Key on a keyboard</comment>
|
||||||
|
</data>
|
||||||
|
</root>
|
@ -1,30 +1,36 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
// Do not define WIN32_LEAN_AND_MEAN as WinUI doesn't work when it is defined
|
|
||||||
#include <unknwn.h>
|
#include "targetver.h"
|
||||||
#include <windows.h>
|
|
||||||
#include <cstdlib>
|
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||||
#include <cstring>
|
#include <unknwn.h>
|
||||||
#include <thread>
|
#include <windows.h>
|
||||||
#include <winrt/Windows.system.h>
|
#include <shellapi.h>
|
||||||
#include <winrt/windows.ui.xaml.hosting.h>
|
|
||||||
#include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
|
#include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
|
||||||
#include <winrt/Windows.UI.Xaml.Automation.h>
|
|
||||||
#include <winrt/windows.ui.xaml.controls.h>
|
#include <winrt/Windows.Foundation.Collections.h>
|
||||||
#include <winrt/Windows.ui.xaml.media.h>
|
#include <winrt/Windows.UI.Core.h>
|
||||||
#include <winrt/Windows.Foundation.Collections.h>
|
#include <winrt/Windows.UI.Text.h>
|
||||||
#include "winrt/Windows.Foundation.h"
|
|
||||||
#include "winrt/Windows.Foundation.Numerics.h"
|
#pragma push_macro("GetCurrentTime")
|
||||||
#include "winrt/Windows.UI.Xaml.Controls.Primitives.h"
|
#undef GetCurrentTime
|
||||||
#include "winrt/Windows.UI.Text.h"
|
#include <winrt/Windows.UI.Xaml.Automation.h>
|
||||||
#include "winrt/Windows.UI.Core.h"
|
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
|
||||||
#include <winrt/Windows.UI.Xaml.Interop.h>
|
#include <winrt/Windows.UI.Xaml.Hosting.h>
|
||||||
#include <mutex>
|
#include <winrt/Windows.UI.Xaml.Interop.h>
|
||||||
#include <common/logger/logger.h>
|
#include <winrt/Windows.ui.xaml.media.h>
|
||||||
|
#pragma pop_macro("GetCurrentTime")
|
||||||
using namespace winrt;
|
|
||||||
using namespace Windows::UI;
|
#include <common/logger/logger.h>
|
||||||
using namespace Windows::UI::Composition;
|
#include <common/utils/resources.h>
|
||||||
using namespace Windows::UI::Xaml::Hosting;
|
|
||||||
using namespace Windows::Foundation::Numerics;
|
#include <Generated Files/resource.h>
|
||||||
using namespace Windows::UI::Xaml;
|
|
||||||
|
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;
|
using namespace Windows::UI::Xaml::Controls;
|
@ -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
|
||||||
|
//////////////////////////////
|
@ -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 <SDKDDKVer.h>
|
@ -1,8 +1,11 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "BufferValidationHelpers.h"
|
#include "BufferValidationHelpers.h"
|
||||||
#include <keyboardmanager/common/KeyboardManagerConstants.h>
|
|
||||||
#include <common/interop/shared_constants.h>
|
#include <common/interop/shared_constants.h>
|
||||||
#include <modules\keyboardmanager\ui\KeyDropDownControl.h>
|
|
||||||
|
#include <KeyboardManagerEditorStrings.h>
|
||||||
|
#include <KeyboardManagerConstants.h>
|
||||||
|
#include <KeyDropDownControl.h>
|
||||||
|
|
||||||
namespace BufferValidationHelpers
|
namespace BufferValidationHelpers
|
||||||
{
|
{
|
||||||
@ -82,9 +85,9 @@ namespace BufferValidationHelpers
|
|||||||
// warn and reset the drop down
|
// warn and reset the drop down
|
||||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
|
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
|
||||||
}
|
}
|
||||||
// If it is the last drop down
|
|
||||||
else if (dropDownIndex == dropDownCount - 1)
|
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 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)
|
if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode) && dropDownCount < KeyboardManagerConstants::MaxShortcutSize)
|
||||||
{
|
{
|
||||||
@ -94,21 +97,21 @@ namespace BufferValidationHelpers
|
|||||||
// warn and reset the drop down
|
// warn and reset the drop down
|
||||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier;
|
errorType = KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier;
|
||||||
}
|
}
|
||||||
// If not, add a new drop down
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// If not, add a new drop down
|
||||||
dropDownAction = BufferValidationHelpers::DropDownAction::AddDropDown;
|
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)
|
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
|
// warn and reset the drop down
|
||||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutMaxShortcutSizeOneActionKey;
|
errorType = KeyboardManagerHelper::ErrorType::ShortcutMaxShortcutSizeOneActionKey;
|
||||||
}
|
}
|
||||||
// If None is selected but it's the last index: warn
|
|
||||||
else if (selectedKeyCode == 0)
|
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 it is a hybrid control and there are 2 drop downs then deletion is allowed
|
||||||
if (isHybridControl && dropDownCount == KeyboardManagerConstants::MinShortcutSize)
|
if (isHybridControl && dropDownCount == KeyboardManagerConstants::MinShortcutSize)
|
||||||
{
|
{
|
||||||
@ -122,16 +125,16 @@ namespace BufferValidationHelpers
|
|||||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutOneActionKey;
|
errorType = KeyboardManagerHelper::ErrorType::ShortcutOneActionKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Disable can not be selected if one modifier key has already been selected
|
|
||||||
else if (selectedKeyCode == CommonSharedConstants::VK_DISABLED && dropDownIndex)
|
else if (selectedKeyCode == CommonSharedConstants::VK_DISABLED && dropDownIndex)
|
||||||
{
|
{
|
||||||
|
// Disable can not be selected if one modifier key has already been selected
|
||||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey;
|
errorType = KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey;
|
||||||
}
|
}
|
||||||
// If none of the above, then the action key will be set
|
// If none of the above, then the action key will be set
|
||||||
}
|
}
|
||||||
// If it is not the last drop down
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// If it is not the last drop down
|
||||||
if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode))
|
if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode))
|
||||||
{
|
{
|
||||||
// If it matched any of the previous modifiers then reset that drop down
|
// 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 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)
|
else if (selectedKeyCode == 0 && dropDownCount > KeyboardManagerConstants::MinShortcutSize)
|
||||||
{
|
{
|
||||||
|
// If None is selected and there are more than 2 drop downs
|
||||||
// set delete drop down flag
|
// set delete drop down flag
|
||||||
dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown;
|
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
|
// 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;
|
errorType = KeyboardManagerHelper::ErrorType::ShortcutAtleast2Keys;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Allow selection of VK_DISABLE only in first dropdown
|
|
||||||
else if (selectedKeyCode == CommonSharedConstants::VK_DISABLED && dropDownIndex)
|
else if (selectedKeyCode == CommonSharedConstants::VK_DISABLED && dropDownIndex)
|
||||||
{
|
{
|
||||||
|
// Allow selection of VK_DISABLE only in first dropdown
|
||||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey;
|
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)
|
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;
|
bool isClear = true;
|
||||||
for (int i = dropDownIndex + 1; i < (int)dropDownCount; i++)
|
for (int i = dropDownIndex + 1; i < (int)dropDownCount; i++)
|
||||||
{
|
{
|
||||||
@ -192,9 +196,9 @@ namespace BufferValidationHelpers
|
|||||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutNotMoreThanOneActionKey;
|
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
|
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
|
// warn and reset the drop down
|
||||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
|
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
|
||||||
}
|
}
|
||||||
@ -217,7 +221,7 @@ namespace BufferValidationHelpers
|
|||||||
|
|
||||||
// Convert app name to lower case
|
// Convert app name to lower case
|
||||||
std::transform(appName.begin(), appName.end(), appName.begin(), towlower);
|
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);
|
std::transform(lowercaseDefAppName.begin(), lowercaseDefAppName.end(), lowercaseDefAppName.begin(), towlower);
|
||||||
if (appName == lowercaseDefAppName)
|
if (appName == lowercaseDefAppName)
|
||||||
{
|
{
|
@ -1,8 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "keyboardmanager/common/Helpers.h"
|
|
||||||
#include <variant>
|
#include <keyboardmanager/common/Helpers.h>
|
||||||
#include <vector>
|
|
||||||
#include "keyboardmanager/common/Shortcut.h"
|
|
||||||
|
|
||||||
namespace BufferValidationHelpers
|
namespace BufferValidationHelpers
|
||||||
{
|
{
|
@ -1,8 +1,5 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "Dialog.h"
|
#include "Dialog.h"
|
||||||
#include <set>
|
|
||||||
#include "keyboardmanager/dll/Generated Files/resource.h"
|
|
||||||
#include <common/utils/resources.h>
|
|
||||||
|
|
||||||
using namespace winrt::Windows::Foundation;
|
using namespace winrt::Windows::Foundation;
|
||||||
|
|
@ -1,20 +1,25 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <common/Display/dpi_aware.h>
|
||||||
|
#include <common/interop/shared_constants.h>
|
||||||
|
#include <common/themes/windows_colors.h>
|
||||||
|
#include <common/utils/EventLocker.h>
|
||||||
|
|
||||||
|
#include <KeyboardManagerConstants.h>
|
||||||
|
#include <KeyboardManagerState.h>
|
||||||
|
|
||||||
#include "EditKeyboardWindow.h"
|
#include "EditKeyboardWindow.h"
|
||||||
|
#include "ErrorTypes.h"
|
||||||
#include "SingleKeyRemapControl.h"
|
#include "SingleKeyRemapControl.h"
|
||||||
#include "KeyDropDownControl.h"
|
#include "KeyDropDownControl.h"
|
||||||
#include "XamlBridge.h"
|
#include "XamlBridge.h"
|
||||||
#include <keyboardmanager/common/trace.h>
|
|
||||||
#include <keyboardmanager/common/KeyboardManagerConstants.h>
|
|
||||||
#include <set>
|
|
||||||
#include <common/themes/windows_colors.h>
|
|
||||||
#include <common/display/dpi_aware.h>
|
|
||||||
#include "Styles.h"
|
#include "Styles.h"
|
||||||
#include "Dialog.h"
|
#include "Dialog.h"
|
||||||
#include <keyboardmanager/dll/Generated Files/resource.h>
|
|
||||||
#include <common/interop/shared_constants.h>
|
|
||||||
#include "keyboardmanager/common/KeyboardManagerState.h"
|
|
||||||
#include "LoadingAndSavingRemappingHelper.h"
|
#include "LoadingAndSavingRemappingHelper.h"
|
||||||
#include "UIHelpers.h"
|
#include "UIHelpers.h"
|
||||||
|
#include <common/utils/winapi_error.h>
|
||||||
|
|
||||||
using namespace winrt::Windows::Foundation;
|
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.
|
// This Hwnd will be the window handler for the Xaml Island: A child window that contains Xaml.
|
||||||
HWND hWndXamlIslandEditKeyboardWindow = nullptr;
|
HWND hWndXamlIslandEditKeyboardWindow = nullptr;
|
||||||
|
|
||||||
// This variable is used to check if window registration has been done to avoid repeated registration leading to an error.
|
// This variable is used to check if window registration has been done to avoid repeated registration leading to an error.
|
||||||
bool isEditKeyboardWindowRegistrationCompleted = false;
|
bool isEditKeyboardWindowRegistrationCompleted = false;
|
||||||
|
|
||||||
// Holds the native window handle of EditKeyboard Window.
|
// Holds the native window handle of EditKeyboard Window.
|
||||||
HWND hwndEditKeyboardNativeWindow = nullptr;
|
HWND hwndEditKeyboardNativeWindow = nullptr;
|
||||||
std::mutex editKeyboardWindowMutex;
|
std::mutex editKeyboardWindowMutex;
|
||||||
|
|
||||||
// Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure
|
// Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure
|
||||||
static XamlBridge* xamlBridgePtr = nullptr;
|
static XamlBridge* xamlBridgePtr = nullptr;
|
||||||
|
|
||||||
@ -52,6 +60,7 @@ static IAsyncOperation<bool> OrphanKeysConfirmationDialog(
|
|||||||
orphanKeyString.append(state.keyboardMap.GetKeyName(k));
|
orphanKeyString.append(state.keyboardMap.GetKeyName(k));
|
||||||
orphanKeyString.append(L", ");
|
orphanKeyString.append(L", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
orphanKeyString = orphanKeyString.substr(0, max(0, orphanKeyString.length() - 2));
|
orphanKeyString = orphanKeyString.substr(0, max(0, orphanKeyString.length() - 2));
|
||||||
orphanKeysBlock.Text(winrt::hstring(orphanKeyString));
|
orphanKeysBlock.Text(winrt::hstring(orphanKeyString));
|
||||||
orphanKeysBlock.TextWrapping(TextWrapping::Wrap);
|
orphanKeysBlock.TextWrapping(TextWrapping::Wrap);
|
||||||
@ -84,13 +93,21 @@ static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, Xa
|
|||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyRemappings();
|
ApplyRemappings();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to create the Edit Keyboard Window
|
// 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
|
// Window Registration
|
||||||
const wchar_t szWindowClass[] = L"EditKeyboardWindow";
|
const wchar_t szWindowClass[] = L"EditKeyboardWindow";
|
||||||
@ -109,6 +126,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||||||
48,
|
48,
|
||||||
48,
|
48,
|
||||||
LR_DEFAULTCOLOR);
|
LR_DEFAULTCOLOR);
|
||||||
|
|
||||||
if (RegisterClassEx(&windowClass) == NULL)
|
if (RegisterClassEx(&windowClass) == NULL)
|
||||||
{
|
{
|
||||||
MessageBox(NULL, GET_RESOURCE_STRING(IDS_REGISTERCLASSFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_REGISTERCLASSFAILED_ERRORTITLE).c_str(), 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,
|
NULL,
|
||||||
hInst,
|
hInst,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
if (_hWndEditKeyboardWindow == NULL)
|
if (_hWndEditKeyboardWindow == NULL)
|
||||||
{
|
{
|
||||||
MessageBox(NULL, GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORTITLE).c_str(), NULL);
|
MessageBox(NULL, GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORTITLE).c_str(), NULL);
|
||||||
return;
|
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.
|
// 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)
|
if (_hWndEditKeyboardWindow)
|
||||||
{
|
{
|
||||||
@ -157,8 +177,10 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||||||
|
|
||||||
// Create the xaml bridge object
|
// Create the xaml bridge object
|
||||||
XamlBridge xamlBridge(_hWndEditKeyboardWindow);
|
XamlBridge xamlBridge(_hWndEditKeyboardWindow);
|
||||||
|
|
||||||
// DesktopSource needs to be declared before the RelativePanel xamlContainer object to avoid errors
|
// DesktopSource needs to be declared before the RelativePanel xamlContainer object to avoid errors
|
||||||
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource;
|
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource;
|
||||||
|
|
||||||
// Create the desktop window xaml source object and set its content
|
// Create the desktop window xaml source object and set its content
|
||||||
hWndXamlIslandEditKeyboardWindow = xamlBridge.InitDesktopWindowsXamlSource(desktopSource);
|
hWndXamlIslandEditKeyboardWindow = xamlBridge.InitDesktopWindowsXamlSource(desktopSource);
|
||||||
|
|
||||||
@ -205,7 +227,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||||||
TextBlock originalKeyRemapHeader;
|
TextBlock originalKeyRemapHeader;
|
||||||
originalKeyRemapHeader.Text(GET_RESOURCE_STRING(IDS_EDITKEYBOARD_SOURCEHEADER));
|
originalKeyRemapHeader.Text(GET_RESOURCE_STRING(IDS_EDITKEYBOARD_SOURCEHEADER));
|
||||||
originalKeyRemapHeader.FontWeight(Text::FontWeights::Bold());
|
originalKeyRemapHeader.FontWeight(Text::FontWeights::Bold());
|
||||||
StackPanel originalKeyHeaderContainer = KeyboardManagerHelper::GetWrapped(originalKeyRemapHeader, KeyboardManagerConstants::RemapTableDropDownWidth + KeyboardManagerConstants::TableArrowColWidth).as<StackPanel>();
|
StackPanel originalKeyHeaderContainer = UIHelpers::GetWrapped(originalKeyRemapHeader, KeyboardManagerConstants::RemapTableDropDownWidth + KeyboardManagerConstants::TableArrowColWidth).as<StackPanel>();
|
||||||
|
|
||||||
// Second header textblock in the header row of the keys remap table
|
// Second header textblock in the header row of the keys remap table
|
||||||
TextBlock newKeyRemapHeader;
|
TextBlock newKeyRemapHeader;
|
||||||
@ -220,11 +242,14 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||||||
|
|
||||||
// Store handle of edit keyboard window
|
// Store handle of edit keyboard window
|
||||||
SingleKeyRemapControl::EditKeyboardWindowHandle = _hWndEditKeyboardWindow;
|
SingleKeyRemapControl::EditKeyboardWindowHandle = _hWndEditKeyboardWindow;
|
||||||
|
|
||||||
// Store keyboard manager state
|
// Store keyboard manager state
|
||||||
SingleKeyRemapControl::keyboardManagerState = &keyboardManagerState;
|
SingleKeyRemapControl::keyboardManagerState = &keyboardManagerState;
|
||||||
KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
|
KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
|
||||||
|
|
||||||
// Clear the single key remap buffer
|
// Clear the single key remap buffer
|
||||||
SingleKeyRemapControl::singleKeyRemapBuffer.clear();
|
SingleKeyRemapControl::singleKeyRemapBuffer.clear();
|
||||||
|
|
||||||
// Vector to store dynamically allocated control objects to avoid early destruction
|
// Vector to store dynamically allocated control objects to avoid early destruction
|
||||||
std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>> keyboardRemapControlObjects;
|
std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>> keyboardRemapControlObjects;
|
||||||
|
|
||||||
@ -251,13 +276,8 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||||||
header.SetLeftOf(applyButton, cancelButton);
|
header.SetLeftOf(applyButton, cancelButton);
|
||||||
|
|
||||||
auto ApplyRemappings = [&keyboardManagerState, _hWndEditKeyboardWindow]() {
|
auto ApplyRemappings = [&keyboardManagerState, _hWndEditKeyboardWindow]() {
|
||||||
// Disable the remappings while the remapping table is updated
|
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(keyboardManagerState, SingleKeyRemapControl::singleKeyRemapBuffer, true);
|
||||||
keyboardManagerState.RemappingsDisabledWrapper(
|
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
||||||
[&keyboardManagerState]() {
|
|
||||||
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(keyboardManagerState, SingleKeyRemapControl::singleKeyRemapBuffer, true);
|
|
||||||
// Save the updated shortcuts remaps to file.
|
|
||||||
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
|
||||||
});
|
|
||||||
PostMessage(_hWndEditKeyboardWindow, WM_CLOSE, 0, 0);
|
PostMessage(_hWndEditKeyboardWindow, WM_CLOSE, 0, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -352,9 +372,40 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||||||
xamlBridge.ClearXamlIslands();
|
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)
|
LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
RECT rcClient;
|
RECT rcClient;
|
||||||
|
auto message = getMessage(messageCode);
|
||||||
|
if (message != L"")
|
||||||
|
{
|
||||||
|
Logger::trace(L"EditKeyboardWindowProc() messageCode={}", getMessage(messageCode));
|
||||||
|
}
|
||||||
|
|
||||||
switch (messageCode)
|
switch (messageCode)
|
||||||
{
|
{
|
||||||
// Resize the XAML window whenever the parent window is painted or resized
|
// 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);
|
PostQuitMessage(0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefWindowProc(hWnd, messageCode, wParam, lParam);
|
return DefWindowProc(hWnd, messageCode, wParam, lParam);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -406,6 +458,7 @@ bool CheckEditKeyboardWindowActive()
|
|||||||
{
|
{
|
||||||
ShowWindow(hwndEditKeyboardNativeWindow, SW_RESTORE);
|
ShowWindow(hwndEditKeyboardNativeWindow, SW_RESTORE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is an already existing window no need to create a new open bring it on foreground.
|
// If there is an already existing window no need to create a new open bring it on foreground.
|
||||||
SetForegroundWindow(hwndEditKeyboardNativeWindow);
|
SetForegroundWindow(hwndEditKeyboardNativeWindow);
|
||||||
result = true;
|
result = true;
|
||||||
@ -420,6 +473,7 @@ void CloseActiveEditKeyboardWindow()
|
|||||||
std::unique_lock<std::mutex> hwndLock(editKeyboardWindowMutex);
|
std::unique_lock<std::mutex> hwndLock(editKeyboardWindowMutex);
|
||||||
if (hwndEditKeyboardNativeWindow != nullptr)
|
if (hwndEditKeyboardNativeWindow != nullptr)
|
||||||
{
|
{
|
||||||
|
Logger::trace("CloseActiveEditKeyboardWindow()");
|
||||||
PostMessage(hwndEditKeyboardNativeWindow, WM_CLOSE, 0, 0);
|
PostMessage(hwndEditKeyboardNativeWindow, WM_CLOSE, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,11 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
class KeyboardManagerState;
|
class KeyboardManagerState;
|
||||||
|
|
||||||
// Function to create the Edit Keyboard Window
|
// Function to create the Edit Keyboard Window
|
||||||
void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState);
|
void CreateEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState);
|
||||||
|
|
||||||
// Function to check if there is already a window active if yes bring to foreground
|
// Function to check if there is already a window active if yes bring to foreground
|
||||||
bool CheckEditKeyboardWindowActive();
|
bool CheckEditKeyboardWindowActive();
|
||||||
|
|
||||||
// Function to close any active Edit Keyboard window
|
// Function to close any active Edit Keyboard window
|
||||||
void CloseActiveEditKeyboardWindow();
|
void CloseActiveEditKeyboardWindow();
|
@ -1,18 +1,20 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "EditShortcutsWindow.h"
|
#include "EditShortcutsWindow.h"
|
||||||
#include "ShortcutControl.h"
|
|
||||||
#include "KeyDropDownControl.h"
|
#include <common/Display/dpi_aware.h>
|
||||||
#include "XamlBridge.h"
|
#include <common/utils/EventLocker.h>
|
||||||
#include <keyboardmanager/common/trace.h>
|
|
||||||
#include <keyboardmanager/common/KeyboardManagerConstants.h>
|
#include <KeyboardManagerState.h>
|
||||||
#include <common/themes/windows_colors.h>
|
|
||||||
#include <common/display/dpi_aware.h>
|
#include <Dialog.h>
|
||||||
#include "Styles.h"
|
#include <ErrorTypes.h>
|
||||||
#include "Dialog.h"
|
#include <KeyDropDownControl.h>
|
||||||
#include <keyboardmanager/dll/Generated Files/resource.h>
|
#include <LoadingAndSavingRemappingHelper.h>
|
||||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
#include <ShortcutControl.h>
|
||||||
#include "LoadingAndSavingRemappingHelper.h"
|
#include <Styles.h>
|
||||||
#include "UIHelpers.h"
|
#include <UIHelpers.h>
|
||||||
|
#include <XamlBridge.h>
|
||||||
|
#include <common/utils/winapi_error.h>
|
||||||
|
|
||||||
using namespace winrt::Windows::Foundation;
|
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.
|
// This Hwnd will be the window handler for the Xaml Island: A child window that contains Xaml.
|
||||||
HWND hWndXamlIslandEditShortcutsWindow = nullptr;
|
HWND hWndXamlIslandEditShortcutsWindow = nullptr;
|
||||||
|
|
||||||
// This variable is used to check if window registration has been done to avoid repeated registration leading to an error.
|
// This variable is used to check if window registration has been done to avoid repeated registration leading to an error.
|
||||||
bool isEditShortcutsWindowRegistrationCompleted = false;
|
bool isEditShortcutsWindowRegistrationCompleted = false;
|
||||||
|
|
||||||
// Holds the native window handle of EditShortcuts Window.
|
// Holds the native window handle of EditShortcuts Window.
|
||||||
HWND hwndEditShortcutsNativeWindow = nullptr;
|
HWND hwndEditShortcutsNativeWindow = nullptr;
|
||||||
std::mutex editShortcutsWindowMutex;
|
std::mutex editShortcutsWindowMutex;
|
||||||
|
|
||||||
// Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure
|
// Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure
|
||||||
static XamlBridge* xamlBridgePtr = nullptr;
|
static XamlBridge* xamlBridgePtr = nullptr;
|
||||||
|
|
||||||
@ -46,9 +51,16 @@ static IAsyncAction OnClickAccept(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Function to create the Edit Shortcuts Window
|
// 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
|
// Window Registration
|
||||||
const wchar_t szWindowClass[] = L"EditShortcutsWindow";
|
const wchar_t szWindowClass[] = L"EditShortcutsWindow";
|
||||||
@ -98,11 +110,13 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
|||||||
NULL,
|
NULL,
|
||||||
hInst,
|
hInst,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
if (_hWndEditShortcutsWindow == NULL)
|
if (_hWndEditShortcutsWindow == NULL)
|
||||||
{
|
{
|
||||||
MessageBox(NULL, GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORTITLE).c_str(), NULL);
|
MessageBox(NULL, GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORTITLE).c_str(), NULL);
|
||||||
return;
|
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.
|
// 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)
|
if (_hWndEditShortcutsWindow)
|
||||||
{
|
{
|
||||||
@ -116,8 +130,10 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
|||||||
|
|
||||||
// Create the xaml bridge object
|
// Create the xaml bridge object
|
||||||
XamlBridge xamlBridge(_hWndEditShortcutsWindow);
|
XamlBridge xamlBridge(_hWndEditShortcutsWindow);
|
||||||
|
|
||||||
// DesktopSource needs to be declared before the RelativePanel xamlContainer object to avoid errors
|
// DesktopSource needs to be declared before the RelativePanel xamlContainer object to avoid errors
|
||||||
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource;
|
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource;
|
||||||
|
|
||||||
// Create the desktop window xaml source object and set its content
|
// Create the desktop window xaml source object and set its content
|
||||||
hWndXamlIslandEditShortcutsWindow = xamlBridge.InitDesktopWindowsXamlSource(desktopSource);
|
hWndXamlIslandEditShortcutsWindow = xamlBridge.InitDesktopWindowsXamlSource(desktopSource);
|
||||||
|
|
||||||
@ -179,19 +195,22 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
|||||||
StackPanel tableHeader = StackPanel();
|
StackPanel tableHeader = StackPanel();
|
||||||
tableHeader.Orientation(Orientation::Horizontal);
|
tableHeader.Orientation(Orientation::Horizontal);
|
||||||
tableHeader.Margin({ 10, 0, 0, 10 });
|
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<FrameworkElement>());
|
tableHeader.Children().Append(originalShortcutContainer.as<FrameworkElement>());
|
||||||
auto newShortcutHeaderContainer = KeyboardManagerHelper::GetWrapped(newShortcutHeader, KeyboardManagerConstants::ShortcutTargetColumnWidth);
|
auto newShortcutHeaderContainer = UIHelpers::GetWrapped(newShortcutHeader, KeyboardManagerConstants::ShortcutTargetColumnWidth);
|
||||||
tableHeader.Children().Append(newShortcutHeaderContainer.as<FrameworkElement>());
|
tableHeader.Children().Append(newShortcutHeaderContainer.as<FrameworkElement>());
|
||||||
tableHeader.Children().Append(targetAppHeader);
|
tableHeader.Children().Append(targetAppHeader);
|
||||||
|
|
||||||
// Store handle of edit shortcuts window
|
// Store handle of edit shortcuts window
|
||||||
ShortcutControl::EditShortcutsWindowHandle = _hWndEditShortcutsWindow;
|
ShortcutControl::editShortcutsWindowHandle = _hWndEditShortcutsWindow;
|
||||||
|
|
||||||
// Store keyboard manager state
|
// Store keyboard manager state
|
||||||
ShortcutControl::keyboardManagerState = &keyboardManagerState;
|
ShortcutControl::keyboardManagerState = &keyboardManagerState;
|
||||||
KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
|
KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
|
||||||
|
|
||||||
// Clear the shortcut remap buffer
|
// Clear the shortcut remap buffer
|
||||||
ShortcutControl::shortcutRemapBuffer.clear();
|
ShortcutControl::shortcutRemapBuffer.clear();
|
||||||
|
|
||||||
// Vector to store dynamically allocated control objects to avoid early destruction
|
// Vector to store dynamically allocated control objects to avoid early destruction
|
||||||
std::vector<std::vector<std::unique_ptr<ShortcutControl>>> keyboardRemapControlObjects;
|
std::vector<std::vector<std::unique_ptr<ShortcutControl>>> keyboardRemapControlObjects;
|
||||||
|
|
||||||
@ -231,13 +250,8 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
|||||||
header.SetLeftOf(applyButton, cancelButton);
|
header.SetLeftOf(applyButton, cancelButton);
|
||||||
|
|
||||||
auto ApplyRemappings = [&keyboardManagerState, _hWndEditShortcutsWindow]() {
|
auto ApplyRemappings = [&keyboardManagerState, _hWndEditShortcutsWindow]() {
|
||||||
// Disable the remappings while the remapping table is updated
|
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(keyboardManagerState, ShortcutControl::shortcutRemapBuffer, true);
|
||||||
keyboardManagerState.RemappingsDisabledWrapper(
|
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
||||||
[&keyboardManagerState]() {
|
|
||||||
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(keyboardManagerState, ShortcutControl::shortcutRemapBuffer, true);
|
|
||||||
// Save the updated key remaps to file.
|
|
||||||
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
|
||||||
});
|
|
||||||
PostMessage(_hWndEditShortcutsWindow, WM_CLOSE, 0, 0);
|
PostMessage(_hWndEditShortcutsWindow, WM_CLOSE, 0, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -332,6 +346,18 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
|||||||
xamlBridge.ClearXamlIslands();
|
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)
|
LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
RECT rcClient;
|
RECT rcClient;
|
@ -1,11 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
class KeyboardManagerState;
|
class KeyboardManagerState;
|
||||||
|
|
||||||
// Function to create the Edit Shortcuts Window
|
// Function to create the Edit Shortcuts Window
|
||||||
void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState);
|
void CreateEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState);
|
||||||
|
|
||||||
// Function to check if there is already a window active if yes bring to foreground
|
// Function to check if there is already a window active if yes bring to foreground
|
||||||
bool CheckEditShortcutsWindowActive();
|
bool CheckEditShortcutsWindowActive();
|
||||||
|
|
||||||
// Function to close any active Edit Shortcuts window
|
// Function to close any active Edit Shortcuts window
|
||||||
void CloseActiveEditShortcutsWindow();
|
void CloseActiveEditShortcutsWindow();
|
@ -1,11 +1,13 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "KeyDropDownControl.h"
|
#include "KeyDropDownControl.h"
|
||||||
#include "keyboardmanager/common/Helpers.h"
|
|
||||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
|
||||||
#include "BufferValidationHelpers.h"
|
|
||||||
#include <common/interop/shared_constants.h>
|
#include <common/interop/shared_constants.h>
|
||||||
#include <common/interop/keyboard_layout_impl.h>
|
#include <KeyboardManagerState.h>
|
||||||
#include <modules/keyboardmanager/common/Helpers.h>
|
|
||||||
|
#include <BufferValidationHelpers.h>
|
||||||
|
#include <KeyboardManagerEditorStrings.h>
|
||||||
|
#include <ErrorTypes.h>
|
||||||
|
#include <UIHelpers.h>
|
||||||
|
|
||||||
// Initialized to null
|
// Initialized to null
|
||||||
KeyboardManagerState* KeyDropDownControl::keyboardManagerState = nullptr;
|
KeyboardManagerState* KeyDropDownControl::keyboardManagerState = nullptr;
|
||||||
@ -15,7 +17,9 @@ DWORD KeyDropDownControl::GetSelectedValue(ComboBox comboBox)
|
|||||||
{
|
{
|
||||||
auto dataContext = comboBox.SelectedValue();
|
auto dataContext = comboBox.SelectedValue();
|
||||||
if (!dataContext)
|
if (!dataContext)
|
||||||
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
auto value = winrt::unbox_value<hstring>(dataContext);
|
auto value = winrt::unbox_value<hstring>(dataContext);
|
||||||
return stoi(std::wstring(value));
|
return stoi(std::wstring(value));
|
||||||
@ -53,11 +57,13 @@ void KeyDropDownControl::SetDefaultProperties(bool isShortcut, bool renderDisabl
|
|||||||
{
|
{
|
||||||
dropDown.as<ComboBox>().Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
|
dropDown.as<ComboBox>().Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
dropDown.as<ComboBox>().MaxDropDownHeight(KeyboardManagerConstants::TableDropDownHeight);
|
dropDown.as<ComboBox>().MaxDropDownHeight(KeyboardManagerConstants::TableDropDownHeight);
|
||||||
|
|
||||||
// Initialise layout attribute
|
// Initialise layout attribute
|
||||||
previousLayout = GetKeyboardLayout(0);
|
previousLayout = GetKeyboardLayout(0);
|
||||||
dropDown.as<ComboBox>().SelectedValuePath(L"DataContext");
|
dropDown.as<ComboBox>().SelectedValuePath(L"DataContext");
|
||||||
dropDown.as<ComboBox>().ItemsSource(KeyboardManagerHelper::ToBoxValue(GetKeyList(isShortcut, renderDisable)));
|
dropDown.as<ComboBox>().ItemsSource(UIHelpers::ToBoxValue(GetKeyList(isShortcut, renderDisable)));
|
||||||
|
|
||||||
// drop down open handler - to reload the items with the latest layout
|
// drop down open handler - to reload the items with the latest layout
|
||||||
dropDown.as<ComboBox>().DropDownOpened([&, isShortcut](winrt::Windows::Foundation::IInspectable const& sender, auto args) {
|
dropDown.as<ComboBox>().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)));
|
style.Setters().Append(Setter(Windows::UI::Xaml::Controls::Control::TabNavigationProperty(), winrt::box_value(Windows::UI::Xaml::Input::KeyboardNavigationMode::Cycle)));
|
||||||
warningFlyout.as<Flyout>().FlyoutPresenterStyle(style);
|
warningFlyout.as<Flyout>().FlyoutPresenterStyle(style);
|
||||||
dropDown.as<ComboBox>().ContextFlyout().SetAttachedFlyout((FrameworkElement)dropDown.as<ComboBox>(), warningFlyout.as<Flyout>());
|
dropDown.as<ComboBox>().ContextFlyout().SetAttachedFlyout((FrameworkElement)dropDown.as<ComboBox>(), warningFlyout.as<Flyout>());
|
||||||
|
|
||||||
// To set the accessible name of the combo-box (by default index 1)
|
// To set the accessible name of the combo-box (by default index 1)
|
||||||
SetAccessibleNameForComboBox(dropDown.as<ComboBox>(), 1);
|
SetAccessibleNameForComboBox(dropDown.as<ComboBox>(), 1);
|
||||||
}
|
}
|
||||||
@ -94,7 +101,7 @@ void KeyDropDownControl::CheckAndUpdateKeyboardLayout(ComboBox currentDropDown,
|
|||||||
// Check if the layout has changed
|
// Check if the layout has changed
|
||||||
if (previousLayout != layout)
|
if (previousLayout != layout)
|
||||||
{
|
{
|
||||||
currentDropDown.ItemsSource(KeyboardManagerHelper::ToBoxValue(GetKeyList(isShortcut, renderDisable)));
|
currentDropDown.ItemsSource(UIHelpers::ToBoxValue(GetKeyList(isShortcut, renderDisable)));
|
||||||
previousLayout = layout;
|
previousLayout = layout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,13 +119,14 @@ void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row,
|
|||||||
|
|
||||||
ComboBox currentDropDown = sender.as<ComboBox>();
|
ComboBox currentDropDown = sender.as<ComboBox>();
|
||||||
int selectedKeyCode = GetSelectedValue(currentDropDown);
|
int selectedKeyCode = GetSelectedValue(currentDropDown);
|
||||||
|
|
||||||
// Validate current remap selection
|
// Validate current remap selection
|
||||||
KeyboardManagerHelper::ErrorType errorType = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(rowIndex, colIndex, selectedKeyCode, singleKeyRemapBuffer);
|
KeyboardManagerHelper::ErrorType errorType = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(rowIndex, colIndex, selectedKeyCode, singleKeyRemapBuffer);
|
||||||
|
|
||||||
// If there is an error set the warning flyout
|
// If there is an error set the warning flyout
|
||||||
if (errorType != KeyboardManagerHelper::ErrorType::NoError)
|
if (errorType != KeyboardManagerHelper::ErrorType::NoError)
|
||||||
{
|
{
|
||||||
SetDropDownError(currentDropDown, KeyboardManagerHelper::GetErrorMessage(errorType));
|
SetDropDownError(currentDropDown, KeyboardManagerEditorStrings::GetErrorMessage(errorType));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -185,7 +193,7 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
|||||||
// If the remapping is invalid display an error message
|
// If the remapping is invalid display an error message
|
||||||
if (validationResult.first != KeyboardManagerHelper::ErrorType::NoError)
|
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
|
// Handle None case if there are no other errors
|
||||||
@ -199,6 +207,7 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
|||||||
}
|
}
|
||||||
|
|
||||||
parent.Children().RemoveAt(dropDownIndex);
|
parent.Children().RemoveAt(dropDownIndex);
|
||||||
|
|
||||||
// delete drop down control object from the vector so that it can be destructed
|
// delete drop down control object from the vector so that it can be destructed
|
||||||
keyDropDownControlObjects.erase(keyDropDownControlObjects.begin() + dropDownIndex);
|
keyDropDownControlObjects.erase(keyDropDownControlObjects.begin() + dropDownIndex);
|
||||||
parent.UpdateLayout();
|
parent.UpdateLayout();
|
||||||
@ -251,10 +260,11 @@ void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row,
|
|||||||
shortcutRemapBuffer[validationResult.second].first[colIndex] = tempShortcut;
|
shortcutRemapBuffer[validationResult.second].first[colIndex] = tempShortcut;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetApp != nullptr)
|
if (targetApp != nullptr)
|
||||||
{
|
{
|
||||||
std::wstring newText = targetApp.Text().c_str();
|
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(newText.begin(), newText.end(), newText.begin(), towlower);
|
||||||
std::transform(lowercaseDefAppName.begin(), lowercaseDefAppName.end(), lowercaseDefAppName.begin(), towlower);
|
std::transform(lowercaseDefAppName.begin(), lowercaseDefAppName.end(), lowercaseDefAppName.begin(), towlower);
|
||||||
if (newText == lowercaseDefAppName)
|
if (newText == lowercaseDefAppName)
|
||||||
@ -372,6 +382,7 @@ void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, StackPanel tabl
|
|||||||
{
|
{
|
||||||
// Delete the existing drop down menus
|
// Delete the existing drop down menus
|
||||||
parent.Children().Clear();
|
parent.Children().Clear();
|
||||||
|
|
||||||
// Remove references to the old drop down objects to destroy them
|
// Remove references to the old drop down objects to destroy them
|
||||||
keyDropDownControlObjects.clear();
|
keyDropDownControlObjects.clear();
|
||||||
std::vector<DWORD> shortcutKeyCodes = shortcut.GetKeyCodes();
|
std::vector<DWORD> shortcutKeyCodes = shortcut.GetKeyCodes();
|
||||||
@ -397,6 +408,7 @@ void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, StackPanel tabl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.UpdateLayout();
|
parent.UpdateLayout();
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <keyboardmanager/common/Shortcut.h>
|
|
||||||
#include <vector>
|
#include <Shortcut.h>
|
||||||
|
|
||||||
class KeyboardManagerState;
|
class KeyboardManagerState;
|
||||||
|
|
||||||
namespace winrt::Windows
|
namespace winrt::Windows
|
||||||
@ -30,12 +31,16 @@ class KeyDropDownControl
|
|||||||
private:
|
private:
|
||||||
// Stores the drop down combo box
|
// Stores the drop down combo box
|
||||||
winrt::Windows::Foundation::IInspectable dropDown;
|
winrt::Windows::Foundation::IInspectable dropDown;
|
||||||
|
|
||||||
// Stores the previous layout
|
// Stores the previous layout
|
||||||
HKL previousLayout = 0;
|
HKL previousLayout = 0;
|
||||||
|
|
||||||
// Stores the flyout warning message
|
// Stores the flyout warning message
|
||||||
winrt::Windows::Foundation::IInspectable warningMessage;
|
winrt::Windows::Foundation::IInspectable warningMessage;
|
||||||
|
|
||||||
// Stores the flyout attached to the current drop down
|
// Stores the flyout attached to the current drop down
|
||||||
winrt::Windows::Foundation::IInspectable warningFlyout;
|
winrt::Windows::Foundation::IInspectable warningFlyout;
|
||||||
|
|
||||||
// Stores whether a key to shortcut warning has to be ignored
|
// Stores whether a key to shortcut warning has to be ignored
|
||||||
bool ignoreKeyToShortcutWarning;
|
bool ignoreKeyToShortcutWarning;
|
||||||
|
|
||||||
@ -50,6 +55,7 @@ private:
|
|||||||
|
|
||||||
// Function to set accessible name for combobox
|
// Function to set accessible name for combobox
|
||||||
static void SetAccessibleNameForComboBox(ComboBox dropDown, int index);
|
static void SetAccessibleNameForComboBox(ComboBox dropDown, int index);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Pointer to the keyboard manager state
|
// Pointer to the keyboard manager state
|
||||||
static KeyboardManagerState* keyboardManagerState;
|
static KeyboardManagerState* keyboardManagerState;
|
@ -1,96 +1,102 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<VCProjectVersion>15.0</VCProjectVersion>
|
<VCProjectVersion>16.0</VCProjectVersion>
|
||||||
<ProjectGuid>{EAF23649-EF6E-478B-980E-81FAD96CCA2A}</ProjectGuid>
|
<Keyword>Win32Proj</Keyword>
|
||||||
<Keyword>Win32Proj</Keyword>
|
<ProjectGuid>{23d2070d-e4ad-4add-85a7-083d9c76ad49}</ProjectGuid>
|
||||||
<RootNamespace>KeyboardManagerUI</RootNamespace>
|
<RootNamespace>KeyboardManagerEditorLibrary</RootNamespace>
|
||||||
<CppWinRTEnabled>True</CppWinRTEnabled>
|
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
|
||||||
<ProjectName>KeyboardManagerUI</ProjectName>
|
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
|
||||||
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
|
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||||
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
</PropertyGroup>
|
||||||
</PropertyGroup>
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<PropertyGroup Label="Configuration">
|
||||||
<PropertyGroup Label="Configuration">
|
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\</OutDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
<ImportGroup Label="ExtensionSettings">
|
<ImportGroup Label="ExtensionSettings">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
<ImportGroup Label="Shared">
|
<ImportGroup Label="Shared">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<ImportGroup Label="PropertySheets">
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<PropertyGroup Label="UserMacros" />
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
<ItemDefinitionGroup>
|
||||||
</ImportGroup>
|
<ClCompile>
|
||||||
<PropertyGroup Label="UserMacros" />
|
<WarningLevel>Level3</WarningLevel>
|
||||||
<PropertyGroup>
|
<AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;./../common;./../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\</OutDir>
|
<SDLCheck>true</SDLCheck>
|
||||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
<PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
</PropertyGroup>
|
</ClCompile>
|
||||||
<ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ClCompile>
|
<ItemGroup>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<ClInclude Include="BufferValidationHelpers.h" />
|
||||||
<SpectreMitigation>Spectre</SpectreMitigation>
|
<ClInclude Include="Dialog.h" />
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ClInclude Include="EditKeyboardWindow.h" />
|
||||||
<DisableSpecificWarnings>4002</DisableSpecificWarnings>
|
<ClInclude Include="EditShortcutsWindow.h" />
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<ClInclude Include="KeyboardManagerEditorStrings.h" />
|
||||||
</ClCompile>
|
<ClInclude Include="KeyDropDownControl.h" />
|
||||||
<Link>
|
<ClInclude Include="LoadingAndSavingRemappingHelper.h" />
|
||||||
<AdditionalDependencies>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)</AdditionalDependencies>
|
<ClInclude Include="pch.h" />
|
||||||
</Link>
|
<ClInclude Include="ShortcutControl.h" />
|
||||||
</ItemDefinitionGroup>
|
<ClInclude Include="SingleKeyRemapControl.h" />
|
||||||
<ItemGroup>
|
<ClInclude Include="Styles.h" />
|
||||||
<ClCompile Include="BufferValidationHelpers.cpp" />
|
<ClInclude Include="targetver.h" />
|
||||||
<ClCompile Include="Dialog.cpp" />
|
<ClInclude Include="trace.h" />
|
||||||
<ClCompile Include="EditKeyboardWindow.cpp" />
|
<ClInclude Include="UIHelpers.h" />
|
||||||
<ClCompile Include="EditShortcutsWindow.cpp" />
|
<ClInclude Include="XamlBridge.h" />
|
||||||
<ClCompile Include="KeyDropDownControl.cpp" />
|
</ItemGroup>
|
||||||
<ClCompile Include="LoadingAndSavingRemappingHelper.cpp" />
|
<ItemGroup>
|
||||||
<ClCompile Include="pch.cpp">
|
<ClCompile Include="BufferValidationHelpers.cpp" />
|
||||||
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
|
<ClCompile Include="Dialog.cpp" />
|
||||||
</ClCompile>
|
<ClCompile Include="EditKeyboardWindow.cpp" />
|
||||||
<ClCompile Include="ShortcutControl.cpp" />
|
<ClCompile Include="EditShortcutsWindow.cpp" />
|
||||||
<ClCompile Include="SingleKeyRemapControl.cpp" />
|
<ClCompile Include="KeyboardManagerEditorStrings.cpp" />
|
||||||
<ClCompile Include="Styles.cpp" />
|
<ClCompile Include="KeyDropDownControl.cpp" />
|
||||||
<ClCompile Include="UIHelpers.cpp" />
|
<ClCompile Include="LoadingAndSavingRemappingHelper.cpp" />
|
||||||
<ClCompile Include="XamlBridge.cpp" />
|
<ClCompile Include="pch.cpp">
|
||||||
</ItemGroup>
|
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||||
<ItemGroup>
|
</ClCompile>
|
||||||
<ClInclude Include="BufferValidationHelpers.h" />
|
<ClCompile Include="ShortcutControl.cpp" />
|
||||||
<ClInclude Include="Dialog.h" />
|
<ClCompile Include="SingleKeyRemapControl.cpp" />
|
||||||
<ClInclude Include="EditKeyboardWindow.h" />
|
<ClCompile Include="Styles.cpp" />
|
||||||
<ClInclude Include="EditShortcutsWindow.h" />
|
<ClCompile Include="trace.cpp" />
|
||||||
<ClInclude Include="LoadingAndSavingRemappingHelper.h" />
|
<ClCompile Include="UIHelpers.cpp" />
|
||||||
<ClInclude Include="Styles.h" />
|
<ClCompile Include="XamlBridge.cpp" />
|
||||||
<ClInclude Include="KeyDropDownControl.h" />
|
</ItemGroup>
|
||||||
<ClInclude Include="pch.h" />
|
<ItemGroup>
|
||||||
<ClInclude Include="ShortcutControl.h" />
|
<None Include="packages.config" />
|
||||||
<ClInclude Include="SingleKeyRemapControl.h" />
|
</ItemGroup>
|
||||||
<ClInclude Include="UIHelpers.h" />
|
<ItemGroup>
|
||||||
<ClInclude Include="XamlBridge.h" />
|
<ProjectReference Include="..\..\..\common\Display\Display.vcxproj">
|
||||||
</ItemGroup>
|
<Project>{caba8dfb-823b-4bf2-93ac-3f31984150d9}</Project>
|
||||||
<ItemGroup>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
|
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
|
||||||
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
|
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
|
||||||
<ItemGroup>
|
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
|
||||||
<None Include="packages.config" />
|
</ProjectReference>
|
||||||
</ItemGroup>
|
<ProjectReference Include="..\common\KeyboardManagerCommon.vcxproj">
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Project>{8affa899-0b73-49ec-8c50-0fadda57b2fc}</Project>
|
||||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
</ProjectReference>
|
||||||
<ImportGroup Label="ExtensionTargets">
|
</ItemGroup>
|
||||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
</ImportGroup>
|
<ImportGroup Label="ExtensionTargets">
|
||||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
<PropertyGroup>
|
</ImportGroup>
|
||||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||||
</PropertyGroup>
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
<PropertyGroup>
|
||||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
</Target>
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
</Target>
|
||||||
|
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
|
||||||
|
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory)\..\KeyboardManagerEditor\ resource.base.h resource.h KeyboardManagerEditor.base.rc KeyboardManagerEditor.rc" />
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
@ -1,96 +1,111 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="Dialog.cpp">
|
<Filter Include="Source Files">
|
||||||
<Filter>Source Files</Filter>
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
</ClCompile>
|
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
<ClCompile Include="EditKeyboardWindow.cpp">
|
</Filter>
|
||||||
<Filter>Source Files</Filter>
|
<Filter Include="Header Files">
|
||||||
</ClCompile>
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
<ClCompile Include="EditShortcutsWindow.cpp">
|
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||||
<Filter>Source Files</Filter>
|
</Filter>
|
||||||
</ClCompile>
|
<Filter Include="Resource Files">
|
||||||
<ClCompile Include="KeyDropDownControl.cpp">
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
<Filter>Source Files</Filter>
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
</ClCompile>
|
</Filter>
|
||||||
<ClCompile Include="pch.cpp">
|
</ItemGroup>
|
||||||
<Filter>Source Files</Filter>
|
<ItemGroup>
|
||||||
</ClCompile>
|
<ClInclude Include="pch.h">
|
||||||
<ClCompile Include="ShortcutControl.cpp">
|
<Filter>Header Files</Filter>
|
||||||
<Filter>Source Files</Filter>
|
</ClInclude>
|
||||||
</ClCompile>
|
<ClInclude Include="BufferValidationHelpers.h">
|
||||||
<ClCompile Include="SingleKeyRemapControl.cpp">
|
<Filter>Header Files</Filter>
|
||||||
<Filter>Source Files</Filter>
|
</ClInclude>
|
||||||
</ClCompile>
|
<ClInclude Include="Dialog.h">
|
||||||
<ClCompile Include="Styles.cpp">
|
<Filter>Header Files</Filter>
|
||||||
<Filter>Source Files</Filter>
|
</ClInclude>
|
||||||
</ClCompile>
|
<ClInclude Include="EditKeyboardWindow.h">
|
||||||
<ClCompile Include="XamlBridge.cpp">
|
<Filter>Header Files</Filter>
|
||||||
<Filter>Source Files</Filter>
|
</ClInclude>
|
||||||
</ClCompile>
|
<ClInclude Include="EditShortcutsWindow.h">
|
||||||
<ClCompile Include="LoadingAndSavingRemappingHelper.cpp">
|
<Filter>Header Files</Filter>
|
||||||
<Filter>Source Files</Filter>
|
</ClInclude>
|
||||||
</ClCompile>
|
<ClInclude Include="KeyDropDownControl.h">
|
||||||
<ClCompile Include="BufferValidationHelpers.cpp">
|
<Filter>Header Files</Filter>
|
||||||
<Filter>Source Files</Filter>
|
</ClInclude>
|
||||||
</ClCompile>
|
<ClInclude Include="LoadingAndSavingRemappingHelper.h">
|
||||||
<ClCompile Include="UIHelpers.cpp">
|
<Filter>Header Files</Filter>
|
||||||
<Filter>Source Files</Filter>
|
</ClInclude>
|
||||||
</ClCompile>
|
<ClInclude Include="ShortcutControl.h">
|
||||||
</ItemGroup>
|
<Filter>Header Files</Filter>
|
||||||
<ItemGroup>
|
</ClInclude>
|
||||||
<ClInclude Include="Dialog.h">
|
<ClInclude Include="SingleKeyRemapControl.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="EditKeyboardWindow.h">
|
<ClInclude Include="Styles.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="EditShortcutsWindow.h">
|
<ClInclude Include="targetver.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="KeyDropDownControl.h">
|
<ClInclude Include="UIHelpers.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="ShortcutControl.h">
|
<ClInclude Include="XamlBridge.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="SingleKeyRemapControl.h">
|
<ClInclude Include="KeyboardManagerEditorStrings.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="Styles.h">
|
<ClInclude Include="trace.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="XamlBridge.h">
|
</ItemGroup>
|
||||||
<Filter>Header Files</Filter>
|
<ItemGroup>
|
||||||
</ClInclude>
|
<ClCompile Include="pch.cpp">
|
||||||
<ClInclude Include="pch.h">
|
<Filter>Source Files</Filter>
|
||||||
<Filter>Header Files</Filter>
|
</ClCompile>
|
||||||
</ClInclude>
|
<ClCompile Include="BufferValidationHelpers.cpp">
|
||||||
<ClInclude Include="LoadingAndSavingRemappingHelper.h">
|
<Filter>Source Files</Filter>
|
||||||
<Filter>Header Files</Filter>
|
</ClCompile>
|
||||||
</ClInclude>
|
<ClCompile Include="Dialog.cpp">
|
||||||
<ClInclude Include="BufferValidationHelpers.h">
|
<Filter>Source Files</Filter>
|
||||||
<Filter>Header Files</Filter>
|
</ClCompile>
|
||||||
</ClInclude>
|
<ClCompile Include="EditKeyboardWindow.cpp">
|
||||||
<ClInclude Include="UIHelpers.h">
|
<Filter>Source Files</Filter>
|
||||||
<Filter>Header Files</Filter>
|
</ClCompile>
|
||||||
</ClInclude>
|
<ClCompile Include="EditShortcutsWindow.cpp">
|
||||||
</ItemGroup>
|
<Filter>Source Files</Filter>
|
||||||
<ItemGroup>
|
</ClCompile>
|
||||||
<Filter Include="Source Files">
|
<ClCompile Include="KeyDropDownControl.cpp">
|
||||||
<UniqueIdentifier>{7ccc5562-a9e1-4a3a-9f23-bdfee9ed5776}</UniqueIdentifier>
|
<Filter>Source Files</Filter>
|
||||||
<Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
</ClCompile>
|
||||||
</Filter>
|
<ClCompile Include="LoadingAndSavingRemappingHelper.cpp">
|
||||||
<Filter Include="Header Files">
|
<Filter>Source Files</Filter>
|
||||||
<UniqueIdentifier>{80d1fd84-2f25-463b-9fc7-ab7e7e9529c0}</UniqueIdentifier>
|
</ClCompile>
|
||||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
<ClCompile Include="ShortcutControl.cpp">
|
||||||
</Filter>
|
<Filter>Source Files</Filter>
|
||||||
<Filter Include="Resource Files">
|
</ClCompile>
|
||||||
<UniqueIdentifier>{7bd580d1-f340-4817-9893-e5cbfd20cf54}</UniqueIdentifier>
|
<ClCompile Include="SingleKeyRemapControl.cpp">
|
||||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
<Filter>Source Files</Filter>
|
||||||
</Filter>
|
</ClCompile>
|
||||||
</ItemGroup>
|
<ClCompile Include="Styles.cpp">
|
||||||
<ItemGroup>
|
<Filter>Source Files</Filter>
|
||||||
<None Include="packages.config" />
|
</ClCompile>
|
||||||
</ItemGroup>
|
<ClCompile Include="UIHelpers.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="XamlBridge.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="KeyboardManagerEditorStrings.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="trace.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include <ErrorTypes.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
@ -1,9 +1,13 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "LoadingAndSavingRemappingHelper.h"
|
#include "LoadingAndSavingRemappingHelper.h"
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include <common/interop/shared_constants.h>
|
#include <common/interop/shared_constants.h>
|
||||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
#include <ErrorTypes.h>
|
||||||
#include <keyboardmanager/common/trace.h>
|
#include <KeyboardManagerState.h>
|
||||||
|
|
||||||
|
#include <keyboardmanager/KeyboardManagerEditorLibrary/trace.h>
|
||||||
|
|
||||||
namespace LoadingAndSavingRemappingHelper
|
namespace LoadingAndSavingRemappingHelper
|
||||||
{
|
{
|
||||||
@ -40,6 +44,7 @@ namespace LoadingAndSavingRemappingHelper
|
|||||||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return isSuccess;
|
return isSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,6 +178,7 @@ namespace LoadingAndSavingRemappingHelper
|
|||||||
DWORD successfulOSLevelShortcutToKeyRemapCount = 0;
|
DWORD successfulOSLevelShortcutToKeyRemapCount = 0;
|
||||||
DWORD successfulAppSpecificShortcutToShortcutRemapCount = 0;
|
DWORD successfulAppSpecificShortcutToShortcutRemapCount = 0;
|
||||||
DWORD successfulAppSpecificShortcutToKeyRemapCount = 0;
|
DWORD successfulAppSpecificShortcutToKeyRemapCount = 0;
|
||||||
|
|
||||||
// Save the shortcuts that are valid and report if any of them were invalid
|
// Save the shortcuts that are valid and report if any of them were invalid
|
||||||
for (int i = 0; i < remappings.size(); i++)
|
for (int i = 0; i < remappings.size(); i++)
|
||||||
{
|
{
|
@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <vector>
|
|
||||||
#include <keyboardmanager/common/Helpers.h>
|
#include <keyboardmanager/common/Helpers.h>
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
class KeyboardManagerState;
|
class KeyboardManagerState;
|
||||||
|
|
@ -1,477 +1,482 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "ShortcutControl.h"
|
#include "ShortcutControl.h"
|
||||||
#include "KeyDropDownControl.h"
|
|
||||||
#include "keyboardmanager/common/KeyboardManagerState.h"
|
#include <common/interop/shared_constants.h>
|
||||||
#include "keyboardmanager/common/Helpers.h"
|
|
||||||
#include "keyboardmanager/dll/Generated Files/resource.h"
|
#include <KeyboardManagerState.h>
|
||||||
#include <common/interop/shared_constants.h>
|
|
||||||
|
#include <KeyboardManagerEditorStrings.h>
|
||||||
//Both static members are initialized to null
|
#include <KeyDropDownControl.h>
|
||||||
HWND ShortcutControl::EditShortcutsWindowHandle = nullptr;
|
#include <UIHelpers.h>
|
||||||
KeyboardManagerState* ShortcutControl::keyboardManagerState = nullptr;
|
|
||||||
// Initialized as new vector
|
//Both static members are initialized to null
|
||||||
RemapBuffer ShortcutControl::shortcutRemapBuffer;
|
HWND ShortcutControl::editShortcutsWindowHandle = nullptr;
|
||||||
|
KeyboardManagerState* ShortcutControl::keyboardManagerState = nullptr;
|
||||||
ShortcutControl::ShortcutControl(StackPanel table, StackPanel row, const int colIndex, TextBox targetApp)
|
// Initialized as new vector
|
||||||
{
|
RemapBuffer ShortcutControl::shortcutRemapBuffer;
|
||||||
shortcutDropDownStackPanel = StackPanel();
|
|
||||||
typeShortcut = Button();
|
ShortcutControl::ShortcutControl(StackPanel table, StackPanel row, const int colIndex, TextBox targetApp)
|
||||||
shortcutControlLayout = StackPanel();
|
{
|
||||||
bool isHybridControl = colIndex == 1 ? true : false;
|
shortcutDropDownStackPanel = StackPanel();
|
||||||
|
typeShortcut = Button();
|
||||||
shortcutDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
shortcutControlLayout = StackPanel();
|
||||||
shortcutDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
bool isHybridControl = colIndex == 1 ? true : false;
|
||||||
|
|
||||||
typeShortcut.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
shortcutDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||||
typeShortcut.as<Button>().Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
|
shortcutDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||||
typeShortcut.as<Button>().Click([&, table, row, colIndex, isHybridControl, targetApp](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
|
||||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowActivated, EditShortcutsWindowHandle);
|
typeShortcut.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
||||||
// Using the XamlRoot of the typeShortcut to get the root of the XAML host
|
typeShortcut.as<Button>().Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
|
||||||
createDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, targetApp, isHybridControl, false, EditShortcutsWindowHandle, shortcutRemapBuffer);
|
typeShortcut.as<Button>().Click([&, table, row, colIndex, isHybridControl, targetApp](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||||
});
|
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowActivated, editShortcutsWindowHandle);
|
||||||
// Set an accessible name for the type shortcut button
|
// Using the XamlRoot of the typeShortcut to get the root of the XAML host
|
||||||
typeShortcut.as<Button>().SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
CreateDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, targetApp, isHybridControl, false, editShortcutsWindowHandle, shortcutRemapBuffer);
|
||||||
|
});
|
||||||
shortcutControlLayout.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
|
||||||
|
// Set an accessible name for the type shortcut button
|
||||||
shortcutControlLayout.as<StackPanel>().Children().Append(typeShortcut.as<Button>());
|
typeShortcut.as<Button>().SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
||||||
shortcutControlLayout.as<StackPanel>().Children().Append(shortcutDropDownStackPanel.as<StackPanel>());
|
|
||||||
KeyDropDownControl::AddDropDown(table, row, shortcutDropDownStackPanel.as<StackPanel>(), colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, false);
|
shortcutControlLayout.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||||
shortcutControlLayout.as<StackPanel>().UpdateLayout();
|
|
||||||
}
|
shortcutControlLayout.as<StackPanel>().Children().Append(typeShortcut.as<Button>());
|
||||||
|
shortcutControlLayout.as<StackPanel>().Children().Append(shortcutDropDownStackPanel.as<StackPanel>());
|
||||||
// Function to set the accessible name of the target App text box
|
KeyDropDownControl::AddDropDown(table, row, shortcutDropDownStackPanel.as<StackPanel>(), colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, false);
|
||||||
void ShortcutControl::SetAccessibleNameForTextBox(TextBox targetAppTextBox, int rowIndex)
|
shortcutControlLayout.as<StackPanel>().UpdateLayout();
|
||||||
{
|
}
|
||||||
// To set the accessible name of the target App text box by adding the string `All Apps` if the text box is empty, if not the application name is read by narrator.
|
|
||||||
std::wstring targetAppTextBoxAccessibleName = GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_TARGETAPPHEADER);
|
// Function to set the accessible name of the target App text box
|
||||||
if (targetAppTextBox.Text() == L"")
|
void ShortcutControl::SetAccessibleNameForTextBox(TextBox targetAppTextBox, int rowIndex)
|
||||||
{
|
{
|
||||||
targetAppTextBoxAccessibleName += GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_ALLAPPS);
|
// To set the accessible name of the target App text box by adding the string `All Apps` if the text box is empty, if not the application name is read by narrator.
|
||||||
}
|
std::wstring targetAppTextBoxAccessibleName = GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_TARGETAPPHEADER);
|
||||||
targetAppTextBox.SetValue(Automation::AutomationProperties::NameProperty(), box_value(targetAppTextBoxAccessibleName));
|
if (targetAppTextBox.Text() == L"")
|
||||||
}
|
{
|
||||||
|
targetAppTextBoxAccessibleName += GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_ALLAPPS);
|
||||||
// Function to set the accessible names for all the controls in a row
|
}
|
||||||
void ShortcutControl::UpdateAccessibleNames(StackPanel sourceColumn, StackPanel mappedToColumn, TextBox targetAppTextBox, Button deleteButton, int rowIndex)
|
|
||||||
{
|
targetAppTextBox.SetValue(Automation::AutomationProperties::NameProperty(), box_value(targetAppTextBoxAccessibleName));
|
||||||
sourceColumn.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_SOURCEHEADER)));
|
}
|
||||||
mappedToColumn.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_TARGETHEADER)));
|
|
||||||
ShortcutControl::SetAccessibleNameForTextBox(targetAppTextBox, rowIndex);
|
// Function to set the accessible names for all the controls in a row
|
||||||
deleteButton.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
void ShortcutControl::UpdateAccessibleNames(StackPanel sourceColumn, StackPanel mappedToColumn, TextBox targetAppTextBox, Button deleteButton, int rowIndex)
|
||||||
}
|
{
|
||||||
|
sourceColumn.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_SOURCEHEADER)));
|
||||||
// Function to add a new row to the shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values.
|
mappedToColumn.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_TARGETHEADER)));
|
||||||
void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys, const KeyShortcutUnion& newKeys, const std::wstring& targetAppName)
|
ShortcutControl::SetAccessibleNameForTextBox(targetAppTextBox, rowIndex);
|
||||||
{
|
deleteButton.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
||||||
// Textbox for target application
|
}
|
||||||
TextBox targetAppTextBox;
|
|
||||||
|
// Function to add a new row to the shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values.
|
||||||
// Create new ShortcutControl objects dynamically so that we does not get destructed
|
void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys, const KeyShortcutUnion& newKeys, const std::wstring& targetAppName)
|
||||||
std::vector<std::unique_ptr<ShortcutControl>> newrow;
|
{
|
||||||
StackPanel row = StackPanel();
|
// Textbox for target application
|
||||||
parent.Children().Append(row);
|
TextBox targetAppTextBox;
|
||||||
newrow.emplace_back(std::make_unique<ShortcutControl>(parent, row, 0, targetAppTextBox));
|
|
||||||
newrow.emplace_back(std::make_unique<ShortcutControl>(parent, row, 1, targetAppTextBox));
|
// Create new ShortcutControl objects dynamically so that we does not get destructed
|
||||||
keyboardRemapControlObjects.push_back(std::move(newrow));
|
std::vector<std::unique_ptr<ShortcutControl>> newrow;
|
||||||
|
StackPanel row = StackPanel();
|
||||||
row.Padding({ 10, 10, 10, 10 });
|
parent.Children().Append(row);
|
||||||
row.Orientation(Orientation::Horizontal);
|
newrow.emplace_back(std::make_unique<ShortcutControl>(parent, row, 0, targetAppTextBox));
|
||||||
auto brush = Windows::UI::Xaml::Application::Current().Resources().Lookup(box_value(L"SystemControlBackgroundListLowBrush")).as<Windows::UI::Xaml::Media::SolidColorBrush>();
|
newrow.emplace_back(std::make_unique<ShortcutControl>(parent, row, 1, targetAppTextBox));
|
||||||
if (keyboardRemapControlObjects.size() % 2)
|
keyboardRemapControlObjects.push_back(std::move(newrow));
|
||||||
{
|
|
||||||
row.Background(brush);
|
row.Padding({ 10, 10, 10, 10 });
|
||||||
}
|
row.Orientation(Orientation::Horizontal);
|
||||||
|
auto brush = Windows::UI::Xaml::Application::Current().Resources().Lookup(box_value(L"SystemControlBackgroundListLowBrush")).as<Windows::UI::Xaml::Media::SolidColorBrush>();
|
||||||
// ShortcutControl for the original shortcut
|
if (keyboardRemapControlObjects.size() % 2)
|
||||||
auto origin = keyboardRemapControlObjects.back()[0]->getShortcutControl();
|
{
|
||||||
origin.Width(KeyboardManagerConstants::ShortcutOriginColumnWidth);
|
row.Background(brush);
|
||||||
row.Children().Append(origin);
|
}
|
||||||
|
|
||||||
// Arrow icon
|
// ShortcutControl for the original shortcut
|
||||||
FontIcon arrowIcon;
|
auto origin = keyboardRemapControlObjects.back()[0]->GetShortcutControl();
|
||||||
arrowIcon.FontFamily(Xaml::Media::FontFamily(L"Segoe MDL2 Assets"));
|
origin.Width(KeyboardManagerConstants::ShortcutOriginColumnWidth);
|
||||||
arrowIcon.Glyph(L"\xE72A");
|
row.Children().Append(origin);
|
||||||
arrowIcon.VerticalAlignment(VerticalAlignment::Center);
|
|
||||||
arrowIcon.HorizontalAlignment(HorizontalAlignment::Center);
|
// Arrow icon
|
||||||
auto arrowIconContainer = KeyboardManagerHelper::GetWrapped(arrowIcon, KeyboardManagerConstants::ShortcutArrowColumnWidth).as<StackPanel>();
|
FontIcon arrowIcon;
|
||||||
arrowIconContainer.Orientation(Orientation::Vertical);
|
arrowIcon.FontFamily(Xaml::Media::FontFamily(L"Segoe MDL2 Assets"));
|
||||||
arrowIconContainer.VerticalAlignment(VerticalAlignment::Center);
|
arrowIcon.Glyph(L"\xE72A");
|
||||||
row.Children().Append(arrowIconContainer);
|
arrowIcon.VerticalAlignment(VerticalAlignment::Center);
|
||||||
|
arrowIcon.HorizontalAlignment(HorizontalAlignment::Center);
|
||||||
// ShortcutControl for the new shortcut
|
auto arrowIconContainer = UIHelpers::GetWrapped(arrowIcon, KeyboardManagerConstants::ShortcutArrowColumnWidth).as<StackPanel>();
|
||||||
auto target = keyboardRemapControlObjects.back()[1]->getShortcutControl();
|
arrowIconContainer.Orientation(Orientation::Vertical);
|
||||||
target.Width(KeyboardManagerConstants::ShortcutTargetColumnWidth);
|
arrowIconContainer.VerticalAlignment(VerticalAlignment::Center);
|
||||||
row.Children().Append(target);
|
row.Children().Append(arrowIconContainer);
|
||||||
|
|
||||||
targetAppTextBox.Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
|
// ShortcutControl for the new shortcut
|
||||||
targetAppTextBox.PlaceholderText(KeyboardManagerConstants::DefaultAppName);
|
auto target = keyboardRemapControlObjects.back()[1]->GetShortcutControl();
|
||||||
targetAppTextBox.Text(targetAppName);
|
target.Width(KeyboardManagerConstants::ShortcutTargetColumnWidth);
|
||||||
|
row.Children().Append(target);
|
||||||
// GotFocus handler will be called whenever the user tabs into or clicks on the textbox
|
|
||||||
targetAppTextBox.GotFocus([targetAppTextBox](auto const& sender, auto const& e) {
|
targetAppTextBox.Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
|
||||||
// Select all text for accessible purpose
|
targetAppTextBox.PlaceholderText(KeyboardManagerEditorStrings::DefaultAppName);
|
||||||
targetAppTextBox.SelectAll();
|
targetAppTextBox.Text(targetAppName);
|
||||||
});
|
|
||||||
|
// GotFocus handler will be called whenever the user tabs into or clicks on the textbox
|
||||||
// LostFocus handler will be called whenever text is updated by a user and then they click something else or tab to another control. Does not get called if Text is updated while the TextBox isn't in focus (i.e. from code)
|
targetAppTextBox.GotFocus([targetAppTextBox](auto const& sender, auto const& e) {
|
||||||
targetAppTextBox.LostFocus([&keyboardRemapControlObjects, parent, row, targetAppTextBox](auto const& sender, auto const& e) {
|
// Select all text for accessible purpose
|
||||||
// Get index of targetAppTextBox button
|
targetAppTextBox.SelectAll();
|
||||||
uint32_t rowIndex;
|
});
|
||||||
if (!parent.Children().IndexOf(row, rowIndex))
|
|
||||||
{
|
// LostFocus handler will be called whenever text is updated by a user and then they click something else or tab to another control. Does not get called if Text is updated while the TextBox isn't in focus (i.e. from code)
|
||||||
return;
|
targetAppTextBox.LostFocus([&keyboardRemapControlObjects, parent, row, targetAppTextBox](auto const& sender, auto const& e) {
|
||||||
}
|
// Get index of targetAppTextBox button
|
||||||
|
uint32_t rowIndex;
|
||||||
// rowIndex could be out of bounds if the the row got deleted after LostFocus handler was invoked. In this case it should return
|
if (!parent.Children().IndexOf(row, rowIndex))
|
||||||
if (rowIndex >= keyboardRemapControlObjects.size())
|
{
|
||||||
{
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
// rowIndex could be out of bounds if the the row got deleted after LostFocus handler was invoked. In this case it should return
|
||||||
// Validate both set of drop downs
|
if (rowIndex >= keyboardRemapControlObjects.size())
|
||||||
KeyDropDownControl::ValidateShortcutFromDropDownList(parent, row, keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownStackPanel.as<StackPanel>(), 0, ShortcutControl::shortcutRemapBuffer, keyboardRemapControlObjects[rowIndex][0]->keyDropDownControlObjects, targetAppTextBox, false, false);
|
{
|
||||||
KeyDropDownControl::ValidateShortcutFromDropDownList(parent, row, keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>(), 1, ShortcutControl::shortcutRemapBuffer, keyboardRemapControlObjects[rowIndex][1]->keyDropDownControlObjects, targetAppTextBox, true, false);
|
return;
|
||||||
|
}
|
||||||
// Reset the buffer based on the selected drop down items
|
|
||||||
std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[0]).SetKeyCodes(KeyDropDownControl::GetSelectedCodesFromStackPanel(keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownStackPanel.as<StackPanel>()));
|
// Validate both set of drop downs
|
||||||
// second column is a hybrid column
|
KeyDropDownControl::ValidateShortcutFromDropDownList(parent, row, keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownStackPanel.as<StackPanel>(), 0, ShortcutControl::shortcutRemapBuffer, keyboardRemapControlObjects[rowIndex][0]->keyDropDownControlObjects, targetAppTextBox, false, false);
|
||||||
|
KeyDropDownControl::ValidateShortcutFromDropDownList(parent, row, keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>(), 1, ShortcutControl::shortcutRemapBuffer, keyboardRemapControlObjects[rowIndex][1]->keyDropDownControlObjects, targetAppTextBox, true, false);
|
||||||
std::vector<int32_t> selectedKeyCodes = KeyDropDownControl::GetSelectedCodesFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>());
|
|
||||||
|
// Reset the buffer based on the selected drop down items
|
||||||
// If exactly one key is selected consider it to be a key remap
|
std::get<Shortcut>(shortcutRemapBuffer[rowIndex].first[0]).SetKeyCodes(KeyDropDownControl::GetSelectedCodesFromStackPanel(keyboardRemapControlObjects[rowIndex][0]->shortcutDropDownStackPanel.as<StackPanel>()));
|
||||||
if (selectedKeyCodes.size() == 1)
|
// second column is a hybrid column
|
||||||
{
|
|
||||||
shortcutRemapBuffer[rowIndex].first[1] = selectedKeyCodes[0];
|
std::vector<int32_t> selectedKeyCodes = KeyDropDownControl::GetSelectedCodesFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>());
|
||||||
}
|
|
||||||
else
|
// If exactly one key is selected consider it to be a key remap
|
||||||
{
|
if (selectedKeyCodes.size() == 1)
|
||||||
Shortcut tempShortcut;
|
{
|
||||||
tempShortcut.SetKeyCodes(selectedKeyCodes);
|
shortcutRemapBuffer[rowIndex].first[1] = selectedKeyCodes[0];
|
||||||
// Assign instead of setting the value in the buffer since the previous value may not be a Shortcut
|
}
|
||||||
shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
else
|
||||||
}
|
{
|
||||||
std::wstring newText = targetAppTextBox.Text().c_str();
|
Shortcut tempShortcut;
|
||||||
std::wstring lowercaseDefAppName = KeyboardManagerConstants::DefaultAppName;
|
tempShortcut.SetKeyCodes(selectedKeyCodes);
|
||||||
std::transform(newText.begin(), newText.end(), newText.begin(), towlower);
|
// Assign instead of setting the value in the buffer since the previous value may not be a Shortcut
|
||||||
std::transform(lowercaseDefAppName.begin(), lowercaseDefAppName.end(), lowercaseDefAppName.begin(), towlower);
|
shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||||
if (newText == lowercaseDefAppName)
|
}
|
||||||
{
|
std::wstring newText = targetAppTextBox.Text().c_str();
|
||||||
shortcutRemapBuffer[rowIndex].second = L"";
|
std::wstring lowercaseDefAppName = KeyboardManagerEditorStrings::DefaultAppName;
|
||||||
}
|
std::transform(newText.begin(), newText.end(), newText.begin(), towlower);
|
||||||
else
|
std::transform(lowercaseDefAppName.begin(), lowercaseDefAppName.end(), lowercaseDefAppName.begin(), towlower);
|
||||||
{
|
if (newText == lowercaseDefAppName)
|
||||||
shortcutRemapBuffer[rowIndex].second = targetAppTextBox.Text().c_str();
|
{
|
||||||
}
|
shortcutRemapBuffer[rowIndex].second = L"";
|
||||||
|
}
|
||||||
// To set the accessibile name of the target app text box when focus is lost
|
else
|
||||||
ShortcutControl::SetAccessibleNameForTextBox(targetAppTextBox, rowIndex + 1);
|
{
|
||||||
});
|
shortcutRemapBuffer[rowIndex].second = targetAppTextBox.Text().c_str();
|
||||||
|
}
|
||||||
// We need two containers in order to align it horizontally and vertically
|
|
||||||
StackPanel targetAppHorizontal = KeyboardManagerHelper::GetWrapped(targetAppTextBox, KeyboardManagerConstants::TableTargetAppColWidth).as<StackPanel>();
|
// To set the accessibile name of the target app text box when focus is lost
|
||||||
targetAppHorizontal.Orientation(Orientation::Horizontal);
|
ShortcutControl::SetAccessibleNameForTextBox(targetAppTextBox, rowIndex + 1);
|
||||||
targetAppHorizontal.HorizontalAlignment(HorizontalAlignment::Left);
|
});
|
||||||
StackPanel targetAppContainer = KeyboardManagerHelper::GetWrapped(targetAppHorizontal, KeyboardManagerConstants::TableTargetAppColWidth).as<StackPanel>();
|
|
||||||
targetAppContainer.Orientation(Orientation::Vertical);
|
// We need two containers in order to align it horizontally and vertically
|
||||||
targetAppContainer.VerticalAlignment(VerticalAlignment::Bottom);
|
StackPanel targetAppHorizontal = UIHelpers::GetWrapped(targetAppTextBox, KeyboardManagerConstants::TableTargetAppColWidth).as<StackPanel>();
|
||||||
row.Children().Append(targetAppContainer);
|
targetAppHorizontal.Orientation(Orientation::Horizontal);
|
||||||
|
targetAppHorizontal.HorizontalAlignment(HorizontalAlignment::Left);
|
||||||
// Delete row button
|
StackPanel targetAppContainer = UIHelpers::GetWrapped(targetAppHorizontal, KeyboardManagerConstants::TableTargetAppColWidth).as<StackPanel>();
|
||||||
Windows::UI::Xaml::Controls::Button deleteShortcut;
|
targetAppContainer.Orientation(Orientation::Vertical);
|
||||||
FontIcon deleteSymbol;
|
targetAppContainer.VerticalAlignment(VerticalAlignment::Bottom);
|
||||||
deleteSymbol.FontFamily(Xaml::Media::FontFamily(L"Segoe MDL2 Assets"));
|
row.Children().Append(targetAppContainer);
|
||||||
deleteSymbol.Glyph(L"\xE74D");
|
|
||||||
deleteShortcut.Content(deleteSymbol);
|
// Delete row button
|
||||||
deleteShortcut.Background(Media::SolidColorBrush(Colors::Transparent()));
|
Windows::UI::Xaml::Controls::Button deleteShortcut;
|
||||||
deleteShortcut.HorizontalAlignment(HorizontalAlignment::Center);
|
FontIcon deleteSymbol;
|
||||||
deleteShortcut.Click([&, parent, row, brush](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
deleteSymbol.FontFamily(Xaml::Media::FontFamily(L"Segoe MDL2 Assets"));
|
||||||
Button currentButton = sender.as<Button>();
|
deleteSymbol.Glyph(L"\xE74D");
|
||||||
uint32_t rowIndex;
|
deleteShortcut.Content(deleteSymbol);
|
||||||
// Get index of delete button
|
deleteShortcut.Background(Media::SolidColorBrush(Colors::Transparent()));
|
||||||
UIElementCollection children = parent.Children();
|
deleteShortcut.HorizontalAlignment(HorizontalAlignment::Center);
|
||||||
bool indexFound = children.IndexOf(row, rowIndex);
|
deleteShortcut.Click([&, parent, row, brush](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||||
|
Button currentButton = sender.as<Button>();
|
||||||
// IndexOf could fail if the the row got deleted and the button handler was invoked twice. In this case it should return
|
uint32_t rowIndex;
|
||||||
if (!indexFound)
|
// Get index of delete button
|
||||||
{
|
UIElementCollection children = parent.Children();
|
||||||
return;
|
bool indexFound = children.IndexOf(row, rowIndex);
|
||||||
}
|
|
||||||
|
// IndexOf could fail if the the row got deleted and the button handler was invoked twice. In this case it should return
|
||||||
for (uint32_t i = rowIndex + 1; i < children.Size(); i++)
|
if (!indexFound)
|
||||||
{
|
{
|
||||||
StackPanel row = children.GetAt(i).as<StackPanel>();
|
return;
|
||||||
row.Background(i % 2 ? brush : Media::SolidColorBrush(Colors::Transparent()));
|
}
|
||||||
StackPanel sourceCol = row.Children().GetAt(0).as<StackPanel>();
|
|
||||||
StackPanel targetCol = row.Children().GetAt(2).as<StackPanel>();
|
for (uint32_t i = rowIndex + 1; i < children.Size(); i++)
|
||||||
TextBox targetApp = row.Children().GetAt(3).as<StackPanel>().Children().GetAt(0).as<StackPanel>().Children().GetAt(0).as<TextBox>();
|
{
|
||||||
Button delButton = row.Children().GetAt(4).as<StackPanel>().Children().GetAt(0).as<Button>();
|
StackPanel row = children.GetAt(i).as<StackPanel>();
|
||||||
UpdateAccessibleNames(sourceCol, targetCol, targetApp, delButton, i);
|
row.Background(i % 2 ? brush : Media::SolidColorBrush(Colors::Transparent()));
|
||||||
}
|
StackPanel sourceCol = row.Children().GetAt(0).as<StackPanel>();
|
||||||
|
StackPanel targetCol = row.Children().GetAt(2).as<StackPanel>();
|
||||||
children.RemoveAt(rowIndex);
|
TextBox targetApp = row.Children().GetAt(3).as<StackPanel>().Children().GetAt(0).as<StackPanel>().Children().GetAt(0).as<TextBox>();
|
||||||
parent.UpdateLayout();
|
Button delButton = row.Children().GetAt(4).as<StackPanel>().Children().GetAt(0).as<Button>();
|
||||||
shortcutRemapBuffer.erase(shortcutRemapBuffer.begin() + rowIndex);
|
UpdateAccessibleNames(sourceCol, targetCol, targetApp, delButton, i);
|
||||||
// delete the SingleKeyRemapControl objects so that they get destructed
|
}
|
||||||
keyboardRemapControlObjects.erase(keyboardRemapControlObjects.begin() + rowIndex);
|
|
||||||
});
|
children.RemoveAt(rowIndex);
|
||||||
|
parent.UpdateLayout();
|
||||||
// To set the accessible name of the delete button
|
shortcutRemapBuffer.erase(shortcutRemapBuffer.begin() + rowIndex);
|
||||||
deleteShortcut.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
// delete the SingleKeyRemapControl objects so that they get destructed
|
||||||
|
keyboardRemapControlObjects.erase(keyboardRemapControlObjects.begin() + rowIndex);
|
||||||
// Add tooltip for delete button which would appear on hover
|
});
|
||||||
ToolTip deleteShortcuttoolTip;
|
|
||||||
deleteShortcuttoolTip.Content(box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
// To set the accessible name of the delete button
|
||||||
ToolTipService::SetToolTip(deleteShortcut, deleteShortcuttoolTip);
|
deleteShortcut.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
||||||
|
|
||||||
StackPanel deleteShortcutContainer = StackPanel();
|
// Add tooltip for delete button which would appear on hover
|
||||||
deleteShortcutContainer.Children().Append(deleteShortcut);
|
ToolTip deleteShortcuttoolTip;
|
||||||
deleteShortcutContainer.Orientation(Orientation::Vertical);
|
deleteShortcuttoolTip.Content(box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
||||||
deleteShortcutContainer.VerticalAlignment(VerticalAlignment::Center);
|
ToolTipService::SetToolTip(deleteShortcut, deleteShortcuttoolTip);
|
||||||
row.Children().Append(deleteShortcutContainer);
|
|
||||||
parent.UpdateLayout();
|
StackPanel deleteShortcutContainer = StackPanel();
|
||||||
|
deleteShortcutContainer.Children().Append(deleteShortcut);
|
||||||
// Set accessible names
|
deleteShortcutContainer.Orientation(Orientation::Vertical);
|
||||||
UpdateAccessibleNames(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->getShortcutControl(), keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->getShortcutControl(), targetAppTextBox, deleteShortcut, (int)keyboardRemapControlObjects.size());
|
deleteShortcutContainer.VerticalAlignment(VerticalAlignment::Center);
|
||||||
|
row.Children().Append(deleteShortcutContainer);
|
||||||
// Set the shortcut text if the two vectors are not empty (i.e. default args)
|
parent.UpdateLayout();
|
||||||
if (originalKeys.IsValidShortcut() && !(newKeys.index() == 0 && std::get<DWORD>(newKeys) == NULL) && !(newKeys.index() == 1 && !std::get<Shortcut>(newKeys).IsValidShortcut()))
|
|
||||||
{
|
// Set accessible names
|
||||||
// change to load app name
|
UpdateAccessibleNames(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->GetShortcutControl(), keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->GetShortcutControl(), targetAppTextBox, deleteShortcut, (int)keyboardRemapControlObjects.size());
|
||||||
shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
|
||||||
KeyDropDownControl::AddShortcutToControl(originalKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->shortcutDropDownStackPanel.as<StackPanel>(), *keyboardManagerState, 0, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects, shortcutRemapBuffer, row, targetAppTextBox, false, false);
|
// Set the shortcut text if the two vectors are not empty (i.e. default args)
|
||||||
|
if (originalKeys.IsValidShortcut() && !(newKeys.index() == 0 && std::get<DWORD>(newKeys) == NULL) && !(newKeys.index() == 1 && !std::get<Shortcut>(newKeys).IsValidShortcut()))
|
||||||
if (newKeys.index() == 0)
|
{
|
||||||
{
|
// change to load app name
|
||||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(std::get<DWORD>(newKeys)));
|
shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||||
}
|
KeyDropDownControl::AddShortcutToControl(originalKeys, parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->shortcutDropDownStackPanel.as<StackPanel>(), *keyboardManagerState, 0, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects, shortcutRemapBuffer, row, targetAppTextBox, false, false);
|
||||||
else
|
|
||||||
{
|
if (newKeys.index() == 0)
|
||||||
KeyDropDownControl::AddShortcutToControl(std::get<Shortcut>(newKeys), parent, keyboardRemapControlObjects.back()[1]->shortcutDropDownStackPanel.as<StackPanel>(), *keyboardManagerState, 1, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects, shortcutRemapBuffer, row, targetAppTextBox, true, false);
|
{
|
||||||
}
|
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(std::get<DWORD>(newKeys)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Initialize both shortcuts as empty shortcuts
|
KeyDropDownControl::AddShortcutToControl(std::get<Shortcut>(newKeys), parent, keyboardRemapControlObjects.back()[1]->shortcutDropDownStackPanel.as<StackPanel>(), *keyboardManagerState, 1, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects, shortcutRemapBuffer, row, targetAppTextBox, true, false);
|
||||||
shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
// Function to return the stack panel element of the ShortcutControl. This is the externally visible UI element which can be used to add it to other layouts
|
// Initialize both shortcuts as empty shortcuts
|
||||||
StackPanel ShortcutControl::getShortcutControl()
|
shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||||
{
|
}
|
||||||
return shortcutControlLayout.as<StackPanel>();
|
}
|
||||||
}
|
|
||||||
|
// Function to return the stack panel element of the ShortcutControl. This is the externally visible UI element which can be used to add it to other layouts
|
||||||
// Function to create the detect shortcut UI window
|
StackPanel ShortcutControl::GetShortcutControl()
|
||||||
void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState, const int colIndex, StackPanel table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer)
|
{
|
||||||
{
|
return shortcutControlLayout.as<StackPanel>();
|
||||||
// ContentDialog for detecting shortcuts. This is the parent UI element.
|
}
|
||||||
ContentDialog detectShortcutBox;
|
|
||||||
|
// Function to create the detect shortcut UI window
|
||||||
// ContentDialog requires manually setting the XamlRoot (https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.contentdialog#contentdialog-in-appwindow-or-xaml-islands)
|
void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState, const int colIndex, StackPanel table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer)
|
||||||
detectShortcutBox.XamlRoot(xamlRoot);
|
{
|
||||||
detectShortcutBox.Title(box_value(GET_RESOURCE_STRING(IDS_TYPESHORTCUT_TITLE)));
|
// ContentDialog for detecting shortcuts. This is the parent UI element.
|
||||||
detectShortcutBox.IsPrimaryButtonEnabled(false);
|
ContentDialog detectShortcutBox;
|
||||||
detectShortcutBox.IsSecondaryButtonEnabled(false);
|
|
||||||
|
// ContentDialog requires manually setting the XamlRoot (https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.contentdialog#contentdialog-in-appwindow-or-xaml-islands)
|
||||||
// Get the linked stack panel for the "Type shortcut" button that was clicked
|
detectShortcutBox.XamlRoot(xamlRoot);
|
||||||
StackPanel linkedShortcutStackPanel = KeyboardManagerHelper::getSiblingElement(sender).as<StackPanel>();
|
detectShortcutBox.Title(box_value(GET_RESOURCE_STRING(IDS_TYPESHORTCUT_TITLE)));
|
||||||
|
detectShortcutBox.IsPrimaryButtonEnabled(false);
|
||||||
auto unregisterKeys = [&keyboardManagerState]() {
|
detectShortcutBox.IsSecondaryButtonEnabled(false);
|
||||||
keyboardManagerState.ClearRegisteredKeyDelays();
|
|
||||||
};
|
// Get the linked stack panel for the "Type shortcut" button that was clicked
|
||||||
|
StackPanel linkedShortcutStackPanel = UIHelpers::GetSiblingElement(sender).as<StackPanel>();
|
||||||
auto selectDetectedShortcutAndResetKeys = [&keyboardManagerState](DWORD key) {
|
|
||||||
keyboardManagerState.SelectDetectedShortcut(key);
|
auto unregisterKeys = [&keyboardManagerState]() {
|
||||||
keyboardManagerState.ResetDetectedShortcutKey(key);
|
keyboardManagerState.ClearRegisteredKeyDelays();
|
||||||
};
|
};
|
||||||
|
|
||||||
auto onPressEnter = [linkedShortcutStackPanel,
|
auto selectDetectedShortcutAndResetKeys = [&keyboardManagerState](DWORD key) {
|
||||||
detectShortcutBox,
|
keyboardManagerState.SelectDetectedShortcut(key);
|
||||||
&keyboardManagerState,
|
keyboardManagerState.ResetDetectedShortcutKey(key);
|
||||||
unregisterKeys,
|
};
|
||||||
colIndex,
|
|
||||||
table,
|
auto onPressEnter = [linkedShortcutStackPanel,
|
||||||
targetApp,
|
detectShortcutBox,
|
||||||
&keyDropDownControlObjects,
|
&keyboardManagerState,
|
||||||
row,
|
unregisterKeys,
|
||||||
isHybridControl,
|
colIndex,
|
||||||
isSingleKeyWindow,
|
table,
|
||||||
&remapBuffer] {
|
targetApp,
|
||||||
// Save the detected shortcut in the linked text block
|
&keyDropDownControlObjects,
|
||||||
Shortcut detectedShortcutKeys = keyboardManagerState.GetDetectedShortcut();
|
row,
|
||||||
|
isHybridControl,
|
||||||
if (!detectedShortcutKeys.IsEmpty())
|
isSingleKeyWindow,
|
||||||
{
|
&remapBuffer] {
|
||||||
// The shortcut buffer gets set in this function
|
// Save the detected shortcut in the linked text block
|
||||||
KeyDropDownControl::AddShortcutToControl(detectedShortcutKeys, table, linkedShortcutStackPanel, keyboardManagerState, colIndex, keyDropDownControlObjects, remapBuffer, row, targetApp, isHybridControl, isSingleKeyWindow);
|
Shortcut detectedShortcutKeys = keyboardManagerState.GetDetectedShortcut();
|
||||||
}
|
|
||||||
// Hide the type shortcut UI
|
if (!detectedShortcutKeys.IsEmpty())
|
||||||
detectShortcutBox.Hide();
|
{
|
||||||
};
|
// The shortcut buffer gets set in this function
|
||||||
|
KeyDropDownControl::AddShortcutToControl(detectedShortcutKeys, table, linkedShortcutStackPanel, keyboardManagerState, colIndex, keyDropDownControlObjects, remapBuffer, row, targetApp, isHybridControl, isSingleKeyWindow);
|
||||||
auto onReleaseEnter = [&keyboardManagerState,
|
}
|
||||||
unregisterKeys,
|
// Hide the type shortcut UI
|
||||||
isSingleKeyWindow,
|
detectShortcutBox.Hide();
|
||||||
parentWindow] {
|
};
|
||||||
// Reset the keyboard manager UI state
|
|
||||||
keyboardManagerState.ResetUIState();
|
auto onReleaseEnter = [&keyboardManagerState,
|
||||||
if (isSingleKeyWindow)
|
unregisterKeys,
|
||||||
{
|
isSingleKeyWindow,
|
||||||
// Revert UI state back to Edit Keyboard window
|
parentWindow] {
|
||||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
|
// Reset the keyboard manager UI state
|
||||||
}
|
keyboardManagerState.ResetUIState();
|
||||||
else
|
if (isSingleKeyWindow)
|
||||||
{
|
{
|
||||||
// Revert UI state back to Edit Shortcut window
|
// Revert UI state back to Edit Keyboard window
|
||||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
|
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
unregisterKeys();
|
{
|
||||||
};
|
// Revert UI state back to Edit Shortcut window
|
||||||
|
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
|
||||||
auto onAccept = [onPressEnter,
|
}
|
||||||
onReleaseEnter] {
|
|
||||||
onPressEnter();
|
unregisterKeys();
|
||||||
onReleaseEnter();
|
};
|
||||||
};
|
|
||||||
|
auto onAccept = [onPressEnter,
|
||||||
TextBlock primaryButtonText;
|
onReleaseEnter] {
|
||||||
primaryButtonText.Text(GET_RESOURCE_STRING(IDS_OK_BUTTON));
|
onPressEnter();
|
||||||
|
onReleaseEnter();
|
||||||
Button primaryButton;
|
};
|
||||||
primaryButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
|
||||||
primaryButton.Margin({ 2, 2, 2, 2 });
|
TextBlock primaryButtonText;
|
||||||
primaryButton.Content(primaryButtonText);
|
primaryButtonText.Text(GET_RESOURCE_STRING(IDS_OK_BUTTON));
|
||||||
|
|
||||||
// OK button
|
Button primaryButton;
|
||||||
primaryButton.Click([onAccept](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
primaryButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||||
onAccept();
|
primaryButton.Margin({ 2, 2, 2, 2 });
|
||||||
});
|
primaryButton.Content(primaryButtonText);
|
||||||
|
|
||||||
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
|
// OK button
|
||||||
keyboardManagerState.RegisterKeyDelay(
|
primaryButton.Click([onAccept](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||||
VK_RETURN,
|
onAccept();
|
||||||
selectDetectedShortcutAndResetKeys,
|
});
|
||||||
[primaryButton, onPressEnter, detectShortcutBox](DWORD) {
|
|
||||||
detectShortcutBox.Dispatcher().RunAsync(
|
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
|
||||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
keyboardManagerState.RegisterKeyDelay(
|
||||||
[primaryButton, onPressEnter] {
|
VK_RETURN,
|
||||||
// Use the base medium low brush to be consistent with the theme
|
selectDetectedShortcutAndResetKeys,
|
||||||
primaryButton.Background(Windows::UI::Xaml::Application::Current().Resources().Lookup(box_value(L"SystemControlBackgroundBaseMediumLowBrush")).as<Windows::UI::Xaml::Media::SolidColorBrush>());
|
[primaryButton, onPressEnter, detectShortcutBox](DWORD) {
|
||||||
onPressEnter();
|
detectShortcutBox.Dispatcher().RunAsync(
|
||||||
});
|
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||||
},
|
[primaryButton, onPressEnter] {
|
||||||
[onReleaseEnter, detectShortcutBox](DWORD) {
|
// Use the base medium low brush to be consistent with the theme
|
||||||
detectShortcutBox.Dispatcher().RunAsync(
|
primaryButton.Background(Windows::UI::Xaml::Application::Current().Resources().Lookup(box_value(L"SystemControlBackgroundBaseMediumLowBrush")).as<Windows::UI::Xaml::Media::SolidColorBrush>());
|
||||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
onPressEnter();
|
||||||
[onReleaseEnter]() {
|
});
|
||||||
onReleaseEnter();
|
},
|
||||||
});
|
[onReleaseEnter, detectShortcutBox](DWORD) {
|
||||||
});
|
detectShortcutBox.Dispatcher().RunAsync(
|
||||||
|
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||||
TextBlock cancelButtonText;
|
[onReleaseEnter]() {
|
||||||
cancelButtonText.Text(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON));
|
onReleaseEnter();
|
||||||
|
});
|
||||||
auto onCancel = [&keyboardManagerState,
|
});
|
||||||
detectShortcutBox,
|
|
||||||
unregisterKeys,
|
TextBlock cancelButtonText;
|
||||||
isSingleKeyWindow,
|
cancelButtonText.Text(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON));
|
||||||
parentWindow] {
|
|
||||||
detectShortcutBox.Hide();
|
auto onCancel = [&keyboardManagerState,
|
||||||
|
detectShortcutBox,
|
||||||
// Reset the keyboard manager UI state
|
unregisterKeys,
|
||||||
keyboardManagerState.ResetUIState();
|
isSingleKeyWindow,
|
||||||
if (isSingleKeyWindow)
|
parentWindow] {
|
||||||
{
|
detectShortcutBox.Hide();
|
||||||
// Revert UI state back to Edit Keyboard window
|
|
||||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
|
// Reset the keyboard manager UI state
|
||||||
}
|
keyboardManagerState.ResetUIState();
|
||||||
else
|
if (isSingleKeyWindow)
|
||||||
{
|
{
|
||||||
// Revert UI state back to Edit Shortcut window
|
// Revert UI state back to Edit Keyboard window
|
||||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
|
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
|
||||||
}
|
}
|
||||||
unregisterKeys();
|
else
|
||||||
};
|
{
|
||||||
|
// Revert UI state back to Edit Shortcut window
|
||||||
Button cancelButton;
|
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
|
||||||
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
}
|
||||||
cancelButton.Margin({ 2, 2, 2, 2 });
|
unregisterKeys();
|
||||||
cancelButton.Content(cancelButtonText);
|
};
|
||||||
// Cancel button
|
|
||||||
cancelButton.Click([onCancel](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
Button cancelButton;
|
||||||
onCancel();
|
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||||
});
|
cancelButton.Margin({ 2, 2, 2, 2 });
|
||||||
|
cancelButton.Content(cancelButtonText);
|
||||||
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
|
// Cancel button
|
||||||
keyboardManagerState.RegisterKeyDelay(
|
cancelButton.Click([onCancel](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||||
VK_ESCAPE,
|
onCancel();
|
||||||
selectDetectedShortcutAndResetKeys,
|
});
|
||||||
[onCancel, detectShortcutBox](DWORD) {
|
|
||||||
detectShortcutBox.Dispatcher().RunAsync(
|
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
|
||||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
keyboardManagerState.RegisterKeyDelay(
|
||||||
[onCancel] {
|
VK_ESCAPE,
|
||||||
onCancel();
|
selectDetectedShortcutAndResetKeys,
|
||||||
});
|
[onCancel, detectShortcutBox](DWORD) {
|
||||||
},
|
detectShortcutBox.Dispatcher().RunAsync(
|
||||||
nullptr);
|
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||||
|
[onCancel] {
|
||||||
// StackPanel parent for the displayed text in the dialog
|
onCancel();
|
||||||
Windows::UI::Xaml::Controls::StackPanel stackPanel;
|
});
|
||||||
detectShortcutBox.Content(stackPanel);
|
},
|
||||||
|
nullptr);
|
||||||
// Header textblock
|
|
||||||
TextBlock text;
|
// StackPanel parent for the displayed text in the dialog
|
||||||
text.Text(GET_RESOURCE_STRING(IDS_TYPESHORTCUT_HEADER));
|
Windows::UI::Xaml::Controls::StackPanel stackPanel;
|
||||||
text.Margin({ 0, 0, 0, 10 });
|
detectShortcutBox.Content(stackPanel);
|
||||||
stackPanel.Children().Append(text);
|
|
||||||
|
// Header textblock
|
||||||
// Target StackPanel to place the selected key - first row (for 1-3 keys)
|
TextBlock text;
|
||||||
Windows::UI::Xaml::Controls::StackPanel keyStackPanel1;
|
text.Text(GET_RESOURCE_STRING(IDS_TYPESHORTCUT_HEADER));
|
||||||
keyStackPanel1.Orientation(Orientation::Horizontal);
|
text.Margin({ 0, 0, 0, 10 });
|
||||||
stackPanel.Children().Append(keyStackPanel1);
|
stackPanel.Children().Append(text);
|
||||||
|
|
||||||
// Target StackPanel to place the selected key - second row (for 4-5 keys)
|
// Target StackPanel to place the selected key - first row (for 1-3 keys)
|
||||||
Windows::UI::Xaml::Controls::StackPanel keyStackPanel2;
|
Windows::UI::Xaml::Controls::StackPanel keyStackPanel1;
|
||||||
keyStackPanel2.Orientation(Orientation::Horizontal);
|
keyStackPanel1.Orientation(Orientation::Horizontal);
|
||||||
keyStackPanel2.Margin({ 0, 20, 0, 0 });
|
stackPanel.Children().Append(keyStackPanel1);
|
||||||
keyStackPanel2.Visibility(Visibility::Collapsed);
|
|
||||||
stackPanel.Children().Append(keyStackPanel2);
|
// Target StackPanel to place the selected key - second row (for 4-5 keys)
|
||||||
|
Windows::UI::Xaml::Controls::StackPanel keyStackPanel2;
|
||||||
TextBlock holdEscInfo;
|
keyStackPanel2.Orientation(Orientation::Horizontal);
|
||||||
holdEscInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDESC));
|
keyStackPanel2.Margin({ 0, 20, 0, 0 });
|
||||||
holdEscInfo.FontSize(12);
|
keyStackPanel2.Visibility(Visibility::Collapsed);
|
||||||
holdEscInfo.Margin({ 0, 20, 0, 0 });
|
stackPanel.Children().Append(keyStackPanel2);
|
||||||
stackPanel.Children().Append(holdEscInfo);
|
|
||||||
|
TextBlock holdEscInfo;
|
||||||
TextBlock holdEnterInfo;
|
holdEscInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDESC));
|
||||||
holdEnterInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDENTER));
|
holdEscInfo.FontSize(12);
|
||||||
holdEnterInfo.FontSize(12);
|
holdEscInfo.Margin({ 0, 20, 0, 0 });
|
||||||
holdEnterInfo.Margin({ 0, 0, 0, 0 });
|
stackPanel.Children().Append(holdEscInfo);
|
||||||
stackPanel.Children().Append(holdEnterInfo);
|
|
||||||
|
TextBlock holdEnterInfo;
|
||||||
ColumnDefinition primaryButtonColumn;
|
holdEnterInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDENTER));
|
||||||
ColumnDefinition cancelButtonColumn;
|
holdEnterInfo.FontSize(12);
|
||||||
|
holdEnterInfo.Margin({ 0, 0, 0, 0 });
|
||||||
Grid buttonPanel;
|
stackPanel.Children().Append(holdEnterInfo);
|
||||||
buttonPanel.Margin({ 0, 20, 0, 0 });
|
|
||||||
buttonPanel.HorizontalAlignment(HorizontalAlignment::Stretch);
|
ColumnDefinition primaryButtonColumn;
|
||||||
buttonPanel.ColumnDefinitions().Append(primaryButtonColumn);
|
ColumnDefinition cancelButtonColumn;
|
||||||
buttonPanel.ColumnDefinitions().Append(cancelButtonColumn);
|
|
||||||
buttonPanel.SetColumn(primaryButton, 0);
|
Grid buttonPanel;
|
||||||
buttonPanel.SetColumn(cancelButton, 1);
|
buttonPanel.Margin({ 0, 20, 0, 0 });
|
||||||
|
buttonPanel.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||||
buttonPanel.Children().Append(primaryButton);
|
buttonPanel.ColumnDefinitions().Append(primaryButtonColumn);
|
||||||
buttonPanel.Children().Append(cancelButton);
|
buttonPanel.ColumnDefinitions().Append(cancelButtonColumn);
|
||||||
|
buttonPanel.SetColumn(primaryButton, 0);
|
||||||
stackPanel.Children().Append(buttonPanel);
|
buttonPanel.SetColumn(cancelButton, 1);
|
||||||
stackPanel.UpdateLayout();
|
|
||||||
|
buttonPanel.Children().Append(primaryButton);
|
||||||
// Configure the keyboardManagerState to store the UI information.
|
buttonPanel.Children().Append(cancelButton);
|
||||||
keyboardManagerState.ConfigureDetectShortcutUI(keyStackPanel1, keyStackPanel2);
|
|
||||||
|
stackPanel.Children().Append(buttonPanel);
|
||||||
// Show the dialog
|
stackPanel.UpdateLayout();
|
||||||
detectShortcutBox.ShowAsync();
|
|
||||||
}
|
// Configure the keyboardManagerState to store the UI information.
|
||||||
|
keyboardManagerState.ConfigureDetectShortcutUI(keyStackPanel1, keyStackPanel2);
|
||||||
|
|
||||||
|
// Show the dialog
|
||||||
|
detectShortcutBox.ShowAsync();
|
||||||
|
}
|
@ -1,57 +1,60 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "keyboardmanager/common/Shortcut.h"
|
|
||||||
#include <variant>
|
#include <Shortcut.h>
|
||||||
|
|
||||||
class KeyboardManagerState;
|
class KeyboardManagerState;
|
||||||
class KeyDropDownControl;
|
class KeyDropDownControl;
|
||||||
namespace winrt::Windows::UI::Xaml
|
namespace winrt::Windows::UI::Xaml
|
||||||
{
|
{
|
||||||
struct XamlRoot;
|
struct XamlRoot;
|
||||||
namespace Controls
|
namespace Controls
|
||||||
{
|
{
|
||||||
struct StackPanel;
|
struct StackPanel;
|
||||||
struct TextBox;
|
struct TextBox;
|
||||||
struct Button;
|
struct Button;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ShortcutControl
|
class ShortcutControl
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
// Stack panel for the drop downs to display the selected shortcut
|
// Stack panel for the drop downs to display the selected shortcut
|
||||||
winrt::Windows::Foundation::IInspectable shortcutDropDownStackPanel;
|
winrt::Windows::Foundation::IInspectable shortcutDropDownStackPanel;
|
||||||
|
|
||||||
// Button to type the shortcut
|
// Button to type the shortcut
|
||||||
winrt::Windows::Foundation::IInspectable typeShortcut;
|
winrt::Windows::Foundation::IInspectable typeShortcut;
|
||||||
|
|
||||||
// StackPanel to parent the above controls
|
// StackPanel to parent the above controls
|
||||||
winrt::Windows::Foundation::IInspectable shortcutControlLayout;
|
winrt::Windows::Foundation::IInspectable shortcutControlLayout;
|
||||||
|
|
||||||
// Function to set the accessible name of the target app text box
|
// Function to set the accessible name of the target app text box
|
||||||
static void SetAccessibleNameForTextBox(TextBox targetAppTextBox, int rowIndex);
|
static void SetAccessibleNameForTextBox(TextBox targetAppTextBox, int rowIndex);
|
||||||
|
|
||||||
// Function to set the accessible names for all the controls in a row
|
// Function to set the accessible names for all the controls in a row
|
||||||
static void UpdateAccessibleNames(StackPanel sourceColumn, StackPanel mappedToColumn, TextBox targetAppTextBox, Button deleteButton, int rowIndex);
|
static void UpdateAccessibleNames(StackPanel sourceColumn, StackPanel mappedToColumn, TextBox targetAppTextBox, Button deleteButton, int rowIndex);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Handle to the current Edit Shortcuts Window
|
// Handle to the current Edit Shortcuts Window
|
||||||
static HWND EditShortcutsWindowHandle;
|
static HWND editShortcutsWindowHandle;
|
||||||
// Pointer to the keyboard manager state
|
|
||||||
static KeyboardManagerState* keyboardManagerState;
|
// Pointer to the keyboard manager state
|
||||||
// Stores the current list of remappings
|
static KeyboardManagerState* keyboardManagerState;
|
||||||
static RemapBuffer shortcutRemapBuffer;
|
|
||||||
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
|
// Stores the current list of remappings
|
||||||
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
|
static RemapBuffer shortcutRemapBuffer;
|
||||||
|
|
||||||
// constructor
|
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
|
||||||
ShortcutControl(StackPanel table, StackPanel row, const int colIndex, TextBox targetApp);
|
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
|
||||||
|
|
||||||
// Function to add a new row to the shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values.
|
// constructor
|
||||||
static void AddNewShortcutControlRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys = Shortcut(), const KeyShortcutUnion& newKeys = Shortcut(), const std::wstring& targetAppName = L"");
|
ShortcutControl(StackPanel table, StackPanel row, const int colIndex, TextBox targetApp);
|
||||||
|
|
||||||
// Function to return the stack panel element of the ShortcutControl. This is the externally visible UI element which can be used to add it to other layouts
|
// Function to add a new row to the shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values.
|
||||||
StackPanel getShortcutControl();
|
static void AddNewShortcutControlRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys = Shortcut(), const KeyShortcutUnion& newKeys = Shortcut(), const std::wstring& targetAppName = L"");
|
||||||
|
|
||||||
// Function to create the detect shortcut UI window
|
// Function to return the stack panel element of the ShortcutControl. This is the externally visible UI element which can be used to add it to other layouts
|
||||||
static void createDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState, const int colIndex, StackPanel table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer);
|
StackPanel GetShortcutControl();
|
||||||
};
|
|
||||||
|
// Function to create the detect shortcut UI window
|
||||||
|
static void CreateDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState, const int colIndex, StackPanel table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer);
|
||||||
|
};
|
@ -1,357 +1,362 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "SingleKeyRemapControl.h"
|
#include "SingleKeyRemapControl.h"
|
||||||
#include "keyboardmanager/common/Helpers.h"
|
|
||||||
#include "keyboardmanager/common/KeyboardManagerConstants.h"
|
#include <KeyboardManagerState.h>
|
||||||
#include "keyboardmanager/common/KeyboardManagerState.h"
|
|
||||||
#include "ShortcutControl.h"
|
#include <ShortcutControl.h>
|
||||||
#include "keyboardmanager/dll/Generated Files/resource.h"
|
#include <UIHelpers.h>
|
||||||
#include <common/interop/shared_constants.h>
|
|
||||||
|
//Both static members are initialized to null
|
||||||
//Both static members are initialized to null
|
HWND SingleKeyRemapControl::EditKeyboardWindowHandle = nullptr;
|
||||||
HWND SingleKeyRemapControl::EditKeyboardWindowHandle = nullptr;
|
KeyboardManagerState* SingleKeyRemapControl::keyboardManagerState = nullptr;
|
||||||
KeyboardManagerState* SingleKeyRemapControl::keyboardManagerState = nullptr;
|
// Initialized as new vector
|
||||||
// Initialized as new vector
|
RemapBuffer SingleKeyRemapControl::singleKeyRemapBuffer;
|
||||||
RemapBuffer SingleKeyRemapControl::singleKeyRemapBuffer;
|
|
||||||
|
SingleKeyRemapControl::SingleKeyRemapControl(StackPanel table, StackPanel row, const int colIndex)
|
||||||
SingleKeyRemapControl::SingleKeyRemapControl(StackPanel table, StackPanel row, const int colIndex)
|
{
|
||||||
{
|
typeKey = Button();
|
||||||
typeKey = Button();
|
typeKey.as<Button>().Width(KeyboardManagerConstants::RemapTableDropDownWidth);
|
||||||
typeKey.as<Button>().Width(KeyboardManagerConstants::RemapTableDropDownWidth);
|
typeKey.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
||||||
typeKey.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
|
||||||
|
singleKeyRemapControlLayout = StackPanel();
|
||||||
singleKeyRemapControlLayout = StackPanel();
|
singleKeyRemapControlLayout.as<StackPanel>().Spacing(10);
|
||||||
singleKeyRemapControlLayout.as<StackPanel>().Spacing(10);
|
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(typeKey.as<Button>());
|
||||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(typeKey.as<Button>());
|
|
||||||
|
// Key column
|
||||||
// Key column
|
if (colIndex == 0)
|
||||||
if (colIndex == 0)
|
{
|
||||||
{
|
keyDropDownControlObjects.emplace_back(std::make_unique<KeyDropDownControl>(false));
|
||||||
keyDropDownControlObjects.emplace_back(std::make_unique<KeyDropDownControl>(false));
|
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(keyDropDownControlObjects[0]->GetComboBox());
|
||||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(keyDropDownControlObjects[0]->GetComboBox());
|
// Set selection handler for the drop down
|
||||||
// Set selection handler for the drop down
|
keyDropDownControlObjects[0]->SetSelectionHandler(table, row, colIndex, singleKeyRemapBuffer);
|
||||||
keyDropDownControlObjects[0]->SetSelectionHandler(table, row, colIndex, singleKeyRemapBuffer);
|
}
|
||||||
}
|
|
||||||
|
// Hybrid column
|
||||||
// Hybrid column
|
else
|
||||||
else
|
{
|
||||||
{
|
hybridDropDownStackPanel = StackPanel();
|
||||||
hybridDropDownStackPanel = StackPanel();
|
hybridDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||||
hybridDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
hybridDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||||
hybridDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
KeyDropDownControl::AddDropDown(table, row, hybridDropDownStackPanel.as<StackPanel>(), colIndex, singleKeyRemapBuffer, keyDropDownControlObjects, nullptr, true, true);
|
||||||
KeyDropDownControl::AddDropDown(table, row, hybridDropDownStackPanel.as<StackPanel>(), colIndex, singleKeyRemapBuffer, keyDropDownControlObjects, nullptr, true, true);
|
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(hybridDropDownStackPanel.as<StackPanel>());
|
||||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(hybridDropDownStackPanel.as<StackPanel>());
|
}
|
||||||
}
|
|
||||||
|
typeKey.as<Button>().Click([&, table, colIndex, row](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||||
typeKey.as<Button>().Click([&, table, colIndex, row](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
// Using the XamlRoot of the typeKey to get the root of the XAML host
|
||||||
// Using the XamlRoot of the typeKey to get the root of the XAML host
|
if (colIndex == 0)
|
||||||
if (colIndex == 0)
|
{
|
||||||
{
|
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated, EditKeyboardWindowHandle);
|
||||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated, EditKeyboardWindowHandle);
|
createDetectKeyWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState);
|
||||||
createDetectKeyWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState);
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
||||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
ShortcutControl::CreateDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, nullptr, true, true, EditKeyboardWindowHandle, singleKeyRemapBuffer);
|
||||||
ShortcutControl::createDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, nullptr, true, true, EditKeyboardWindowHandle, singleKeyRemapBuffer);
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
singleKeyRemapControlLayout.as<StackPanel>().UpdateLayout();
|
||||||
singleKeyRemapControlLayout.as<StackPanel>().UpdateLayout();
|
}
|
||||||
}
|
|
||||||
|
// Function to set the accessible names for all the controls in a row
|
||||||
// Function to set the accessible names for all the controls in a row
|
void SingleKeyRemapControl::UpdateAccessibleNames(StackPanel sourceColumn, StackPanel mappedToColumn, Button deleteButton, int rowIndex)
|
||||||
void SingleKeyRemapControl::UpdateAccessibleNames(StackPanel sourceColumn, StackPanel mappedToColumn, Button deleteButton, int rowIndex)
|
{
|
||||||
{
|
sourceColumn.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_EDITKEYBOARD_SOURCEHEADER)));
|
||||||
sourceColumn.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_EDITKEYBOARD_SOURCEHEADER)));
|
mappedToColumn.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_EDITKEYBOARD_TARGETHEADER)));
|
||||||
mappedToColumn.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_EDITKEYBOARD_TARGETHEADER)));
|
deleteButton.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
||||||
deleteButton.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
}
|
||||||
}
|
|
||||||
|
// Function to add a new row to the remap keys table. If the originalKey and newKey args are provided, then the displayed remap keys are set to those values.
|
||||||
// Function to add a new row to the remap keys table. If the originalKey and newKey args are provided, then the displayed remap keys are set to those values.
|
void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey, const KeyShortcutUnion newKey)
|
||||||
void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey, const KeyShortcutUnion newKey)
|
{
|
||||||
{
|
// Create new SingleKeyRemapControl objects dynamically so that we does not get destructed
|
||||||
// Create new SingleKeyRemapControl objects dynamically so that we does not get destructed
|
std::vector<std::unique_ptr<SingleKeyRemapControl>> newrow;
|
||||||
std::vector<std::unique_ptr<SingleKeyRemapControl>> newrow;
|
StackPanel row = StackPanel();
|
||||||
StackPanel row = StackPanel();
|
parent.Children().Append(row);
|
||||||
parent.Children().Append(row);
|
newrow.emplace_back(std::make_unique<SingleKeyRemapControl>(parent, row, 0));
|
||||||
newrow.emplace_back(std::make_unique<SingleKeyRemapControl>(parent, row, 0));
|
newrow.emplace_back(std::make_unique<SingleKeyRemapControl>(parent, row, 1));
|
||||||
newrow.emplace_back(std::make_unique<SingleKeyRemapControl>(parent, row, 1));
|
keyboardRemapControlObjects.push_back(std::move(newrow));
|
||||||
keyboardRemapControlObjects.push_back(std::move(newrow));
|
|
||||||
|
row.Padding({ 10, 10, 10, 10 });
|
||||||
row.Padding({ 10, 10, 10, 10 });
|
row.Orientation(Orientation::Horizontal);
|
||||||
row.Orientation(Orientation::Horizontal);
|
auto brush = Windows::UI::Xaml::Application::Current().Resources().Lookup(box_value(L"SystemControlBackgroundListLowBrush")).as<Windows::UI::Xaml::Media::SolidColorBrush>();
|
||||||
auto brush = Windows::UI::Xaml::Application::Current().Resources().Lookup(box_value(L"SystemControlBackgroundListLowBrush")).as<Windows::UI::Xaml::Media::SolidColorBrush>();
|
if (keyboardRemapControlObjects.size() % 2)
|
||||||
if (keyboardRemapControlObjects.size() % 2)
|
{
|
||||||
{
|
row.Background(brush);
|
||||||
row.Background(brush);
|
}
|
||||||
}
|
|
||||||
|
// SingleKeyRemapControl for the original key.
|
||||||
// SingleKeyRemapControl for the original key.
|
auto originalElement = keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl();
|
||||||
auto originalElement = keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl();
|
originalElement.Width(KeyboardManagerConstants::RemapTableDropDownWidth);
|
||||||
originalElement.Width(KeyboardManagerConstants::RemapTableDropDownWidth);
|
row.Children().Append(originalElement);
|
||||||
row.Children().Append(originalElement);
|
|
||||||
// Arrow icon
|
// Arrow icon
|
||||||
FontIcon arrowIcon;
|
FontIcon arrowIcon;
|
||||||
arrowIcon.FontFamily(Media::FontFamily(L"Segoe MDL2 Assets"));
|
arrowIcon.FontFamily(Media::FontFamily(L"Segoe MDL2 Assets"));
|
||||||
arrowIcon.Glyph(L"\xE72A");
|
arrowIcon.Glyph(L"\xE72A");
|
||||||
arrowIcon.VerticalAlignment(VerticalAlignment::Center);
|
arrowIcon.VerticalAlignment(VerticalAlignment::Center);
|
||||||
arrowIcon.HorizontalAlignment(HorizontalAlignment::Center);
|
arrowIcon.HorizontalAlignment(HorizontalAlignment::Center);
|
||||||
auto arrowIconContainer = KeyboardManagerHelper::GetWrapped(arrowIcon, KeyboardManagerConstants::TableArrowColWidth).as<StackPanel>();
|
auto arrowIconContainer = UIHelpers::GetWrapped(arrowIcon, KeyboardManagerConstants::TableArrowColWidth).as<StackPanel>();
|
||||||
arrowIconContainer.Orientation(Orientation::Vertical);
|
arrowIconContainer.Orientation(Orientation::Vertical);
|
||||||
arrowIconContainer.VerticalAlignment(VerticalAlignment::Center);
|
arrowIconContainer.VerticalAlignment(VerticalAlignment::Center);
|
||||||
row.Children().Append(arrowIconContainer);
|
row.Children().Append(arrowIconContainer);
|
||||||
|
|
||||||
// SingleKeyRemapControl for the new remap key
|
// SingleKeyRemapControl for the new remap key
|
||||||
auto targetElement = keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl();
|
auto targetElement = keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl();
|
||||||
targetElement.Width(KeyboardManagerConstants::ShortcutTargetColumnWidth);
|
targetElement.Width(KeyboardManagerConstants::ShortcutTargetColumnWidth);
|
||||||
row.Children().Append(targetElement);
|
row.Children().Append(targetElement);
|
||||||
|
|
||||||
// Set the key text if the two keys are not null (i.e. default args)
|
// Set the key text if the two keys are not null (i.e. default args)
|
||||||
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !std::get<Shortcut>(newKey).IsValidShortcut()))
|
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !std::get<Shortcut>(newKey).IsValidShortcut()))
|
||||||
{
|
{
|
||||||
singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ originalKey, newKey }, L""));
|
singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ originalKey, newKey }, L""));
|
||||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(originalKey));
|
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(originalKey));
|
||||||
if (newKey.index() == 0)
|
if (newKey.index() == 0)
|
||||||
{
|
{
|
||||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(std::get<DWORD>(newKey)));
|
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(std::get<DWORD>(newKey)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
KeyDropDownControl::AddShortcutToControl(std::get<Shortcut>(newKey), parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->hybridDropDownStackPanel.as<StackPanel>(), *keyboardManagerState, 1, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects, singleKeyRemapBuffer, row, nullptr, true, true);
|
KeyDropDownControl::AddShortcutToControl(std::get<Shortcut>(newKey), parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->hybridDropDownStackPanel.as<StackPanel>(), *keyboardManagerState, 1, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects, singleKeyRemapBuffer, row, nullptr, true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Initialize both keys to NULL
|
// Initialize both keys to NULL
|
||||||
singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ NULL, NULL }, L""));
|
singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ NULL, NULL }, L""));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete row button
|
// Delete row button
|
||||||
Windows::UI::Xaml::Controls::Button deleteRemapKeys;
|
Windows::UI::Xaml::Controls::Button deleteRemapKeys;
|
||||||
FontIcon deleteSymbol;
|
FontIcon deleteSymbol;
|
||||||
deleteSymbol.FontFamily(Media::FontFamily(L"Segoe MDL2 Assets"));
|
deleteSymbol.FontFamily(Media::FontFamily(L"Segoe MDL2 Assets"));
|
||||||
deleteSymbol.Glyph(L"\xE74D");
|
deleteSymbol.Glyph(L"\xE74D");
|
||||||
deleteRemapKeys.Content(deleteSymbol);
|
deleteRemapKeys.Content(deleteSymbol);
|
||||||
deleteRemapKeys.Background(Media::SolidColorBrush(Colors::Transparent()));
|
deleteRemapKeys.Background(Media::SolidColorBrush(Colors::Transparent()));
|
||||||
deleteRemapKeys.HorizontalAlignment(HorizontalAlignment::Center);
|
deleteRemapKeys.HorizontalAlignment(HorizontalAlignment::Center);
|
||||||
deleteRemapKeys.Click([&, parent, row, brush](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
deleteRemapKeys.Click([&, parent, row, brush](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||||
uint32_t rowIndex;
|
uint32_t rowIndex;
|
||||||
// Get index of delete button
|
// Get index of delete button
|
||||||
UIElementCollection children = parent.Children();
|
UIElementCollection children = parent.Children();
|
||||||
bool indexFound = children.IndexOf(row, rowIndex);
|
bool indexFound = children.IndexOf(row, rowIndex);
|
||||||
|
|
||||||
// IndexOf could fail if the the row got deleted and the button handler was invoked twice. In this case it should return
|
// IndexOf could fail if the the row got deleted and the button handler was invoked twice. In this case it should return
|
||||||
if (!indexFound)
|
if (!indexFound)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update accessible names and background for each row after the deleted row
|
// Update accessible names and background for each row after the deleted row
|
||||||
for (uint32_t i = rowIndex + 1; i < children.Size(); i++)
|
for (uint32_t i = rowIndex + 1; i < children.Size(); i++)
|
||||||
{
|
{
|
||||||
StackPanel row = children.GetAt(i).as<StackPanel>();
|
StackPanel row = children.GetAt(i).as<StackPanel>();
|
||||||
row.Background(i % 2 ? brush : Media::SolidColorBrush(Colors::Transparent()));
|
row.Background(i % 2 ? brush : Media::SolidColorBrush(Colors::Transparent()));
|
||||||
StackPanel sourceCol = row.Children().GetAt(0).as<StackPanel>();
|
StackPanel sourceCol = row.Children().GetAt(0).as<StackPanel>();
|
||||||
StackPanel targetCol = row.Children().GetAt(2).as<StackPanel>();
|
StackPanel targetCol = row.Children().GetAt(2).as<StackPanel>();
|
||||||
Button delButton = row.Children().GetAt(3).as<Button>();
|
Button delButton = row.Children().GetAt(3).as<Button>();
|
||||||
UpdateAccessibleNames(sourceCol, targetCol, delButton, i);
|
UpdateAccessibleNames(sourceCol, targetCol, delButton, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
children.RemoveAt(rowIndex);
|
children.RemoveAt(rowIndex);
|
||||||
parent.UpdateLayout();
|
parent.UpdateLayout();
|
||||||
singleKeyRemapBuffer.erase(singleKeyRemapBuffer.begin() + rowIndex);
|
singleKeyRemapBuffer.erase(singleKeyRemapBuffer.begin() + rowIndex);
|
||||||
// delete the SingleKeyRemapControl objects so that they get destructed
|
|
||||||
keyboardRemapControlObjects.erase(keyboardRemapControlObjects.begin() + rowIndex);
|
// delete the SingleKeyRemapControl objects so that they get destructed
|
||||||
});
|
keyboardRemapControlObjects.erase(keyboardRemapControlObjects.begin() + rowIndex);
|
||||||
|
});
|
||||||
// To set the accessible name of the delete button
|
|
||||||
deleteRemapKeys.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
// To set the accessible name of the delete button
|
||||||
|
deleteRemapKeys.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
||||||
// Add tooltip for delete button which would appear on hover
|
|
||||||
ToolTip deleteRemapKeystoolTip;
|
// Add tooltip for delete button which would appear on hover
|
||||||
deleteRemapKeystoolTip.Content(box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
ToolTip deleteRemapKeystoolTip;
|
||||||
ToolTipService::SetToolTip(deleteRemapKeys, deleteRemapKeystoolTip);
|
deleteRemapKeystoolTip.Content(box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
||||||
row.Children().Append(deleteRemapKeys);
|
ToolTipService::SetToolTip(deleteRemapKeys, deleteRemapKeystoolTip);
|
||||||
parent.UpdateLayout();
|
row.Children().Append(deleteRemapKeys);
|
||||||
|
parent.UpdateLayout();
|
||||||
// Set accessible names
|
|
||||||
UpdateAccessibleNames(keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl(), keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl(), deleteRemapKeys, (int)keyboardRemapControlObjects.size());
|
// Set accessible names
|
||||||
}
|
UpdateAccessibleNames(keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl(), keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl(), deleteRemapKeys, (int)keyboardRemapControlObjects.size());
|
||||||
|
}
|
||||||
// Function to return the stack panel element of the SingleKeyRemapControl. This is the externally visible UI element which can be used to add it to other layouts
|
|
||||||
StackPanel SingleKeyRemapControl::getSingleKeyRemapControl()
|
// Function to return the stack panel element of the SingleKeyRemapControl. This is the externally visible UI element which can be used to add it to other layouts
|
||||||
{
|
StackPanel SingleKeyRemapControl::getSingleKeyRemapControl()
|
||||||
return singleKeyRemapControlLayout.as<StackPanel>();
|
{
|
||||||
}
|
return singleKeyRemapControlLayout.as<StackPanel>();
|
||||||
|
}
|
||||||
// Function to create the detect remap key UI window
|
|
||||||
void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState)
|
// Function to create the detect remap key UI window
|
||||||
{
|
void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState)
|
||||||
// ContentDialog for detecting remap key. This is the parent UI element.
|
{
|
||||||
ContentDialog detectRemapKeyBox;
|
// ContentDialog for detecting remap key. This is the parent UI element.
|
||||||
|
ContentDialog detectRemapKeyBox;
|
||||||
// ContentDialog requires manually setting the XamlRoot (https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.contentdialog#contentdialog-in-appwindow-or-xaml-islands)
|
|
||||||
detectRemapKeyBox.XamlRoot(xamlRoot);
|
// ContentDialog requires manually setting the XamlRoot (https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.contentdialog#contentdialog-in-appwindow-or-xaml-islands)
|
||||||
detectRemapKeyBox.Title(box_value(GET_RESOURCE_STRING(IDS_TYPEKEY_TITLE)));
|
detectRemapKeyBox.XamlRoot(xamlRoot);
|
||||||
detectRemapKeyBox.IsPrimaryButtonEnabled(false);
|
detectRemapKeyBox.Title(box_value(GET_RESOURCE_STRING(IDS_TYPEKEY_TITLE)));
|
||||||
detectRemapKeyBox.IsSecondaryButtonEnabled(false);
|
detectRemapKeyBox.IsPrimaryButtonEnabled(false);
|
||||||
|
detectRemapKeyBox.IsSecondaryButtonEnabled(false);
|
||||||
// Get the linked text block for the "Type Key" button that was clicked
|
|
||||||
ComboBox linkedRemapDropDown = KeyboardManagerHelper::getSiblingElement(sender).as<ComboBox>();
|
// Get the linked text block for the "Type Key" button that was clicked
|
||||||
|
ComboBox linkedRemapDropDown = UIHelpers::GetSiblingElement(sender).as<ComboBox>();
|
||||||
auto unregisterKeys = [&keyboardManagerState]() {
|
|
||||||
keyboardManagerState.ClearRegisteredKeyDelays();
|
auto unregisterKeys = [&keyboardManagerState]() {
|
||||||
};
|
keyboardManagerState.ClearRegisteredKeyDelays();
|
||||||
|
};
|
||||||
auto onPressEnter = [linkedRemapDropDown,
|
|
||||||
detectRemapKeyBox,
|
auto onPressEnter = [linkedRemapDropDown,
|
||||||
&keyboardManagerState,
|
detectRemapKeyBox,
|
||||||
unregisterKeys] {
|
&keyboardManagerState,
|
||||||
// Save the detected key in the linked text block
|
unregisterKeys] {
|
||||||
DWORD detectedKey = keyboardManagerState.GetDetectedSingleRemapKey();
|
// Save the detected key in the linked text block
|
||||||
|
DWORD detectedKey = keyboardManagerState.GetDetectedSingleRemapKey();
|
||||||
if (detectedKey != NULL)
|
|
||||||
{
|
if (detectedKey != NULL)
|
||||||
std::vector<DWORD> keyCodeList = keyboardManagerState.keyboardMap.GetKeyCodeList();
|
{
|
||||||
// Update the drop down list with the new language to ensure that the correct key is displayed
|
std::vector<DWORD> keyCodeList = keyboardManagerState.keyboardMap.GetKeyCodeList();
|
||||||
linkedRemapDropDown.ItemsSource(KeyboardManagerHelper::ToBoxValue(keyboardManagerState.keyboardMap.GetKeyNameList()));
|
|
||||||
linkedRemapDropDown.SelectedValue(winrt::box_value(std::to_wstring(detectedKey)));
|
// Update the drop down list with the new language to ensure that the correct key is displayed
|
||||||
}
|
linkedRemapDropDown.ItemsSource(UIHelpers::ToBoxValue(keyboardManagerState.keyboardMap.GetKeyNameList()));
|
||||||
// Hide the type key UI
|
linkedRemapDropDown.SelectedValue(winrt::box_value(std::to_wstring(detectedKey)));
|
||||||
detectRemapKeyBox.Hide();
|
}
|
||||||
};
|
|
||||||
|
// Hide the type key UI
|
||||||
auto onReleaseEnter = [&keyboardManagerState,
|
detectRemapKeyBox.Hide();
|
||||||
unregisterKeys] {
|
};
|
||||||
// Reset the keyboard manager UI state
|
|
||||||
keyboardManagerState.ResetUIState();
|
auto onReleaseEnter = [&keyboardManagerState,
|
||||||
// Revert UI state back to Edit Keyboard window
|
unregisterKeys] {
|
||||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
// Reset the keyboard manager UI state
|
||||||
unregisterKeys();
|
keyboardManagerState.ResetUIState();
|
||||||
};
|
// Revert UI state back to Edit Keyboard window
|
||||||
|
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
||||||
auto onAccept = [onPressEnter,
|
unregisterKeys();
|
||||||
onReleaseEnter] {
|
};
|
||||||
onPressEnter();
|
|
||||||
onReleaseEnter();
|
auto onAccept = [onPressEnter,
|
||||||
};
|
onReleaseEnter] {
|
||||||
|
onPressEnter();
|
||||||
TextBlock primaryButtonText;
|
onReleaseEnter();
|
||||||
primaryButtonText.Text(GET_RESOURCE_STRING(IDS_OK_BUTTON));
|
};
|
||||||
|
|
||||||
Button primaryButton;
|
TextBlock primaryButtonText;
|
||||||
primaryButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
primaryButtonText.Text(GET_RESOURCE_STRING(IDS_OK_BUTTON));
|
||||||
primaryButton.Margin({ 2, 2, 2, 2 });
|
|
||||||
primaryButton.Content(primaryButtonText);
|
Button primaryButton;
|
||||||
primaryButton.Click([onAccept](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
primaryButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||||
onAccept();
|
primaryButton.Margin({ 2, 2, 2, 2 });
|
||||||
});
|
primaryButton.Content(primaryButtonText);
|
||||||
|
primaryButton.Click([onAccept](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||||
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
|
onAccept();
|
||||||
keyboardManagerState.RegisterKeyDelay(
|
});
|
||||||
VK_RETURN,
|
|
||||||
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
|
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
|
||||||
[primaryButton, onPressEnter, detectRemapKeyBox](DWORD) {
|
keyboardManagerState.RegisterKeyDelay(
|
||||||
detectRemapKeyBox.Dispatcher().RunAsync(
|
VK_RETURN,
|
||||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
|
||||||
[primaryButton, onPressEnter] {
|
[primaryButton, onPressEnter, detectRemapKeyBox](DWORD) {
|
||||||
// Use the base medium low brush to be consistent with the theme
|
detectRemapKeyBox.Dispatcher().RunAsync(
|
||||||
primaryButton.Background(Windows::UI::Xaml::Application::Current().Resources().Lookup(box_value(L"SystemControlBackgroundBaseMediumLowBrush")).as<Windows::UI::Xaml::Media::SolidColorBrush>());
|
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||||
onPressEnter();
|
[primaryButton, onPressEnter] {
|
||||||
});
|
// Use the base medium low brush to be consistent with the theme
|
||||||
},
|
primaryButton.Background(Windows::UI::Xaml::Application::Current().Resources().Lookup(box_value(L"SystemControlBackgroundBaseMediumLowBrush")).as<Windows::UI::Xaml::Media::SolidColorBrush>());
|
||||||
[onReleaseEnter, detectRemapKeyBox](DWORD) {
|
onPressEnter();
|
||||||
detectRemapKeyBox.Dispatcher().RunAsync(
|
});
|
||||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
},
|
||||||
[onReleaseEnter]() {
|
[onReleaseEnter, detectRemapKeyBox](DWORD) {
|
||||||
onReleaseEnter();
|
detectRemapKeyBox.Dispatcher().RunAsync(
|
||||||
});
|
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||||
});
|
[onReleaseEnter]() {
|
||||||
|
onReleaseEnter();
|
||||||
TextBlock cancelButtonText;
|
});
|
||||||
cancelButtonText.Text(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON));
|
});
|
||||||
|
|
||||||
auto onCancel = [&keyboardManagerState,
|
TextBlock cancelButtonText;
|
||||||
detectRemapKeyBox,
|
cancelButtonText.Text(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON));
|
||||||
unregisterKeys] {
|
|
||||||
detectRemapKeyBox.Hide();
|
auto onCancel = [&keyboardManagerState,
|
||||||
|
detectRemapKeyBox,
|
||||||
// Reset the keyboard manager UI state
|
unregisterKeys] {
|
||||||
keyboardManagerState.ResetUIState();
|
detectRemapKeyBox.Hide();
|
||||||
// Revert UI state back to Edit Keyboard window
|
|
||||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
// Reset the keyboard manager UI state
|
||||||
unregisterKeys();
|
keyboardManagerState.ResetUIState();
|
||||||
};
|
|
||||||
|
// Revert UI state back to Edit Keyboard window
|
||||||
Button cancelButton;
|
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
||||||
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
unregisterKeys();
|
||||||
cancelButton.Margin({ 2, 2, 2, 2 });
|
};
|
||||||
cancelButton.Content(cancelButtonText);
|
|
||||||
// Cancel button
|
Button cancelButton;
|
||||||
cancelButton.Click([onCancel](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||||
onCancel();
|
cancelButton.Margin({ 2, 2, 2, 2 });
|
||||||
});
|
cancelButton.Content(cancelButtonText);
|
||||||
|
|
||||||
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
|
// Cancel button
|
||||||
keyboardManagerState.RegisterKeyDelay(
|
cancelButton.Click([onCancel](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||||
VK_ESCAPE,
|
onCancel();
|
||||||
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
|
});
|
||||||
[onCancel, detectRemapKeyBox](DWORD) {
|
|
||||||
detectRemapKeyBox.Dispatcher().RunAsync(
|
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
|
||||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
keyboardManagerState.RegisterKeyDelay(
|
||||||
[onCancel] {
|
VK_ESCAPE,
|
||||||
onCancel();
|
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
|
||||||
});
|
[onCancel, detectRemapKeyBox](DWORD) {
|
||||||
},
|
detectRemapKeyBox.Dispatcher().RunAsync(
|
||||||
nullptr);
|
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||||
|
[onCancel] {
|
||||||
// StackPanel parent for the displayed text in the dialog
|
onCancel();
|
||||||
Windows::UI::Xaml::Controls::StackPanel stackPanel;
|
});
|
||||||
detectRemapKeyBox.Content(stackPanel);
|
},
|
||||||
|
nullptr);
|
||||||
// Header textblock
|
|
||||||
TextBlock text;
|
// StackPanel parent for the displayed text in the dialog
|
||||||
text.Text(GET_RESOURCE_STRING(IDS_TYPEKEY_HEADER));
|
Windows::UI::Xaml::Controls::StackPanel stackPanel;
|
||||||
text.Margin({ 0, 0, 0, 10 });
|
detectRemapKeyBox.Content(stackPanel);
|
||||||
stackPanel.Children().Append(text);
|
|
||||||
|
// Header textblock
|
||||||
// Target StackPanel to place the selected key
|
TextBlock text;
|
||||||
Windows::UI::Xaml::Controls::StackPanel keyStackPanel;
|
text.Text(GET_RESOURCE_STRING(IDS_TYPEKEY_HEADER));
|
||||||
keyStackPanel.Orientation(Orientation::Horizontal);
|
text.Margin({ 0, 0, 0, 10 });
|
||||||
stackPanel.Children().Append(keyStackPanel);
|
stackPanel.Children().Append(text);
|
||||||
|
|
||||||
TextBlock holdEscInfo;
|
// Target StackPanel to place the selected key
|
||||||
holdEscInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDESC));
|
Windows::UI::Xaml::Controls::StackPanel keyStackPanel;
|
||||||
holdEscInfo.FontSize(12);
|
keyStackPanel.Orientation(Orientation::Horizontal);
|
||||||
holdEscInfo.Margin({ 0, 20, 0, 0 });
|
stackPanel.Children().Append(keyStackPanel);
|
||||||
stackPanel.Children().Append(holdEscInfo);
|
|
||||||
|
TextBlock holdEscInfo;
|
||||||
TextBlock holdEnterInfo;
|
holdEscInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDESC));
|
||||||
holdEnterInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDENTER));
|
holdEscInfo.FontSize(12);
|
||||||
holdEnterInfo.FontSize(12);
|
holdEscInfo.Margin({ 0, 20, 0, 0 });
|
||||||
holdEnterInfo.Margin({ 0, 0, 0, 0 });
|
stackPanel.Children().Append(holdEscInfo);
|
||||||
stackPanel.Children().Append(holdEnterInfo);
|
|
||||||
|
TextBlock holdEnterInfo;
|
||||||
ColumnDefinition primaryButtonColumn;
|
holdEnterInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDENTER));
|
||||||
ColumnDefinition cancelButtonColumn;
|
holdEnterInfo.FontSize(12);
|
||||||
|
holdEnterInfo.Margin({ 0, 0, 0, 0 });
|
||||||
Grid buttonPanel;
|
stackPanel.Children().Append(holdEnterInfo);
|
||||||
buttonPanel.Margin({ 0, 20, 0, 0 });
|
|
||||||
buttonPanel.HorizontalAlignment(HorizontalAlignment::Stretch);
|
ColumnDefinition primaryButtonColumn;
|
||||||
buttonPanel.ColumnDefinitions().Append(primaryButtonColumn);
|
ColumnDefinition cancelButtonColumn;
|
||||||
buttonPanel.ColumnDefinitions().Append(cancelButtonColumn);
|
|
||||||
buttonPanel.SetColumn(primaryButton, 0);
|
Grid buttonPanel;
|
||||||
buttonPanel.SetColumn(cancelButton, 1);
|
buttonPanel.Margin({ 0, 20, 0, 0 });
|
||||||
|
buttonPanel.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||||
buttonPanel.Children().Append(primaryButton);
|
buttonPanel.ColumnDefinitions().Append(primaryButtonColumn);
|
||||||
buttonPanel.Children().Append(cancelButton);
|
buttonPanel.ColumnDefinitions().Append(cancelButtonColumn);
|
||||||
|
buttonPanel.SetColumn(primaryButton, 0);
|
||||||
stackPanel.Children().Append(buttonPanel);
|
buttonPanel.SetColumn(cancelButton, 1);
|
||||||
stackPanel.UpdateLayout();
|
|
||||||
|
buttonPanel.Children().Append(primaryButton);
|
||||||
// Configure the keyboardManagerState to store the UI information.
|
buttonPanel.Children().Append(cancelButton);
|
||||||
keyboardManagerState.ConfigureDetectSingleKeyRemapUI(keyStackPanel);
|
|
||||||
|
stackPanel.Children().Append(buttonPanel);
|
||||||
// Show the dialog
|
stackPanel.UpdateLayout();
|
||||||
detectRemapKeyBox.ShowAsync();
|
|
||||||
|
// Configure the keyboardManagerState to store the UI information.
|
||||||
|
keyboardManagerState.ConfigureDetectSingleKeyRemapUI(keyStackPanel);
|
||||||
|
|
||||||
|
// Show the dialog
|
||||||
|
detectRemapKeyBox.ShowAsync();
|
||||||
}
|
}
|
@ -1,53 +1,58 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "KeyDropDownControl.h"
|
|
||||||
#include <keyboardmanager/common/Shortcut.h>
|
#include <Shortcut.h>
|
||||||
|
|
||||||
class KeyboardManagerState;
|
#include <KeyDropDownControl.h>
|
||||||
namespace winrt::Windows::UI::Xaml
|
|
||||||
{
|
class KeyboardManagerState;
|
||||||
struct XamlRoot;
|
namespace winrt::Windows::UI::Xaml
|
||||||
namespace Controls
|
{
|
||||||
{
|
struct XamlRoot;
|
||||||
struct StackPanel;
|
namespace Controls
|
||||||
struct Grid;
|
{
|
||||||
struct Button;
|
struct StackPanel;
|
||||||
}
|
struct Grid;
|
||||||
}
|
struct Button;
|
||||||
|
}
|
||||||
class SingleKeyRemapControl
|
}
|
||||||
{
|
|
||||||
private:
|
class SingleKeyRemapControl
|
||||||
// Button to type the remap key
|
{
|
||||||
winrt::Windows::Foundation::IInspectable typeKey;
|
private:
|
||||||
|
// Button to type the remap key
|
||||||
// StackPanel to parent the above controls
|
winrt::Windows::Foundation::IInspectable typeKey;
|
||||||
winrt::Windows::Foundation::IInspectable singleKeyRemapControlLayout;
|
|
||||||
|
// StackPanel to parent the above controls
|
||||||
// Stack panel for the drop downs to display the selected shortcut for the hybrid case
|
winrt::Windows::Foundation::IInspectable singleKeyRemapControlLayout;
|
||||||
winrt::Windows::Foundation::IInspectable hybridDropDownStackPanel;
|
|
||||||
|
// Stack panel for the drop downs to display the selected shortcut for the hybrid case
|
||||||
// Function to set the accessible names for all the controls in a row
|
winrt::Windows::Foundation::IInspectable hybridDropDownStackPanel;
|
||||||
static void UpdateAccessibleNames(StackPanel sourceColumn, StackPanel mappedToColumn, Button deleteButton, int rowIndex);
|
|
||||||
|
// Function to set the accessible names for all the controls in a row
|
||||||
public:
|
static void UpdateAccessibleNames(StackPanel sourceColumn, StackPanel mappedToColumn, Button deleteButton, int rowIndex);
|
||||||
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
|
|
||||||
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
|
public:
|
||||||
// Handle to the current Edit Keyboard Window
|
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
|
||||||
static HWND EditKeyboardWindowHandle;
|
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
|
||||||
// Pointer to the keyboard manager state
|
|
||||||
static KeyboardManagerState* keyboardManagerState;
|
// Handle to the current Edit Keyboard Window
|
||||||
// Stores the current list of remappings
|
static HWND EditKeyboardWindowHandle;
|
||||||
static RemapBuffer singleKeyRemapBuffer;
|
|
||||||
|
// Pointer to the keyboard manager state
|
||||||
// constructor
|
static KeyboardManagerState* keyboardManagerState;
|
||||||
SingleKeyRemapControl(StackPanel table, StackPanel row, const int colIndex);
|
|
||||||
|
// Stores the current list of remappings
|
||||||
// Function to add a new row to the remap keys table. If the originalKey and newKey args are provided, then the displayed remap keys are set to those values.
|
static RemapBuffer singleKeyRemapBuffer;
|
||||||
static void AddNewControlKeyRemapRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey = NULL, const KeyShortcutUnion newKey = NULL);
|
|
||||||
|
// constructor
|
||||||
// Function to return the stack panel element of the SingleKeyRemapControl. This is the externally visible UI element which can be used to add it to other layouts
|
SingleKeyRemapControl(StackPanel table, StackPanel row, const int colIndex);
|
||||||
winrt::Windows::UI::Xaml::Controls::StackPanel getSingleKeyRemapControl();
|
|
||||||
|
// Function to add a new row to the remap keys table. If the originalKey and newKey args are provided, then the displayed remap keys are set to those values.
|
||||||
// Function to create the detect remap keys UI window
|
static void AddNewControlKeyRemapRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey = NULL, const KeyShortcutUnion newKey = NULL);
|
||||||
void createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState);
|
|
||||||
};
|
// Function to return the stack panel element of the SingleKeyRemapControl. This is the externally visible UI element which can be used to add it to other layouts
|
||||||
|
winrt::Windows::UI::Xaml::Controls::StackPanel getSingleKeyRemapControl();
|
||||||
|
|
||||||
|
// Function to create the detect remap keys UI window
|
||||||
|
void createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState);
|
||||||
|
};
|
@ -0,0 +1,71 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "UIHelpers.h"
|
||||||
|
|
||||||
|
#include <common/monitor_utils.h>
|
||||||
|
|
||||||
|
namespace UIHelpers
|
||||||
|
{
|
||||||
|
// This method sets focus to the first Type button on the last row of the Grid
|
||||||
|
void SetFocusOnTypeButtonInLastRow(StackPanel& parent, long colCount)
|
||||||
|
{
|
||||||
|
// First element in the last row (StackPanel)
|
||||||
|
StackPanel firstElementInLastRow = parent.Children().GetAt(parent.Children().Size() - 1).as<StackPanel>().Children().GetAt(0).as<StackPanel>();
|
||||||
|
|
||||||
|
// Type button is the first child in the StackPanel
|
||||||
|
Button firstTypeButtonInLastRow = firstElementInLastRow.Children().GetAt(0).as<Button>();
|
||||||
|
|
||||||
|
// Set programmatic focus on the button
|
||||||
|
firstTypeButtonInLastRow.Focus(FocusState::Programmatic);
|
||||||
|
}
|
||||||
|
|
||||||
|
RECT GetForegroundWindowDesktopRect()
|
||||||
|
{
|
||||||
|
HWND window = GetForegroundWindow();
|
||||||
|
HMONITOR settingsMonitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
||||||
|
RECT desktopRect{};
|
||||||
|
auto monitors = GetAllMonitorRects<&MONITORINFOEX::rcWork>();
|
||||||
|
for (const auto& monitor : monitors)
|
||||||
|
{
|
||||||
|
if (settingsMonitor == monitor.first)
|
||||||
|
{
|
||||||
|
desktopRect = monitor.second;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return desktopRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to return the next sibling element for an element under a stack panel
|
||||||
|
winrt::Windows::Foundation::IInspectable GetSiblingElement(winrt::Windows::Foundation::IInspectable const& element)
|
||||||
|
{
|
||||||
|
FrameworkElement frameworkElement = element.as<FrameworkElement>();
|
||||||
|
StackPanel parentElement = frameworkElement.Parent().as<StackPanel>();
|
||||||
|
uint32_t index;
|
||||||
|
|
||||||
|
parentElement.Children().IndexOf(frameworkElement, index);
|
||||||
|
return parentElement.Children().GetAt(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
winrt::Windows::Foundation::IInspectable GetWrapped(const winrt::Windows::Foundation::IInspectable& element, double width)
|
||||||
|
{
|
||||||
|
StackPanel sp = StackPanel();
|
||||||
|
sp.Width(width);
|
||||||
|
sp.Children().Append(element.as<FrameworkElement>());
|
||||||
|
return sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
winrt::Windows::Foundation::Collections::IVector<winrt::Windows::Foundation::IInspectable> ToBoxValue(const std::vector<std::pair<DWORD, std::wstring>>& list)
|
||||||
|
{
|
||||||
|
winrt::Windows::Foundation::Collections::IVector<winrt::Windows::Foundation::IInspectable> boxList = single_threaded_vector<winrt::Windows::Foundation::IInspectable>();
|
||||||
|
for (auto& val : list)
|
||||||
|
{
|
||||||
|
auto comboBox = ComboBoxItem();
|
||||||
|
comboBox.DataContext(winrt::box_value(std::to_wstring(val.first)));
|
||||||
|
comboBox.Content(winrt::box_value(val.second));
|
||||||
|
boxList.Append(winrt::box_value(comboBox));
|
||||||
|
}
|
||||||
|
|
||||||
|
return boxList;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace winrt
|
||||||
|
{
|
||||||
|
struct hstring;
|
||||||
|
namespace Windows::Foundation
|
||||||
|
{
|
||||||
|
struct IInspectable;
|
||||||
|
namespace Collections
|
||||||
|
{
|
||||||
|
template<typename T>
|
||||||
|
struct IVector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This namespace contains UI methods that are to be used for both KBM windows
|
||||||
|
namespace UIHelpers
|
||||||
|
{
|
||||||
|
// This method sets focus to the first Type button on the last row of the Grid
|
||||||
|
void SetFocusOnTypeButtonInLastRow(StackPanel& parent, long colCount);
|
||||||
|
|
||||||
|
RECT GetForegroundWindowDesktopRect();
|
||||||
|
|
||||||
|
// Function to return the next sibling element for an element under a stack panel
|
||||||
|
winrt::Windows::Foundation::IInspectable GetSiblingElement(winrt::Windows::Foundation::IInspectable const& element);
|
||||||
|
|
||||||
|
winrt::Windows::Foundation::IInspectable GetWrapped(const winrt::Windows::Foundation::IInspectable& element, double width);
|
||||||
|
|
||||||
|
// Function to return the list of key name in the order for the drop down based on the key codes
|
||||||
|
winrt::Windows::Foundation::Collections::IVector<winrt::Windows::Foundation::IInspectable> ToBoxValue(const std::vector<std::pair<DWORD, std::wstring>>& list);
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "XamlBridge.h"
|
#include "XamlBridge.h"
|
||||||
#include <windowsx.h>
|
#include <windowsx.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
bool XamlBridge::FilterMessage(const MSG* msg)
|
bool XamlBridge::FilterMessage(const MSG* msg)
|
||||||
{
|
{
|
||||||
@ -144,6 +145,7 @@ int XamlBridge::MessageLoop()
|
|||||||
{
|
{
|
||||||
MSG msg = {};
|
MSG msg = {};
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
|
Logger::trace("XamlBridge::MessageLoop()");
|
||||||
while (GetMessage(&msg, nullptr, 0, 0))
|
while (GetMessage(&msg, nullptr, 0, 0))
|
||||||
{
|
{
|
||||||
const bool xamlSourceProcessedMessage = FilterMessage(&msg);
|
const bool xamlSourceProcessedMessage = FilterMessage(&msg);
|
||||||
@ -157,6 +159,7 @@ int XamlBridge::MessageLoop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger::trace("XamlBridge::MessageLoop() stopped");
|
||||||
return (int)msg.wParam;
|
return (int)msg.wParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,6 +194,7 @@ WPARAM GetKeyFromReason(winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNaviga
|
|||||||
// Event triggered when focus is requested
|
// Event triggered when focus is requested
|
||||||
void XamlBridge::OnTakeFocusRequested(winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource const& sender, winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSourceTakeFocusRequestedEventArgs const& args)
|
void XamlBridge::OnTakeFocusRequested(winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource const& sender, winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSourceTakeFocusRequestedEventArgs const& args)
|
||||||
{
|
{
|
||||||
|
Logger::trace("XamlBridge::OnTakeFocusRequested()");
|
||||||
if (args.Request().CorrelationId() != lastFocusRequestId)
|
if (args.Request().CorrelationId() != lastFocusRequestId)
|
||||||
{
|
{
|
||||||
const auto reason = args.Request().Reason();
|
const auto reason = args.Request().Reason();
|
||||||
@ -226,6 +230,7 @@ void XamlBridge::OnTakeFocusRequested(winrt::Windows::UI::Xaml::Hosting::Desktop
|
|||||||
// Function to initialise the xaml source object
|
// Function to initialise the xaml source object
|
||||||
HWND XamlBridge::InitDesktopWindowsXamlSource(winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource)
|
HWND XamlBridge::InitDesktopWindowsXamlSource(winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource)
|
||||||
{
|
{
|
||||||
|
Logger::trace("XamlBridge::InitDesktopWindowsXamlSource()");
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
winrt::init_apartment(apartment_type::single_threaded);
|
winrt::init_apartment(apartment_type::single_threaded);
|
||||||
winxamlmanager = WindowsXamlManager::InitializeForCurrentThread();
|
winxamlmanager = WindowsXamlManager::InitializeForCurrentThread();
|
||||||
@ -249,6 +254,7 @@ HWND XamlBridge::InitDesktopWindowsXamlSource(winrt::Windows::UI::Xaml::Hosting:
|
|||||||
// Function to close and delete all the xaml source objects
|
// Function to close and delete all the xaml source objects
|
||||||
void XamlBridge::ClearXamlIslands()
|
void XamlBridge::ClearXamlIslands()
|
||||||
{
|
{
|
||||||
|
Logger::trace("XamlBridge::ClearXamlIslands() {} focus event revokers", m_takeFocusEventRevokers.size());
|
||||||
for (auto& takeFocusRevoker : m_takeFocusEventRevokers)
|
for (auto& takeFocusRevoker : m_takeFocusEventRevokers)
|
||||||
{
|
{
|
||||||
takeFocusRevoker.revoke();
|
takeFocusRevoker.revoke();
|
||||||
@ -267,6 +273,7 @@ void XamlBridge::ClearXamlIslands()
|
|||||||
// Function invoked when the window is destroyed
|
// Function invoked when the window is destroyed
|
||||||
void XamlBridge::OnDestroy(HWND)
|
void XamlBridge::OnDestroy(HWND)
|
||||||
{
|
{
|
||||||
|
Logger::trace("XamlBridge::OnDestroy()");
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,6 +282,7 @@ void XamlBridge::OnActivate(HWND, UINT state, HWND hwndActDeact, BOOL fMinimized
|
|||||||
{
|
{
|
||||||
if (state == WA_INACTIVE)
|
if (state == WA_INACTIVE)
|
||||||
{
|
{
|
||||||
|
Logger::trace("XamlBridge::OnActivate()");
|
||||||
m_hwndLastFocus = GetFocus();
|
m_hwndLastFocus = GetFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,18 +292,42 @@ void XamlBridge::OnSetFocus(HWND, HWND hwndOldFocus)
|
|||||||
{
|
{
|
||||||
if (m_hwndLastFocus)
|
if (m_hwndLastFocus)
|
||||||
{
|
{
|
||||||
|
Logger::trace("XamlBridge::OnSetFocus()");
|
||||||
SetFocus(m_hwndLastFocus);
|
SetFocus(m_hwndLastFocus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::wstring getMessageString(const UINT message)
|
||||||
|
{
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
case WM_NCDESTROY:
|
||||||
|
return L"WM_NCDESTROY";
|
||||||
|
case WM_ACTIVATE:
|
||||||
|
return L"WM_ACTIVATE";
|
||||||
|
case WM_SETFOCUS:
|
||||||
|
return L"WM_SETFOCUS";
|
||||||
|
default:
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Message Handler function for Xaml Island windows
|
// Message Handler function for Xaml Island windows
|
||||||
LRESULT XamlBridge::MessageHandler(UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept
|
LRESULT XamlBridge::MessageHandler(UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept
|
||||||
{
|
{
|
||||||
|
auto msg = getMessageString(message);
|
||||||
|
if (msg != L"")
|
||||||
|
{
|
||||||
|
Logger::trace(L"XamlBridge::MessageHandler() message: {}", msg);
|
||||||
|
}
|
||||||
|
|
||||||
switch (message)
|
switch (message)
|
||||||
{
|
{
|
||||||
HANDLE_MSG(parentWindow, WM_NCDESTROY, OnDestroy);
|
HANDLE_MSG(parentWindow, WM_NCDESTROY, OnDestroy);
|
||||||
HANDLE_MSG(parentWindow, WM_ACTIVATE, OnActivate);
|
HANDLE_MSG(parentWindow, WM_ACTIVATE, OnActivate);
|
||||||
HANDLE_MSG(parentWindow, WM_SETFOCUS, OnSetFocus);
|
HANDLE_MSG(parentWindow, WM_SETFOCUS, OnSetFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefWindowProc(parentWindow, message, wParam, lParam);
|
return DefWindowProc(parentWindow, message, wParam, lParam);
|
||||||
}
|
}
|
@ -1,5 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <unknwn.h> // To enable support for non-WinRT interfaces, unknwn.h must be included before any C++/WinRT headers.
|
|
||||||
|
|
||||||
// This class is used for handling XAML Island operations
|
// This class is used for handling XAML Island operations
|
||||||
class XamlBridge
|
class XamlBridge
|
@ -0,0 +1,5 @@
|
|||||||
|
// pch.cpp: source file corresponding to the pre-compiled header
|
||||||
|
|
||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
|
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "targetver.h"
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||||
|
#include <unknwn.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <shellapi.h>
|
||||||
|
|
||||||
|
#include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
|
||||||
|
|
||||||
|
#include <winrt/Windows.Foundation.Collections.h>
|
||||||
|
#include <winrt/Windows.UI.Core.h>
|
||||||
|
#include <winrt/Windows.UI.Text.h>
|
||||||
|
|
||||||
|
#pragma push_macro("GetCurrentTime")
|
||||||
|
#undef GetCurrentTime
|
||||||
|
#include <winrt/Windows.UI.Xaml.Automation.h>
|
||||||
|
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
|
||||||
|
#include <winrt/Windows.UI.Xaml.Hosting.h>
|
||||||
|
#include <winrt/Windows.UI.Xaml.Interop.h>
|
||||||
|
#include <winrt/Windows.ui.xaml.media.h>
|
||||||
|
#pragma pop_macro("GetCurrentTime")
|
||||||
|
|
||||||
|
#include <common/logger/logger.h>
|
||||||
|
#include <common/utils/resources.h>
|
||||||
|
|
||||||
|
#include <ProjectTelemetry.h>
|
||||||
|
|
||||||
|
#include <keyboardmanager/KeyboardManagerEditor/Generated Files/resource.h>
|
||||||
|
//#include <Generated Files/resource.h>
|
||||||
|
|
||||||
|
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;
|
@ -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 <SDKDDKVer.h>
|
@ -0,0 +1,71 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
TRACELOGGING_DEFINE_PROVIDER(
|
||||||
|
g_hProvider,
|
||||||
|
"Microsoft.PowerToys",
|
||||||
|
// {38e8889b-9731-53f5-e901-e8a7c1753074}
|
||||||
|
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
|
||||||
|
TraceLoggingOptionProjectTelemetry());
|
||||||
|
|
||||||
|
void Trace::RegisterProvider() noexcept
|
||||||
|
{
|
||||||
|
TraceLoggingRegister(g_hProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Trace::UnregisterProvider() noexcept
|
||||||
|
{
|
||||||
|
TraceLoggingUnregister(g_hProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log number of key remaps when the user uses Edit Keyboard and saves settings
|
||||||
|
void Trace::KeyRemapCount(const DWORD keyToKeyCount, const DWORD keyToShortcutCount) noexcept
|
||||||
|
{
|
||||||
|
TraceLoggingWrite(
|
||||||
|
g_hProvider,
|
||||||
|
"KeyboardManager_KeyRemapCount",
|
||||||
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||||
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||||
|
TraceLoggingValue(keyToKeyCount + keyToShortcutCount, "KeyRemapCount"),
|
||||||
|
TraceLoggingValue(keyToKeyCount, "KeyToKeyRemapCount"),
|
||||||
|
TraceLoggingValue(keyToShortcutCount, "KeyToShortcutRemapCount"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log number of os level shortcut remaps when the user uses Edit Shortcuts and saves settings
|
||||||
|
void Trace::OSLevelShortcutRemapCount(const DWORD shortcutToShortcutCount, const DWORD shortcutToKeyCount) noexcept
|
||||||
|
{
|
||||||
|
TraceLoggingWrite(
|
||||||
|
g_hProvider,
|
||||||
|
"KeyboardManager_OSLevelShortcutRemapCount",
|
||||||
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||||
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||||
|
TraceLoggingValue(shortcutToShortcutCount + shortcutToKeyCount, "OSLevelShortcutRemapCount"),
|
||||||
|
TraceLoggingValue(shortcutToShortcutCount, "OSLevelShortcutToShortcutRemapCount"),
|
||||||
|
TraceLoggingValue(shortcutToKeyCount, "OSLevelShortcutToKeyRemapCount"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log number of app specific shortcut remaps when the user uses Edit Shortcuts and saves settings
|
||||||
|
void Trace::AppSpecificShortcutRemapCount(const DWORD shortcutToShortcutCount, const DWORD shortcutToKeyCount) noexcept
|
||||||
|
{
|
||||||
|
TraceLoggingWrite(
|
||||||
|
g_hProvider,
|
||||||
|
"KeyboardManager_AppSpecificShortcutRemapCount",
|
||||||
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||||
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||||
|
TraceLoggingValue(shortcutToShortcutCount + shortcutToKeyCount, "AppSpecificShortcutRemapCount"),
|
||||||
|
TraceLoggingValue(shortcutToShortcutCount, "AppSpecificShortcutToShortcutRemapCount"),
|
||||||
|
TraceLoggingValue(shortcutToKeyCount, "AppSpecificShortcutToKeyRemapCount"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log if an error occurs in KBM
|
||||||
|
void Trace::Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept
|
||||||
|
{
|
||||||
|
TraceLoggingWrite(
|
||||||
|
g_hProvider,
|
||||||
|
"KeyboardManager_Error",
|
||||||
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||||
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||||
|
TraceLoggingValue(methodName.c_str(), "MethodName"),
|
||||||
|
TraceLoggingValue(errorCode, "ErrorCode"),
|
||||||
|
TraceLoggingValue(errorMessage.c_str(), "ErrorMessage"));
|
||||||
|
}
|
@ -6,9 +6,6 @@ public:
|
|||||||
static void RegisterProvider() noexcept;
|
static void RegisterProvider() noexcept;
|
||||||
static void UnregisterProvider() noexcept;
|
static void UnregisterProvider() noexcept;
|
||||||
|
|
||||||
// Log if the user has KBM enabled or disabled - Can also be used to see how often users have to restart the keyboard hook
|
|
||||||
static void EnableKeyboardManager(const bool enabled) noexcept;
|
|
||||||
|
|
||||||
// Log number of key remaps when the user uses Edit Keyboard and saves settings
|
// Log number of key remaps when the user uses Edit Keyboard and saves settings
|
||||||
static void KeyRemapCount(const DWORD keyToKeyCount, const DWORD keyToShortcutCount) noexcept;
|
static void KeyRemapCount(const DWORD keyToKeyCount, const DWORD keyToShortcutCount) noexcept;
|
||||||
|
|
||||||
@ -17,13 +14,7 @@ public:
|
|||||||
|
|
||||||
// Log number of app specific shortcut remaps when the user uses Edit Shortcuts and saves settings
|
// Log number of app specific shortcut remaps when the user uses Edit Shortcuts and saves settings
|
||||||
static void AppSpecificShortcutRemapCount(const DWORD shortcutToShortcutCount, const DWORD shortcutToKeyCount) noexcept;
|
static void AppSpecificShortcutRemapCount(const DWORD shortcutToShortcutCount, const DWORD shortcutToKeyCount) noexcept;
|
||||||
|
|
||||||
// Log if a key remap has been invoked
|
|
||||||
static void KeyRemapInvoked(bool isKeyToKey) noexcept;
|
|
||||||
|
|
||||||
// Log if a shortcut remap has been invoked
|
|
||||||
static void ShortcutRemapInvoked(bool isShortcutToShortcut, bool isAppSpecific) noexcept;
|
|
||||||
|
|
||||||
// Log if an error occurs in KBM
|
// Log if an error occurs in KBM
|
||||||
static void Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept;
|
static void Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept;
|
||||||
};
|
};
|
@ -1,10 +1,11 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "CppUnitTest.h"
|
#include "CppUnitTest.h"
|
||||||
#include <keyboardmanager/ui/BufferValidationHelpers.h>
|
#include <keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.h>
|
||||||
#include "TestHelpers.h"
|
#include "TestHelpers.h"
|
||||||
#include <common/interop/keyboard_layout.h>
|
#include <common/interop/keyboard_layout.h>
|
||||||
#include <common/interop/shared_constants.h>
|
#include <common/interop/shared_constants.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <keyboardmanager/common/ErrorTypes.h>
|
||||||
|
|
||||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||||
using namespace TestHelpers;
|
using namespace TestHelpers;
|
@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>16.0</VCProjectVersion>
|
||||||
|
<ProjectGuid>{62173D9A-6724-4C00-A1C8-FB646480A9EC}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>KeyboardManagerEditorTest</RootNamespace>
|
||||||
|
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
|
||||||
|
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||||
|
<ProjectSubType>NativeUnitTestProject</ProjectSubType>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\KeyboardManagerEditor\</OutDir>
|
||||||
|
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ClCompile>
|
||||||
|
<AdditionalIncludeDirectories>$(VCInstallDir)UnitTest\include;$(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<UseFullPaths>true</UseFullPaths>
|
||||||
|
<DisableSpecificWarnings>4002</DisableSpecificWarnings>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="BufferValidationTests.cpp" />
|
||||||
|
<ClCompile Include="LoadingAndSavingRemappingTests.cpp" />
|
||||||
|
<ClCompile Include="MockedInput.cpp" />
|
||||||
|
<ClCompile Include="pch.cpp">
|
||||||
|
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="TestHelpers.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="MockedInput.h" />
|
||||||
|
<ClInclude Include="pch.h" />
|
||||||
|
<ClInclude Include="resource.h" />
|
||||||
|
<ClInclude Include="TestHelpers.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SetttingsAPI.vcxproj">
|
||||||
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\common\KeyboardManagerCommon.vcxproj">
|
||||||
|
<Project>{8affa899-0b73-49ec-8c50-0fadda57b2fc}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\KeyboardManagerEditorLibrary\KeyboardManagerEditorLibrary.vcxproj">
|
||||||
|
<Project>{23d2070d-e4ad-4add-85a7-083d9c76ad49}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ResourceCompile Include="KeyboardManagerEditorTest.rc" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
|
<Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files">
|
||||||
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
|
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Resource Files">
|
||||||
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="pch.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="LoadingAndSavingRemappingTests.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="BufferValidationTests.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="TestHelpers.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="MockedInput.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="pch.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="MockedInput.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="resource.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="TestHelpers.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ResourceCompile Include="KeyboardManagerEditorTest.rc">
|
||||||
|
<Filter>Resource Files</Filter>
|
||||||
|
</ResourceCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
@ -1,9 +1,10 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "CppUnitTest.h"
|
#include "CppUnitTest.h"
|
||||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||||
#include <keyboardmanager/ui/LoadingAndSavingRemappingHelper.h>
|
#include <keyboardmanager/KeyboardManagerEditorLibrary/LoadingAndSavingRemappingHelper.h>
|
||||||
#include "TestHelpers.h"
|
#include "TestHelpers.h"
|
||||||
#include <common/interop/shared_constants.h>
|
#include <common/interop/shared_constants.h>
|
||||||
|
#include <keyboardmanager/common/ErrorTypes.h>
|
||||||
|
|
||||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||||
|
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
// Class for mocked keyboard input
|
// Class for mocked keyboard input
|
||||||
class MockedInput :
|
class MockedInput :
|
||||||
public InputInterface
|
public KeyboardManagerInput::InputInterface
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
// Stores the states for all the keys - false for key up, and true for key down
|
// Stores the states for all the keys - false for key up, and true for key down
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
||||||
</packages>
|
</packages>
|
BIN
src/modules/keyboardmanager/KeyboardManagerEngine/Keyboard.ico
Normal file
BIN
src/modules/keyboardmanager/KeyboardManagerEngine/Keyboard.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 113 KiB |
@ -0,0 +1,111 @@
|
|||||||
|
// Microsoft Visual C++ generated resource script.
|
||||||
|
//
|
||||||
|
#include "resource.h"
|
||||||
|
#include "../../../common/version/version.h"
|
||||||
|
|
||||||
|
#define APSTUDIO_READONLY_SYMBOLS
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Generated from the TEXTINCLUDE 2 resource.
|
||||||
|
//
|
||||||
|
#include "winres.h"
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
#undef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// English (United States) resources
|
||||||
|
|
||||||
|
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||||
|
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
|
#pragma code_page(1252)
|
||||||
|
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// TEXTINCLUDE
|
||||||
|
//
|
||||||
|
|
||||||
|
1 TEXTINCLUDE
|
||||||
|
BEGIN
|
||||||
|
"resource.h\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
2 TEXTINCLUDE
|
||||||
|
BEGIN
|
||||||
|
"#include ""winres.h""\r\n"
|
||||||
|
"\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
3 TEXTINCLUDE
|
||||||
|
BEGIN
|
||||||
|
"\r\n"
|
||||||
|
"\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
#endif // APSTUDIO_INVOKED
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Icon
|
||||||
|
//
|
||||||
|
|
||||||
|
// Icon with lowest ID value placed first to ensure application icon
|
||||||
|
// remains consistent on all systems.
|
||||||
|
IDI_ICON1 ICON "Keyboard.ico"
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 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 Engine"
|
||||||
|
VALUE "FileVersion", FILE_VERSION_STRING
|
||||||
|
VALUE "InternalName", "PowerToys.KeyboardManagerEngine.exe"
|
||||||
|
VALUE "LegalCopyright", COPYRIGHT_NOTE
|
||||||
|
VALUE "OriginalFilename", "PowerToys.KeyboardManagerEngine.exe"
|
||||||
|
VALUE "ProductName", PRODUCT_NAME
|
||||||
|
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||||
|
END
|
||||||
|
END
|
||||||
|
BLOCK "VarFileInfo"
|
||||||
|
BEGIN
|
||||||
|
VALUE "Translation", 0x409, 1200
|
||||||
|
END
|
||||||
|
END
|
||||||
|
|
||||||
|
#endif // English (United States) resources
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef APSTUDIO_INVOKED
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Generated from the TEXTINCLUDE 3 resource.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
#endif // not APSTUDIO_INVOKED
|
||||||
|
|
@ -0,0 +1,95 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||||
|
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||||
|
<CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata>
|
||||||
|
<MinimalCoreWin>true</MinimalCoreWin>
|
||||||
|
<VCProjectVersion>15.0</VCProjectVersion>
|
||||||
|
<ProjectGuid>{ba661f5b-1d5a-4ffc-9bf1-fc39df280bdd}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>KeyboardManagerEngine</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.18362.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets">
|
||||||
|
<Import Project="PropertySheet.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetName>PowerToys.$(MSBuildProjectName)</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\$(ProjectName)\</OutDir>
|
||||||
|
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ClCompile>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PreprocessorDefinitions>EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<DisableSpecificWarnings>4002</DisableSpecificWarnings>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||||
|
<AdditionalDependencies>Shell32.lib;Shcore.lib;OleAut32.lib;Dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="pch.h" />
|
||||||
|
<ClInclude Include="resource.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="main.cpp" />
|
||||||
|
<ClCompile Include="pch.cpp">
|
||||||
|
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<None Include="PropertySheet.props" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
|
||||||
|
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SetttingsAPI.vcxproj">
|
||||||
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\common\KeyboardManagerCommon.vcxproj">
|
||||||
|
<Project>{8affa899-0b73-49ec-8c50-0fadda57b2fc}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\KeyboardManagerEngineLibrary\KeyboardManagerEngineLibrary.vcxproj">
|
||||||
|
<Project>{e496b7fc-1e99-4bab-849b-0e8367040b02}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ResourceCompile Include="KeyboardManagerEngine.rc" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Image Include="Keyboard.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
|
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files">
|
||||||
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
|
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Resource Files">
|
||||||
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="pch.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="resource.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="pch.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="main.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="PropertySheet.props" />
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ResourceCompile Include="KeyboardManagerEngine.rc">
|
||||||
|
<Filter>Resource Files</Filter>
|
||||||
|
</ResourceCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Image Include="Keyboard.ico">
|
||||||
|
<Filter>Resource Files</Filter>
|
||||||
|
</Image>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ImportGroup Label="PropertySheets" />
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<!--
|
||||||
|
To customize common C++/WinRT project properties:
|
||||||
|
* right-click the project node
|
||||||
|
* expand the Common Properties item
|
||||||
|
* select the C++/WinRT property page
|
||||||
|
|
||||||
|
For more advanced scenarios, and complete documentation, please see:
|
||||||
|
https://github.com/Microsoft/cppwinrt/tree/master/nuget
|
||||||
|
-->
|
||||||
|
<PropertyGroup />
|
||||||
|
<ItemDefinitionGroup />
|
||||||
|
</Project>
|
65
src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp
Normal file
65
src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include <common/utils/window.h>
|
||||||
|
#include <common/utils/ProcessWaiter.h>
|
||||||
|
#include <common/utils/winapi_error.h>
|
||||||
|
#include <common/utils/logger_helper.h>
|
||||||
|
#include <keyboardmanager/common/KeyboardManagerConstants.h>
|
||||||
|
#include <keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.h>
|
||||||
|
#include <keyboardmanager/KeyboardManagerEngineLibrary/trace.h>
|
||||||
|
#include <common/utils/UnhandledExceptionHandler_x64.h>
|
||||||
|
|
||||||
|
using namespace winrt;
|
||||||
|
using namespace Windows::Foundation;
|
||||||
|
|
||||||
|
const std::wstring instanceMutexName = L"Local\\PowerToys_KBMEngine_InstanceMutex";
|
||||||
|
|
||||||
|
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR lpCmdLine, int nCmdShow)
|
||||||
|
{
|
||||||
|
init_apartment();
|
||||||
|
LoggerHelpers::init_logger(KeyboardManagerConstants::ModuleName, L"Engine", LogSettings::keyboardManagerLoggerName);
|
||||||
|
|
||||||
|
InitUnhandledExceptionHandler_x64();
|
||||||
|
|
||||||
|
auto mutex = CreateMutex(nullptr, true, instanceMutexName.c_str());
|
||||||
|
if (mutex == nullptr)
|
||||||
|
{
|
||||||
|
Logger::error(L"Failed to create mutex. {}", get_last_error_or_default(GetLastError()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetLastError() == ERROR_ALREADY_EXISTS)
|
||||||
|
{
|
||||||
|
Logger::warn(L"KBM engine instance is already running");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Trace::RegisterProvider();
|
||||||
|
|
||||||
|
std::wstring pid = std::wstring(lpCmdLine);
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::trace(L"PowerToys runner exited.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::trace(L"Exiting KeyboardManager engine");
|
||||||
|
PostThreadMessage(mainThreadId, WM_QUIT, 0, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto kbm = KeyboardManager();
|
||||||
|
kbm.StartLowlevelKeyboardHook();
|
||||||
|
|
||||||
|
run_message_loop();
|
||||||
|
|
||||||
|
kbm.StopLowlevelKeyboardHook();
|
||||||
|
Trace::UnregisterProvider();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
||||||
|
</packages>
|
@ -0,0 +1 @@
|
|||||||
|
#include "pch.h"
|
10
src/modules/keyboardmanager/KeyboardManagerEngine/pch.h
Normal file
10
src/modules/keyboardmanager/KeyboardManagerEngine/pch.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#include <ProjectTelemetry.h>
|
||||||
|
#include <shlwapi.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <winrt/base.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <common/SettingsAPI/settings_helpers.h>
|
||||||
|
#include <common/logger/logger.h>
|
14
src/modules/keyboardmanager/KeyboardManagerEngine/resource.h
Normal file
14
src/modules/keyboardmanager/KeyboardManagerEngine/resource.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//{{NO_DEPENDENCIES}}
|
||||||
|
// Microsoft Visual C++ generated include file.
|
||||||
|
// Used by KeyboardManagerEngine.rc
|
||||||
|
|
||||||
|
// Next default values for new objects
|
||||||
|
//
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
#define _APS_NEXT_RESOURCE_VALUE 101
|
||||||
|
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||||
|
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||||
|
#define _APS_NEXT_SYMED_VALUE 101
|
||||||
|
#endif
|
||||||
|
#endif
|
@ -1,17 +1,17 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "KeyboardEventHandlers.h"
|
#include "KeyboardEventHandlers.h"
|
||||||
#include "keyboardmanager/common/Shortcut.h"
|
|
||||||
#include "keyboardmanager/common/RemapShortcut.h"
|
|
||||||
#include <common/interop/shared_constants.h>
|
#include <common/interop/shared_constants.h>
|
||||||
|
|
||||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||||
#include <keyboardmanager/common/InputInterface.h>
|
#include <keyboardmanager/common/InputInterface.h>
|
||||||
#include <keyboardmanager/common/Helpers.h>
|
#include <keyboardmanager/common/Helpers.h>
|
||||||
#include <keyboardmanager/common/trace.h>
|
#include <keyboardmanager/KeyboardManagerEngineLibrary/trace.h>
|
||||||
|
|
||||||
namespace KeyboardEventHandlers
|
namespace KeyboardEventHandlers
|
||||||
{
|
{
|
||||||
// Function to a handle a single key remap
|
// Function to a handle a single key remap
|
||||||
__declspec(dllexport) intptr_t HandleSingleKeyRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept
|
intptr_t HandleSingleKeyRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept
|
||||||
{
|
{
|
||||||
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
|
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
|
||||||
if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG))
|
if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG))
|
||||||
@ -42,6 +42,7 @@ namespace KeyboardEventHandlers
|
|||||||
{
|
{
|
||||||
key_count = std::get<Shortcut>(it->second).Size();
|
key_count = std::get<Shortcut>(it->second).Size();
|
||||||
}
|
}
|
||||||
|
|
||||||
LPINPUT keyEventList = new INPUT[size_t(key_count)]();
|
LPINPUT keyEventList = new INPUT[size_t(key_count)]();
|
||||||
memset(keyEventList, 0, sizeof(keyEventList));
|
memset(keyEventList, 0, sizeof(keyEventList));
|
||||||
|
|
||||||
@ -176,7 +177,7 @@ namespace KeyboardEventHandlers
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Function to a handle a shortcut remap
|
// Function to a handle a shortcut remap
|
||||||
__declspec(dllexport) intptr_t HandleShortcutRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState, const std::optional<std::wstring>& activatedApp) noexcept
|
intptr_t HandleShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState, const std::optional<std::wstring>& activatedApp) noexcept
|
||||||
{
|
{
|
||||||
// Check if any shortcut is currently in the invoked state
|
// Check if any shortcut is currently in the invoked state
|
||||||
bool isShortcutInvoked = keyboardManagerState.CheckShortcutRemapInvoked(activatedApp);
|
bool isShortcutInvoked = keyboardManagerState.CheckShortcutRemapInvoked(activatedApp);
|
||||||
@ -194,6 +195,7 @@ namespace KeyboardEventHandlers
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the remap is to a key or a shortcut
|
// Check if the remap is to a key or a shortcut
|
||||||
bool remapToShortcut = (it->second.targetShortcut.index() == 1);
|
bool remapToShortcut = (it->second.targetShortcut.index() == 1);
|
||||||
|
|
||||||
@ -275,6 +277,7 @@ namespace KeyboardEventHandlers
|
|||||||
{
|
{
|
||||||
// Dummy key, key up for all the original shortcut modifier keys and key down for remapped key
|
// Dummy key, key up for all the original shortcut modifier keys and key down for remapped key
|
||||||
key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + (src_size - 1) + dest_size;
|
key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + (src_size - 1) + dest_size;
|
||||||
|
|
||||||
// Do not send Disable key
|
// Do not send Disable key
|
||||||
if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||||
{
|
{
|
||||||
@ -323,16 +326,17 @@ namespace KeyboardEventHandlers
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The shortcut has already been pressed down at least once, i.e. the shortcut has been invoked
|
|
||||||
// There are 6 cases to be handled if the shortcut has been pressed down
|
|
||||||
// 1. The user lets go of one of the modifier keys - reset the keyboard back to the state of the keys actually being pressed down
|
|
||||||
// 2. The user keeps the shortcut pressed - the shortcut is repeated (for example you could hold down Ctrl+V and it will keep pasting)
|
|
||||||
// 3. The user lets go of the action key - keep modifiers of the new shortcut until some other key event which doesn't apply to the original shortcut
|
|
||||||
// 4. The user presses a modifier key in the original shortcut - suppress that key event since the original shortcut is already held down physically (This case can occur only if a user has a duplicated modifier key (possibly by remapping) or if user presses both L/R versions of a modifier remapped with "Both")
|
|
||||||
// 5. The user presses any key apart from the action key or a modifier key in the original shortcut - revert the keyboard state to just the original modifiers being held down along with the current key press
|
|
||||||
// 6. The user releases any key apart from original modifier or original action key - This can't happen since the key down would have to happen first, which is handled above
|
|
||||||
else if (it->second.isShortcutInvoked)
|
else if (it->second.isShortcutInvoked)
|
||||||
{
|
{
|
||||||
|
// The shortcut has already been pressed down at least once, i.e. the shortcut has been invoked
|
||||||
|
// There are 6 cases to be handled if the shortcut has been pressed down
|
||||||
|
// 1. The user lets go of one of the modifier keys - reset the keyboard back to the state of the keys actually being pressed down
|
||||||
|
// 2. The user keeps the shortcut pressed - the shortcut is repeated (for example you could hold down Ctrl+V and it will keep pasting)
|
||||||
|
// 3. The user lets go of the action key - keep modifiers of the new shortcut until some other key event which doesn't apply to the original shortcut
|
||||||
|
// 4. The user presses a modifier key in the original shortcut - suppress that key event since the original shortcut is already held down physically (This case can occur only if a user has a duplicated modifier key (possibly by remapping) or if user presses both L/R versions of a modifier remapped with "Both")
|
||||||
|
// 5. The user presses any key apart from the action key or a modifier key in the original shortcut - revert the keyboard state to just the original modifiers being held down along with the current key press
|
||||||
|
// 6. The user releases any key apart from original modifier or original action key - This can't happen since the key down would have to happen first, which is handled above
|
||||||
|
|
||||||
// Get the common keys between the two shortcuts
|
// Get the common keys between the two shortcuts
|
||||||
int commonKeys = remapToShortcut ? it->first.GetCommonModifiersCount(std::get<Shortcut>(it->second.targetShortcut)) : 0;
|
int commonKeys = remapToShortcut ? it->first.GetCommonModifiersCount(std::get<Shortcut>(it->second.targetShortcut)) : 0;
|
||||||
|
|
||||||
@ -425,6 +429,7 @@ namespace KeyboardEventHandlers
|
|||||||
it->second.isShortcutInvoked = false;
|
it->second.isShortcutInvoked = false;
|
||||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||||
it->second.isOriginalActionKeyPressed = false;
|
it->second.isOriginalActionKeyPressed = false;
|
||||||
|
|
||||||
// If app specific shortcut has finished invoking, reset the target application
|
// If app specific shortcut has finished invoking, reset the target application
|
||||||
if (activatedApp)
|
if (activatedApp)
|
||||||
{
|
{
|
||||||
@ -482,9 +487,9 @@ namespace KeyboardEventHandlers
|
|||||||
memset(keyEventList, 0, sizeof(keyEventList));
|
memset(keyEventList, 0, sizeof(keyEventList));
|
||||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||||
}
|
}
|
||||||
// If remapped to disable, do nothing and suppress the key event
|
|
||||||
else if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
else if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||||
{
|
{
|
||||||
|
// If remapped to disable, do nothing and suppress the key event
|
||||||
// Since the original shortcut's action key is released, set it to false
|
// Since the original shortcut's action key is released, set it to false
|
||||||
it->second.isOriginalActionKeyPressed = false;
|
it->second.isOriginalActionKeyPressed = false;
|
||||||
return 1;
|
return 1;
|
||||||
@ -493,6 +498,7 @@ namespace KeyboardEventHandlers
|
|||||||
{
|
{
|
||||||
// Check if the keyboard state is clear apart from the target remap key (by creating a temp Shortcut object with the target key)
|
// Check if the keyboard state is clear apart from the target remap key (by creating a temp Shortcut object with the target key)
|
||||||
bool isKeyboardStateClear = Shortcut(std::vector<int32_t>({ KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)) })).IsKeyboardStateClearExceptShortcut(ii);
|
bool isKeyboardStateClear = Shortcut(std::vector<int32_t>({ KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)) })).IsKeyboardStateClearExceptShortcut(ii);
|
||||||
|
|
||||||
// If the keyboard state is clear, we release the target key but do not reset the remap state
|
// If the keyboard state is clear, we release the target key but do not reset the remap state
|
||||||
if (isKeyboardStateClear)
|
if (isKeyboardStateClear)
|
||||||
{
|
{
|
||||||
@ -500,9 +506,11 @@ namespace KeyboardEventHandlers
|
|||||||
memset(keyEventList, 0, sizeof(keyEventList));
|
memset(keyEventList, 0, sizeof(keyEventList));
|
||||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||||
}
|
}
|
||||||
// If any other key is pressed, then the keyboard state must be reverted back to the physical keys. This is to take cases like Ctrl+A->D remap and user presses B+Ctrl+A and releases A, or Ctrl+A+B and releases A
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// If any other key is pressed, then the keyboard state must be reverted back to the physical keys.
|
||||||
|
// This is to take cases like Ctrl+A->D remap and user presses B+Ctrl+A and releases A, or Ctrl+A+B and releases A
|
||||||
|
|
||||||
// 1 for releasing new key and original shortcut modifiers, and dummy key
|
// 1 for releasing new key and original shortcut modifiers, and dummy key
|
||||||
key_count = dest_size + (src_size - 1) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
key_count = dest_size + (src_size - 1) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||||
|
|
||||||
@ -524,6 +532,7 @@ namespace KeyboardEventHandlers
|
|||||||
it->second.isShortcutInvoked = false;
|
it->second.isShortcutInvoked = false;
|
||||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||||
it->second.isOriginalActionKeyPressed = false;
|
it->second.isOriginalActionKeyPressed = false;
|
||||||
|
|
||||||
// If app specific shortcut has finished invoking, reset the target application
|
// If app specific shortcut has finished invoking, reset the target application
|
||||||
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
||||||
{
|
{
|
||||||
@ -548,9 +557,9 @@ namespace KeyboardEventHandlers
|
|||||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
|
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If it is not remapped to Disable
|
|
||||||
else if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED)
|
else if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED)
|
||||||
{
|
{
|
||||||
|
// If it is not remapped to Disable
|
||||||
// Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps
|
// Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps
|
||||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)));
|
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)));
|
||||||
}
|
}
|
||||||
@ -656,6 +665,7 @@ namespace KeyboardEventHandlers
|
|||||||
it->second.isShortcutInvoked = false;
|
it->second.isShortcutInvoked = false;
|
||||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||||
it->second.isOriginalActionKeyPressed = false;
|
it->second.isOriginalActionKeyPressed = false;
|
||||||
|
|
||||||
// If app specific shortcut has finished invoking, reset the target application
|
// If app specific shortcut has finished invoking, reset the target application
|
||||||
if (activatedApp)
|
if (activatedApp)
|
||||||
{
|
{
|
||||||
@ -666,9 +676,10 @@ namespace KeyboardEventHandlers
|
|||||||
delete[] keyEventList;
|
delete[] keyEventList;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// For remap to key, if the original action key is not currently pressed, we should revert the keyboard state to the physical keys. If it is pressed we should not suppress the event so that shortcut to key remaps can be pressed with other keys. Example use-case: Alt+D->Win, allows Alt+D+A to perform Win+A
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// For remap to key, if the original action key is not currently pressed, we should revert the keyboard state to the physical keys. If it is pressed we should not suppress the event so that shortcut to key remaps can be pressed with other keys. Example use-case: Alt+D->Win, allows Alt+D+A to perform Win+A
|
||||||
|
|
||||||
// Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps, Shift is pressed. System should not see Shift and Caps pressed together
|
// Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps, Shift is pressed. System should not see Shift and Caps pressed together
|
||||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)));
|
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)));
|
||||||
|
|
||||||
@ -723,6 +734,7 @@ namespace KeyboardEventHandlers
|
|||||||
it->second.isShortcutInvoked = false;
|
it->second.isShortcutInvoked = false;
|
||||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||||
it->second.isOriginalActionKeyPressed = false;
|
it->second.isOriginalActionKeyPressed = false;
|
||||||
|
|
||||||
// If app specific shortcut has finished invoking, reset the target application
|
// If app specific shortcut has finished invoking, reset the target application
|
||||||
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
||||||
{
|
{
|
||||||
@ -748,7 +760,7 @@ namespace KeyboardEventHandlers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Function to a handle an os-level shortcut remap
|
// Function to a handle an os-level shortcut remap
|
||||||
__declspec(dllexport) intptr_t HandleOSLevelShortcutRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept
|
intptr_t HandleOSLevelShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept
|
||||||
{
|
{
|
||||||
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
|
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
|
||||||
if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG)
|
if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG)
|
||||||
@ -761,7 +773,7 @@ namespace KeyboardEventHandlers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Function to a handle an app-specific shortcut remap
|
// Function to a handle an app-specific shortcut remap
|
||||||
__declspec(dllexport) intptr_t HandleAppSpecificShortcutRemapEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept
|
intptr_t HandleAppSpecificShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept
|
||||||
{
|
{
|
||||||
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
|
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
|
||||||
if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG)
|
if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG)
|
||||||
@ -786,6 +798,7 @@ namespace KeyboardEventHandlers
|
|||||||
std::wstring query_string;
|
std::wstring query_string;
|
||||||
|
|
||||||
AppSpecificShortcutRemapTable::iterator it;
|
AppSpecificShortcutRemapTable::iterator it;
|
||||||
|
|
||||||
// Check if an app-specific shortcut is already activated
|
// Check if an app-specific shortcut is already activated
|
||||||
if (keyboardManagerState.GetActivatedApp() == KeyboardManagerConstants::NoActivatedApp)
|
if (keyboardManagerState.GetActivatedApp() == KeyboardManagerConstants::NoActivatedApp)
|
||||||
{
|
{
|
||||||
@ -817,24 +830,8 @@ namespace KeyboardEventHandlers
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to ensure Num Lock state does not change when it is suppressed by the low level hook
|
|
||||||
void SetNumLockToPreviousState(InputInterface& ii)
|
|
||||||
{
|
|
||||||
// Num Lock's key state is applied before it is intercepted by low level keyboard hooks, so we have to manually set back the state when we suppress the key. This is done by sending an additional key up, key down set of messages.
|
|
||||||
// We need 2 key events because after Num Lock is suppressed, key up to release num lock key and key down to revert the num lock state
|
|
||||||
int key_count = 2;
|
|
||||||
LPINPUT keyEventList = new INPUT[size_t(key_count)]();
|
|
||||||
memset(keyEventList, 0, sizeof(keyEventList));
|
|
||||||
|
|
||||||
// Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts
|
|
||||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, VK_NUMLOCK, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
|
|
||||||
KeyboardManagerHelper::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, VK_NUMLOCK, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
|
|
||||||
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
|
|
||||||
delete[] keyEventList;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to ensure Ctrl/Shift/Alt modifier key state is not detected as pressed down by applications which detect keys at a lower level than hooks when it is remapped for scenarios where its required
|
// Function to ensure Ctrl/Shift/Alt modifier key state is not detected as pressed down by applications which detect keys at a lower level than hooks when it is remapped for scenarios where its required
|
||||||
void ResetIfModifierKeyForLowerLevelKeyHandlers(InputInterface& ii, DWORD key, DWORD target)
|
void ResetIfModifierKeyForLowerLevelKeyHandlers(KeyboardManagerInput::InputInterface& ii, DWORD key, DWORD target)
|
||||||
{
|
{
|
||||||
// If the target is Caps Lock and the other key is either Ctrl/Alt/Shift then reset the modifier state to lower level handlers
|
// If the target is Caps Lock and the other key is either Ctrl/Alt/Shift then reset the modifier state to lower level handlers
|
||||||
if (target == VK_CAPITAL)
|
if (target == VK_CAPITAL)
|
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <common/hooks/LowlevelKeyboardEvent.h>
|
||||||
|
|
||||||
|
namespace KeyboardManagerInput
|
||||||
|
{
|
||||||
|
class InputInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
class KeyboardManagerState;
|
||||||
|
|
||||||
|
namespace KeyboardEventHandlers
|
||||||
|
{
|
||||||
|
// Function to a handle a single key remap
|
||||||
|
intptr_t HandleSingleKeyRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept;
|
||||||
|
|
||||||
|
/* This feature has not been enabled (code from proof of concept stage)
|
||||||
|
// Function to a change a key's behavior from toggle to modifier
|
||||||
|
__declspec(dllexport) intptr_t HandleSingleKeyToggleToModEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept;
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Function to a handle a shortcut remap
|
||||||
|
intptr_t HandleShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState, const std::optional<std::wstring>& activatedApp = std::nullopt) noexcept;
|
||||||
|
|
||||||
|
// Function to a handle an os-level shortcut remap
|
||||||
|
intptr_t HandleOSLevelShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept;
|
||||||
|
|
||||||
|
// Function to a handle an app-specific shortcut remap
|
||||||
|
intptr_t HandleAppSpecificShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept;
|
||||||
|
|
||||||
|
// Function to ensure Ctrl/Shift/Alt modifier key state is not detected as pressed down by applications which detect keys at a lower level than hooks when it is remapped for scenarios where its required
|
||||||
|
void ResetIfModifierKeyForLowerLevelKeyHandlers(KeyboardManagerInput::InputInterface& ii, DWORD key, DWORD target);
|
||||||
|
};
|
@ -0,0 +1,167 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "KeyboardManager.h"
|
||||||
|
#include <interface/powertoy_module_interface.h>
|
||||||
|
#include <common/SettingsAPI/settings_objects.h>
|
||||||
|
#include <common/interop/shared_constants.h>
|
||||||
|
#include <common/debug_control.h>
|
||||||
|
#include <common/utils/winapi_error.h>
|
||||||
|
#include <common/logger/logger_settings.h>
|
||||||
|
|
||||||
|
#include <keyboardmanager/common/Shortcut.h>
|
||||||
|
#include <keyboardmanager/common/RemapShortcut.h>
|
||||||
|
#include <keyboardmanager/common/KeyboardManagerConstants.h>
|
||||||
|
#include <keyboardmanager/common/Helpers.h>
|
||||||
|
#include <keyboardmanager/common/KeyboardEventHandlers.h>
|
||||||
|
#include <keyboardmanager/common/SettingsHelper.h>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
#include "KeyboardEventHandlers.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
HHOOK KeyboardManager::hookHandleCopy;
|
||||||
|
HHOOK KeyboardManager::hookHandle;
|
||||||
|
KeyboardManager* KeyboardManager::keyboardManagerObjectPtr;
|
||||||
|
|
||||||
|
KeyboardManager::KeyboardManager()
|
||||||
|
{
|
||||||
|
// Load the initial settings.
|
||||||
|
LoadSettings();
|
||||||
|
|
||||||
|
// Set the static pointer to the newest object of the class
|
||||||
|
keyboardManagerObjectPtr = this;
|
||||||
|
|
||||||
|
std::filesystem::path modulePath(PTSettingsHelper::get_module_save_folder_location(moduleName));
|
||||||
|
auto changeSettingsCallback = [this](DWORD err) {
|
||||||
|
Logger::trace(L"{} event was signaled", KeyboardManagerConstants::SettingsEventName);
|
||||||
|
if (err != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
Logger::error(L"Failed to watch settings changes. {}", get_last_error_or_default(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingSettings = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LoadSettings();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
Logger::error("Failed to load settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingSettings = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
settingsEventWaiter = EventWaiter(KeyboardManagerConstants::SettingsEventName, changeSettingsCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardManager::LoadSettings()
|
||||||
|
{
|
||||||
|
bool loadedSuccessful = SettingsHelper::LoadSettings(keyboardManagerState);
|
||||||
|
if (!loadedSuccessful)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
|
|
||||||
|
// retry once
|
||||||
|
SettingsHelper::LoadSettings(keyboardManagerState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CALLBACK KeyboardManager::HookProc(int nCode, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
LowlevelKeyboardEvent event;
|
||||||
|
if (nCode == HC_ACTION)
|
||||||
|
{
|
||||||
|
event.lParam = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
||||||
|
event.wParam = wParam;
|
||||||
|
if (keyboardManagerObjectPtr->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(keyboardManagerObjectPtr->inputHandler);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CallNextHookEx(hookHandleCopy, nCode, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardManager::StartLowlevelKeyboardHook()
|
||||||
|
{
|
||||||
|
#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED)
|
||||||
|
if (IsDebuggerPresent())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!hookHandle)
|
||||||
|
{
|
||||||
|
hookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, HookProc, GetModuleHandle(NULL), NULL);
|
||||||
|
hookHandleCopy = hookHandle;
|
||||||
|
if (!hookHandle)
|
||||||
|
{
|
||||||
|
DWORD errorCode = GetLastError();
|
||||||
|
show_last_error_message(L"SetWindowsHookEx", errorCode, L"PowerToys - Keyboard Manager");
|
||||||
|
auto errorMessage = get_last_error_message(errorCode);
|
||||||
|
Trace::Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"StartLowlevelKeyboardHook::SetWindowsHookEx");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardManager::StopLowlevelKeyboardHook()
|
||||||
|
{
|
||||||
|
if (hookHandle)
|
||||||
|
{
|
||||||
|
UnhookWindowsHookEx(hookHandle);
|
||||||
|
hookHandle = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
intptr_t KeyboardManager::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept
|
||||||
|
{
|
||||||
|
if (loadingSettings)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspend remapping if remap key/shortcut window is opened
|
||||||
|
auto h = CreateEvent(nullptr, true, false, KeyboardManagerConstants::EditorWindowEventName.c_str());
|
||||||
|
if (h != nullptr && WaitForSingleObject(h, 0) == WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If key has suppress flag, then suppress it
|
||||||
|
if (data->lParam->dwExtraInfo == KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remap a key
|
||||||
|
intptr_t SingleKeyRemapResult = KeyboardEventHandlers::HandleSingleKeyRemapEvent(inputHandler, data, keyboardManagerState);
|
||||||
|
|
||||||
|
// Single key remaps have priority. If a key is remapped, only the remapped version should be visible to the shortcuts and hence the event should be suppressed here.
|
||||||
|
if (SingleKeyRemapResult == 1)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This feature has not been enabled (code from proof of concept stage)
|
||||||
|
// Remap a key to behave like a modifier instead of a toggle
|
||||||
|
intptr_t SingleKeyToggleToModResult = KeyboardEventHandlers::HandleSingleKeyToggleToModEvent(inputHandler, data, keyboardManagerState);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Handle an app-specific shortcut remapping
|
||||||
|
intptr_t AppSpecificShortcutRemapResult = KeyboardEventHandlers::HandleAppSpecificShortcutRemapEvent(inputHandler, data, keyboardManagerState);
|
||||||
|
|
||||||
|
// If an app-specific shortcut is remapped then the os-level shortcut remapping should be suppressed.
|
||||||
|
if (AppSpecificShortcutRemapResult == 1)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle an os-level shortcut remapping
|
||||||
|
return KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent(inputHandler, data, keyboardManagerState);
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <common/utils/EventWaiter.h>
|
||||||
|
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||||
|
#include <keyboardmanager/common/Input.h>
|
||||||
|
|
||||||
|
class KeyboardManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Constructor
|
||||||
|
KeyboardManager();
|
||||||
|
|
||||||
|
void StartLowlevelKeyboardHook();
|
||||||
|
void StopLowlevelKeyboardHook();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Contains the non localized module name
|
||||||
|
std::wstring moduleName = KeyboardManagerConstants::ModuleName;
|
||||||
|
|
||||||
|
// Low level hook handles
|
||||||
|
static HHOOK hookHandle;
|
||||||
|
|
||||||
|
// Required for Unhook in old versions of Windows
|
||||||
|
static HHOOK hookHandleCopy;
|
||||||
|
|
||||||
|
// Static pointer to the current KeyboardManager object required for accessing the HandleKeyboardHookEvent function in the hook procedure
|
||||||
|
// Only global or static variables can be accessed in a hook procedure CALLBACK
|
||||||
|
static KeyboardManager* keyboardManagerObjectPtr;
|
||||||
|
|
||||||
|
// Variable which stores all the state information to be shared between the UI and back-end
|
||||||
|
KeyboardManagerState keyboardManagerState;
|
||||||
|
|
||||||
|
// Object of class which implements InputInterface. Required for calling library functions while enabling testing
|
||||||
|
KeyboardManagerInput::Input inputHandler;
|
||||||
|
|
||||||
|
// Auto reset event for waiting for settings changes. The event is signaled when settings are changed
|
||||||
|
EventWaiter settingsEventWaiter;
|
||||||
|
|
||||||
|
std::atomic_bool loadingSettings = false;
|
||||||
|
|
||||||
|
// Hook procedure definition
|
||||||
|
static LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
|
// Load settings from the file.
|
||||||
|
void LoadSettings();
|
||||||
|
|
||||||
|
// Function called by the hook procedure to handle the events. This is the starting point function for remapping
|
||||||
|
intptr_t HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept;
|
||||||
|
};
|
@ -0,0 +1,74 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>16.0</VCProjectVersion>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<ProjectGuid>{e496b7fc-1e99-4bab-849b-0e8367040b02}</ProjectGuid>
|
||||||
|
<RootNamespace>KeyboardManagerEngineLibrary</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.18362.0</WindowsTargetPlatformVersion>
|
||||||
|
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||||
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\</OutDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="KeyboardEventHandlers.h" />
|
||||||
|
<ClInclude Include="KeyboardManager.h" />
|
||||||
|
<ClInclude Include="pch.h" />
|
||||||
|
<ClInclude Include="trace.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="KeyboardEventHandlers.cpp" />
|
||||||
|
<ClCompile Include="KeyboardManager.cpp" />
|
||||||
|
<ClCompile Include="pch.cpp">
|
||||||
|
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="trace.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
|
||||||
|
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\..\common\SettingsAPI\SetttingsAPI.vcxproj">
|
||||||
|
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\common\KeyboardManagerCommon.vcxproj">
|
||||||
|
<Project>{8affa899-0b73-49ec-8c50-0fadda57b2fc}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||||
|
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
|
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files">
|
||||||
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
|
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Resource Files">
|
||||||
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="pch.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="KeyboardManager.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="trace.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="KeyboardEventHandlers.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="pch.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="KeyboardManager.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="trace.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="KeyboardEventHandlers.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
||||||
|
</packages>
|
@ -0,0 +1 @@
|
|||||||
|
#include "pch.h"
|
@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#include <ProjectTelemetry.h>
|
||||||
|
#include <shlwapi.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <winrt/base.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <common/SettingsAPI/settings_helpers.h>
|
||||||
|
#include <common/logger/logger.h>
|
@ -18,56 +18,6 @@ void Trace::UnregisterProvider() noexcept
|
|||||||
TraceLoggingUnregister(g_hProvider);
|
TraceLoggingUnregister(g_hProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log if the user has KBM enabled or disabled - Can also be used to see how often users have to restart the keyboard hook
|
|
||||||
void Trace::EnableKeyboardManager(const bool enabled) noexcept
|
|
||||||
{
|
|
||||||
TraceLoggingWrite(
|
|
||||||
g_hProvider,
|
|
||||||
"KeyboardManager_EnableKeyboardManager",
|
|
||||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
||||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
|
||||||
TraceLoggingBoolean(enabled, "Enabled"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log number of key remaps when the user uses Edit Keyboard and saves settings
|
|
||||||
void Trace::KeyRemapCount(const DWORD keyToKeyCount, const DWORD keyToShortcutCount) noexcept
|
|
||||||
{
|
|
||||||
TraceLoggingWrite(
|
|
||||||
g_hProvider,
|
|
||||||
"KeyboardManager_KeyRemapCount",
|
|
||||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
||||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
|
||||||
TraceLoggingValue(keyToKeyCount + keyToShortcutCount, "KeyRemapCount"),
|
|
||||||
TraceLoggingValue(keyToKeyCount, "KeyToKeyRemapCount"),
|
|
||||||
TraceLoggingValue(keyToShortcutCount, "KeyToShortcutRemapCount"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log number of os level shortcut remaps when the user uses Edit Shortcuts and saves settings
|
|
||||||
void Trace::OSLevelShortcutRemapCount(const DWORD shortcutToShortcutCount, const DWORD shortcutToKeyCount) noexcept
|
|
||||||
{
|
|
||||||
TraceLoggingWrite(
|
|
||||||
g_hProvider,
|
|
||||||
"KeyboardManager_OSLevelShortcutRemapCount",
|
|
||||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
||||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
|
||||||
TraceLoggingValue(shortcutToShortcutCount + shortcutToKeyCount, "OSLevelShortcutRemapCount"),
|
|
||||||
TraceLoggingValue(shortcutToShortcutCount, "OSLevelShortcutToShortcutRemapCount"),
|
|
||||||
TraceLoggingValue(shortcutToKeyCount, "OSLevelShortcutToKeyRemapCount"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log number of app specific shortcut remaps when the user uses Edit Shortcuts and saves settings
|
|
||||||
void Trace::AppSpecificShortcutRemapCount(const DWORD shortcutToShortcutCount, const DWORD shortcutToKeyCount) noexcept
|
|
||||||
{
|
|
||||||
TraceLoggingWrite(
|
|
||||||
g_hProvider,
|
|
||||||
"KeyboardManager_AppSpecificShortcutRemapCount",
|
|
||||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
||||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
|
||||||
TraceLoggingValue(shortcutToShortcutCount + shortcutToKeyCount, "AppSpecificShortcutRemapCount"),
|
|
||||||
TraceLoggingValue(shortcutToShortcutCount, "AppSpecificShortcutToShortcutRemapCount"),
|
|
||||||
TraceLoggingValue(shortcutToKeyCount, "AppSpecificShortcutToKeyRemapCount"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log if a key remap has been invoked
|
// Log if a key remap has been invoked
|
||||||
void Trace::KeyRemapInvoked(bool isKeyToKey) noexcept
|
void Trace::KeyRemapInvoked(bool isKeyToKey) noexcept
|
||||||
{
|
{
|
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class Trace
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void RegisterProvider() noexcept;
|
||||||
|
static void UnregisterProvider() noexcept;
|
||||||
|
|
||||||
|
// Log if a key remap has been invoked
|
||||||
|
static void KeyRemapInvoked(bool isKeyToKey) noexcept;
|
||||||
|
|
||||||
|
// Log if a shortcut remap has been invoked
|
||||||
|
static void ShortcutRemapInvoked(bool isShortcutToShortcut, bool isAppSpecific) noexcept;
|
||||||
|
|
||||||
|
// Log if an error occurs in KBM
|
||||||
|
static void Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept;
|
||||||
|
};
|
@ -2,7 +2,7 @@
|
|||||||
#include "CppUnitTest.h"
|
#include "CppUnitTest.h"
|
||||||
#include "MockedInput.h"
|
#include "MockedInput.h"
|
||||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||||
#include <keyboardmanager/dll/KeyboardEventHandlers.h>
|
#include <keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h>
|
||||||
#include "TestHelpers.h"
|
#include "TestHelpers.h"
|
||||||
#include <common/interop/shared_constants.h>
|
#include <common/interop/shared_constants.h>
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ namespace RemappingLogicTests
|
|||||||
|
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
MockedInput mockedInputHandler;
|
KeyboardManagerInput::MockedInput mockedInputHandler;
|
||||||
KeyboardManagerState testState;
|
KeyboardManagerState testState;
|
||||||
std::wstring testApp1 = L"testtrocess1.exe";
|
std::wstring testApp1 = L"testtrocess1.exe";
|
||||||
std::wstring testApp2 = L"testprocess2.exe";
|
std::wstring testApp2 = L"testprocess2.exe";
|
@ -1,5 +1,6 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "CppUnitTest.h"
|
#include "CppUnitTest.h"
|
||||||
|
#include <keyboardmanager/common/ErrorTypes.h>
|
||||||
#include <keyboardmanager/common/Helpers.h>
|
#include <keyboardmanager/common/Helpers.h>
|
||||||
#include "TestHelpers.h"
|
#include "TestHelpers.h"
|
||||||
#include <common/interop/keyboard_layout.h>
|
#include <common/interop/keyboard_layout.h>
|
@ -3,12 +3,13 @@
|
|||||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
|
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<VCProjectVersion>16.0</VCProjectVersion>
|
<VCProjectVersion>16.0</VCProjectVersion>
|
||||||
<ProjectGuid>{62173D9A-6724-4C00-A1C8-FB646480A9EC}</ProjectGuid>
|
<ProjectGuid>{7f4b3a60-bc27-45a7-8000-68b0b6ea7466}</ProjectGuid>
|
||||||
<Keyword>Win32Proj</Keyword>
|
<Keyword>Win32Proj</Keyword>
|
||||||
<RootNamespace>KeyboardManagerTest</RootNamespace>
|
<RootNamespace>KeyboardManagerEngineTest</RootNamespace>
|
||||||
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
|
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
|
||||||
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||||
<ProjectSubType>NativeUnitTestProject</ProjectSubType>
|
<ProjectSubType>NativeUnitTestProject</ProjectSubType>
|
||||||
|
<ProjectName>KeyboardManagerEngineTest</ProjectName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Label="Configuration">
|
<PropertyGroup Label="Configuration">
|
||||||
@ -19,15 +20,12 @@
|
|||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
<ImportGroup Label="Shared">
|
<ImportGroup Label="Shared">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<ImportGroup Label="PropertySheets">
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
<PropertyGroup Label="UserMacros" />
|
<PropertyGroup Label="UserMacros" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\</OutDir>
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\KeyboardManagerEngine\</OutDir>
|
||||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
@ -42,8 +40,6 @@
|
|||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="AppSpecificShortcutRemappingTests.cpp" />
|
<ClCompile Include="AppSpecificShortcutRemappingTests.cpp" />
|
||||||
<ClCompile Include="BufferValidationTests.cpp" />
|
|
||||||
<ClCompile Include="LoadingAndSavingRemappingTests.cpp" />
|
|
||||||
<ClCompile Include="MockedInputSanityTests.cpp" />
|
<ClCompile Include="MockedInputSanityTests.cpp" />
|
||||||
<ClCompile Include="SetKeyEventTests.cpp" />
|
<ClCompile Include="SetKeyEventTests.cpp" />
|
||||||
<ClCompile Include="OSLevelShortcutRemappingTests.cpp" />
|
<ClCompile Include="OSLevelShortcutRemappingTests.cpp" />
|
||||||
@ -53,7 +49,7 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="ShortcutTests.cpp" />
|
<ClCompile Include="ShortcutTests.cpp" />
|
||||||
<ClCompile Include="SingleKeyRemappingTests.cpp" />
|
<ClCompile Include="SingleKeyRemappingTests.cpp" />
|
||||||
<ClCompile Include="KeyboardManagerHelperTests.cpp" />
|
<ClCompile Include="HelperTests.cpp" />
|
||||||
<ClCompile Include="TestHelpers.cpp" />
|
<ClCompile Include="TestHelpers.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -69,15 +65,9 @@
|
|||||||
<ProjectReference Include="..\common\KeyboardManagerCommon.vcxproj">
|
<ProjectReference Include="..\common\KeyboardManagerCommon.vcxproj">
|
||||||
<Project>{8affa899-0b73-49ec-8c50-0fadda57b2fc}</Project>
|
<Project>{8affa899-0b73-49ec-8c50-0fadda57b2fc}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\dll\KeyboardManager.vcxproj">
|
<ProjectReference Include="..\KeyboardManagerEngineLibrary\KeyboardManagerEngineLibrary.vcxproj">
|
||||||
<Project>{89f34af7-1c34-4a72-aa6e-534bcf972bd9}</Project>
|
<Project>{e496b7fc-1e99-4bab-849b-0e8367040b02}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\ui\KeyboardManagerUI.vcxproj">
|
|
||||||
<Project>{eaf23649-ef6e-478b-980e-81fad96cca2a}</Project>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ResourceCompile Include="KeyboardManagerTest.rc" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
@ -39,13 +39,7 @@
|
|||||||
<ClCompile Include="AppSpecificShortcutRemappingTests.cpp">
|
<ClCompile Include="AppSpecificShortcutRemappingTests.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="LoadingAndSavingRemappingTests.cpp">
|
<ClCompile Include="HelperTests.cpp">
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="BufferValidationTests.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="KeyboardManagerHelperTests.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="ShortcutTests.cpp">
|
<ClCompile Include="ShortcutTests.cpp">
|
||||||
@ -66,11 +60,6 @@
|
|||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<ResourceCompile Include="KeyboardManagerTest.rc">
|
|
||||||
<Filter>Resource Files</Filter>
|
|
||||||
</ResourceCompile>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
@ -0,0 +1,163 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "MockedInput.h"
|
||||||
|
|
||||||
|
using namespace KeyboardManagerInput;
|
||||||
|
|
||||||
|
// Set the keyboard hook procedure to be tested
|
||||||
|
void MockedInput::SetHookProc(std::function<intptr_t(LowlevelKeyboardEvent*)> hookProcedure)
|
||||||
|
{
|
||||||
|
hookProc = hookProcedure;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to simulate keyboard input - arguments and return value based on SendInput function (https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput)
|
||||||
|
UINT MockedInput::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize)
|
||||||
|
{
|
||||||
|
// Iterate over inputs
|
||||||
|
for (UINT i = 0; i < cInputs; i++)
|
||||||
|
{
|
||||||
|
LowlevelKeyboardEvent keyEvent;
|
||||||
|
|
||||||
|
// Distinguish between key and sys key by checking if the key is either F10 (for syskeydown) or if the key message is sent while Alt is held down. SYSKEY messages are also sent if there is no window in focus, but that has not been mocked since it would require many changes. More details on key messages at https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-syskeydown
|
||||||
|
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
|
||||||
|
{
|
||||||
|
if (keyboardState[VK_MENU] == true)
|
||||||
|
{
|
||||||
|
keyEvent.wParam = WM_SYSKEYUP;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
keyEvent.wParam = WM_KEYUP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (pInputs[i].ki.wVk == VK_F10 || keyboardState[VK_MENU] == true)
|
||||||
|
{
|
||||||
|
keyEvent.wParam = WM_SYSKEYDOWN;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
keyEvent.wParam = WM_KEYDOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KBDLLHOOKSTRUCT lParam = {};
|
||||||
|
|
||||||
|
// Set only vkCode and dwExtraInfo since other values are unused
|
||||||
|
lParam.vkCode = pInputs[i].ki.wVk;
|
||||||
|
lParam.dwExtraInfo = pInputs[i].ki.dwExtraInfo;
|
||||||
|
keyEvent.lParam = &lParam;
|
||||||
|
|
||||||
|
// If the SendVirtualInput call condition is true, increment the count. If no condition is set then always increment the count
|
||||||
|
if (sendVirtualInputCallCondition == nullptr || sendVirtualInputCallCondition(&keyEvent))
|
||||||
|
{
|
||||||
|
sendVirtualInputCallCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call low level hook handler
|
||||||
|
intptr_t result = MockedKeyboardHook(&keyEvent);
|
||||||
|
|
||||||
|
// Set keyboard state if the hook does not suppress the input
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
// If key up flag is set, then set keyboard state to false
|
||||||
|
keyboardState[pInputs[i].ki.wVk] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||||
|
|
||||||
|
// Handling modifier key codes
|
||||||
|
switch (pInputs[i].ki.wVk)
|
||||||
|
{
|
||||||
|
case VK_CONTROL:
|
||||||
|
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
|
||||||
|
{
|
||||||
|
keyboardState[VK_LCONTROL] = false;
|
||||||
|
keyboardState[VK_RCONTROL] = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case VK_LCONTROL:
|
||||||
|
keyboardState[VK_CONTROL] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||||
|
break;
|
||||||
|
case VK_RCONTROL:
|
||||||
|
keyboardState[VK_CONTROL] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||||
|
break;
|
||||||
|
case VK_MENU:
|
||||||
|
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
|
||||||
|
{
|
||||||
|
keyboardState[VK_LMENU] = false;
|
||||||
|
keyboardState[VK_RMENU] = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case VK_LMENU:
|
||||||
|
keyboardState[VK_MENU] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||||
|
break;
|
||||||
|
case VK_RMENU:
|
||||||
|
keyboardState[VK_MENU] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||||
|
break;
|
||||||
|
case VK_SHIFT:
|
||||||
|
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
|
||||||
|
{
|
||||||
|
keyboardState[VK_LSHIFT] = false;
|
||||||
|
keyboardState[VK_RSHIFT] = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case VK_LSHIFT:
|
||||||
|
keyboardState[VK_SHIFT] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||||
|
break;
|
||||||
|
case VK_RSHIFT:
|
||||||
|
keyboardState[VK_SHIFT] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cInputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to simulate keyboard hook behavior
|
||||||
|
intptr_t MockedInput::MockedKeyboardHook(LowlevelKeyboardEvent* data)
|
||||||
|
{
|
||||||
|
// If the hookProc is set to null, then skip the hook
|
||||||
|
if (hookProc != nullptr)
|
||||||
|
{
|
||||||
|
return hookProc(data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to get the state of a particular key
|
||||||
|
bool MockedInput::GetVirtualKeyState(int key)
|
||||||
|
{
|
||||||
|
return keyboardState[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to reset the mocked keyboard state
|
||||||
|
void MockedInput::ResetKeyboardState()
|
||||||
|
{
|
||||||
|
std::fill(keyboardState.begin(), keyboardState.end(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to set SendVirtualInput call count condition
|
||||||
|
void MockedInput::SetSendVirtualInputTestHandler(std::function<bool(LowlevelKeyboardEvent*)> condition)
|
||||||
|
{
|
||||||
|
sendVirtualInputCallCount = 0;
|
||||||
|
sendVirtualInputCallCondition = condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to get SendVirtualInput call count
|
||||||
|
int MockedInput::GetSendVirtualInputCallCount()
|
||||||
|
{
|
||||||
|
return sendVirtualInputCallCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to get the foreground process name
|
||||||
|
void MockedInput::SetForegroundProcess(std::wstring process)
|
||||||
|
{
|
||||||
|
currentProcess = process;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to get the foreground process name
|
||||||
|
void MockedInput::GetForegroundProcess(_Out_ std::wstring& foregroundProcess)
|
||||||
|
{
|
||||||
|
foregroundProcess = currentProcess;
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <keyboardmanager/common/InputInterface.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <common/hooks/LowlevelKeyboardEvent.h>
|
||||||
|
|
||||||
|
namespace KeyboardManagerInput
|
||||||
|
{
|
||||||
|
// Class for mocked keyboard input
|
||||||
|
class MockedInput :
|
||||||
|
public InputInterface
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Stores the states for all the keys - false for key up, and true for key down
|
||||||
|
std::vector<bool> keyboardState;
|
||||||
|
|
||||||
|
// Function to be executed as a low level hook. By default it is nullptr so the hook is skipped
|
||||||
|
std::function<intptr_t(LowlevelKeyboardEvent*)> hookProc;
|
||||||
|
|
||||||
|
// Stores the count of sendVirtualInput calls given if the condition sendVirtualInputCallCondition is satisfied
|
||||||
|
int sendVirtualInputCallCount = 0;
|
||||||
|
std::function<bool(LowlevelKeyboardEvent*)> sendVirtualInputCallCondition;
|
||||||
|
|
||||||
|
std::wstring currentProcess;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MockedInput()
|
||||||
|
{
|
||||||
|
keyboardState.resize(256, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the keyboard hook procedure to be tested
|
||||||
|
void SetHookProc(std::function<intptr_t(LowlevelKeyboardEvent*)> hookProcedure);
|
||||||
|
|
||||||
|
// Function to simulate keyboard input
|
||||||
|
UINT SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize);
|
||||||
|
|
||||||
|
// Function to simulate keyboard hook behavior
|
||||||
|
intptr_t MockedKeyboardHook(LowlevelKeyboardEvent* data);
|
||||||
|
|
||||||
|
// Function to get the state of a particular key
|
||||||
|
bool GetVirtualKeyState(int key);
|
||||||
|
|
||||||
|
// Function to reset the mocked keyboard state
|
||||||
|
void ResetKeyboardState();
|
||||||
|
|
||||||
|
// Function to set SendVirtualInput call count condition
|
||||||
|
void SetSendVirtualInputTestHandler(std::function<bool(LowlevelKeyboardEvent*)> condition);
|
||||||
|
|
||||||
|
// Function to get SendVirtualInput call count
|
||||||
|
int GetSendVirtualInputCallCount();
|
||||||
|
|
||||||
|
// Function to get the foreground process name
|
||||||
|
void SetForegroundProcess(std::wstring process);
|
||||||
|
|
||||||
|
// Function to get the foreground process name
|
||||||
|
void GetForegroundProcess(_Out_ std::wstring& foregroundProcess);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user