[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
|
||||
abgr
|
||||
ABlocked
|
||||
ABOUTBOX
|
||||
Abug
|
||||
accctrl
|
||||
Acceleratorkeys
|
||||
|
@ -118,6 +119,7 @@ atlstr
|
|||
attr
|
||||
Attribs
|
||||
aumid
|
||||
Aut
|
||||
AUTHN
|
||||
AUTOAPPEND
|
||||
autocomplete
|
||||
|
@ -396,6 +398,7 @@ davidegiacometti
|
|||
Dayof
|
||||
dbdfc
|
||||
Dbg
|
||||
Dbghelp
|
||||
DBLCLKS
|
||||
DBLEPSILON
|
||||
DCOM
|
||||
|
@ -1117,6 +1120,7 @@ KEYBDINPUT
|
|||
keyboardeventhandlers
|
||||
keyboardmanager
|
||||
keyboardmanagercommon
|
||||
KEYBOARDMANAGEREDITOR
|
||||
keyboardmanagerstate
|
||||
keyboardmanagerui
|
||||
keycode
|
||||
|
@ -1510,6 +1514,7 @@ oldnewthing
|
|||
oldpath
|
||||
oldtheme
|
||||
oleaut
|
||||
OleAut
|
||||
OLECHAR
|
||||
OLEDB
|
||||
OLIVEGREEN
|
||||
|
@ -1737,6 +1742,7 @@ readme
|
|||
READMODE
|
||||
readonly
|
||||
READWRITE
|
||||
REALTIME
|
||||
RECTDESTINATION
|
||||
RECTL
|
||||
rectp
|
||||
|
@ -2008,6 +2014,7 @@ stdcall
|
|||
stdcpp
|
||||
stdcpplatest
|
||||
stdexcept
|
||||
stdio
|
||||
stdin
|
||||
stdlib
|
||||
STDMETHODCALLTYPE
|
||||
|
@ -2027,6 +2034,7 @@ Strikethrough
|
|||
Stringified
|
||||
stringify
|
||||
STRINGIZE
|
||||
stringstream
|
||||
stringtable
|
||||
stringval
|
||||
Strmiids
|
||||
|
@ -2179,6 +2187,7 @@ Tz
|
|||
UAC
|
||||
UAL
|
||||
uap
|
||||
UCHAR
|
||||
udit
|
||||
UIA
|
||||
Uid
|
||||
|
@ -2423,6 +2432,7 @@ wstr
|
|||
wstring
|
||||
wstringstream
|
||||
wsz
|
||||
wtoi
|
||||
WTS
|
||||
WTSAT
|
||||
wu
|
||||
|
|
|
@ -163,7 +163,8 @@ steps:
|
|||
configuration: '$(BuildConfiguration)'
|
||||
testSelector: 'testAssemblies'
|
||||
testAssemblyVer2: |
|
||||
**\KeyboardManagerTest.dll
|
||||
**\KeyboardManagerEngineTest.dll
|
||||
**\KeyboardManagerEditorTest.dll
|
||||
**\UnitTests-CommonLib.dll
|
||||
**\PowerRenameUnitTests.dll
|
||||
**\powerpreviewTest.dll
|
||||
|
|
|
@ -98,6 +98,8 @@ build:
|
|||
- 'modules\ImageResizer\ManagedTelemetry.dll'
|
||||
- 'modules\ImageResizer\Microsoft.PowerToys.Common.UI.dll'
|
||||
- 'modules\KeyboardManager\KeyboardManager.dll'
|
||||
- 'modules\KeyboardManager\KeyboardManagerEditor\PowerToys.KeyboardManagerEditor.exe'
|
||||
- 'modules\KeyboardManager\KeyboardManagerEngine\PowerToys.KeyboardManagerEngine.exe'
|
||||
- 'modules\launcher\Microsoft.PowerToys.Settings.UI.Lib.dll'
|
||||
- 'modules\launcher\ManagedCommon.dll'
|
||||
- 'modules\launcher\Microsoft.PowerToys.Common.UI.dll'
|
||||
|
|
|
@ -103,8 +103,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImageResizerExt", "src\modu
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageResizerUITest", "src\modules\imageresizer\tests\ImageResizerUITest.csproj", "{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerUI", "src\modules\keyboardmanager\ui\KeyboardManagerUI.vcxproj", "{EAF23649-EF6E-478B-980E-81FAD96CCA2A}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "action_runner", "src\action_runner\action_runner.vcxproj", "{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {17DA04DF-E393-4397-9CF0-84DABE11032E}
|
||||
|
@ -210,8 +208,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerLauncher.Telemetry", "
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedTelemetry", "src\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj", "{5D00D290-4016-4CFE-9E41-1E7C724509BA}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerTest", "src\modules\keyboardmanager\test\KeyboardManagerTest.vcxproj", "{62173D9A-6724-4C00-A1C8-FB646480A9EC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCommon", "src\common\ManagedCommon\ManagedCommon.csproj", "{4AED67B6-55FD-486F-B917-E543DEE2CB3C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Plugin.Program.UnitTests", "src\modules\launcher\Plugins\Microsoft.Plugin.Program.UnitTests\Microsoft.Plugin.Program.UnitTests.csproj", "{42851751-CBC8-45A6-97F5-7A0753F7B4D1}"
|
||||
|
@ -285,14 +281,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "utils", "utils", "{B39DC643
|
|||
src\common\utils\appMutex.h = src\common\utils\appMutex.h
|
||||
src\common\utils\com_object_factory.h = src\common\utils\com_object_factory.h
|
||||
src\common\utils\elevation.h = src\common\utils\elevation.h
|
||||
src\common\utils\EventLocker.h = src\common\utils\EventLocker.h
|
||||
src\common\utils\EventWaiter.h = src\common\utils\EventWaiter.h
|
||||
src\common\utils\exec.h = src\common\utils\exec.h
|
||||
src\common\utils\json.h = src\common\utils\json.h
|
||||
src\common\utils\logger_helper.h = src\common\utils\logger_helper.h
|
||||
src\common\utils\os-detect.h = src\common\utils\os-detect.h
|
||||
src\common\utils\process_path.h = src\common\utils\process_path.h
|
||||
src\common\utils\ProcessWaiter.h = src\common\utils\ProcessWaiter.h
|
||||
src\common\utils\resources.h = src\common\utils\resources.h
|
||||
src\common\utils\string_utils.h = src\common\utils\string_utils.h
|
||||
src\common\utils\timeutil.h = src\common\utils\timeutil.h
|
||||
src\common\utils\UnhandledExceptionHandler_x64.h = src\common\utils\UnhandledExceptionHandler_x64.h
|
||||
src\common\utils\winapi_error.h = src\common\utils\winapi_error.h
|
||||
src\common\utils\window.h = src\common\utils\window.h
|
||||
EndProjectSection
|
||||
|
@ -312,6 +312,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plu
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerToys.Settings", "src\settings-ui\PowerToys.Settings\PowerToys.Settings.csproj", "{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEngine", "src\modules\keyboardmanager\KeyboardManagerEngine\KeyboardManagerEngine.vcxproj", "{BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEngineLibrary", "src\modules\keyboardmanager\KeyboardManagerEngineLibrary\KeyboardManagerEngineLibrary.vcxproj", "{E496B7FC-1E99-4BAB-849B-0E8367040B02}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEngineTest", "src\modules\keyboardmanager\KeyboardManagerEngineTest\KeyboardManagerEngineTest.vcxproj", "{7F4B3A60-BC27-45A7-8000-68B0B6EA7466}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEditor", "src\modules\keyboardmanager\KeyboardManagerEditor\KeyboardManagerEditor.vcxproj", "{8DF78B53-200E-451F-9328-01EB907193AE}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEditorLibrary", "src\modules\keyboardmanager\KeyboardManagerEditorLibrary\KeyboardManagerEditorLibrary.vcxproj", "{23D2070D-E4AD-4ADD-85A7-083D9C76AD49}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEditorTest", "src\modules\keyboardmanager\KeyboardManagerEditorTest\KeyboardManagerEditorTest.vcxproj", "{62173D9A-6724-4C00-A1C8-FB646480A9EC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
|
@ -390,10 +402,6 @@ Global
|
|||
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Debug|x64.Build.0 = Debug|x64
|
||||
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x64.ActiveCfg = Release|x64
|
||||
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x64.Build.0 = Release|x64
|
||||
{EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Debug|x64.Build.0 = Debug|x64
|
||||
{EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Release|x64.ActiveCfg = Release|x64
|
||||
{EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Release|x64.Build.0 = Release|x64
|
||||
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x64.Build.0 = Debug|x64
|
||||
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x64.ActiveCfg = Release|x64
|
||||
|
@ -506,10 +514,6 @@ Global
|
|||
{5D00D290-4016-4CFE-9E41-1E7C724509BA}.Debug|x64.Build.0 = Debug|x64
|
||||
{5D00D290-4016-4CFE-9E41-1E7C724509BA}.Release|x64.ActiveCfg = Release|x64
|
||||
{5D00D290-4016-4CFE-9E41-1E7C724509BA}.Release|x64.Build.0 = Release|x64
|
||||
{62173D9A-6724-4C00-A1C8-FB646480A9EC}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{62173D9A-6724-4C00-A1C8-FB646480A9EC}.Debug|x64.Build.0 = Debug|x64
|
||||
{62173D9A-6724-4C00-A1C8-FB646480A9EC}.Release|x64.ActiveCfg = Release|x64
|
||||
{62173D9A-6724-4C00-A1C8-FB646480A9EC}.Release|x64.Build.0 = Release|x64
|
||||
{4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Debug|x64.Build.0 = Debug|x64
|
||||
{4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Release|x64.ActiveCfg = Release|x64
|
||||
|
@ -634,6 +638,30 @@ Global
|
|||
{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Debug|x64.Build.0 = Debug|x64
|
||||
{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Release|x64.ActiveCfg = Release|x64
|
||||
{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Release|x64.Build.0 = Release|x64
|
||||
{BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}.Debug|x64.Build.0 = Debug|x64
|
||||
{BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}.Release|x64.ActiveCfg = Release|x64
|
||||
{BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}.Release|x64.Build.0 = Release|x64
|
||||
{E496B7FC-1E99-4BAB-849B-0E8367040B02}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{E496B7FC-1E99-4BAB-849B-0E8367040B02}.Debug|x64.Build.0 = Debug|x64
|
||||
{E496B7FC-1E99-4BAB-849B-0E8367040B02}.Release|x64.ActiveCfg = Release|x64
|
||||
{E496B7FC-1E99-4BAB-849B-0E8367040B02}.Release|x64.Build.0 = Release|x64
|
||||
{7F4B3A60-BC27-45A7-8000-68B0B6EA7466}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{7F4B3A60-BC27-45A7-8000-68B0B6EA7466}.Debug|x64.Build.0 = Debug|x64
|
||||
{7F4B3A60-BC27-45A7-8000-68B0B6EA7466}.Release|x64.ActiveCfg = Release|x64
|
||||
{7F4B3A60-BC27-45A7-8000-68B0B6EA7466}.Release|x64.Build.0 = Release|x64
|
||||
{8DF78B53-200E-451F-9328-01EB907193AE}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{8DF78B53-200E-451F-9328-01EB907193AE}.Debug|x64.Build.0 = Debug|x64
|
||||
{8DF78B53-200E-451F-9328-01EB907193AE}.Release|x64.ActiveCfg = Release|x64
|
||||
{8DF78B53-200E-451F-9328-01EB907193AE}.Release|x64.Build.0 = Release|x64
|
||||
{23D2070D-E4AD-4ADD-85A7-083D9C76AD49}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{23D2070D-E4AD-4ADD-85A7-083D9C76AD49}.Debug|x64.Build.0 = Debug|x64
|
||||
{23D2070D-E4AD-4ADD-85A7-083D9C76AD49}.Release|x64.ActiveCfg = Release|x64
|
||||
{23D2070D-E4AD-4ADD-85A7-083D9C76AD49}.Release|x64.Build.0 = Release|x64
|
||||
{62173D9A-6724-4C00-A1C8-FB646480A9EC}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{62173D9A-6724-4C00-A1C8-FB646480A9EC}.Debug|x64.Build.0 = Debug|x64
|
||||
{62173D9A-6724-4C00-A1C8-FB646480A9EC}.Release|x64.ActiveCfg = Release|x64
|
||||
{62173D9A-6724-4C00-A1C8-FB646480A9EC}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -659,7 +687,6 @@ Global
|
|||
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34} = {6C7F47CC-2151-44A3-A546-41C70025132C}
|
||||
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B} = {6C7F47CC-2151-44A3-A546-41C70025132C}
|
||||
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8} = {6C7F47CC-2151-44A3-A546-41C70025132C}
|
||||
{EAF23649-EF6E-478B-980E-81FAD96CCA2A} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
|
||||
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||
{38BDB927-829B-4C65-9CD9-93FB05D66D65} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{8AFFA899-0B73-49EC-8C50-0FADDA57B2FC} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
|
||||
|
@ -691,7 +718,6 @@ Global
|
|||
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
|
||||
{08C8C05F-0362-41BC-818C-724572DF8B06} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
|
||||
{5D00D290-4016-4CFE-9E41-1E7C724509BA} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||
{62173D9A-6724-4C00-A1C8-FB646480A9EC} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
|
||||
{4AED67B6-55FD-486F-B917-E543DEE2CB3C} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||
{42851751-CBC8-45A6-97F5-7A0753F7B4D1} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
|
||||
{1EF1EEF0-10F0-4F2E-8550-39B6D8044D3E} = {2F305555-C296-497E-AC20-5FA1B237996A}
|
||||
|
@ -729,6 +755,12 @@ Global
|
|||
{4BABF3FE-3451-42FD-873F-3C332E18DCEF} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
|
||||
{0648DF05-5DDA-4BE1-B5F2-584926EBDB65} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
|
||||
{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A} = {C3081D9A-1586-441A-B5F4-ED815B3719C1}
|
||||
{BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
|
||||
{E496B7FC-1E99-4BAB-849B-0E8367040B02} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
|
||||
{7F4B3A60-BC27-45A7-8000-68B0B6EA7466} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
|
||||
{8DF78B53-200E-451F-9328-01EB907193AE} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
|
||||
{23D2070D-E4AD-4ADD-85A7-083D9C76AD49} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
|
||||
{62173D9A-6724-4C00-A1C8-FB646480A9EC} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||
|
|
|
@ -212,7 +212,10 @@
|
|||
<Directory Id="ShortcutGuideInstallFolder" Name="$(var.ShortcutGuideProjectName)"/>
|
||||
<Directory Id="FileExplorerPreviewInstallFolder" Name="FileExplorerPreview" />
|
||||
<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="ColorPickerResourcesFolder" Name="Resources"/>
|
||||
</Directory>
|
||||
|
@ -589,6 +592,18 @@
|
|||
</Component>
|
||||
</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)">
|
||||
<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?>
|
||||
|
@ -779,6 +794,8 @@
|
|||
<ComponentRef Id="Module_PowerPreview" />
|
||||
<ComponentRef Id="Module_PowerPreview_PerUserRegistry" />
|
||||
<ComponentRef Id="Module_KeyboardManager" />
|
||||
<ComponentRef Id="Module_KeyboardManager_Editor" />
|
||||
<ComponentRef Id="Module_KeyboardManager_Engine" />
|
||||
<ComponentRef Id="Module_ColorPicker" />
|
||||
<ComponentRef Id="Module_ColorPicker_Resources"/>
|
||||
<ComponentRef Id="SettingsV2" />
|
||||
|
|
|
@ -155,9 +155,5 @@ public
|
|||
static String ^ ShowShortcutGuideSharedEvent() {
|
||||
return gcnew String(CommonSharedConstants::SHOW_SHORTCUT_GUIDE_SHARED_EVENT);
|
||||
}
|
||||
|
||||
static String ^ KeyboardManagerConfigFileMutexName() {
|
||||
return gcnew String(CommonSharedConstants::KEYBOARD_MANAGER_CONFIG_FILE_MUTEX_NAME);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -27,7 +27,4 @@ namespace CommonSharedConstants
|
|||
|
||||
// Max DWORD for key code to disable keys.
|
||||
const int VK_DISABLED = 0x100;
|
||||
|
||||
// Name of the mutex which controls access to the configuration file for Keyboard Manager
|
||||
const wchar_t KEYBOARD_MANAGER_CONFIG_FILE_MUTEX_NAME[] = L"Local\\PowerToys_KeyboardManager_ConfigFileMutex";
|
||||
}
|
||||
|
|
|
@ -55,4 +55,9 @@ public:
|
|||
{
|
||||
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 <common/version/version.h>
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
|
||||
namespace LoggerHelpers
|
||||
{
|
||||
|
@ -79,4 +80,20 @@ namespace LoggerHelpers
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void init_logger(std::wstring moduleName, std::wstring internalPath, std::string loggerName)
|
||||
{
|
||||
std::filesystem::path rootFolder(PTSettingsHelper::get_module_save_folder_location(moduleName));
|
||||
rootFolder.append(internalPath);
|
||||
|
||||
auto currentFolder = rootFolder;
|
||||
currentFolder.append(LogSettings::logPath);
|
||||
currentFolder.append(get_product_version());
|
||||
|
||||
auto logsPath = currentFolder;
|
||||
logsPath.append(L"log.txt");
|
||||
Logger::init(loggerName, logsPath.wstring(), PTSettingsHelper::get_log_settings_file_location());
|
||||
|
||||
delete_other_versions_log_folders(rootFolder.wstring(), currentFolder);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,12 @@ inline std::optional<std::wstring> get_last_error_message(const DWORD dw)
|
|||
return message;
|
||||
}
|
||||
|
||||
inline std::wstring get_last_error_or_default(const DWORD dw)
|
||||
{
|
||||
auto message = get_last_error_message(dw);
|
||||
return message.has_value() ? message.value() : L"";
|
||||
}
|
||||
|
||||
inline void show_last_error_message(const wchar_t* functionName, DWORD dw, const wchar_t* errorTitle)
|
||||
{
|
||||
const auto system_message = get_last_error_message(dw);
|
||||
|
|
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
|
||||
// Do not define WIN32_LEAN_AND_MEAN as WinUI doesn't work when it is defined
|
||||
#include <unknwn.h>
|
||||
#include <windows.h>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
#include <winrt/Windows.system.h>
|
||||
#include <winrt/windows.ui.xaml.hosting.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.ui.xaml.media.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include "winrt/Windows.Foundation.h"
|
||||
#include "winrt/Windows.Foundation.Numerics.h"
|
||||
#include "winrt/Windows.UI.Xaml.Controls.Primitives.h"
|
||||
#include "winrt/Windows.UI.Text.h"
|
||||
#include "winrt/Windows.UI.Core.h"
|
||||
#include <winrt/Windows.UI.Xaml.Interop.h>
|
||||
#include <mutex>
|
||||
#include <common/logger/logger.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;
|
||||
#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 <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,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 "BufferValidationHelpers.h"
|
||||
#include <keyboardmanager/common/KeyboardManagerConstants.h>
|
||||
|
||||
#include <common/interop/shared_constants.h>
|
||||
#include <modules\keyboardmanager\ui\KeyDropDownControl.h>
|
||||
|
||||
#include <KeyboardManagerEditorStrings.h>
|
||||
#include <KeyboardManagerConstants.h>
|
||||
#include <KeyDropDownControl.h>
|
||||
|
||||
namespace BufferValidationHelpers
|
||||
{
|
||||
|
@ -82,9 +85,9 @@ namespace BufferValidationHelpers
|
|||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
|
||||
}
|
||||
// If it is the last drop down
|
||||
else if (dropDownIndex == dropDownCount - 1)
|
||||
{
|
||||
// If it is the last drop down
|
||||
// If last drop down and a modifier is selected: add a new drop down (max drop down count should be enforced)
|
||||
if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode) && dropDownCount < KeyboardManagerConstants::MaxShortcutSize)
|
||||
{
|
||||
|
@ -94,21 +97,21 @@ namespace BufferValidationHelpers
|
|||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier;
|
||||
}
|
||||
// If not, add a new drop down
|
||||
else
|
||||
{
|
||||
// If not, add a new drop down
|
||||
dropDownAction = BufferValidationHelpers::DropDownAction::AddDropDown;
|
||||
}
|
||||
}
|
||||
// If last drop down and a modifier is selected but there are already max drop downs: warn the user
|
||||
else if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode) && dropDownCount >= KeyboardManagerConstants::MaxShortcutSize)
|
||||
{
|
||||
// If last drop down and a modifier is selected but there are already max drop downs: warn the user
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutMaxShortcutSizeOneActionKey;
|
||||
}
|
||||
// If None is selected but it's the last index: warn
|
||||
else if (selectedKeyCode == 0)
|
||||
{
|
||||
// If None is selected but it's the last index: warn
|
||||
// If it is a hybrid control and there are 2 drop downs then deletion is allowed
|
||||
if (isHybridControl && dropDownCount == KeyboardManagerConstants::MinShortcutSize)
|
||||
{
|
||||
|
@ -122,16 +125,16 @@ namespace BufferValidationHelpers
|
|||
errorType = KeyboardManagerHelper::ErrorType::ShortcutOneActionKey;
|
||||
}
|
||||
}
|
||||
// Disable can not be selected if one modifier key has already been selected
|
||||
else if (selectedKeyCode == CommonSharedConstants::VK_DISABLED && dropDownIndex)
|
||||
{
|
||||
// Disable can not be selected if one modifier key has already been selected
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey;
|
||||
}
|
||||
// If none of the above, then the action key will be set
|
||||
}
|
||||
// If it is not the last drop down
|
||||
else
|
||||
{
|
||||
// If it is not the last drop down
|
||||
if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode))
|
||||
{
|
||||
// If it matched any of the previous modifiers then reset that drop down
|
||||
|
@ -142,9 +145,9 @@ namespace BufferValidationHelpers
|
|||
}
|
||||
// If not, the modifier key will be set
|
||||
}
|
||||
// If None is selected and there are more than 2 drop downs
|
||||
else if (selectedKeyCode == 0 && dropDownCount > KeyboardManagerConstants::MinShortcutSize)
|
||||
{
|
||||
// If None is selected and there are more than 2 drop downs
|
||||
// set delete drop down flag
|
||||
dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown;
|
||||
// do not delete the drop down now since there may be some other error which would cause the drop down to be invalid after removal
|
||||
|
@ -164,14 +167,15 @@ namespace BufferValidationHelpers
|
|||
errorType = KeyboardManagerHelper::ErrorType::ShortcutAtleast2Keys;
|
||||
}
|
||||
}
|
||||
// Allow selection of VK_DISABLE only in first dropdown
|
||||
else if (selectedKeyCode == CommonSharedConstants::VK_DISABLED && dropDownIndex)
|
||||
{
|
||||
// Allow selection of VK_DISABLE only in first dropdown
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey;
|
||||
}
|
||||
// If the user tries to set an action key check if all drop down menus after this are empty if it is not the first key. If it is a hybrid control, this can be done even on the first key
|
||||
else if (dropDownIndex != 0 || isHybridControl)
|
||||
{
|
||||
// If the user tries to set an action key check if all drop down menus after this are empty if it is not the first key.
|
||||
// If it is a hybrid control, this can be done even on the first key
|
||||
bool isClear = true;
|
||||
for (int i = dropDownIndex + 1; i < (int)dropDownCount; i++)
|
||||
{
|
||||
|
@ -192,9 +196,9 @@ namespace BufferValidationHelpers
|
|||
errorType = KeyboardManagerHelper::ErrorType::ShortcutNotMoreThanOneActionKey;
|
||||
}
|
||||
}
|
||||
// If there an action key is chosen on the first drop down and there are more than one drop down menus
|
||||
else
|
||||
{
|
||||
// If there an action key is chosen on the first drop down and there are more than one drop down menus
|
||||
// warn and reset the drop down
|
||||
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier;
|
||||
}
|
||||
|
@ -217,7 +221,7 @@ namespace BufferValidationHelpers
|
|||
|
||||
// Convert app name to lower case
|
||||
std::transform(appName.begin(), appName.end(), appName.begin(), towlower);
|
||||
std::wstring lowercaseDefAppName = KeyboardManagerConstants::DefaultAppName;
|
||||
std::wstring lowercaseDefAppName = KeyboardManagerEditorStrings::DefaultAppName;
|
||||
std::transform(lowercaseDefAppName.begin(), lowercaseDefAppName.end(), lowercaseDefAppName.begin(), towlower);
|
||||
if (appName == lowercaseDefAppName)
|
||||
{
|
|
@ -1,8 +1,6 @@
|
|||
#pragma once
|
||||
#include "keyboardmanager/common/Helpers.h"
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include "keyboardmanager/common/Shortcut.h"
|
||||
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
|
||||
namespace BufferValidationHelpers
|
||||
{
|
|
@ -1,8 +1,5 @@
|
|||
#include "pch.h"
|
||||
#include "Dialog.h"
|
||||
#include <set>
|
||||
#include "keyboardmanager/dll/Generated Files/resource.h"
|
||||
#include <common/utils/resources.h>
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
|
|
@ -1,20 +1,25 @@
|
|||
#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 "ErrorTypes.h"
|
||||
#include "SingleKeyRemapControl.h"
|
||||
#include "KeyDropDownControl.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 "Dialog.h"
|
||||
#include <keyboardmanager/dll/Generated Files/resource.h>
|
||||
#include <common/interop/shared_constants.h>
|
||||
#include "keyboardmanager/common/KeyboardManagerState.h"
|
||||
#include "LoadingAndSavingRemappingHelper.h"
|
||||
#include "UIHelpers.h"
|
||||
#include <common/utils/winapi_error.h>
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
|
||||
|
@ -22,11 +27,14 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND, UINT, WPARAM, LPARAM);
|
|||
|
||||
// This Hwnd will be the window handler for the Xaml Island: A child window that contains Xaml.
|
||||
HWND hWndXamlIslandEditKeyboardWindow = nullptr;
|
||||
|
||||
// This variable is used to check if window registration has been done to avoid repeated registration leading to an error.
|
||||
bool isEditKeyboardWindowRegistrationCompleted = false;
|
||||
|
||||
// Holds the native window handle of EditKeyboard Window.
|
||||
HWND hwndEditKeyboardNativeWindow = nullptr;
|
||||
std::mutex editKeyboardWindowMutex;
|
||||
|
||||
// Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure
|
||||
static XamlBridge* xamlBridgePtr = nullptr;
|
||||
|
||||
|
@ -52,6 +60,7 @@ static IAsyncOperation<bool> OrphanKeysConfirmationDialog(
|
|||
orphanKeyString.append(state.keyboardMap.GetKeyName(k));
|
||||
orphanKeyString.append(L", ");
|
||||
}
|
||||
|
||||
orphanKeyString = orphanKeyString.substr(0, max(0, orphanKeyString.length() - 2));
|
||||
orphanKeysBlock.Text(winrt::hstring(orphanKeyString));
|
||||
orphanKeysBlock.TextWrapping(TextWrapping::Wrap);
|
||||
|
@ -84,13 +93,21 @@ static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, Xa
|
|||
co_return;
|
||||
}
|
||||
}
|
||||
|
||||
ApplyRemappings();
|
||||
}
|
||||
|
||||
// Function to create the Edit Keyboard Window
|
||||
void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
|
||||
inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
|
||||
{
|
||||
Logger::trace("Creating Remap keys window");
|
||||
Logger::trace("CreateEditKeyboardWindowImpl()");
|
||||
auto locker = EventLocker::Get(KeyboardManagerConstants::EditorWindowEventName.c_str());
|
||||
if (!locker.has_value())
|
||||
{
|
||||
Logger::error(L"Failed to lock event {}. {}", KeyboardManagerConstants::EditorWindowEventName, get_last_error_or_default(GetLastError()));
|
||||
}
|
||||
|
||||
Logger::trace(L"Signaled {} event to suspend the KBM engine", KeyboardManagerConstants::EditorWindowEventName);
|
||||
|
||||
// Window Registration
|
||||
const wchar_t szWindowClass[] = L"EditKeyboardWindow";
|
||||
|
@ -109,6 +126,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||
48,
|
||||
48,
|
||||
LR_DEFAULTCOLOR);
|
||||
|
||||
if (RegisterClassEx(&windowClass) == NULL)
|
||||
{
|
||||
MessageBox(NULL, GET_RESOURCE_STRING(IDS_REGISTERCLASSFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_REGISTERCLASSFAILED_ERRORTITLE).c_str(), NULL);
|
||||
|
@ -139,11 +157,13 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||
NULL,
|
||||
hInst,
|
||||
NULL);
|
||||
|
||||
if (_hWndEditKeyboardWindow == NULL)
|
||||
{
|
||||
MessageBox(NULL, GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORTITLE).c_str(), NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensures the window is in foreground on first startup. If this is not done, the window appears behind because the thread is not on the foreground.
|
||||
if (_hWndEditKeyboardWindow)
|
||||
{
|
||||
|
@ -157,8 +177,10 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||
|
||||
// Create the xaml bridge object
|
||||
XamlBridge xamlBridge(_hWndEditKeyboardWindow);
|
||||
|
||||
// DesktopSource needs to be declared before the RelativePanel xamlContainer object to avoid errors
|
||||
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource;
|
||||
|
||||
// Create the desktop window xaml source object and set its content
|
||||
hWndXamlIslandEditKeyboardWindow = xamlBridge.InitDesktopWindowsXamlSource(desktopSource);
|
||||
|
||||
|
@ -205,7 +227,7 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||
TextBlock originalKeyRemapHeader;
|
||||
originalKeyRemapHeader.Text(GET_RESOURCE_STRING(IDS_EDITKEYBOARD_SOURCEHEADER));
|
||||
originalKeyRemapHeader.FontWeight(Text::FontWeights::Bold());
|
||||
StackPanel originalKeyHeaderContainer = KeyboardManagerHelper::GetWrapped(originalKeyRemapHeader, KeyboardManagerConstants::RemapTableDropDownWidth + KeyboardManagerConstants::TableArrowColWidth).as<StackPanel>();
|
||||
StackPanel originalKeyHeaderContainer = UIHelpers::GetWrapped(originalKeyRemapHeader, KeyboardManagerConstants::RemapTableDropDownWidth + KeyboardManagerConstants::TableArrowColWidth).as<StackPanel>();
|
||||
|
||||
// Second header textblock in the header row of the keys remap table
|
||||
TextBlock newKeyRemapHeader;
|
||||
|
@ -220,11 +242,14 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||
|
||||
// Store handle of edit keyboard window
|
||||
SingleKeyRemapControl::EditKeyboardWindowHandle = _hWndEditKeyboardWindow;
|
||||
|
||||
// Store keyboard manager state
|
||||
SingleKeyRemapControl::keyboardManagerState = &keyboardManagerState;
|
||||
KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
|
||||
|
||||
// Clear the single key remap buffer
|
||||
SingleKeyRemapControl::singleKeyRemapBuffer.clear();
|
||||
|
||||
// Vector to store dynamically allocated control objects to avoid early destruction
|
||||
std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>> keyboardRemapControlObjects;
|
||||
|
||||
|
@ -251,13 +276,8 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||
header.SetLeftOf(applyButton, cancelButton);
|
||||
|
||||
auto ApplyRemappings = [&keyboardManagerState, _hWndEditKeyboardWindow]() {
|
||||
// Disable the remappings while the remapping table is updated
|
||||
keyboardManagerState.RemappingsDisabledWrapper(
|
||||
[&keyboardManagerState]() {
|
||||
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(keyboardManagerState, SingleKeyRemapControl::singleKeyRemapBuffer, true);
|
||||
// Save the updated shortcuts remaps to file.
|
||||
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
||||
});
|
||||
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(keyboardManagerState, SingleKeyRemapControl::singleKeyRemapBuffer, true);
|
||||
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
||||
PostMessage(_hWndEditKeyboardWindow, WM_CLOSE, 0, 0);
|
||||
};
|
||||
|
||||
|
@ -352,9 +372,40 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
|
|||
xamlBridge.ClearXamlIslands();
|
||||
}
|
||||
|
||||
void CreateEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
|
||||
{
|
||||
// Move implementation into the separate method so resources get destroyed correctly
|
||||
CreateEditKeyboardWindowImpl(hInst, keyboardManagerState);
|
||||
|
||||
// Calling ClearXamlIslands() outside of the message loop is not enough to prevent
|
||||
// Microsoft.UI.XAML.dll from crashing during deinitialization, see https://github.com/microsoft/PowerToys/issues/10906
|
||||
Logger::trace("Terminating process {}", GetCurrentProcessId());
|
||||
Logger::flush();
|
||||
TerminateProcess(GetCurrentProcess(), 0);
|
||||
}
|
||||
|
||||
inline std::wstring getMessage(UINT messageCode)
|
||||
{
|
||||
switch (messageCode)
|
||||
{
|
||||
case WM_SIZE:
|
||||
return L"WM_SIZE";
|
||||
case WM_NCDESTROY:
|
||||
return L"WM_NCDESTROY";
|
||||
default:
|
||||
return L"";
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
RECT rcClient;
|
||||
auto message = getMessage(messageCode);
|
||||
if (message != L"")
|
||||
{
|
||||
Logger::trace(L"EditKeyboardWindowProc() messageCode={}", getMessage(messageCode));
|
||||
}
|
||||
|
||||
switch (messageCode)
|
||||
{
|
||||
// Resize the XAML window whenever the parent window is painted or resized
|
||||
|
@ -387,6 +438,7 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wPar
|
|||
PostQuitMessage(0);
|
||||
break;
|
||||
}
|
||||
|
||||
return DefWindowProc(hWnd, messageCode, wParam, lParam);
|
||||
break;
|
||||
}
|
||||
|
@ -406,6 +458,7 @@ bool CheckEditKeyboardWindowActive()
|
|||
{
|
||||
ShowWindow(hwndEditKeyboardNativeWindow, SW_RESTORE);
|
||||
}
|
||||
|
||||
// If there is an already existing window no need to create a new open bring it on foreground.
|
||||
SetForegroundWindow(hwndEditKeyboardNativeWindow);
|
||||
result = true;
|
||||
|
@ -420,6 +473,7 @@ void CloseActiveEditKeyboardWindow()
|
|||
std::unique_lock<std::mutex> hwndLock(editKeyboardWindowMutex);
|
||||
if (hwndEditKeyboardNativeWindow != nullptr)
|
||||
{
|
||||
Logger::trace("CloseActiveEditKeyboardWindow()");
|
||||
PostMessage(hwndEditKeyboardNativeWindow, WM_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
#pragma once
|
||||
class KeyboardManagerState;
|
||||
|
||||
// Function to create the Edit Keyboard Window
|
||||
void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState);
|
||||
|
||||
// Function to check if there is already a window active if yes bring to foreground
|
||||
bool CheckEditKeyboardWindowActive();
|
||||
|
||||
// Function to close any active Edit Keyboard window
|
||||
#pragma once
|
||||
class KeyboardManagerState;
|
||||
|
||||
// Function to create the Edit Keyboard Window
|
||||
void CreateEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState);
|
||||
|
||||
// Function to check if there is already a window active if yes bring to foreground
|
||||
bool CheckEditKeyboardWindowActive();
|
||||
|
||||
// Function to close any active Edit Keyboard window
|
||||
void CloseActiveEditKeyboardWindow();
|
|
@ -1,18 +1,20 @@
|
|||
#include "pch.h"
|
||||
#include "EditShortcutsWindow.h"
|
||||
#include "ShortcutControl.h"
|
||||
#include "KeyDropDownControl.h"
|
||||
#include "XamlBridge.h"
|
||||
#include <keyboardmanager/common/trace.h>
|
||||
#include <keyboardmanager/common/KeyboardManagerConstants.h>
|
||||
#include <common/themes/windows_colors.h>
|
||||
#include <common/display/dpi_aware.h>
|
||||
#include "Styles.h"
|
||||
#include "Dialog.h"
|
||||
#include <keyboardmanager/dll/Generated Files/resource.h>
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include "LoadingAndSavingRemappingHelper.h"
|
||||
#include "UIHelpers.h"
|
||||
|
||||
#include <common/Display/dpi_aware.h>
|
||||
#include <common/utils/EventLocker.h>
|
||||
|
||||
#include <KeyboardManagerState.h>
|
||||
|
||||
#include <Dialog.h>
|
||||
#include <ErrorTypes.h>
|
||||
#include <KeyDropDownControl.h>
|
||||
#include <LoadingAndSavingRemappingHelper.h>
|
||||
#include <ShortcutControl.h>
|
||||
#include <Styles.h>
|
||||
#include <UIHelpers.h>
|
||||
#include <XamlBridge.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
|
||||
|
@ -20,11 +22,14 @@ LRESULT CALLBACK EditShortcutsWindowProc(HWND, UINT, WPARAM, LPARAM);
|
|||
|
||||
// This Hwnd will be the window handler for the Xaml Island: A child window that contains Xaml.
|
||||
HWND hWndXamlIslandEditShortcutsWindow = nullptr;
|
||||
|
||||
// This variable is used to check if window registration has been done to avoid repeated registration leading to an error.
|
||||
bool isEditShortcutsWindowRegistrationCompleted = false;
|
||||
|
||||
// Holds the native window handle of EditShortcuts Window.
|
||||
HWND hwndEditShortcutsNativeWindow = nullptr;
|
||||
std::mutex editShortcutsWindowMutex;
|
||||
|
||||
// Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure
|
||||
static XamlBridge* xamlBridgePtr = nullptr;
|
||||
|
||||
|
@ -46,9 +51,16 @@ static IAsyncAction OnClickAccept(
|
|||
}
|
||||
|
||||
// Function to create the Edit Shortcuts Window
|
||||
void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
|
||||
inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
|
||||
{
|
||||
Logger::trace("Creating Remap shortcuts window");
|
||||
Logger::trace("CreateEditShortcutsWindowImpl()");
|
||||
auto locker = EventLocker::Get(KeyboardManagerConstants::EditorWindowEventName.c_str());
|
||||
if (!locker.has_value())
|
||||
{
|
||||
Logger::error(L"Failed to lock event {}. {}", KeyboardManagerConstants::EditorWindowEventName, get_last_error_or_default(GetLastError()));
|
||||
}
|
||||
|
||||
Logger::trace(L"Signaled {} event to suspend the KBM engine", KeyboardManagerConstants::EditorWindowEventName);
|
||||
|
||||
// Window Registration
|
||||
const wchar_t szWindowClass[] = L"EditShortcutsWindow";
|
||||
|
@ -98,11 +110,13 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
|||
NULL,
|
||||
hInst,
|
||||
NULL);
|
||||
|
||||
if (_hWndEditShortcutsWindow == NULL)
|
||||
{
|
||||
MessageBox(NULL, GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORTITLE).c_str(), NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensures the window is in foreground on first startup. If this is not done, the window appears behind because the thread is not on the foreground.
|
||||
if (_hWndEditShortcutsWindow)
|
||||
{
|
||||
|
@ -116,8 +130,10 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
|||
|
||||
// Create the xaml bridge object
|
||||
XamlBridge xamlBridge(_hWndEditShortcutsWindow);
|
||||
|
||||
// DesktopSource needs to be declared before the RelativePanel xamlContainer object to avoid errors
|
||||
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource;
|
||||
|
||||
// Create the desktop window xaml source object and set its content
|
||||
hWndXamlIslandEditShortcutsWindow = xamlBridge.InitDesktopWindowsXamlSource(desktopSource);
|
||||
|
||||
|
@ -179,19 +195,22 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
|||
StackPanel tableHeader = StackPanel();
|
||||
tableHeader.Orientation(Orientation::Horizontal);
|
||||
tableHeader.Margin({ 10, 0, 0, 10 });
|
||||
auto originalShortcutContainer = KeyboardManagerHelper::GetWrapped(originalShortcutHeader, KeyboardManagerConstants::ShortcutOriginColumnWidth + (double)KeyboardManagerConstants::ShortcutArrowColumnWidth);
|
||||
auto originalShortcutContainer = UIHelpers::GetWrapped(originalShortcutHeader, KeyboardManagerConstants::ShortcutOriginColumnWidth + (double)KeyboardManagerConstants::ShortcutArrowColumnWidth);
|
||||
tableHeader.Children().Append(originalShortcutContainer.as<FrameworkElement>());
|
||||
auto newShortcutHeaderContainer = KeyboardManagerHelper::GetWrapped(newShortcutHeader, KeyboardManagerConstants::ShortcutTargetColumnWidth);
|
||||
auto newShortcutHeaderContainer = UIHelpers::GetWrapped(newShortcutHeader, KeyboardManagerConstants::ShortcutTargetColumnWidth);
|
||||
tableHeader.Children().Append(newShortcutHeaderContainer.as<FrameworkElement>());
|
||||
tableHeader.Children().Append(targetAppHeader);
|
||||
|
||||
// Store handle of edit shortcuts window
|
||||
ShortcutControl::EditShortcutsWindowHandle = _hWndEditShortcutsWindow;
|
||||
ShortcutControl::editShortcutsWindowHandle = _hWndEditShortcutsWindow;
|
||||
|
||||
// Store keyboard manager state
|
||||
ShortcutControl::keyboardManagerState = &keyboardManagerState;
|
||||
KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
|
||||
|
||||
// Clear the shortcut remap buffer
|
||||
ShortcutControl::shortcutRemapBuffer.clear();
|
||||
|
||||
// Vector to store dynamically allocated control objects to avoid early destruction
|
||||
std::vector<std::vector<std::unique_ptr<ShortcutControl>>> keyboardRemapControlObjects;
|
||||
|
||||
|
@ -231,13 +250,8 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
|||
header.SetLeftOf(applyButton, cancelButton);
|
||||
|
||||
auto ApplyRemappings = [&keyboardManagerState, _hWndEditShortcutsWindow]() {
|
||||
// Disable the remappings while the remapping table is updated
|
||||
keyboardManagerState.RemappingsDisabledWrapper(
|
||||
[&keyboardManagerState]() {
|
||||
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(keyboardManagerState, ShortcutControl::shortcutRemapBuffer, true);
|
||||
// Save the updated key remaps to file.
|
||||
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
||||
});
|
||||
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(keyboardManagerState, ShortcutControl::shortcutRemapBuffer, true);
|
||||
bool saveResult = keyboardManagerState.SaveConfigToFile();
|
||||
PostMessage(_hWndEditShortcutsWindow, WM_CLOSE, 0, 0);
|
||||
};
|
||||
|
||||
|
@ -332,6 +346,18 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa
|
|||
xamlBridge.ClearXamlIslands();
|
||||
}
|
||||
|
||||
void CreateEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
|
||||
{
|
||||
// Move implementation into the separate method so resources get destroyed correctly
|
||||
CreateEditShortcutsWindowImpl(hInst, keyboardManagerState);
|
||||
|
||||
// Calling ClearXamlIslands() outside of the message loop is not enough to prevent
|
||||
// Microsoft.UI.XAML.dll from crashing during deinitialization, see https://github.com/microsoft/PowerToys/issues/10906
|
||||
Logger::trace("Terminating process {}", GetCurrentProcessId());
|
||||
Logger::flush();
|
||||
TerminateProcess(GetCurrentProcess(), 0);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
RECT rcClient;
|
|
@ -1,11 +1,11 @@
|
|||
#pragma once
|
||||
class KeyboardManagerState;
|
||||
|
||||
// Function to create the Edit Shortcuts Window
|
||||
void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState);
|
||||
|
||||
// Function to check if there is already a window active if yes bring to foreground
|
||||
bool CheckEditShortcutsWindowActive();
|
||||
|
||||
// Function to close any active Edit Shortcuts window
|
||||
#pragma once
|
||||
class KeyboardManagerState;
|
||||
|
||||
// Function to create the Edit Shortcuts Window
|
||||
void CreateEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState);
|
||||
|
||||
// Function to check if there is already a window active if yes bring to foreground
|
||||
bool CheckEditShortcutsWindowActive();
|
||||
|
||||
// Function to close any active Edit Shortcuts window
|
||||
void CloseActiveEditShortcutsWindow();
|
|
@ -1,11 +1,13 @@
|
|||
#include "pch.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/keyboard_layout_impl.h>
|
||||
#include <modules/keyboardmanager/common/Helpers.h>
|
||||
#include <KeyboardManagerState.h>
|
||||
|
||||
#include <BufferValidationHelpers.h>
|
||||
#include <KeyboardManagerEditorStrings.h>
|
||||
#include <ErrorTypes.h>
|
||||
#include <UIHelpers.h>
|
||||
|
||||
// Initialized to null
|
||||
KeyboardManagerState* KeyDropDownControl::keyboardManagerState = nullptr;
|
||||
|
@ -15,7 +17,9 @@ DWORD KeyDropDownControl::GetSelectedValue(ComboBox comboBox)
|
|||
{
|
||||
auto dataContext = comboBox.SelectedValue();
|
||||
if (!dataContext)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto value = winrt::unbox_value<hstring>(dataContext);
|
||||
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>().MaxDropDownHeight(KeyboardManagerConstants::TableDropDownHeight);
|
||||
|
||||
// Initialise layout attribute
|
||||
previousLayout = GetKeyboardLayout(0);
|
||||
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
|
||||
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)));
|
||||
warningFlyout.as<Flyout>().FlyoutPresenterStyle(style);
|
||||
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)
|
||||
SetAccessibleNameForComboBox(dropDown.as<ComboBox>(), 1);
|
||||
}
|
||||
|
@ -94,7 +101,7 @@ void KeyDropDownControl::CheckAndUpdateKeyboardLayout(ComboBox currentDropDown,
|
|||
// Check if the layout has changed
|
||||
if (previousLayout != layout)
|
||||
{
|
||||
currentDropDown.ItemsSource(KeyboardManagerHelper::ToBoxValue(GetKeyList(isShortcut, renderDisable)));
|
||||
currentDropDown.ItemsSource(UIHelpers::ToBoxValue(GetKeyList(isShortcut, renderDisable)));
|
||||
previousLayout = layout;
|
||||
}
|
||||
}
|
||||
|
@ -112,13 +119,14 @@ void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row,
|
|||
|
||||
ComboBox currentDropDown = sender.as<ComboBox>();
|
||||
int selectedKeyCode = GetSelectedValue(currentDropDown);
|
||||
|
||||
// Validate current remap selection
|
||||
KeyboardManagerHelper::ErrorType errorType = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(rowIndex, colIndex, selectedKeyCode, singleKeyRemapBuffer);
|
||||
|
||||
// If there is an error set the warning flyout
|
||||
if (errorType != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
SetDropDownError(currentDropDown, KeyboardManagerHelper::GetErrorMessage(errorType));
|
||||
SetDropDownError(currentDropDown, KeyboardManagerEditorStrings::GetErrorMessage(errorType));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -185,7 +193,7 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
|||
// If the remapping is invalid display an error message
|
||||
if (validationResult.first != KeyboardManagerHelper::ErrorType::NoError)
|
||||
{
|
||||
SetDropDownError(currentDropDown, KeyboardManagerHelper::GetErrorMessage(validationResult.first));
|
||||
SetDropDownError(currentDropDown, KeyboardManagerEditorStrings::GetErrorMessage(validationResult.first));
|
||||
}
|
||||
|
||||
// Handle None case if there are no other errors
|
||||
|
@ -199,6 +207,7 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
|
|||
}
|
||||
|
||||
parent.Children().RemoveAt(dropDownIndex);
|
||||
|
||||
// delete drop down control object from the vector so that it can be destructed
|
||||
keyDropDownControlObjects.erase(keyDropDownControlObjects.begin() + dropDownIndex);
|
||||
parent.UpdateLayout();
|
||||
|
@ -251,10 +260,11 @@ void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row,
|
|||
shortcutRemapBuffer[validationResult.second].first[colIndex] = tempShortcut;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetApp != nullptr)
|
||||
{
|
||||
std::wstring newText = targetApp.Text().c_str();
|
||||
std::wstring lowercaseDefAppName = KeyboardManagerConstants::DefaultAppName;
|
||||
std::wstring lowercaseDefAppName = KeyboardManagerEditorStrings::DefaultAppName;
|
||||
std::transform(newText.begin(), newText.end(), newText.begin(), towlower);
|
||||
std::transform(lowercaseDefAppName.begin(), lowercaseDefAppName.end(), lowercaseDefAppName.begin(), towlower);
|
||||
if (newText == lowercaseDefAppName)
|
||||
|
@ -372,6 +382,7 @@ void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, StackPanel tabl
|
|||
{
|
||||
// Delete the existing drop down menus
|
||||
parent.Children().Clear();
|
||||
|
||||
// Remove references to the old drop down objects to destroy them
|
||||
keyDropDownControlObjects.clear();
|
||||
std::vector<DWORD> shortcutKeyCodes = shortcut.GetKeyCodes();
|
||||
|
@ -397,6 +408,7 @@ void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, StackPanel tabl
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
parent.UpdateLayout();
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include <keyboardmanager/common/Shortcut.h>
|
||||
#include <vector>
|
||||
|
||||
#include <Shortcut.h>
|
||||
|
||||
class KeyboardManagerState;
|
||||
|
||||
namespace winrt::Windows
|
||||
|
@ -30,12 +31,16 @@ class KeyDropDownControl
|
|||
private:
|
||||
// Stores the drop down combo box
|
||||
winrt::Windows::Foundation::IInspectable dropDown;
|
||||
|
||||
// Stores the previous layout
|
||||
HKL previousLayout = 0;
|
||||
|
||||
// Stores the flyout warning message
|
||||
winrt::Windows::Foundation::IInspectable warningMessage;
|
||||
|
||||
// Stores the flyout attached to the current drop down
|
||||
winrt::Windows::Foundation::IInspectable warningFlyout;
|
||||
|
||||
// Stores whether a key to shortcut warning has to be ignored
|
||||
bool ignoreKeyToShortcutWarning;
|
||||
|
||||
|
@ -50,6 +55,7 @@ private:
|
|||
|
||||
// Function to set accessible name for combobox
|
||||
static void SetAccessibleNameForComboBox(ComboBox dropDown, int index);
|
||||
|
||||
public:
|
||||
// Pointer to the keyboard manager state
|
||||
static KeyboardManagerState* keyboardManagerState;
|
|
@ -1,96 +1,102 @@
|
|||
<?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">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{EAF23649-EF6E-478B-980E-81FAD96CCA2A}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>KeyboardManagerUI</RootNamespace>
|
||||
<CppWinRTEnabled>True</CppWinRTEnabled>
|
||||
<ProjectName>KeyboardManagerUI</ProjectName>
|
||||
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\</OutDir>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>Spectre</SpectreMitigation>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<DisableSpecificWarnings>4002</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<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>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BufferValidationHelpers.cpp" />
|
||||
<ClCompile Include="Dialog.cpp" />
|
||||
<ClCompile Include="EditKeyboardWindow.cpp" />
|
||||
<ClCompile Include="EditShortcutsWindow.cpp" />
|
||||
<ClCompile Include="KeyDropDownControl.cpp" />
|
||||
<ClCompile Include="LoadingAndSavingRemappingHelper.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShortcutControl.cpp" />
|
||||
<ClCompile Include="SingleKeyRemapControl.cpp" />
|
||||
<ClCompile Include="Styles.cpp" />
|
||||
<ClCompile Include="UIHelpers.cpp" />
|
||||
<ClCompile Include="XamlBridge.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="BufferValidationHelpers.h" />
|
||||
<ClInclude Include="Dialog.h" />
|
||||
<ClInclude Include="EditKeyboardWindow.h" />
|
||||
<ClInclude Include="EditShortcutsWindow.h" />
|
||||
<ClInclude Include="LoadingAndSavingRemappingHelper.h" />
|
||||
<ClInclude Include="Styles.h" />
|
||||
<ClInclude Include="KeyDropDownControl.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="ShortcutControl.h" />
|
||||
<ClInclude Include="SingleKeyRemapControl.h" />
|
||||
<ClInclude Include="UIHelpers.h" />
|
||||
<ClInclude Include="XamlBridge.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
|
||||
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</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>
|
||||
<?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>{23d2070d-e4ad-4add-85a7-083d9c76ad49}</ProjectGuid>
|
||||
<RootNamespace>KeyboardManagerEditorLibrary</RootNamespace>
|
||||
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
|
||||
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||
</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\modules\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;./../common;./../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="BufferValidationHelpers.h" />
|
||||
<ClInclude Include="Dialog.h" />
|
||||
<ClInclude Include="EditKeyboardWindow.h" />
|
||||
<ClInclude Include="EditShortcutsWindow.h" />
|
||||
<ClInclude Include="KeyboardManagerEditorStrings.h" />
|
||||
<ClInclude Include="KeyDropDownControl.h" />
|
||||
<ClInclude Include="LoadingAndSavingRemappingHelper.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="ShortcutControl.h" />
|
||||
<ClInclude Include="SingleKeyRemapControl.h" />
|
||||
<ClInclude Include="Styles.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
<ClInclude Include="trace.h" />
|
||||
<ClInclude Include="UIHelpers.h" />
|
||||
<ClInclude Include="XamlBridge.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BufferValidationHelpers.cpp" />
|
||||
<ClCompile Include="Dialog.cpp" />
|
||||
<ClCompile Include="EditKeyboardWindow.cpp" />
|
||||
<ClCompile Include="EditShortcutsWindow.cpp" />
|
||||
<ClCompile Include="KeyboardManagerEditorStrings.cpp" />
|
||||
<ClCompile Include="KeyDropDownControl.cpp" />
|
||||
<ClCompile Include="LoadingAndSavingRemappingHelper.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShortcutControl.cpp" />
|
||||
<ClCompile Include="SingleKeyRemapControl.cpp" />
|
||||
<ClCompile Include="Styles.cpp" />
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<ClCompile Include="UIHelpers.cpp" />
|
||||
<ClCompile Include="XamlBridge.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</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>
|
||||
</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)\..\KeyboardManagerEditor\ resource.base.h resource.h KeyboardManagerEditor.base.rc KeyboardManagerEditor.rc" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -1,96 +1,111 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Dialog.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EditKeyboardWindow.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EditShortcutsWindow.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KeyDropDownControl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShortcutControl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SingleKeyRemapControl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Styles.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="XamlBridge.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LoadingAndSavingRemappingHelper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BufferValidationHelpers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UIHelpers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Dialog.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="EditKeyboardWindow.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="EditShortcutsWindow.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="KeyDropDownControl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ShortcutControl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SingleKeyRemapControl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Styles.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="XamlBridge.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LoadingAndSavingRemappingHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BufferValidationHelpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="UIHelpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{7ccc5562-a9e1-4a3a-9f23-bdfee9ed5776}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{80d1fd84-2f25-463b-9fc7-ab7e7e9529c0}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{7bd580d1-f340-4817-9893-e5cbfd20cf54}</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>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<?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="BufferValidationHelpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Dialog.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="EditKeyboardWindow.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="EditShortcutsWindow.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="KeyDropDownControl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LoadingAndSavingRemappingHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ShortcutControl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SingleKeyRemapControl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Styles.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="targetver.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="UIHelpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="XamlBridge.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="KeyboardManagerEditorStrings.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="trace.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BufferValidationHelpers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Dialog.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EditKeyboardWindow.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EditShortcutsWindow.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KeyDropDownControl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LoadingAndSavingRemappingHelper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShortcutControl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SingleKeyRemapControl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Styles.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<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>
|
|
@ -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 "LoadingAndSavingRemappingHelper.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <common/interop/shared_constants.h>
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/common/trace.h>
|
||||
#include <ErrorTypes.h>
|
||||
#include <KeyboardManagerState.h>
|
||||
|
||||
#include <keyboardmanager/KeyboardManagerEditorLibrary/trace.h>
|
||||
|
||||
namespace LoadingAndSavingRemappingHelper
|
||||
{
|
||||
|
@ -40,6 +44,7 @@ namespace LoadingAndSavingRemappingHelper
|
|||
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful;
|
||||
}
|
||||
}
|
||||
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
|
@ -173,6 +178,7 @@ namespace LoadingAndSavingRemappingHelper
|
|||
DWORD successfulOSLevelShortcutToKeyRemapCount = 0;
|
||||
DWORD successfulAppSpecificShortcutToShortcutRemapCount = 0;
|
||||
DWORD successfulAppSpecificShortcutToKeyRemapCount = 0;
|
||||
|
||||
// Save the shortcuts that are valid and report if any of them were invalid
|
||||
for (int i = 0; i < remappings.size(); i++)
|
||||
{
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include <variant>
|
||||
|
||||
class KeyboardManagerState;
|
||||
|
|
@ -1,477 +1,482 @@
|
|||
#include "pch.h"
|
||||
#include "ShortcutControl.h"
|
||||
#include "KeyDropDownControl.h"
|
||||
#include "keyboardmanager/common/KeyboardManagerState.h"
|
||||
#include "keyboardmanager/common/Helpers.h"
|
||||
#include "keyboardmanager/dll/Generated Files/resource.h"
|
||||
#include <common/interop/shared_constants.h>
|
||||
|
||||
//Both static members are initialized to null
|
||||
HWND ShortcutControl::EditShortcutsWindowHandle = nullptr;
|
||||
KeyboardManagerState* ShortcutControl::keyboardManagerState = nullptr;
|
||||
// Initialized as new vector
|
||||
RemapBuffer ShortcutControl::shortcutRemapBuffer;
|
||||
|
||||
ShortcutControl::ShortcutControl(StackPanel table, StackPanel row, const int colIndex, TextBox targetApp)
|
||||
{
|
||||
shortcutDropDownStackPanel = StackPanel();
|
||||
typeShortcut = Button();
|
||||
shortcutControlLayout = StackPanel();
|
||||
bool isHybridControl = colIndex == 1 ? true : false;
|
||||
|
||||
shortcutDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
shortcutDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
|
||||
typeShortcut.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
||||
typeShortcut.as<Button>().Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
|
||||
typeShortcut.as<Button>().Click([&, table, row, colIndex, isHybridControl, targetApp](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowActivated, EditShortcutsWindowHandle);
|
||||
// Using the XamlRoot of the typeShortcut to get the root of the XAML host
|
||||
createDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, targetApp, isHybridControl, false, EditShortcutsWindowHandle, shortcutRemapBuffer);
|
||||
});
|
||||
// Set an accessible name for the type shortcut button
|
||||
typeShortcut.as<Button>().SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
||||
|
||||
shortcutControlLayout.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
|
||||
shortcutControlLayout.as<StackPanel>().Children().Append(typeShortcut.as<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>().UpdateLayout();
|
||||
}
|
||||
|
||||
// Function to set the accessible name of the target App text box
|
||||
void ShortcutControl::SetAccessibleNameForTextBox(TextBox targetAppTextBox, int rowIndex)
|
||||
{
|
||||
// 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);
|
||||
if (targetAppTextBox.Text() == L"")
|
||||
{
|
||||
targetAppTextBoxAccessibleName += GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_ALLAPPS);
|
||||
}
|
||||
targetAppTextBox.SetValue(Automation::AutomationProperties::NameProperty(), box_value(targetAppTextBoxAccessibleName));
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
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 shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values.
|
||||
void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys, const KeyShortcutUnion& newKeys, const std::wstring& targetAppName)
|
||||
{
|
||||
// Textbox for target application
|
||||
TextBox targetAppTextBox;
|
||||
|
||||
// Create new ShortcutControl objects dynamically so that we does not get destructed
|
||||
std::vector<std::unique_ptr<ShortcutControl>> newrow;
|
||||
StackPanel row = StackPanel();
|
||||
parent.Children().Append(row);
|
||||
newrow.emplace_back(std::make_unique<ShortcutControl>(parent, row, 0, targetAppTextBox));
|
||||
newrow.emplace_back(std::make_unique<ShortcutControl>(parent, row, 1, targetAppTextBox));
|
||||
keyboardRemapControlObjects.push_back(std::move(newrow));
|
||||
|
||||
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>();
|
||||
if (keyboardRemapControlObjects.size() % 2)
|
||||
{
|
||||
row.Background(brush);
|
||||
}
|
||||
|
||||
// ShortcutControl for the original shortcut
|
||||
auto origin = keyboardRemapControlObjects.back()[0]->getShortcutControl();
|
||||
origin.Width(KeyboardManagerConstants::ShortcutOriginColumnWidth);
|
||||
row.Children().Append(origin);
|
||||
|
||||
// Arrow icon
|
||||
FontIcon arrowIcon;
|
||||
arrowIcon.FontFamily(Xaml::Media::FontFamily(L"Segoe MDL2 Assets"));
|
||||
arrowIcon.Glyph(L"\xE72A");
|
||||
arrowIcon.VerticalAlignment(VerticalAlignment::Center);
|
||||
arrowIcon.HorizontalAlignment(HorizontalAlignment::Center);
|
||||
auto arrowIconContainer = KeyboardManagerHelper::GetWrapped(arrowIcon, KeyboardManagerConstants::ShortcutArrowColumnWidth).as<StackPanel>();
|
||||
arrowIconContainer.Orientation(Orientation::Vertical);
|
||||
arrowIconContainer.VerticalAlignment(VerticalAlignment::Center);
|
||||
row.Children().Append(arrowIconContainer);
|
||||
|
||||
// ShortcutControl for the new shortcut
|
||||
auto target = keyboardRemapControlObjects.back()[1]->getShortcutControl();
|
||||
target.Width(KeyboardManagerConstants::ShortcutTargetColumnWidth);
|
||||
row.Children().Append(target);
|
||||
|
||||
targetAppTextBox.Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
|
||||
targetAppTextBox.PlaceholderText(KeyboardManagerConstants::DefaultAppName);
|
||||
targetAppTextBox.Text(targetAppName);
|
||||
|
||||
// GotFocus handler will be called whenever the user tabs into or clicks on the textbox
|
||||
targetAppTextBox.GotFocus([targetAppTextBox](auto const& sender, auto const& e) {
|
||||
// Select all text for accessible purpose
|
||||
targetAppTextBox.SelectAll();
|
||||
});
|
||||
|
||||
// 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.LostFocus([&keyboardRemapControlObjects, parent, row, targetAppTextBox](auto const& sender, auto const& e) {
|
||||
// Get index of targetAppTextBox button
|
||||
uint32_t rowIndex;
|
||||
if (!parent.Children().IndexOf(row, rowIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// rowIndex could be out of bounds if the the row got deleted after LostFocus handler was invoked. In this case it should return
|
||||
if (rowIndex >= keyboardRemapControlObjects.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate both set of drop downs
|
||||
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);
|
||||
|
||||
// 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>()));
|
||||
// second column is a hybrid column
|
||||
|
||||
std::vector<int32_t> selectedKeyCodes = KeyDropDownControl::GetSelectedCodesFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>());
|
||||
|
||||
// If exactly one key is selected consider it to be a key remap
|
||||
if (selectedKeyCodes.size() == 1)
|
||||
{
|
||||
shortcutRemapBuffer[rowIndex].first[1] = selectedKeyCodes[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
Shortcut tempShortcut;
|
||||
tempShortcut.SetKeyCodes(selectedKeyCodes);
|
||||
// Assign instead of setting the value in the buffer since the previous value may not be a Shortcut
|
||||
shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||
}
|
||||
std::wstring newText = targetAppTextBox.Text().c_str();
|
||||
std::wstring lowercaseDefAppName = KeyboardManagerConstants::DefaultAppName;
|
||||
std::transform(newText.begin(), newText.end(), newText.begin(), towlower);
|
||||
std::transform(lowercaseDefAppName.begin(), lowercaseDefAppName.end(), lowercaseDefAppName.begin(), towlower);
|
||||
if (newText == lowercaseDefAppName)
|
||||
{
|
||||
shortcutRemapBuffer[rowIndex].second = L"";
|
||||
}
|
||||
else
|
||||
{
|
||||
shortcutRemapBuffer[rowIndex].second = targetAppTextBox.Text().c_str();
|
||||
}
|
||||
|
||||
// To set the accessibile name of the target app text box when focus is lost
|
||||
ShortcutControl::SetAccessibleNameForTextBox(targetAppTextBox, rowIndex + 1);
|
||||
});
|
||||
|
||||
// We need two containers in order to align it horizontally and vertically
|
||||
StackPanel targetAppHorizontal = KeyboardManagerHelper::GetWrapped(targetAppTextBox, KeyboardManagerConstants::TableTargetAppColWidth).as<StackPanel>();
|
||||
targetAppHorizontal.Orientation(Orientation::Horizontal);
|
||||
targetAppHorizontal.HorizontalAlignment(HorizontalAlignment::Left);
|
||||
StackPanel targetAppContainer = KeyboardManagerHelper::GetWrapped(targetAppHorizontal, KeyboardManagerConstants::TableTargetAppColWidth).as<StackPanel>();
|
||||
targetAppContainer.Orientation(Orientation::Vertical);
|
||||
targetAppContainer.VerticalAlignment(VerticalAlignment::Bottom);
|
||||
row.Children().Append(targetAppContainer);
|
||||
|
||||
// Delete row button
|
||||
Windows::UI::Xaml::Controls::Button deleteShortcut;
|
||||
FontIcon deleteSymbol;
|
||||
deleteSymbol.FontFamily(Xaml::Media::FontFamily(L"Segoe MDL2 Assets"));
|
||||
deleteSymbol.Glyph(L"\xE74D");
|
||||
deleteShortcut.Content(deleteSymbol);
|
||||
deleteShortcut.Background(Media::SolidColorBrush(Colors::Transparent()));
|
||||
deleteShortcut.HorizontalAlignment(HorizontalAlignment::Center);
|
||||
deleteShortcut.Click([&, parent, row, brush](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
Button currentButton = sender.as<Button>();
|
||||
uint32_t rowIndex;
|
||||
// Get index of delete button
|
||||
UIElementCollection children = parent.Children();
|
||||
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
|
||||
if (!indexFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = rowIndex + 1; i < children.Size(); i++)
|
||||
{
|
||||
StackPanel row = children.GetAt(i).as<StackPanel>();
|
||||
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>();
|
||||
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>();
|
||||
UpdateAccessibleNames(sourceCol, targetCol, targetApp, delButton, i);
|
||||
}
|
||||
|
||||
children.RemoveAt(rowIndex);
|
||||
parent.UpdateLayout();
|
||||
shortcutRemapBuffer.erase(shortcutRemapBuffer.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
|
||||
deleteShortcut.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
||||
|
||||
// Add tooltip for delete button which would appear on hover
|
||||
ToolTip deleteShortcuttoolTip;
|
||||
deleteShortcuttoolTip.Content(box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
||||
ToolTipService::SetToolTip(deleteShortcut, deleteShortcuttoolTip);
|
||||
|
||||
StackPanel deleteShortcutContainer = StackPanel();
|
||||
deleteShortcutContainer.Children().Append(deleteShortcut);
|
||||
deleteShortcutContainer.Orientation(Orientation::Vertical);
|
||||
deleteShortcutContainer.VerticalAlignment(VerticalAlignment::Center);
|
||||
row.Children().Append(deleteShortcutContainer);
|
||||
parent.UpdateLayout();
|
||||
|
||||
// Set accessible names
|
||||
UpdateAccessibleNames(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->getShortcutControl(), keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->getShortcutControl(), targetAppTextBox, deleteShortcut, (int)keyboardRemapControlObjects.size());
|
||||
|
||||
// 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()))
|
||||
{
|
||||
// change to load app name
|
||||
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);
|
||||
|
||||
if (newKeys.index() == 0)
|
||||
{
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(std::get<DWORD>(newKeys)));
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize both shortcuts as empty shortcuts
|
||||
shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
StackPanel ShortcutControl::getShortcutControl()
|
||||
{
|
||||
return shortcutControlLayout.as<StackPanel>();
|
||||
}
|
||||
|
||||
// Function to create the detect shortcut UI window
|
||||
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)
|
||||
{
|
||||
// ContentDialog for detecting shortcuts. This is the parent UI element.
|
||||
ContentDialog detectShortcutBox;
|
||||
|
||||
// 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)
|
||||
detectShortcutBox.XamlRoot(xamlRoot);
|
||||
detectShortcutBox.Title(box_value(GET_RESOURCE_STRING(IDS_TYPESHORTCUT_TITLE)));
|
||||
detectShortcutBox.IsPrimaryButtonEnabled(false);
|
||||
detectShortcutBox.IsSecondaryButtonEnabled(false);
|
||||
|
||||
// Get the linked stack panel for the "Type shortcut" button that was clicked
|
||||
StackPanel linkedShortcutStackPanel = KeyboardManagerHelper::getSiblingElement(sender).as<StackPanel>();
|
||||
|
||||
auto unregisterKeys = [&keyboardManagerState]() {
|
||||
keyboardManagerState.ClearRegisteredKeyDelays();
|
||||
};
|
||||
|
||||
auto selectDetectedShortcutAndResetKeys = [&keyboardManagerState](DWORD key) {
|
||||
keyboardManagerState.SelectDetectedShortcut(key);
|
||||
keyboardManagerState.ResetDetectedShortcutKey(key);
|
||||
};
|
||||
|
||||
auto onPressEnter = [linkedShortcutStackPanel,
|
||||
detectShortcutBox,
|
||||
&keyboardManagerState,
|
||||
unregisterKeys,
|
||||
colIndex,
|
||||
table,
|
||||
targetApp,
|
||||
&keyDropDownControlObjects,
|
||||
row,
|
||||
isHybridControl,
|
||||
isSingleKeyWindow,
|
||||
&remapBuffer] {
|
||||
// Save the detected shortcut in the linked text block
|
||||
Shortcut detectedShortcutKeys = keyboardManagerState.GetDetectedShortcut();
|
||||
|
||||
if (!detectedShortcutKeys.IsEmpty())
|
||||
{
|
||||
// The shortcut buffer gets set in this function
|
||||
KeyDropDownControl::AddShortcutToControl(detectedShortcutKeys, table, linkedShortcutStackPanel, keyboardManagerState, colIndex, keyDropDownControlObjects, remapBuffer, row, targetApp, isHybridControl, isSingleKeyWindow);
|
||||
}
|
||||
// Hide the type shortcut UI
|
||||
detectShortcutBox.Hide();
|
||||
};
|
||||
|
||||
auto onReleaseEnter = [&keyboardManagerState,
|
||||
unregisterKeys,
|
||||
isSingleKeyWindow,
|
||||
parentWindow] {
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
if (isSingleKeyWindow)
|
||||
{
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Revert UI state back to Edit Shortcut window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
|
||||
}
|
||||
|
||||
unregisterKeys();
|
||||
};
|
||||
|
||||
auto onAccept = [onPressEnter,
|
||||
onReleaseEnter] {
|
||||
onPressEnter();
|
||||
onReleaseEnter();
|
||||
};
|
||||
|
||||
TextBlock primaryButtonText;
|
||||
primaryButtonText.Text(GET_RESOURCE_STRING(IDS_OK_BUTTON));
|
||||
|
||||
Button primaryButton;
|
||||
primaryButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
primaryButton.Margin({ 2, 2, 2, 2 });
|
||||
primaryButton.Content(primaryButtonText);
|
||||
|
||||
// OK button
|
||||
primaryButton.Click([onAccept](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
onAccept();
|
||||
});
|
||||
|
||||
// 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
|
||||
keyboardManagerState.RegisterKeyDelay(
|
||||
VK_RETURN,
|
||||
selectDetectedShortcutAndResetKeys,
|
||||
[primaryButton, onPressEnter, detectShortcutBox](DWORD) {
|
||||
detectShortcutBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[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>());
|
||||
onPressEnter();
|
||||
});
|
||||
},
|
||||
[onReleaseEnter, detectShortcutBox](DWORD) {
|
||||
detectShortcutBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[onReleaseEnter]() {
|
||||
onReleaseEnter();
|
||||
});
|
||||
});
|
||||
|
||||
TextBlock cancelButtonText;
|
||||
cancelButtonText.Text(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON));
|
||||
|
||||
auto onCancel = [&keyboardManagerState,
|
||||
detectShortcutBox,
|
||||
unregisterKeys,
|
||||
isSingleKeyWindow,
|
||||
parentWindow] {
|
||||
detectShortcutBox.Hide();
|
||||
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
if (isSingleKeyWindow)
|
||||
{
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Revert UI state back to Edit Shortcut window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
|
||||
}
|
||||
unregisterKeys();
|
||||
};
|
||||
|
||||
Button cancelButton;
|
||||
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
cancelButton.Margin({ 2, 2, 2, 2 });
|
||||
cancelButton.Content(cancelButtonText);
|
||||
// Cancel button
|
||||
cancelButton.Click([onCancel](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
onCancel();
|
||||
});
|
||||
|
||||
// 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
|
||||
keyboardManagerState.RegisterKeyDelay(
|
||||
VK_ESCAPE,
|
||||
selectDetectedShortcutAndResetKeys,
|
||||
[onCancel, detectShortcutBox](DWORD) {
|
||||
detectShortcutBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[onCancel] {
|
||||
onCancel();
|
||||
});
|
||||
},
|
||||
nullptr);
|
||||
|
||||
// StackPanel parent for the displayed text in the dialog
|
||||
Windows::UI::Xaml::Controls::StackPanel stackPanel;
|
||||
detectShortcutBox.Content(stackPanel);
|
||||
|
||||
// Header textblock
|
||||
TextBlock text;
|
||||
text.Text(GET_RESOURCE_STRING(IDS_TYPESHORTCUT_HEADER));
|
||||
text.Margin({ 0, 0, 0, 10 });
|
||||
stackPanel.Children().Append(text);
|
||||
|
||||
// Target StackPanel to place the selected key - first row (for 1-3 keys)
|
||||
Windows::UI::Xaml::Controls::StackPanel keyStackPanel1;
|
||||
keyStackPanel1.Orientation(Orientation::Horizontal);
|
||||
stackPanel.Children().Append(keyStackPanel1);
|
||||
|
||||
// Target StackPanel to place the selected key - second row (for 4-5 keys)
|
||||
Windows::UI::Xaml::Controls::StackPanel keyStackPanel2;
|
||||
keyStackPanel2.Orientation(Orientation::Horizontal);
|
||||
keyStackPanel2.Margin({ 0, 20, 0, 0 });
|
||||
keyStackPanel2.Visibility(Visibility::Collapsed);
|
||||
stackPanel.Children().Append(keyStackPanel2);
|
||||
|
||||
TextBlock holdEscInfo;
|
||||
holdEscInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDESC));
|
||||
holdEscInfo.FontSize(12);
|
||||
holdEscInfo.Margin({ 0, 20, 0, 0 });
|
||||
stackPanel.Children().Append(holdEscInfo);
|
||||
|
||||
TextBlock holdEnterInfo;
|
||||
holdEnterInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDENTER));
|
||||
holdEnterInfo.FontSize(12);
|
||||
holdEnterInfo.Margin({ 0, 0, 0, 0 });
|
||||
stackPanel.Children().Append(holdEnterInfo);
|
||||
|
||||
ColumnDefinition primaryButtonColumn;
|
||||
ColumnDefinition cancelButtonColumn;
|
||||
|
||||
Grid buttonPanel;
|
||||
buttonPanel.Margin({ 0, 20, 0, 0 });
|
||||
buttonPanel.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
buttonPanel.ColumnDefinitions().Append(primaryButtonColumn);
|
||||
buttonPanel.ColumnDefinitions().Append(cancelButtonColumn);
|
||||
buttonPanel.SetColumn(primaryButton, 0);
|
||||
buttonPanel.SetColumn(cancelButton, 1);
|
||||
|
||||
buttonPanel.Children().Append(primaryButton);
|
||||
buttonPanel.Children().Append(cancelButton);
|
||||
|
||||
stackPanel.Children().Append(buttonPanel);
|
||||
stackPanel.UpdateLayout();
|
||||
|
||||
// Configure the keyboardManagerState to store the UI information.
|
||||
keyboardManagerState.ConfigureDetectShortcutUI(keyStackPanel1, keyStackPanel2);
|
||||
|
||||
// Show the dialog
|
||||
detectShortcutBox.ShowAsync();
|
||||
}
|
||||
#include "pch.h"
|
||||
#include "ShortcutControl.h"
|
||||
|
||||
#include <common/interop/shared_constants.h>
|
||||
|
||||
#include <KeyboardManagerState.h>
|
||||
|
||||
#include <KeyboardManagerEditorStrings.h>
|
||||
#include <KeyDropDownControl.h>
|
||||
#include <UIHelpers.h>
|
||||
|
||||
//Both static members are initialized to null
|
||||
HWND ShortcutControl::editShortcutsWindowHandle = nullptr;
|
||||
KeyboardManagerState* ShortcutControl::keyboardManagerState = nullptr;
|
||||
// Initialized as new vector
|
||||
RemapBuffer ShortcutControl::shortcutRemapBuffer;
|
||||
|
||||
ShortcutControl::ShortcutControl(StackPanel table, StackPanel row, const int colIndex, TextBox targetApp)
|
||||
{
|
||||
shortcutDropDownStackPanel = StackPanel();
|
||||
typeShortcut = Button();
|
||||
shortcutControlLayout = StackPanel();
|
||||
bool isHybridControl = colIndex == 1 ? true : false;
|
||||
|
||||
shortcutDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
shortcutDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
|
||||
typeShortcut.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
||||
typeShortcut.as<Button>().Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
|
||||
typeShortcut.as<Button>().Click([&, table, row, colIndex, isHybridControl, targetApp](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowActivated, editShortcutsWindowHandle);
|
||||
// Using the XamlRoot of the typeShortcut to get the root of the XAML host
|
||||
CreateDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, targetApp, isHybridControl, false, editShortcutsWindowHandle, shortcutRemapBuffer);
|
||||
});
|
||||
|
||||
// Set an accessible name for the type shortcut button
|
||||
typeShortcut.as<Button>().SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
||||
|
||||
shortcutControlLayout.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
|
||||
shortcutControlLayout.as<StackPanel>().Children().Append(typeShortcut.as<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>().UpdateLayout();
|
||||
}
|
||||
|
||||
// Function to set the accessible name of the target App text box
|
||||
void ShortcutControl::SetAccessibleNameForTextBox(TextBox targetAppTextBox, int rowIndex)
|
||||
{
|
||||
// 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);
|
||||
if (targetAppTextBox.Text() == L"")
|
||||
{
|
||||
targetAppTextBoxAccessibleName += GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_ALLAPPS);
|
||||
}
|
||||
|
||||
targetAppTextBox.SetValue(Automation::AutomationProperties::NameProperty(), box_value(targetAppTextBoxAccessibleName));
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
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 shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values.
|
||||
void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<ShortcutControl>>>& keyboardRemapControlObjects, const Shortcut& originalKeys, const KeyShortcutUnion& newKeys, const std::wstring& targetAppName)
|
||||
{
|
||||
// Textbox for target application
|
||||
TextBox targetAppTextBox;
|
||||
|
||||
// Create new ShortcutControl objects dynamically so that we does not get destructed
|
||||
std::vector<std::unique_ptr<ShortcutControl>> newrow;
|
||||
StackPanel row = StackPanel();
|
||||
parent.Children().Append(row);
|
||||
newrow.emplace_back(std::make_unique<ShortcutControl>(parent, row, 0, targetAppTextBox));
|
||||
newrow.emplace_back(std::make_unique<ShortcutControl>(parent, row, 1, targetAppTextBox));
|
||||
keyboardRemapControlObjects.push_back(std::move(newrow));
|
||||
|
||||
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>();
|
||||
if (keyboardRemapControlObjects.size() % 2)
|
||||
{
|
||||
row.Background(brush);
|
||||
}
|
||||
|
||||
// ShortcutControl for the original shortcut
|
||||
auto origin = keyboardRemapControlObjects.back()[0]->GetShortcutControl();
|
||||
origin.Width(KeyboardManagerConstants::ShortcutOriginColumnWidth);
|
||||
row.Children().Append(origin);
|
||||
|
||||
// Arrow icon
|
||||
FontIcon arrowIcon;
|
||||
arrowIcon.FontFamily(Xaml::Media::FontFamily(L"Segoe MDL2 Assets"));
|
||||
arrowIcon.Glyph(L"\xE72A");
|
||||
arrowIcon.VerticalAlignment(VerticalAlignment::Center);
|
||||
arrowIcon.HorizontalAlignment(HorizontalAlignment::Center);
|
||||
auto arrowIconContainer = UIHelpers::GetWrapped(arrowIcon, KeyboardManagerConstants::ShortcutArrowColumnWidth).as<StackPanel>();
|
||||
arrowIconContainer.Orientation(Orientation::Vertical);
|
||||
arrowIconContainer.VerticalAlignment(VerticalAlignment::Center);
|
||||
row.Children().Append(arrowIconContainer);
|
||||
|
||||
// ShortcutControl for the new shortcut
|
||||
auto target = keyboardRemapControlObjects.back()[1]->GetShortcutControl();
|
||||
target.Width(KeyboardManagerConstants::ShortcutTargetColumnWidth);
|
||||
row.Children().Append(target);
|
||||
|
||||
targetAppTextBox.Width(KeyboardManagerConstants::ShortcutTableDropDownWidth);
|
||||
targetAppTextBox.PlaceholderText(KeyboardManagerEditorStrings::DefaultAppName);
|
||||
targetAppTextBox.Text(targetAppName);
|
||||
|
||||
// GotFocus handler will be called whenever the user tabs into or clicks on the textbox
|
||||
targetAppTextBox.GotFocus([targetAppTextBox](auto const& sender, auto const& e) {
|
||||
// Select all text for accessible purpose
|
||||
targetAppTextBox.SelectAll();
|
||||
});
|
||||
|
||||
// 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.LostFocus([&keyboardRemapControlObjects, parent, row, targetAppTextBox](auto const& sender, auto const& e) {
|
||||
// Get index of targetAppTextBox button
|
||||
uint32_t rowIndex;
|
||||
if (!parent.Children().IndexOf(row, rowIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// rowIndex could be out of bounds if the the row got deleted after LostFocus handler was invoked. In this case it should return
|
||||
if (rowIndex >= keyboardRemapControlObjects.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate both set of drop downs
|
||||
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);
|
||||
|
||||
// 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>()));
|
||||
// second column is a hybrid column
|
||||
|
||||
std::vector<int32_t> selectedKeyCodes = KeyDropDownControl::GetSelectedCodesFromStackPanel(keyboardRemapControlObjects[rowIndex][1]->shortcutDropDownStackPanel.as<StackPanel>());
|
||||
|
||||
// If exactly one key is selected consider it to be a key remap
|
||||
if (selectedKeyCodes.size() == 1)
|
||||
{
|
||||
shortcutRemapBuffer[rowIndex].first[1] = selectedKeyCodes[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
Shortcut tempShortcut;
|
||||
tempShortcut.SetKeyCodes(selectedKeyCodes);
|
||||
// Assign instead of setting the value in the buffer since the previous value may not be a Shortcut
|
||||
shortcutRemapBuffer[rowIndex].first[1] = tempShortcut;
|
||||
}
|
||||
std::wstring newText = targetAppTextBox.Text().c_str();
|
||||
std::wstring lowercaseDefAppName = KeyboardManagerEditorStrings::DefaultAppName;
|
||||
std::transform(newText.begin(), newText.end(), newText.begin(), towlower);
|
||||
std::transform(lowercaseDefAppName.begin(), lowercaseDefAppName.end(), lowercaseDefAppName.begin(), towlower);
|
||||
if (newText == lowercaseDefAppName)
|
||||
{
|
||||
shortcutRemapBuffer[rowIndex].second = L"";
|
||||
}
|
||||
else
|
||||
{
|
||||
shortcutRemapBuffer[rowIndex].second = targetAppTextBox.Text().c_str();
|
||||
}
|
||||
|
||||
// To set the accessibile name of the target app text box when focus is lost
|
||||
ShortcutControl::SetAccessibleNameForTextBox(targetAppTextBox, rowIndex + 1);
|
||||
});
|
||||
|
||||
// We need two containers in order to align it horizontally and vertically
|
||||
StackPanel targetAppHorizontal = UIHelpers::GetWrapped(targetAppTextBox, KeyboardManagerConstants::TableTargetAppColWidth).as<StackPanel>();
|
||||
targetAppHorizontal.Orientation(Orientation::Horizontal);
|
||||
targetAppHorizontal.HorizontalAlignment(HorizontalAlignment::Left);
|
||||
StackPanel targetAppContainer = UIHelpers::GetWrapped(targetAppHorizontal, KeyboardManagerConstants::TableTargetAppColWidth).as<StackPanel>();
|
||||
targetAppContainer.Orientation(Orientation::Vertical);
|
||||
targetAppContainer.VerticalAlignment(VerticalAlignment::Bottom);
|
||||
row.Children().Append(targetAppContainer);
|
||||
|
||||
// Delete row button
|
||||
Windows::UI::Xaml::Controls::Button deleteShortcut;
|
||||
FontIcon deleteSymbol;
|
||||
deleteSymbol.FontFamily(Xaml::Media::FontFamily(L"Segoe MDL2 Assets"));
|
||||
deleteSymbol.Glyph(L"\xE74D");
|
||||
deleteShortcut.Content(deleteSymbol);
|
||||
deleteShortcut.Background(Media::SolidColorBrush(Colors::Transparent()));
|
||||
deleteShortcut.HorizontalAlignment(HorizontalAlignment::Center);
|
||||
deleteShortcut.Click([&, parent, row, brush](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
Button currentButton = sender.as<Button>();
|
||||
uint32_t rowIndex;
|
||||
// Get index of delete button
|
||||
UIElementCollection children = parent.Children();
|
||||
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
|
||||
if (!indexFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = rowIndex + 1; i < children.Size(); i++)
|
||||
{
|
||||
StackPanel row = children.GetAt(i).as<StackPanel>();
|
||||
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>();
|
||||
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>();
|
||||
UpdateAccessibleNames(sourceCol, targetCol, targetApp, delButton, i);
|
||||
}
|
||||
|
||||
children.RemoveAt(rowIndex);
|
||||
parent.UpdateLayout();
|
||||
shortcutRemapBuffer.erase(shortcutRemapBuffer.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
|
||||
deleteShortcut.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
||||
|
||||
// Add tooltip for delete button which would appear on hover
|
||||
ToolTip deleteShortcuttoolTip;
|
||||
deleteShortcuttoolTip.Content(box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
||||
ToolTipService::SetToolTip(deleteShortcut, deleteShortcuttoolTip);
|
||||
|
||||
StackPanel deleteShortcutContainer = StackPanel();
|
||||
deleteShortcutContainer.Children().Append(deleteShortcut);
|
||||
deleteShortcutContainer.Orientation(Orientation::Vertical);
|
||||
deleteShortcutContainer.VerticalAlignment(VerticalAlignment::Center);
|
||||
row.Children().Append(deleteShortcutContainer);
|
||||
parent.UpdateLayout();
|
||||
|
||||
// Set accessible names
|
||||
UpdateAccessibleNames(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->GetShortcutControl(), keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->GetShortcutControl(), targetAppTextBox, deleteShortcut, (int)keyboardRemapControlObjects.size());
|
||||
|
||||
// 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()))
|
||||
{
|
||||
// change to load app name
|
||||
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);
|
||||
|
||||
if (newKeys.index() == 0)
|
||||
{
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(std::get<DWORD>(newKeys)));
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize both shortcuts as empty shortcuts
|
||||
shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
StackPanel ShortcutControl::GetShortcutControl()
|
||||
{
|
||||
return shortcutControlLayout.as<StackPanel>();
|
||||
}
|
||||
|
||||
// Function to create the detect shortcut UI window
|
||||
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)
|
||||
{
|
||||
// ContentDialog for detecting shortcuts. This is the parent UI element.
|
||||
ContentDialog detectShortcutBox;
|
||||
|
||||
// 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)
|
||||
detectShortcutBox.XamlRoot(xamlRoot);
|
||||
detectShortcutBox.Title(box_value(GET_RESOURCE_STRING(IDS_TYPESHORTCUT_TITLE)));
|
||||
detectShortcutBox.IsPrimaryButtonEnabled(false);
|
||||
detectShortcutBox.IsSecondaryButtonEnabled(false);
|
||||
|
||||
// Get the linked stack panel for the "Type shortcut" button that was clicked
|
||||
StackPanel linkedShortcutStackPanel = UIHelpers::GetSiblingElement(sender).as<StackPanel>();
|
||||
|
||||
auto unregisterKeys = [&keyboardManagerState]() {
|
||||
keyboardManagerState.ClearRegisteredKeyDelays();
|
||||
};
|
||||
|
||||
auto selectDetectedShortcutAndResetKeys = [&keyboardManagerState](DWORD key) {
|
||||
keyboardManagerState.SelectDetectedShortcut(key);
|
||||
keyboardManagerState.ResetDetectedShortcutKey(key);
|
||||
};
|
||||
|
||||
auto onPressEnter = [linkedShortcutStackPanel,
|
||||
detectShortcutBox,
|
||||
&keyboardManagerState,
|
||||
unregisterKeys,
|
||||
colIndex,
|
||||
table,
|
||||
targetApp,
|
||||
&keyDropDownControlObjects,
|
||||
row,
|
||||
isHybridControl,
|
||||
isSingleKeyWindow,
|
||||
&remapBuffer] {
|
||||
// Save the detected shortcut in the linked text block
|
||||
Shortcut detectedShortcutKeys = keyboardManagerState.GetDetectedShortcut();
|
||||
|
||||
if (!detectedShortcutKeys.IsEmpty())
|
||||
{
|
||||
// The shortcut buffer gets set in this function
|
||||
KeyDropDownControl::AddShortcutToControl(detectedShortcutKeys, table, linkedShortcutStackPanel, keyboardManagerState, colIndex, keyDropDownControlObjects, remapBuffer, row, targetApp, isHybridControl, isSingleKeyWindow);
|
||||
}
|
||||
// Hide the type shortcut UI
|
||||
detectShortcutBox.Hide();
|
||||
};
|
||||
|
||||
auto onReleaseEnter = [&keyboardManagerState,
|
||||
unregisterKeys,
|
||||
isSingleKeyWindow,
|
||||
parentWindow] {
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
if (isSingleKeyWindow)
|
||||
{
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Revert UI state back to Edit Shortcut window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
|
||||
}
|
||||
|
||||
unregisterKeys();
|
||||
};
|
||||
|
||||
auto onAccept = [onPressEnter,
|
||||
onReleaseEnter] {
|
||||
onPressEnter();
|
||||
onReleaseEnter();
|
||||
};
|
||||
|
||||
TextBlock primaryButtonText;
|
||||
primaryButtonText.Text(GET_RESOURCE_STRING(IDS_OK_BUTTON));
|
||||
|
||||
Button primaryButton;
|
||||
primaryButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
primaryButton.Margin({ 2, 2, 2, 2 });
|
||||
primaryButton.Content(primaryButtonText);
|
||||
|
||||
// OK button
|
||||
primaryButton.Click([onAccept](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
onAccept();
|
||||
});
|
||||
|
||||
// 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
|
||||
keyboardManagerState.RegisterKeyDelay(
|
||||
VK_RETURN,
|
||||
selectDetectedShortcutAndResetKeys,
|
||||
[primaryButton, onPressEnter, detectShortcutBox](DWORD) {
|
||||
detectShortcutBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[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>());
|
||||
onPressEnter();
|
||||
});
|
||||
},
|
||||
[onReleaseEnter, detectShortcutBox](DWORD) {
|
||||
detectShortcutBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[onReleaseEnter]() {
|
||||
onReleaseEnter();
|
||||
});
|
||||
});
|
||||
|
||||
TextBlock cancelButtonText;
|
||||
cancelButtonText.Text(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON));
|
||||
|
||||
auto onCancel = [&keyboardManagerState,
|
||||
detectShortcutBox,
|
||||
unregisterKeys,
|
||||
isSingleKeyWindow,
|
||||
parentWindow] {
|
||||
detectShortcutBox.Hide();
|
||||
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
if (isSingleKeyWindow)
|
||||
{
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Revert UI state back to Edit Shortcut window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
|
||||
}
|
||||
unregisterKeys();
|
||||
};
|
||||
|
||||
Button cancelButton;
|
||||
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
cancelButton.Margin({ 2, 2, 2, 2 });
|
||||
cancelButton.Content(cancelButtonText);
|
||||
// Cancel button
|
||||
cancelButton.Click([onCancel](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
onCancel();
|
||||
});
|
||||
|
||||
// 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
|
||||
keyboardManagerState.RegisterKeyDelay(
|
||||
VK_ESCAPE,
|
||||
selectDetectedShortcutAndResetKeys,
|
||||
[onCancel, detectShortcutBox](DWORD) {
|
||||
detectShortcutBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[onCancel] {
|
||||
onCancel();
|
||||
});
|
||||
},
|
||||
nullptr);
|
||||
|
||||
// StackPanel parent for the displayed text in the dialog
|
||||
Windows::UI::Xaml::Controls::StackPanel stackPanel;
|
||||
detectShortcutBox.Content(stackPanel);
|
||||
|
||||
// Header textblock
|
||||
TextBlock text;
|
||||
text.Text(GET_RESOURCE_STRING(IDS_TYPESHORTCUT_HEADER));
|
||||
text.Margin({ 0, 0, 0, 10 });
|
||||
stackPanel.Children().Append(text);
|
||||
|
||||
// Target StackPanel to place the selected key - first row (for 1-3 keys)
|
||||
Windows::UI::Xaml::Controls::StackPanel keyStackPanel1;
|
||||
keyStackPanel1.Orientation(Orientation::Horizontal);
|
||||
stackPanel.Children().Append(keyStackPanel1);
|
||||
|
||||
// Target StackPanel to place the selected key - second row (for 4-5 keys)
|
||||
Windows::UI::Xaml::Controls::StackPanel keyStackPanel2;
|
||||
keyStackPanel2.Orientation(Orientation::Horizontal);
|
||||
keyStackPanel2.Margin({ 0, 20, 0, 0 });
|
||||
keyStackPanel2.Visibility(Visibility::Collapsed);
|
||||
stackPanel.Children().Append(keyStackPanel2);
|
||||
|
||||
TextBlock holdEscInfo;
|
||||
holdEscInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDESC));
|
||||
holdEscInfo.FontSize(12);
|
||||
holdEscInfo.Margin({ 0, 20, 0, 0 });
|
||||
stackPanel.Children().Append(holdEscInfo);
|
||||
|
||||
TextBlock holdEnterInfo;
|
||||
holdEnterInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDENTER));
|
||||
holdEnterInfo.FontSize(12);
|
||||
holdEnterInfo.Margin({ 0, 0, 0, 0 });
|
||||
stackPanel.Children().Append(holdEnterInfo);
|
||||
|
||||
ColumnDefinition primaryButtonColumn;
|
||||
ColumnDefinition cancelButtonColumn;
|
||||
|
||||
Grid buttonPanel;
|
||||
buttonPanel.Margin({ 0, 20, 0, 0 });
|
||||
buttonPanel.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
buttonPanel.ColumnDefinitions().Append(primaryButtonColumn);
|
||||
buttonPanel.ColumnDefinitions().Append(cancelButtonColumn);
|
||||
buttonPanel.SetColumn(primaryButton, 0);
|
||||
buttonPanel.SetColumn(cancelButton, 1);
|
||||
|
||||
buttonPanel.Children().Append(primaryButton);
|
||||
buttonPanel.Children().Append(cancelButton);
|
||||
|
||||
stackPanel.Children().Append(buttonPanel);
|
||||
stackPanel.UpdateLayout();
|
||||
|
||||
// Configure the keyboardManagerState to store the UI information.
|
||||
keyboardManagerState.ConfigureDetectShortcutUI(keyStackPanel1, keyStackPanel2);
|
||||
|
||||
// Show the dialog
|
||||
detectShortcutBox.ShowAsync();
|
||||
}
|
|
@ -1,57 +1,60 @@
|
|||
#pragma once
|
||||
#include "keyboardmanager/common/Shortcut.h"
|
||||
#include <variant>
|
||||
|
||||
class KeyboardManagerState;
|
||||
class KeyDropDownControl;
|
||||
namespace winrt::Windows::UI::Xaml
|
||||
{
|
||||
struct XamlRoot;
|
||||
namespace Controls
|
||||
{
|
||||
struct StackPanel;
|
||||
struct TextBox;
|
||||
struct Button;
|
||||
}
|
||||
}
|
||||
|
||||
class ShortcutControl
|
||||
{
|
||||
private:
|
||||
// Stack panel for the drop downs to display the selected shortcut
|
||||
winrt::Windows::Foundation::IInspectable shortcutDropDownStackPanel;
|
||||
|
||||
// Button to type the shortcut
|
||||
winrt::Windows::Foundation::IInspectable typeShortcut;
|
||||
|
||||
// StackPanel to parent the above controls
|
||||
winrt::Windows::Foundation::IInspectable shortcutControlLayout;
|
||||
|
||||
// Function to set the accessible name of the target app text box
|
||||
static void SetAccessibleNameForTextBox(TextBox targetAppTextBox, int rowIndex);
|
||||
|
||||
// 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);
|
||||
|
||||
public:
|
||||
// Handle to the current Edit Shortcuts Window
|
||||
static HWND EditShortcutsWindowHandle;
|
||||
// Pointer to the keyboard manager state
|
||||
static KeyboardManagerState* keyboardManagerState;
|
||||
// Stores the current list of remappings
|
||||
static RemapBuffer shortcutRemapBuffer;
|
||||
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
|
||||
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
|
||||
|
||||
// constructor
|
||||
ShortcutControl(StackPanel table, StackPanel row, const int colIndex, TextBox targetApp);
|
||||
|
||||
// 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.
|
||||
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 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
|
||||
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);
|
||||
};
|
||||
#pragma once
|
||||
|
||||
#include <Shortcut.h>
|
||||
|
||||
class KeyboardManagerState;
|
||||
class KeyDropDownControl;
|
||||
namespace winrt::Windows::UI::Xaml
|
||||
{
|
||||
struct XamlRoot;
|
||||
namespace Controls
|
||||
{
|
||||
struct StackPanel;
|
||||
struct TextBox;
|
||||
struct Button;
|
||||
}
|
||||
}
|
||||
|
||||
class ShortcutControl
|
||||
{
|
||||
private:
|
||||
// Stack panel for the drop downs to display the selected shortcut
|
||||
winrt::Windows::Foundation::IInspectable shortcutDropDownStackPanel;
|
||||
|
||||
// Button to type the shortcut
|
||||
winrt::Windows::Foundation::IInspectable typeShortcut;
|
||||
|
||||
// StackPanel to parent the above controls
|
||||
winrt::Windows::Foundation::IInspectable shortcutControlLayout;
|
||||
|
||||
// Function to set the accessible name of the target app text box
|
||||
static void SetAccessibleNameForTextBox(TextBox targetAppTextBox, int rowIndex);
|
||||
|
||||
// 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);
|
||||
|
||||
public:
|
||||
// Handle to the current Edit Shortcuts Window
|
||||
static HWND editShortcutsWindowHandle;
|
||||
|
||||
// Pointer to the keyboard manager state
|
||||
static KeyboardManagerState* keyboardManagerState;
|
||||
|
||||
// Stores the current list of remappings
|
||||
static RemapBuffer shortcutRemapBuffer;
|
||||
|
||||
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
|
||||
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
|
||||
|
||||
// constructor
|
||||
ShortcutControl(StackPanel table, StackPanel row, const int colIndex, TextBox targetApp);
|
||||
|
||||
// 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.
|
||||
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 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
|
||||
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 "SingleKeyRemapControl.h"
|
||||
#include "keyboardmanager/common/Helpers.h"
|
||||
#include "keyboardmanager/common/KeyboardManagerConstants.h"
|
||||
#include "keyboardmanager/common/KeyboardManagerState.h"
|
||||
#include "ShortcutControl.h"
|
||||
#include "keyboardmanager/dll/Generated Files/resource.h"
|
||||
#include <common/interop/shared_constants.h>
|
||||
|
||||
//Both static members are initialized to null
|
||||
HWND SingleKeyRemapControl::EditKeyboardWindowHandle = nullptr;
|
||||
KeyboardManagerState* SingleKeyRemapControl::keyboardManagerState = nullptr;
|
||||
// Initialized as new vector
|
||||
RemapBuffer SingleKeyRemapControl::singleKeyRemapBuffer;
|
||||
|
||||
SingleKeyRemapControl::SingleKeyRemapControl(StackPanel table, StackPanel row, const int colIndex)
|
||||
{
|
||||
typeKey = Button();
|
||||
typeKey.as<Button>().Width(KeyboardManagerConstants::RemapTableDropDownWidth);
|
||||
typeKey.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
||||
|
||||
singleKeyRemapControlLayout = StackPanel();
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Spacing(10);
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(typeKey.as<Button>());
|
||||
|
||||
// Key column
|
||||
if (colIndex == 0)
|
||||
{
|
||||
keyDropDownControlObjects.emplace_back(std::make_unique<KeyDropDownControl>(false));
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(keyDropDownControlObjects[0]->GetComboBox());
|
||||
// Set selection handler for the drop down
|
||||
keyDropDownControlObjects[0]->SetSelectionHandler(table, row, colIndex, singleKeyRemapBuffer);
|
||||
}
|
||||
|
||||
// Hybrid column
|
||||
else
|
||||
{
|
||||
hybridDropDownStackPanel = StackPanel();
|
||||
hybridDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
hybridDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
KeyDropDownControl::AddDropDown(table, row, hybridDropDownStackPanel.as<StackPanel>(), colIndex, singleKeyRemapBuffer, keyDropDownControlObjects, nullptr, true, true);
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(hybridDropDownStackPanel.as<StackPanel>());
|
||||
}
|
||||
|
||||
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
|
||||
if (colIndex == 0)
|
||||
{
|
||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated, EditKeyboardWindowHandle);
|
||||
createDetectKeyWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState);
|
||||
}
|
||||
else
|
||||
{
|
||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
||||
ShortcutControl::createDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, nullptr, true, true, EditKeyboardWindowHandle, singleKeyRemapBuffer);
|
||||
}
|
||||
});
|
||||
|
||||
singleKeyRemapControlLayout.as<StackPanel>().UpdateLayout();
|
||||
}
|
||||
|
||||
// Function to set the accessible names for all the controls in a row
|
||||
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)));
|
||||
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)));
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// Create new SingleKeyRemapControl objects dynamically so that we does not get destructed
|
||||
std::vector<std::unique_ptr<SingleKeyRemapControl>> newrow;
|
||||
StackPanel row = StackPanel();
|
||||
parent.Children().Append(row);
|
||||
newrow.emplace_back(std::make_unique<SingleKeyRemapControl>(parent, row, 0));
|
||||
newrow.emplace_back(std::make_unique<SingleKeyRemapControl>(parent, row, 1));
|
||||
keyboardRemapControlObjects.push_back(std::move(newrow));
|
||||
|
||||
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>();
|
||||
if (keyboardRemapControlObjects.size() % 2)
|
||||
{
|
||||
row.Background(brush);
|
||||
}
|
||||
|
||||
// SingleKeyRemapControl for the original key.
|
||||
auto originalElement = keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl();
|
||||
originalElement.Width(KeyboardManagerConstants::RemapTableDropDownWidth);
|
||||
row.Children().Append(originalElement);
|
||||
// Arrow icon
|
||||
FontIcon arrowIcon;
|
||||
arrowIcon.FontFamily(Media::FontFamily(L"Segoe MDL2 Assets"));
|
||||
arrowIcon.Glyph(L"\xE72A");
|
||||
arrowIcon.VerticalAlignment(VerticalAlignment::Center);
|
||||
arrowIcon.HorizontalAlignment(HorizontalAlignment::Center);
|
||||
auto arrowIconContainer = KeyboardManagerHelper::GetWrapped(arrowIcon, KeyboardManagerConstants::TableArrowColWidth).as<StackPanel>();
|
||||
arrowIconContainer.Orientation(Orientation::Vertical);
|
||||
arrowIconContainer.VerticalAlignment(VerticalAlignment::Center);
|
||||
row.Children().Append(arrowIconContainer);
|
||||
|
||||
// SingleKeyRemapControl for the new remap key
|
||||
auto targetElement = keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl();
|
||||
targetElement.Width(KeyboardManagerConstants::ShortcutTargetColumnWidth);
|
||||
row.Children().Append(targetElement);
|
||||
|
||||
// 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()))
|
||||
{
|
||||
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));
|
||||
if (newKey.index() == 0)
|
||||
{
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(std::get<DWORD>(newKey)));
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize both keys to NULL
|
||||
singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ NULL, NULL }, L""));
|
||||
}
|
||||
|
||||
// Delete row button
|
||||
Windows::UI::Xaml::Controls::Button deleteRemapKeys;
|
||||
FontIcon deleteSymbol;
|
||||
deleteSymbol.FontFamily(Media::FontFamily(L"Segoe MDL2 Assets"));
|
||||
deleteSymbol.Glyph(L"\xE74D");
|
||||
deleteRemapKeys.Content(deleteSymbol);
|
||||
deleteRemapKeys.Background(Media::SolidColorBrush(Colors::Transparent()));
|
||||
deleteRemapKeys.HorizontalAlignment(HorizontalAlignment::Center);
|
||||
deleteRemapKeys.Click([&, parent, row, brush](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
uint32_t rowIndex;
|
||||
// Get index of delete button
|
||||
UIElementCollection children = parent.Children();
|
||||
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
|
||||
if (!indexFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update accessible names and background for each row after the deleted row
|
||||
for (uint32_t i = rowIndex + 1; i < children.Size(); i++)
|
||||
{
|
||||
StackPanel row = children.GetAt(i).as<StackPanel>();
|
||||
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>();
|
||||
Button delButton = row.Children().GetAt(3).as<Button>();
|
||||
UpdateAccessibleNames(sourceCol, targetCol, delButton, i);
|
||||
}
|
||||
|
||||
children.RemoveAt(rowIndex);
|
||||
parent.UpdateLayout();
|
||||
singleKeyRemapBuffer.erase(singleKeyRemapBuffer.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)));
|
||||
|
||||
// Add tooltip for delete button which would appear on hover
|
||||
ToolTip deleteRemapKeystoolTip;
|
||||
deleteRemapKeystoolTip.Content(box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
||||
ToolTipService::SetToolTip(deleteRemapKeys, deleteRemapKeystoolTip);
|
||||
row.Children().Append(deleteRemapKeys);
|
||||
parent.UpdateLayout();
|
||||
|
||||
// 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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
// 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);
|
||||
detectRemapKeyBox.Title(box_value(GET_RESOURCE_STRING(IDS_TYPEKEY_TITLE)));
|
||||
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>();
|
||||
|
||||
auto unregisterKeys = [&keyboardManagerState]() {
|
||||
keyboardManagerState.ClearRegisteredKeyDelays();
|
||||
};
|
||||
|
||||
auto onPressEnter = [linkedRemapDropDown,
|
||||
detectRemapKeyBox,
|
||||
&keyboardManagerState,
|
||||
unregisterKeys] {
|
||||
// Save the detected key in the linked text block
|
||||
DWORD detectedKey = keyboardManagerState.GetDetectedSingleRemapKey();
|
||||
|
||||
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
|
||||
linkedRemapDropDown.ItemsSource(KeyboardManagerHelper::ToBoxValue(keyboardManagerState.keyboardMap.GetKeyNameList()));
|
||||
linkedRemapDropDown.SelectedValue(winrt::box_value(std::to_wstring(detectedKey)));
|
||||
}
|
||||
// Hide the type key UI
|
||||
detectRemapKeyBox.Hide();
|
||||
};
|
||||
|
||||
auto onReleaseEnter = [&keyboardManagerState,
|
||||
unregisterKeys] {
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
||||
unregisterKeys();
|
||||
};
|
||||
|
||||
auto onAccept = [onPressEnter,
|
||||
onReleaseEnter] {
|
||||
onPressEnter();
|
||||
onReleaseEnter();
|
||||
};
|
||||
|
||||
TextBlock primaryButtonText;
|
||||
primaryButtonText.Text(GET_RESOURCE_STRING(IDS_OK_BUTTON));
|
||||
|
||||
Button primaryButton;
|
||||
primaryButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
primaryButton.Margin({ 2, 2, 2, 2 });
|
||||
primaryButton.Content(primaryButtonText);
|
||||
primaryButton.Click([onAccept](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
onAccept();
|
||||
});
|
||||
|
||||
// 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
|
||||
keyboardManagerState.RegisterKeyDelay(
|
||||
VK_RETURN,
|
||||
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
|
||||
[primaryButton, onPressEnter, detectRemapKeyBox](DWORD) {
|
||||
detectRemapKeyBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[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>());
|
||||
onPressEnter();
|
||||
});
|
||||
},
|
||||
[onReleaseEnter, detectRemapKeyBox](DWORD) {
|
||||
detectRemapKeyBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[onReleaseEnter]() {
|
||||
onReleaseEnter();
|
||||
});
|
||||
});
|
||||
|
||||
TextBlock cancelButtonText;
|
||||
cancelButtonText.Text(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON));
|
||||
|
||||
auto onCancel = [&keyboardManagerState,
|
||||
detectRemapKeyBox,
|
||||
unregisterKeys] {
|
||||
detectRemapKeyBox.Hide();
|
||||
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
||||
unregisterKeys();
|
||||
};
|
||||
|
||||
Button cancelButton;
|
||||
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
cancelButton.Margin({ 2, 2, 2, 2 });
|
||||
cancelButton.Content(cancelButtonText);
|
||||
// Cancel button
|
||||
cancelButton.Click([onCancel](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
onCancel();
|
||||
});
|
||||
|
||||
// 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
|
||||
keyboardManagerState.RegisterKeyDelay(
|
||||
VK_ESCAPE,
|
||||
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
|
||||
[onCancel, detectRemapKeyBox](DWORD) {
|
||||
detectRemapKeyBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[onCancel] {
|
||||
onCancel();
|
||||
});
|
||||
},
|
||||
nullptr);
|
||||
|
||||
// StackPanel parent for the displayed text in the dialog
|
||||
Windows::UI::Xaml::Controls::StackPanel stackPanel;
|
||||
detectRemapKeyBox.Content(stackPanel);
|
||||
|
||||
// Header textblock
|
||||
TextBlock text;
|
||||
text.Text(GET_RESOURCE_STRING(IDS_TYPEKEY_HEADER));
|
||||
text.Margin({ 0, 0, 0, 10 });
|
||||
stackPanel.Children().Append(text);
|
||||
|
||||
// Target StackPanel to place the selected key
|
||||
Windows::UI::Xaml::Controls::StackPanel keyStackPanel;
|
||||
keyStackPanel.Orientation(Orientation::Horizontal);
|
||||
stackPanel.Children().Append(keyStackPanel);
|
||||
|
||||
TextBlock holdEscInfo;
|
||||
holdEscInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDESC));
|
||||
holdEscInfo.FontSize(12);
|
||||
holdEscInfo.Margin({ 0, 20, 0, 0 });
|
||||
stackPanel.Children().Append(holdEscInfo);
|
||||
|
||||
TextBlock holdEnterInfo;
|
||||
holdEnterInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDENTER));
|
||||
holdEnterInfo.FontSize(12);
|
||||
holdEnterInfo.Margin({ 0, 0, 0, 0 });
|
||||
stackPanel.Children().Append(holdEnterInfo);
|
||||
|
||||
ColumnDefinition primaryButtonColumn;
|
||||
ColumnDefinition cancelButtonColumn;
|
||||
|
||||
Grid buttonPanel;
|
||||
buttonPanel.Margin({ 0, 20, 0, 0 });
|
||||
buttonPanel.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
buttonPanel.ColumnDefinitions().Append(primaryButtonColumn);
|
||||
buttonPanel.ColumnDefinitions().Append(cancelButtonColumn);
|
||||
buttonPanel.SetColumn(primaryButton, 0);
|
||||
buttonPanel.SetColumn(cancelButton, 1);
|
||||
|
||||
buttonPanel.Children().Append(primaryButton);
|
||||
buttonPanel.Children().Append(cancelButton);
|
||||
|
||||
stackPanel.Children().Append(buttonPanel);
|
||||
stackPanel.UpdateLayout();
|
||||
|
||||
// Configure the keyboardManagerState to store the UI information.
|
||||
keyboardManagerState.ConfigureDetectSingleKeyRemapUI(keyStackPanel);
|
||||
|
||||
// Show the dialog
|
||||
detectRemapKeyBox.ShowAsync();
|
||||
#include "pch.h"
|
||||
#include "SingleKeyRemapControl.h"
|
||||
|
||||
#include <KeyboardManagerState.h>
|
||||
|
||||
#include <ShortcutControl.h>
|
||||
#include <UIHelpers.h>
|
||||
|
||||
//Both static members are initialized to null
|
||||
HWND SingleKeyRemapControl::EditKeyboardWindowHandle = nullptr;
|
||||
KeyboardManagerState* SingleKeyRemapControl::keyboardManagerState = nullptr;
|
||||
// Initialized as new vector
|
||||
RemapBuffer SingleKeyRemapControl::singleKeyRemapBuffer;
|
||||
|
||||
SingleKeyRemapControl::SingleKeyRemapControl(StackPanel table, StackPanel row, const int colIndex)
|
||||
{
|
||||
typeKey = Button();
|
||||
typeKey.as<Button>().Width(KeyboardManagerConstants::RemapTableDropDownWidth);
|
||||
typeKey.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
|
||||
|
||||
singleKeyRemapControlLayout = StackPanel();
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Spacing(10);
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(typeKey.as<Button>());
|
||||
|
||||
// Key column
|
||||
if (colIndex == 0)
|
||||
{
|
||||
keyDropDownControlObjects.emplace_back(std::make_unique<KeyDropDownControl>(false));
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(keyDropDownControlObjects[0]->GetComboBox());
|
||||
// Set selection handler for the drop down
|
||||
keyDropDownControlObjects[0]->SetSelectionHandler(table, row, colIndex, singleKeyRemapBuffer);
|
||||
}
|
||||
|
||||
// Hybrid column
|
||||
else
|
||||
{
|
||||
hybridDropDownStackPanel = StackPanel();
|
||||
hybridDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
|
||||
hybridDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
|
||||
KeyDropDownControl::AddDropDown(table, row, hybridDropDownStackPanel.as<StackPanel>(), colIndex, singleKeyRemapBuffer, keyDropDownControlObjects, nullptr, true, true);
|
||||
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(hybridDropDownStackPanel.as<StackPanel>());
|
||||
}
|
||||
|
||||
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
|
||||
if (colIndex == 0)
|
||||
{
|
||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated, EditKeyboardWindowHandle);
|
||||
createDetectKeyWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState);
|
||||
}
|
||||
else
|
||||
{
|
||||
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
||||
ShortcutControl::CreateDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, nullptr, true, true, EditKeyboardWindowHandle, singleKeyRemapBuffer);
|
||||
}
|
||||
});
|
||||
|
||||
singleKeyRemapControlLayout.as<StackPanel>().UpdateLayout();
|
||||
}
|
||||
|
||||
// Function to set the accessible names for all the controls in a row
|
||||
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)));
|
||||
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)));
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// Create new SingleKeyRemapControl objects dynamically so that we does not get destructed
|
||||
std::vector<std::unique_ptr<SingleKeyRemapControl>> newrow;
|
||||
StackPanel row = StackPanel();
|
||||
parent.Children().Append(row);
|
||||
newrow.emplace_back(std::make_unique<SingleKeyRemapControl>(parent, row, 0));
|
||||
newrow.emplace_back(std::make_unique<SingleKeyRemapControl>(parent, row, 1));
|
||||
keyboardRemapControlObjects.push_back(std::move(newrow));
|
||||
|
||||
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>();
|
||||
if (keyboardRemapControlObjects.size() % 2)
|
||||
{
|
||||
row.Background(brush);
|
||||
}
|
||||
|
||||
// SingleKeyRemapControl for the original key.
|
||||
auto originalElement = keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl();
|
||||
originalElement.Width(KeyboardManagerConstants::RemapTableDropDownWidth);
|
||||
row.Children().Append(originalElement);
|
||||
|
||||
// Arrow icon
|
||||
FontIcon arrowIcon;
|
||||
arrowIcon.FontFamily(Media::FontFamily(L"Segoe MDL2 Assets"));
|
||||
arrowIcon.Glyph(L"\xE72A");
|
||||
arrowIcon.VerticalAlignment(VerticalAlignment::Center);
|
||||
arrowIcon.HorizontalAlignment(HorizontalAlignment::Center);
|
||||
auto arrowIconContainer = UIHelpers::GetWrapped(arrowIcon, KeyboardManagerConstants::TableArrowColWidth).as<StackPanel>();
|
||||
arrowIconContainer.Orientation(Orientation::Vertical);
|
||||
arrowIconContainer.VerticalAlignment(VerticalAlignment::Center);
|
||||
row.Children().Append(arrowIconContainer);
|
||||
|
||||
// SingleKeyRemapControl for the new remap key
|
||||
auto targetElement = keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl();
|
||||
targetElement.Width(KeyboardManagerConstants::ShortcutTargetColumnWidth);
|
||||
row.Children().Append(targetElement);
|
||||
|
||||
// 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()))
|
||||
{
|
||||
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));
|
||||
if (newKey.index() == 0)
|
||||
{
|
||||
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(std::get<DWORD>(newKey)));
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialize both keys to NULL
|
||||
singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ NULL, NULL }, L""));
|
||||
}
|
||||
|
||||
// Delete row button
|
||||
Windows::UI::Xaml::Controls::Button deleteRemapKeys;
|
||||
FontIcon deleteSymbol;
|
||||
deleteSymbol.FontFamily(Media::FontFamily(L"Segoe MDL2 Assets"));
|
||||
deleteSymbol.Glyph(L"\xE74D");
|
||||
deleteRemapKeys.Content(deleteSymbol);
|
||||
deleteRemapKeys.Background(Media::SolidColorBrush(Colors::Transparent()));
|
||||
deleteRemapKeys.HorizontalAlignment(HorizontalAlignment::Center);
|
||||
deleteRemapKeys.Click([&, parent, row, brush](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
uint32_t rowIndex;
|
||||
// Get index of delete button
|
||||
UIElementCollection children = parent.Children();
|
||||
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
|
||||
if (!indexFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update accessible names and background for each row after the deleted row
|
||||
for (uint32_t i = rowIndex + 1; i < children.Size(); i++)
|
||||
{
|
||||
StackPanel row = children.GetAt(i).as<StackPanel>();
|
||||
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>();
|
||||
Button delButton = row.Children().GetAt(3).as<Button>();
|
||||
UpdateAccessibleNames(sourceCol, targetCol, delButton, i);
|
||||
}
|
||||
|
||||
children.RemoveAt(rowIndex);
|
||||
parent.UpdateLayout();
|
||||
singleKeyRemapBuffer.erase(singleKeyRemapBuffer.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)));
|
||||
|
||||
// Add tooltip for delete button which would appear on hover
|
||||
ToolTip deleteRemapKeystoolTip;
|
||||
deleteRemapKeystoolTip.Content(box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
|
||||
ToolTipService::SetToolTip(deleteRemapKeys, deleteRemapKeystoolTip);
|
||||
row.Children().Append(deleteRemapKeys);
|
||||
parent.UpdateLayout();
|
||||
|
||||
// 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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
// 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);
|
||||
detectRemapKeyBox.Title(box_value(GET_RESOURCE_STRING(IDS_TYPEKEY_TITLE)));
|
||||
detectRemapKeyBox.IsPrimaryButtonEnabled(false);
|
||||
detectRemapKeyBox.IsSecondaryButtonEnabled(false);
|
||||
|
||||
// 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 onPressEnter = [linkedRemapDropDown,
|
||||
detectRemapKeyBox,
|
||||
&keyboardManagerState,
|
||||
unregisterKeys] {
|
||||
// Save the detected key in the linked text block
|
||||
DWORD detectedKey = keyboardManagerState.GetDetectedSingleRemapKey();
|
||||
|
||||
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
|
||||
linkedRemapDropDown.ItemsSource(UIHelpers::ToBoxValue(keyboardManagerState.keyboardMap.GetKeyNameList()));
|
||||
linkedRemapDropDown.SelectedValue(winrt::box_value(std::to_wstring(detectedKey)));
|
||||
}
|
||||
|
||||
// Hide the type key UI
|
||||
detectRemapKeyBox.Hide();
|
||||
};
|
||||
|
||||
auto onReleaseEnter = [&keyboardManagerState,
|
||||
unregisterKeys] {
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
||||
unregisterKeys();
|
||||
};
|
||||
|
||||
auto onAccept = [onPressEnter,
|
||||
onReleaseEnter] {
|
||||
onPressEnter();
|
||||
onReleaseEnter();
|
||||
};
|
||||
|
||||
TextBlock primaryButtonText;
|
||||
primaryButtonText.Text(GET_RESOURCE_STRING(IDS_OK_BUTTON));
|
||||
|
||||
Button primaryButton;
|
||||
primaryButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
primaryButton.Margin({ 2, 2, 2, 2 });
|
||||
primaryButton.Content(primaryButtonText);
|
||||
primaryButton.Click([onAccept](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
onAccept();
|
||||
});
|
||||
|
||||
// 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
|
||||
keyboardManagerState.RegisterKeyDelay(
|
||||
VK_RETURN,
|
||||
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
|
||||
[primaryButton, onPressEnter, detectRemapKeyBox](DWORD) {
|
||||
detectRemapKeyBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[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>());
|
||||
onPressEnter();
|
||||
});
|
||||
},
|
||||
[onReleaseEnter, detectRemapKeyBox](DWORD) {
|
||||
detectRemapKeyBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[onReleaseEnter]() {
|
||||
onReleaseEnter();
|
||||
});
|
||||
});
|
||||
|
||||
TextBlock cancelButtonText;
|
||||
cancelButtonText.Text(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON));
|
||||
|
||||
auto onCancel = [&keyboardManagerState,
|
||||
detectRemapKeyBox,
|
||||
unregisterKeys] {
|
||||
detectRemapKeyBox.Hide();
|
||||
|
||||
// Reset the keyboard manager UI state
|
||||
keyboardManagerState.ResetUIState();
|
||||
|
||||
// Revert UI state back to Edit Keyboard window
|
||||
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
|
||||
unregisterKeys();
|
||||
};
|
||||
|
||||
Button cancelButton;
|
||||
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
cancelButton.Margin({ 2, 2, 2, 2 });
|
||||
cancelButton.Content(cancelButtonText);
|
||||
|
||||
// Cancel button
|
||||
cancelButton.Click([onCancel](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
|
||||
onCancel();
|
||||
});
|
||||
|
||||
// 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
|
||||
keyboardManagerState.RegisterKeyDelay(
|
||||
VK_ESCAPE,
|
||||
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
|
||||
[onCancel, detectRemapKeyBox](DWORD) {
|
||||
detectRemapKeyBox.Dispatcher().RunAsync(
|
||||
Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
[onCancel] {
|
||||
onCancel();
|
||||
});
|
||||
},
|
||||
nullptr);
|
||||
|
||||
// StackPanel parent for the displayed text in the dialog
|
||||
Windows::UI::Xaml::Controls::StackPanel stackPanel;
|
||||
detectRemapKeyBox.Content(stackPanel);
|
||||
|
||||
// Header textblock
|
||||
TextBlock text;
|
||||
text.Text(GET_RESOURCE_STRING(IDS_TYPEKEY_HEADER));
|
||||
text.Margin({ 0, 0, 0, 10 });
|
||||
stackPanel.Children().Append(text);
|
||||
|
||||
// Target StackPanel to place the selected key
|
||||
Windows::UI::Xaml::Controls::StackPanel keyStackPanel;
|
||||
keyStackPanel.Orientation(Orientation::Horizontal);
|
||||
stackPanel.Children().Append(keyStackPanel);
|
||||
|
||||
TextBlock holdEscInfo;
|
||||
holdEscInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDESC));
|
||||
holdEscInfo.FontSize(12);
|
||||
holdEscInfo.Margin({ 0, 20, 0, 0 });
|
||||
stackPanel.Children().Append(holdEscInfo);
|
||||
|
||||
TextBlock holdEnterInfo;
|
||||
holdEnterInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDENTER));
|
||||
holdEnterInfo.FontSize(12);
|
||||
holdEnterInfo.Margin({ 0, 0, 0, 0 });
|
||||
stackPanel.Children().Append(holdEnterInfo);
|
||||
|
||||
ColumnDefinition primaryButtonColumn;
|
||||
ColumnDefinition cancelButtonColumn;
|
||||
|
||||
Grid buttonPanel;
|
||||
buttonPanel.Margin({ 0, 20, 0, 0 });
|
||||
buttonPanel.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
buttonPanel.ColumnDefinitions().Append(primaryButtonColumn);
|
||||
buttonPanel.ColumnDefinitions().Append(cancelButtonColumn);
|
||||
buttonPanel.SetColumn(primaryButton, 0);
|
||||
buttonPanel.SetColumn(cancelButton, 1);
|
||||
|
||||
buttonPanel.Children().Append(primaryButton);
|
||||
buttonPanel.Children().Append(cancelButton);
|
||||
|
||||
stackPanel.Children().Append(buttonPanel);
|
||||
stackPanel.UpdateLayout();
|
||||
|
||||
// Configure the keyboardManagerState to store the UI information.
|
||||
keyboardManagerState.ConfigureDetectSingleKeyRemapUI(keyStackPanel);
|
||||
|
||||
// Show the dialog
|
||||
detectRemapKeyBox.ShowAsync();
|
||||
}
|
|
@ -1,53 +1,58 @@
|
|||
#pragma once
|
||||
#include "KeyDropDownControl.h"
|
||||
#include <keyboardmanager/common/Shortcut.h>
|
||||
|
||||
class KeyboardManagerState;
|
||||
namespace winrt::Windows::UI::Xaml
|
||||
{
|
||||
struct XamlRoot;
|
||||
namespace Controls
|
||||
{
|
||||
struct StackPanel;
|
||||
struct Grid;
|
||||
struct Button;
|
||||
}
|
||||
}
|
||||
|
||||
class SingleKeyRemapControl
|
||||
{
|
||||
private:
|
||||
// Button to type the remap key
|
||||
winrt::Windows::Foundation::IInspectable typeKey;
|
||||
|
||||
// StackPanel to parent the above controls
|
||||
winrt::Windows::Foundation::IInspectable singleKeyRemapControlLayout;
|
||||
|
||||
// Stack panel for the drop downs to display the selected shortcut for the hybrid case
|
||||
winrt::Windows::Foundation::IInspectable hybridDropDownStackPanel;
|
||||
|
||||
// Function to set the accessible names for all the controls in a row
|
||||
static void UpdateAccessibleNames(StackPanel sourceColumn, StackPanel mappedToColumn, Button deleteButton, int rowIndex);
|
||||
|
||||
public:
|
||||
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
|
||||
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
|
||||
// Handle to the current Edit Keyboard Window
|
||||
static HWND EditKeyboardWindowHandle;
|
||||
// Pointer to the keyboard manager state
|
||||
static KeyboardManagerState* keyboardManagerState;
|
||||
// Stores the current list of remappings
|
||||
static RemapBuffer singleKeyRemapBuffer;
|
||||
|
||||
// constructor
|
||||
SingleKeyRemapControl(StackPanel table, StackPanel row, const int colIndex);
|
||||
|
||||
// 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 void AddNewControlKeyRemapRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey = NULL, const KeyShortcutUnion newKey = NULL);
|
||||
|
||||
// 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);
|
||||
};
|
||||
#pragma once
|
||||
|
||||
#include <Shortcut.h>
|
||||
|
||||
#include <KeyDropDownControl.h>
|
||||
|
||||
class KeyboardManagerState;
|
||||
namespace winrt::Windows::UI::Xaml
|
||||
{
|
||||
struct XamlRoot;
|
||||
namespace Controls
|
||||
{
|
||||
struct StackPanel;
|
||||
struct Grid;
|
||||
struct Button;
|
||||
}
|
||||
}
|
||||
|
||||
class SingleKeyRemapControl
|
||||
{
|
||||
private:
|
||||
// Button to type the remap key
|
||||
winrt::Windows::Foundation::IInspectable typeKey;
|
||||
|
||||
// StackPanel to parent the above controls
|
||||
winrt::Windows::Foundation::IInspectable singleKeyRemapControlLayout;
|
||||
|
||||
// Stack panel for the drop downs to display the selected shortcut for the hybrid case
|
||||
winrt::Windows::Foundation::IInspectable hybridDropDownStackPanel;
|
||||
|
||||
// Function to set the accessible names for all the controls in a row
|
||||
static void UpdateAccessibleNames(StackPanel sourceColumn, StackPanel mappedToColumn, Button deleteButton, int rowIndex);
|
||||
|
||||
public:
|
||||
// Vector to store dynamically allocated KeyDropDownControl objects to avoid early destruction
|
||||
std::vector<std::unique_ptr<KeyDropDownControl>> keyDropDownControlObjects;
|
||||
|
||||
// Handle to the current Edit Keyboard Window
|
||||
static HWND EditKeyboardWindowHandle;
|
||||
|
||||
// Pointer to the keyboard manager state
|
||||
static KeyboardManagerState* keyboardManagerState;
|
||||
|
||||
// Stores the current list of remappings
|
||||
static RemapBuffer singleKeyRemapBuffer;
|
||||
|
||||
// constructor
|
||||
SingleKeyRemapControl(StackPanel table, StackPanel row, const int colIndex);
|
||||
|
||||
// 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 void AddNewControlKeyRemapRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey = NULL, const KeyShortcutUnion newKey = NULL);
|
||||
|
||||
// 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 "XamlBridge.h"
|
||||
#include <windowsx.h>
|
||||
#include <string>
|
||||
|
||||
bool XamlBridge::FilterMessage(const MSG* msg)
|
||||
{
|
||||
|
@ -144,6 +145,7 @@ int XamlBridge::MessageLoop()
|
|||
{
|
||||
MSG msg = {};
|
||||
HRESULT hr = S_OK;
|
||||
Logger::trace("XamlBridge::MessageLoop()");
|
||||
while (GetMessage(&msg, nullptr, 0, 0))
|
||||
{
|
||||
const bool xamlSourceProcessedMessage = FilterMessage(&msg);
|
||||
|
@ -157,6 +159,7 @@ int XamlBridge::MessageLoop()
|
|||
}
|
||||
}
|
||||
|
||||
Logger::trace("XamlBridge::MessageLoop() stopped");
|
||||
return (int)msg.wParam;
|
||||
}
|
||||
|
||||
|
@ -191,6 +194,7 @@ WPARAM GetKeyFromReason(winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNaviga
|
|||
// 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)
|
||||
{
|
||||
Logger::trace("XamlBridge::OnTakeFocusRequested()");
|
||||
if (args.Request().CorrelationId() != lastFocusRequestId)
|
||||
{
|
||||
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
|
||||
HWND XamlBridge::InitDesktopWindowsXamlSource(winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource)
|
||||
{
|
||||
Logger::trace("XamlBridge::InitDesktopWindowsXamlSource()");
|
||||
HRESULT hr = S_OK;
|
||||
winrt::init_apartment(apartment_type::single_threaded);
|
||||
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
|
||||
void XamlBridge::ClearXamlIslands()
|
||||
{
|
||||
Logger::trace("XamlBridge::ClearXamlIslands() {} focus event revokers", m_takeFocusEventRevokers.size());
|
||||
for (auto& takeFocusRevoker : m_takeFocusEventRevokers)
|
||||
{
|
||||
takeFocusRevoker.revoke();
|
||||
|
@ -267,6 +273,7 @@ void XamlBridge::ClearXamlIslands()
|
|||
// Function invoked when the window is destroyed
|
||||
void XamlBridge::OnDestroy(HWND)
|
||||
{
|
||||
Logger::trace("XamlBridge::OnDestroy()");
|
||||
PostQuitMessage(0);
|
||||
}
|
||||
|
||||
|
@ -275,6 +282,7 @@ void XamlBridge::OnActivate(HWND, UINT state, HWND hwndActDeact, BOOL fMinimized
|
|||
{
|
||||
if (state == WA_INACTIVE)
|
||||
{
|
||||
Logger::trace("XamlBridge::OnActivate()");
|
||||
m_hwndLastFocus = GetFocus();
|
||||
}
|
||||
}
|
||||
|
@ -284,18 +292,42 @@ void XamlBridge::OnSetFocus(HWND, HWND hwndOldFocus)
|
|||
{
|
||||
if (m_hwndLastFocus)
|
||||
{
|
||||
Logger::trace("XamlBridge::OnSetFocus()");
|
||||
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
|
||||
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)
|
||||
{
|
||||
HANDLE_MSG(parentWindow, WM_NCDESTROY, OnDestroy);
|
||||
HANDLE_MSG(parentWindow, WM_ACTIVATE, OnActivate);
|
||||
HANDLE_MSG(parentWindow, WM_SETFOCUS, OnSetFocus);
|
||||
}
|
||||
|
||||
return DefWindowProc(parentWindow, message, wParam, lParam);
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
#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
|
||||
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 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
|
||||
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
|
||||
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
|
||||
static void Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept;
|
||||
};
|
|
@ -1,10 +1,11 @@
|
|||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
#include <keyboardmanager/ui/BufferValidationHelpers.h>
|
||||
#include <keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.h>
|
||||
#include "TestHelpers.h"
|
||||
#include <common/interop/keyboard_layout.h>
|
||||
#include <common/interop/shared_constants.h>
|
||||
#include <functional>
|
||||
#include <keyboardmanager/common/ErrorTypes.h>
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
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 "CppUnitTest.h"
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/ui/LoadingAndSavingRemappingHelper.h>
|
||||
#include <keyboardmanager/KeyboardManagerEditorLibrary/LoadingAndSavingRemappingHelper.h>
|
||||
#include "TestHelpers.h"
|
||||
#include <common/interop/shared_constants.h>
|
||||
#include <keyboardmanager/common/ErrorTypes.h>
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
// Class for mocked keyboard input
|
||||
class MockedInput :
|
||||
public InputInterface
|
||||
public KeyboardManagerInput::InputInterface
|
||||
{
|
||||
private:
|
||||
// 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"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
||||
</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 "KeyboardEventHandlers.h"
|
||||
#include "keyboardmanager/common/Shortcut.h"
|
||||
#include "keyboardmanager/common/RemapShortcut.h"
|
||||
|
||||
#include <common/interop/shared_constants.h>
|
||||
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/common/InputInterface.h>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include <keyboardmanager/common/trace.h>
|
||||
#include <keyboardmanager/KeyboardManagerEngineLibrary/trace.h>
|
||||
|
||||
namespace KeyboardEventHandlers
|
||||
{
|
||||
// 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.
|
||||
if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG))
|
||||
|
@ -42,6 +42,7 @@ namespace KeyboardEventHandlers
|
|||
{
|
||||
key_count = std::get<Shortcut>(it->second).Size();
|
||||
}
|
||||
|
||||
LPINPUT keyEventList = new INPUT[size_t(key_count)]();
|
||||
memset(keyEventList, 0, sizeof(keyEventList));
|
||||
|
||||
|
@ -176,7 +177,7 @@ namespace KeyboardEventHandlers
|
|||
*/
|
||||
|
||||
// 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
|
||||
bool isShortcutInvoked = keyboardManagerState.CheckShortcutRemapInvoked(activatedApp);
|
||||
|
@ -194,6 +195,7 @@ namespace KeyboardEventHandlers
|
|||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the remap is to a key or a shortcut
|
||||
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
|
||||
key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + (src_size - 1) + dest_size;
|
||||
|
||||
// Do not send Disable key
|
||||
if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
|
||||
{
|
||||
|
@ -323,16 +326,17 @@ namespace KeyboardEventHandlers
|
|||
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)
|
||||
{
|
||||
// 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
|
||||
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.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp)
|
||||
{
|
||||
|
@ -482,9 +487,9 @@ namespace KeyboardEventHandlers
|
|||
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);
|
||||
}
|
||||
// If remapped to disable, do nothing and suppress the key event
|
||||
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
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
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)
|
||||
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 (isKeyboardStateClear)
|
||||
{
|
||||
|
@ -500,9 +506,11 @@ namespace KeyboardEventHandlers
|
|||
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);
|
||||
}
|
||||
// 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
|
||||
{
|
||||
// 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
|
||||
key_count = dest_size + (src_size - 1) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE;
|
||||
|
||||
|
@ -524,6 +532,7 @@ namespace KeyboardEventHandlers
|
|||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
||||
{
|
||||
|
@ -548,9 +557,9 @@ namespace KeyboardEventHandlers
|
|||
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)
|
||||
{
|
||||
// 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
|
||||
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.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp)
|
||||
{
|
||||
|
@ -666,9 +676,10 @@ namespace KeyboardEventHandlers
|
|||
delete[] keyEventList;
|
||||
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
|
||||
{
|
||||
// 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
|
||||
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.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
|
||||
{
|
||||
|
@ -748,7 +760,7 @@ namespace KeyboardEventHandlers
|
|||
}
|
||||
|
||||
// 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.
|
||||
if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG)
|
||||
|
@ -761,7 +773,7 @@ namespace KeyboardEventHandlers
|
|||
}
|
||||
|
||||
// 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.
|
||||
if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG)
|
||||
|
@ -786,6 +798,7 @@ namespace KeyboardEventHandlers
|
|||
std::wstring query_string;
|
||||
|
||||
AppSpecificShortcutRemapTable::iterator it;
|
||||
|
||||
// Check if an app-specific shortcut is already activated
|
||||
if (keyboardManagerState.GetActivatedApp() == KeyboardManagerConstants::NoActivatedApp)
|
||||
{
|
||||
|
@ -817,24 +830,8 @@ namespace KeyboardEventHandlers
|
|||
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
|
||||
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 (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);
|
||||
}
|
||||
|
||||
// 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
|
||||
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 "MockedInput.h"
|
||||
#include <keyboardmanager/common/KeyboardManagerState.h>
|
||||
#include <keyboardmanager/dll/KeyboardEventHandlers.h>
|
||||
#include <keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h>
|
||||
#include "TestHelpers.h"
|
||||
#include <common/interop/shared_constants.h>
|
||||
|
||||
|
@ -14,7 +14,7 @@ namespace RemappingLogicTests
|
|||
|
||||
{
|
||||
private:
|
||||
MockedInput mockedInputHandler;
|
||||
KeyboardManagerInput::MockedInput mockedInputHandler;
|
||||
KeyboardManagerState testState;
|
||||
std::wstring testApp1 = L"testtrocess1.exe";
|
||||
std::wstring testApp2 = L"testprocess2.exe";
|
|
@ -1,5 +1,6 @@
|
|||
#include "pch.h"
|
||||
#include "CppUnitTest.h"
|
||||
#include <keyboardmanager/common/ErrorTypes.h>
|
||||
#include <keyboardmanager/common/Helpers.h>
|
||||
#include "TestHelpers.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')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{62173D9A-6724-4C00-A1C8-FB646480A9EC}</ProjectGuid>
|
||||
<ProjectGuid>{7f4b3a60-bc27-45a7-8000-68b0b6ea7466}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>KeyboardManagerTest</RootNamespace>
|
||||
<RootNamespace>KeyboardManagerEngineTest</RootNamespace>
|
||||
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||
<ProjectSubType>NativeUnitTestProject</ProjectSubType>
|
||||
<ProjectName>KeyboardManagerEngineTest</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
|
@ -19,15 +20,12 @@
|
|||
</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'">
|
||||
<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\</OutDir>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\KeyboardManager\KeyboardManagerEngine\</OutDir>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
|
@ -42,8 +40,6 @@
|
|||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AppSpecificShortcutRemappingTests.cpp" />
|
||||
<ClCompile Include="BufferValidationTests.cpp" />
|
||||
<ClCompile Include="LoadingAndSavingRemappingTests.cpp" />
|
||||
<ClCompile Include="MockedInputSanityTests.cpp" />
|
||||
<ClCompile Include="SetKeyEventTests.cpp" />
|
||||
<ClCompile Include="OSLevelShortcutRemappingTests.cpp" />
|
||||
|
@ -53,7 +49,7 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="ShortcutTests.cpp" />
|
||||
<ClCompile Include="SingleKeyRemappingTests.cpp" />
|
||||
<ClCompile Include="KeyboardManagerHelperTests.cpp" />
|
||||
<ClCompile Include="HelperTests.cpp" />
|
||||
<ClCompile Include="TestHelpers.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -69,15 +65,9 @@
|
|||
<ProjectReference Include="..\common\KeyboardManagerCommon.vcxproj">
|
||||
<Project>{8affa899-0b73-49ec-8c50-0fadda57b2fc}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\dll\KeyboardManager.vcxproj">
|
||||
<Project>{89f34af7-1c34-4a72-aa6e-534bcf972bd9}</Project>
|
||||
<ProjectReference Include="..\KeyboardManagerEngineLibrary\KeyboardManagerEngineLibrary.vcxproj">
|
||||
<Project>{e496b7fc-1e99-4bab-849b-0e8367040b02}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ui\KeyboardManagerUI.vcxproj">
|
||||
<Project>{eaf23649-ef6e-478b-980e-81fad96cca2a}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="KeyboardManagerTest.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
|
@ -39,13 +39,7 @@
|
|||
<ClCompile Include="AppSpecificShortcutRemappingTests.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="KeyboardManagerHelperTests.cpp">
|
||||
<ClCompile Include="HelperTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShortcutTests.cpp">
|
||||
|
@ -66,11 +60,6 @@
|
|||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="KeyboardManagerTest.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</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 a new issue