diff --git a/PowerToys.sln b/PowerToys.sln
index a9aa8f8074..411be17fdd 100644
--- a/PowerToys.sln
+++ b/PowerToys.sln
@@ -147,6 +147,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImageResizerExt", "src\modu
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageResizerUITest", "src\modules\imageresizer\tests\ImageResizerUITest.csproj", "{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerKeys", "src\modules\PowerKeys\PowerKeys.vcxproj", "{89F34AF7-1C34-4A72-AA6E-534BCF972BD9}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerKeysUI", "src\modules\PowerKeysUI\PowerKeysUI.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}
@@ -201,94 +205,215 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Setting
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM = Debug|ARM
Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|ARM = Release|ARM
Release|x64 = Release|x64
+ Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Debug|ARM.ActiveCfg = Debug|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Debug|x64.ActiveCfg = Debug|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Debug|x64.Build.0 = Debug|x64
+ {9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Debug|x86.ActiveCfg = Debug|x64
+ {9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Release|ARM.ActiveCfg = Release|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Release|x64.ActiveCfg = Release|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Release|x64.Build.0 = Release|x64
+ {9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Release|x86.ActiveCfg = Release|x64
+ {74485049-C722-400F-ABE5-86AC52D929B3}.Debug|ARM.ActiveCfg = Debug|x64
{74485049-C722-400F-ABE5-86AC52D929B3}.Debug|x64.ActiveCfg = Debug|x64
{74485049-C722-400F-ABE5-86AC52D929B3}.Debug|x64.Build.0 = Debug|x64
+ {74485049-C722-400F-ABE5-86AC52D929B3}.Debug|x86.ActiveCfg = Debug|x64
+ {74485049-C722-400F-ABE5-86AC52D929B3}.Release|ARM.ActiveCfg = Release|x64
{74485049-C722-400F-ABE5-86AC52D929B3}.Release|x64.ActiveCfg = Release|x64
{74485049-C722-400F-ABE5-86AC52D929B3}.Release|x64.Build.0 = Release|x64
+ {74485049-C722-400F-ABE5-86AC52D929B3}.Release|x86.ActiveCfg = Release|x64
+ {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Debug|ARM.ActiveCfg = Debug|x64
{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Debug|x64.ActiveCfg = Debug|x64
{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Debug|x64.Build.0 = Debug|x64
+ {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Debug|x86.ActiveCfg = Debug|x64
+ {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Release|ARM.ActiveCfg = Release|x64
{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Release|x64.ActiveCfg = Release|x64
{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Release|x64.Build.0 = Release|x64
+ {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Release|x86.ActiveCfg = Release|x64
+ {44CC9375-3E6E-4D99-8913-7FB748807EBD}.Debug|ARM.ActiveCfg = Debug|x64
{44CC9375-3E6E-4D99-8913-7FB748807EBD}.Debug|x64.ActiveCfg = Debug|x64
{44CC9375-3E6E-4D99-8913-7FB748807EBD}.Debug|x64.Build.0 = Debug|x64
+ {44CC9375-3E6E-4D99-8913-7FB748807EBD}.Debug|x86.ActiveCfg = Debug|x64
+ {44CC9375-3E6E-4D99-8913-7FB748807EBD}.Release|ARM.ActiveCfg = Release|x64
{44CC9375-3E6E-4D99-8913-7FB748807EBD}.Release|x64.ActiveCfg = Release|x64
{44CC9375-3E6E-4D99-8913-7FB748807EBD}.Release|x64.Build.0 = Release|x64
+ {44CC9375-3E6E-4D99-8913-7FB748807EBD}.Release|x86.ActiveCfg = Release|x64
+ {07C389E3-6BC8-41CF-923E-307B1265FA2D}.Debug|ARM.ActiveCfg = Debug|x64
{07C389E3-6BC8-41CF-923E-307B1265FA2D}.Debug|x64.ActiveCfg = Debug|x64
{07C389E3-6BC8-41CF-923E-307B1265FA2D}.Debug|x64.Build.0 = Debug|x64
+ {07C389E3-6BC8-41CF-923E-307B1265FA2D}.Debug|x86.ActiveCfg = Debug|x64
+ {07C389E3-6BC8-41CF-923E-307B1265FA2D}.Release|ARM.ActiveCfg = Release|x64
{07C389E3-6BC8-41CF-923E-307B1265FA2D}.Release|x64.ActiveCfg = Release|x64
{07C389E3-6BC8-41CF-923E-307B1265FA2D}.Release|x64.Build.0 = Release|x64
+ {07C389E3-6BC8-41CF-923E-307B1265FA2D}.Release|x86.ActiveCfg = Release|x64
+ {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Debug|ARM.ActiveCfg = Debug|x64
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Debug|x64.ActiveCfg = Debug|x64
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Debug|x64.Build.0 = Debug|x64
+ {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Debug|x86.ActiveCfg = Debug|x64
+ {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Release|ARM.ActiveCfg = Release|x64
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Release|x64.ActiveCfg = Release|x64
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Release|x64.Build.0 = Release|x64
+ {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Release|x86.ActiveCfg = Release|x64
+ {48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|ARM.ActiveCfg = Debug|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|x64.ActiveCfg = Debug|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|x64.Build.0 = Debug|x64
+ {48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|x86.ActiveCfg = Debug|x64
+ {48804216-2A0E-4168-A6D8-9CD068D14227}.Release|ARM.ActiveCfg = Release|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Release|x64.ActiveCfg = Release|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Release|x64.Build.0 = Release|x64
+ {48804216-2A0E-4168-A6D8-9CD068D14227}.Release|x86.ActiveCfg = Release|x64
+ {9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Debug|ARM.ActiveCfg = Debug|x64
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Debug|x64.ActiveCfg = Debug|x64
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Debug|x64.Build.0 = Debug|x64
+ {9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Debug|x86.ActiveCfg = Debug|x64
+ {9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Release|ARM.ActiveCfg = Release|x64
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Release|x64.ActiveCfg = Release|x64
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Release|x64.Build.0 = Release|x64
+ {9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Release|x86.ActiveCfg = Release|x64
+ {1A066C63-64B3-45F8-92FE-664E1CCE8077}.Debug|ARM.ActiveCfg = Debug|x64
{1A066C63-64B3-45F8-92FE-664E1CCE8077}.Debug|x64.ActiveCfg = Debug|x64
{1A066C63-64B3-45F8-92FE-664E1CCE8077}.Debug|x64.Build.0 = Debug|x64
+ {1A066C63-64B3-45F8-92FE-664E1CCE8077}.Debug|x86.ActiveCfg = Debug|x64
+ {1A066C63-64B3-45F8-92FE-664E1CCE8077}.Release|ARM.ActiveCfg = Release|x64
{1A066C63-64B3-45F8-92FE-664E1CCE8077}.Release|x64.ActiveCfg = Release|x64
{1A066C63-64B3-45F8-92FE-664E1CCE8077}.Release|x64.Build.0 = Release|x64
+ {1A066C63-64B3-45F8-92FE-664E1CCE8077}.Release|x86.ActiveCfg = Release|x64
+ {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|ARM.ActiveCfg = Debug|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x64.ActiveCfg = Debug|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x64.Build.0 = Debug|x64
+ {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x86.ActiveCfg = Debug|x64
+ {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|ARM.ActiveCfg = Release|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x64.ActiveCfg = Release|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x64.Build.0 = Release|x64
+ {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x86.ActiveCfg = Release|x64
+ {B25AC7A5-FB9F-4789-B392-D5C85E948670}.Debug|ARM.ActiveCfg = Debug|Win32
{B25AC7A5-FB9F-4789-B392-D5C85E948670}.Debug|x64.ActiveCfg = Debug|x64
{B25AC7A5-FB9F-4789-B392-D5C85E948670}.Debug|x64.Build.0 = Debug|x64
+ {B25AC7A5-FB9F-4789-B392-D5C85E948670}.Debug|x86.ActiveCfg = Debug|Win32
+ {B25AC7A5-FB9F-4789-B392-D5C85E948670}.Debug|x86.Build.0 = Debug|Win32
+ {B25AC7A5-FB9F-4789-B392-D5C85E948670}.Release|ARM.ActiveCfg = Release|Win32
{B25AC7A5-FB9F-4789-B392-D5C85E948670}.Release|x64.ActiveCfg = Release|x64
{B25AC7A5-FB9F-4789-B392-D5C85E948670}.Release|x64.Build.0 = Release|x64
+ {B25AC7A5-FB9F-4789-B392-D5C85E948670}.Release|x86.ActiveCfg = Release|Win32
+ {B25AC7A5-FB9F-4789-B392-D5C85E948670}.Release|x86.Build.0 = Release|Win32
+ {51920F1F-C28C-4ADF-8660-4238766796C2}.Debug|ARM.ActiveCfg = Debug|Win32
{51920F1F-C28C-4ADF-8660-4238766796C2}.Debug|x64.ActiveCfg = Debug|x64
{51920F1F-C28C-4ADF-8660-4238766796C2}.Debug|x64.Build.0 = Debug|x64
+ {51920F1F-C28C-4ADF-8660-4238766796C2}.Debug|x86.ActiveCfg = Debug|Win32
+ {51920F1F-C28C-4ADF-8660-4238766796C2}.Debug|x86.Build.0 = Debug|Win32
+ {51920F1F-C28C-4ADF-8660-4238766796C2}.Release|ARM.ActiveCfg = Release|Win32
{51920F1F-C28C-4ADF-8660-4238766796C2}.Release|x64.ActiveCfg = Release|x64
{51920F1F-C28C-4ADF-8660-4238766796C2}.Release|x64.Build.0 = Release|x64
+ {51920F1F-C28C-4ADF-8660-4238766796C2}.Release|x86.ActiveCfg = Release|Win32
+ {51920F1F-C28C-4ADF-8660-4238766796C2}.Release|x86.Build.0 = Release|Win32
+ {0E072714-D127-460B-AFAD-B4C40B412798}.Debug|ARM.ActiveCfg = Debug|x64
{0E072714-D127-460B-AFAD-B4C40B412798}.Debug|x64.ActiveCfg = Debug|x64
{0E072714-D127-460B-AFAD-B4C40B412798}.Debug|x64.Build.0 = Debug|x64
+ {0E072714-D127-460B-AFAD-B4C40B412798}.Debug|x86.ActiveCfg = Debug|x64
+ {0E072714-D127-460B-AFAD-B4C40B412798}.Release|ARM.ActiveCfg = Release|x64
{0E072714-D127-460B-AFAD-B4C40B412798}.Release|x64.ActiveCfg = Release|x64
{0E072714-D127-460B-AFAD-B4C40B412798}.Release|x64.Build.0 = Release|x64
+ {0E072714-D127-460B-AFAD-B4C40B412798}.Release|x86.ActiveCfg = Release|x64
+ {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|ARM.ActiveCfg = Debug|Win32
{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|x64.ActiveCfg = Debug|x64
{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|x64.Build.0 = Debug|x64
+ {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|x86.ActiveCfg = Debug|Win32
+ {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|x86.Build.0 = Debug|Win32
+ {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Release|ARM.ActiveCfg = Release|Win32
{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Release|x64.ActiveCfg = Release|x64
{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Release|x64.Build.0 = Release|x64
+ {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Release|x86.ActiveCfg = Release|Win32
+ {A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Release|x86.Build.0 = Release|Win32
+ {2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Debug|ARM.ActiveCfg = Debug|Win32
{2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Debug|x64.ActiveCfg = Debug|x64
{2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Debug|x64.Build.0 = Debug|x64
+ {2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Debug|x86.ActiveCfg = Debug|Win32
+ {2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Debug|x86.Build.0 = Debug|Win32
+ {2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Release|ARM.ActiveCfg = Release|Win32
{2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Release|x64.ActiveCfg = Release|x64
{2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Release|x64.Build.0 = Release|x64
+ {2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Release|x86.ActiveCfg = Release|Win32
+ {2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Release|x86.Build.0 = Release|Win32
+ {64A80062-4D8B-4229-8A38-DFA1D7497749}.Debug|ARM.ActiveCfg = Debug|x64
{64A80062-4D8B-4229-8A38-DFA1D7497749}.Debug|x64.ActiveCfg = Debug|x64
{64A80062-4D8B-4229-8A38-DFA1D7497749}.Debug|x64.Build.0 = Debug|x64
+ {64A80062-4D8B-4229-8A38-DFA1D7497749}.Debug|x86.ActiveCfg = Debug|x64
+ {64A80062-4D8B-4229-8A38-DFA1D7497749}.Release|ARM.ActiveCfg = Release|x64
{64A80062-4D8B-4229-8A38-DFA1D7497749}.Release|x64.ActiveCfg = Release|x64
{64A80062-4D8B-4229-8A38-DFA1D7497749}.Release|x64.Build.0 = Release|x64
+ {64A80062-4D8B-4229-8A38-DFA1D7497749}.Release|x86.ActiveCfg = Release|x64
+ {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Debug|ARM.ActiveCfg = Debug|x64
{0485F45C-EA7A-4BB5-804B-3E8D14699387}.Debug|x64.ActiveCfg = Debug|x64
{0485F45C-EA7A-4BB5-804B-3E8D14699387}.Debug|x64.Build.0 = Debug|x64
+ {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Debug|x86.ActiveCfg = Debug|x64
+ {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|ARM.ActiveCfg = Release|x64
{0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|x64.ActiveCfg = Release|x64
{0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|x64.Build.0 = Release|x64
+ {0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|x86.ActiveCfg = Release|x64
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|ARM.ActiveCfg = Debug|ARM
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|ARM.Build.0 = Debug|ARM
{0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|x64.ActiveCfg = Debug|x64
{0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|x64.Build.0 = Debug|x64
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|x86.ActiveCfg = Debug|Win32
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|x86.Build.0 = Debug|Win32
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|ARM.ActiveCfg = Release|ARM
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|ARM.Build.0 = Release|ARM
{0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|x64.ActiveCfg = Release|x64
{0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|x64.Build.0 = Release|x64
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|x86.ActiveCfg = Release|Win32
+ {0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|x86.Build.0 = Release|Win32
+ {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|ARM.ActiveCfg = Debug|Win32
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|x64.ActiveCfg = Debug|x64
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|x64.Build.0 = Debug|x64
+ {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|x86.ActiveCfg = Debug|Win32
+ {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|x86.Build.0 = Debug|Win32
+ {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|ARM.ActiveCfg = Release|Win32
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|x64.ActiveCfg = Release|x64
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|x64.Build.0 = Release|x64
+ {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|x86.ActiveCfg = Release|Win32
+ {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|x86.Build.0 = Release|Win32
+ {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|ARM.ActiveCfg = Debug|x64
{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|x64.ActiveCfg = Debug|x64
{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|x64.Build.0 = Debug|x64
+ {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Debug|x86.ActiveCfg = Debug|x64
+ {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|ARM.ActiveCfg = Release|x64
{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|x64.ActiveCfg = Release|x64
{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|x64.Build.0 = Release|x64
+ {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}.Release|x86.ActiveCfg = Release|x64
+ {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Debug|ARM.ActiveCfg = Debug|x64
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Debug|x64.ActiveCfg = Debug|x64
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Debug|x64.Build.0 = Debug|x64
+ {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Debug|x86.ActiveCfg = Debug|x64
+ {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|ARM.ActiveCfg = Release|x64
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|x64.ActiveCfg = Release|x64
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|x64.Build.0 = Release|x64
+ {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|x86.ActiveCfg = Release|x64
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Debug|ARM.ActiveCfg = Debug|x64
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Debug|x64.ActiveCfg = Debug|x64
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Debug|x64.Build.0 = Debug|x64
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Debug|x86.ActiveCfg = Debug|x64
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Release|ARM.ActiveCfg = Release|x64
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Release|x64.ActiveCfg = Release|x64
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Release|x64.Build.0 = Release|x64
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Release|x86.ActiveCfg = Release|x64
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Debug|ARM.ActiveCfg = Debug|Win32
+ {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}.Debug|x86.ActiveCfg = Debug|Win32
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Debug|x86.Build.0 = Debug|Win32
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Release|ARM.ActiveCfg = Release|Win32
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Release|x64.ActiveCfg = Release|x64
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Release|x64.Build.0 = Release|x64
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Release|x86.ActiveCfg = Release|Win32
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A}.Release|x86.Build.0 = Release|Win32
+ {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|ARM.ActiveCfg = Debug|Win32
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Debug|x64.ActiveCfg = Debug|x64
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Debug|x64.Build.0 = Debug|x64
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Release|x64.ActiveCfg = Release|x64
@@ -303,12 +428,23 @@ Global
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.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}.Debug|x86.ActiveCfg = Debug|Win32
+ {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x86.Build.0 = Debug|Win32
+ {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|ARM.ActiveCfg = Release|Win32
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x64.ActiveCfg = Release|x64
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x64.Build.0 = Release|x64
+ {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x86.ActiveCfg = Release|Win32
+ {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x86.Build.0 = Release|Win32
+ {17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|ARM.ActiveCfg = Debug|Win32
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|x64.ActiveCfg = Debug|x64
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|x64.Build.0 = Debug|x64
+ {17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|x86.ActiveCfg = Debug|Win32
+ {17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|x86.Build.0 = Debug|Win32
+ {17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|ARM.ActiveCfg = Release|Win32
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|x64.ActiveCfg = Release|x64
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|x64.Build.0 = Release|x64
+ {17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|x86.ActiveCfg = Release|Win32
+ {17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|x86.Build.0 = Release|Win32
{880ED251-9E16-4713-9A70-D35FE0C01669}.Debug|x64.ActiveCfg = Debug|x64
{880ED251-9E16-4713-9A70-D35FE0C01669}.Debug|x64.Build.0 = Debug|x64
{880ED251-9E16-4713-9A70-D35FE0C01669}.Release|x64.ActiveCfg = Release|x64
@@ -399,6 +535,8 @@ Global
{8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB} = {8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03}
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6} = {8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03}
+ {89F34AF7-1C34-4A72-AA6E-534BCF972BD9} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{6C7F47CC-2151-44A3-A546-41C70025132C} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34} = {6C7F47CC-2151-44A3-A546-41C70025132C}
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B} = {6C7F47CC-2151-44A3-A546-41C70025132C}
diff --git a/installer/MSIX/PackagingLayout.xml b/installer/MSIX/PackagingLayout.xml
index 6202cbcd0a..be49f2a0c0 100644
--- a/installer/MSIX/PackagingLayout.xml
+++ b/installer/MSIX/PackagingLayout.xml
@@ -24,6 +24,7 @@
+
diff --git a/src/modules/PowerKeys/PowerKeys.rc b/src/modules/PowerKeys/PowerKeys.rc
new file mode 100644
index 0000000000..3cef7a3903
--- /dev/null
+++ b/src/modules/PowerKeys/PowerKeys.rc
@@ -0,0 +1,32 @@
+1 VERSIONINFO
+ FILEVERSION 0,1,0,0
+ PRODUCTVERSION 0,1,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Company Name"
+ VALUE "FileDescription", "$projectname$ Module"
+ VALUE "FileVersion", "0.1.0.0"
+ VALUE "InternalName", "$projectname$"
+ VALUE "LegalCopyright", "Copyright (C) 2019 Company Name"
+ VALUE "OriginalFilename", "$projectname$.dll"
+ VALUE "ProductName", "$projectname$"
+ VALUE "ProductVersion", "0.1.0.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
\ No newline at end of file
diff --git a/src/modules/PowerKeys/PowerKeys.vcxproj b/src/modules/PowerKeys/PowerKeys.vcxproj
new file mode 100644
index 0000000000..e1d0a5cae6
--- /dev/null
+++ b/src/modules/PowerKeys/PowerKeys.vcxproj
@@ -0,0 +1,135 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 15.0
+ {89f34af7-1c34-4a72-aa6e-534bcf972bd9}
+ Win32Proj
+ PowerKeys
+ 10.0
+ PowerKeys
+
+
+
+ DynamicLibrary
+ true
+ v142
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v142
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)$(Platform)\$(Configuration)\modules\
+
+
+ false
+ $(SolutionDir)$(Platform)\$(Configuration)\modules\
+
+
+
+ Use
+ Level3
+ Disabled
+ true
+ _DEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ pch.h
+ MultiThreadedDebug
+ stdcpplatest
+
+
+ Windows
+ true
+ $(OutDir)$(TargetName)$(TargetExt)
+
+
+
+
+ Use
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ NDEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ pch.h
+ MultiThreaded
+ stdcpplatest
+
+
+ Windows
+ true
+ true
+ true
+ $(OutDir)$(TargetName)$(TargetExt)
+
+
+
+
+ $(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories)
+
+
+ shlwapi.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)
+
+
+ shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+ pch.h
+ pch.h
+
+
+
+
+
+ {74485049-c722-400f-abe5-86ac52d929b3}
+
+
+ {eaf23649-ef6e-478b-980e-81fad96cca2a}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/PowerKeys/dllmain.cpp b/src/modules/PowerKeys/dllmain.cpp
new file mode 100644
index 0000000000..03d765b6e4
--- /dev/null
+++ b/src/modules/PowerKeys/dllmain.cpp
@@ -0,0 +1,602 @@
+#include "pch.h"
+#include
+#include
+#include
+#include
+#include "trace.h"
+#include
+
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ Trace::RegisterProvider();
+ break;
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ case DLL_PROCESS_DETACH:
+ Trace::UnregisterProvider();
+ break;
+ }
+ return TRUE;
+}
+
+// The PowerToy name that will be shown in the settings.
+const static wchar_t* MODULE_NAME = L"PowerKeys";
+// Add a description that will we shown in the module settings page.
+const static wchar_t* MODULE_DESC = L"Customize your experience by remapping keys or creating new shortcuts!";
+
+// Implement the PowerToy Module Interface and all the required methods.
+class PowerKeys : public PowertoyModuleIface
+{
+private:
+ // The PowerToy state.
+ bool m_enabled = false;
+
+ // Maps which store the remappings for each of the features. The bool fields should be initalised to false. They are used to check the current state of the shortcut (i.e is that particular shortcut currently pressed down or not).
+ std::unordered_map singleKeyReMap;
+ std::map, std::pair, bool>> osLevelShortcutReMap;
+ // Maps application name to the shortcut map
+ std::map, std::pair, bool>>> appSpecificShortcutReMap;
+ std::unordered_map singleKeyToggleToMod;
+
+ // Flags used for distinguishing key events sent by PowerKeys
+ static const ULONG_PTR POWERKEYS_INJECTED_FLAG = 0x1;
+ static const ULONG_PTR POWERKEYS_SINGLEKEY_FLAG = 0x11;
+ static const ULONG_PTR POWERKEYS_SHORTCUT_FLAG = 0x101;
+
+ // Dummy key event used in between key up and down events to prevent certain global events from happening
+ static const DWORD DUMMY_KEY = 0xFF;
+
+ // Low level hook handles
+ static HHOOK hook_handle;
+ static HHOOK hook_handle_copy; // make sure we do use nullptr in CallNextHookEx call
+
+ // Static pointer to the current powerkeys object required for accessing the HandleKeyboardHookEvent function in the hook procedure
+ static PowerKeys* powerkeys_object_ptr;
+
+ // Flag used to check if the UI window is currently up. A reference to the variable can be sent while calling the UI window
+ bool uiFlag = false;
+
+public:
+ // Constructor
+ PowerKeys()
+ {
+ init_map();
+ powerkeys_object_ptr = this;
+ };
+
+ // This function is used to add the hardcoded mappings
+ void init_map()
+ {
+ //// If mapped to 0x0 then key is disabled.
+ ////singleKeyReMap[0x41] = 0x42;
+ ////singleKeyReMap[0x42] = 0x43;
+ ////singleKeyReMap[0x43] = 0x41;
+ //singleKeyReMap[VK_LWIN] = VK_LCONTROL;
+ //singleKeyReMap[VK_LCONTROL] = VK_LWIN;
+ //singleKeyReMap[VK_OEM_4] = 0x0;
+ //singleKeyToggleToMod[VK_CAPITAL] = false;
+
+ //// OS-level shortcut remappings
+ //osLevelShortcutReMap[std::vector({ VK_LMENU, 0x44 })] = std::make_pair(std::vector({ VK_LCONTROL, 0x56 }), false);
+ //osLevelShortcutReMap[std::vector({ VK_LMENU, 0x45 })] = std::make_pair(std::vector({ VK_LCONTROL, 0x58 }), false);
+ ////osLevelShortcutReMap[std::vector({ VK_LWIN, 0x46 })] = std::make_pair(std::vector({ VK_LWIN, 0x53 }), false);
+ ///*osLevelShortcutReMap[std::vector({ VK_LWIN, 0x41 })] = std::make_pair(std::vector({ VK_LCONTROL, 0x58 }), false);
+ //osLevelShortcutReMap[std::vector({ VK_LCONTROL, 0x58 })] = std::make_pair(std::vector({ VK_LWIN, 0x41 }), false);*/
+
+ //osLevelShortcutReMap[std::vector({ VK_LWIN, 0x41 })] = std::make_pair(std::vector({ VK_LCONTROL, 0x58 }), false);
+ //osLevelShortcutReMap[std::vector({ VK_LCONTROL, 0x58 })] = std::make_pair(std::vector({ VK_LMENU, 0x44 }), false);
+ //osLevelShortcutReMap[std::vector({ VK_LCONTROL, 0x56 })] = std::make_pair(std::vector({ VK_LWIN, 0x41 }), false);
+
+ ////App-specific shortcut remappings
+ //appSpecificShortcutReMap[L"msedge.exe"][std::vector({ VK_LCONTROL, 0x43 })] = std::make_pair(std::vector({ VK_LCONTROL, 0x56 }), false); // Ctrl+C to Ctrl+V
+ //appSpecificShortcutReMap[L"msedge.exe"][std::vector({ VK_LMENU, 0x44 })] = std::make_pair(std::vector({ VK_LCONTROL, 0x46 }), false); // Alt+D to Ctrl+F
+ //appSpecificShortcutReMap[L"OUTLOOK.EXE"][std::vector({ VK_LCONTROL, 0x46 })] = std::make_pair(std::vector({ VK_LCONTROL, 0x45 }), false); // Ctrl+F to Ctrl+E
+ //appSpecificShortcutReMap[L"MicrosoftEdge.exe"][std::vector({ VK_LCONTROL, 0x58 })] = std::make_pair(std::vector({ VK_LCONTROL, 0x56 }), false); // Ctrl+X to Ctrl+V
+ //appSpecificShortcutReMap[L"Calculator.exe"][std::vector({ VK_LCONTROL, 0x47 })] = std::make_pair(std::vector({ VK_LSHIFT, 0x32 }), false); // Ctrl+G to Shift+2
+ }
+
+ // Destroy the powertoy and free memory
+ virtual void destroy() override
+ {
+ stop_lowlevel_keyboard_hook();
+ delete this;
+ }
+
+ // Return the display name of the powertoy, this will be cached by the runner
+ virtual const wchar_t* get_name() override
+ {
+ return MODULE_NAME;
+ }
+
+ // Return array of the names of all events that this powertoy listens for, with
+ // nullptr as the last element of the array. Nullptr can also be retured for empty
+ // list.
+ virtual const wchar_t** get_events() override
+ {
+ //static const wchar_t* events[] = { nullptr };
+ // Available events:
+ // - ll_keyboard
+ // - win_hook_event
+ //
+ static const wchar_t* events[] = { ll_keyboard, nullptr };
+
+ return events;
+ }
+
+ // Return JSON with the configuration options.
+ virtual bool get_config(wchar_t* buffer, int* buffer_size) override
+ {
+ HINSTANCE hinstance = reinterpret_cast(&__ImageBase);
+
+ // Create a Settings object.
+ PowerToysSettings::Settings settings(hinstance, get_name());
+ settings.set_description(MODULE_DESC);
+
+ return settings.serialize_to_buffer(buffer, buffer_size);
+ }
+
+ // Signal from the Settings editor to call a custom action.
+ // This can be used to spawn more complex editors.
+ virtual void call_custom_action(const wchar_t* action) override
+ {
+ static UINT custom_action_num_calls = 0;
+ try
+ {
+ // Parse the action values, including name.
+ PowerToysSettings::CustomActionObject action_object =
+ PowerToysSettings::CustomActionObject::from_json_string(action);
+
+ //if (action_object.get_name() == L"custom_action_id") {
+ // // Execute your custom action
+ //}
+ }
+ catch (std::exception&)
+ {
+ // Improper JSON.
+ }
+ }
+
+ // Called by the runner to pass the updated settings values as a serialized JSON.
+ virtual void set_config(const wchar_t* config) override
+ {
+ try
+ {
+ // Parse the input JSON string.
+ PowerToysSettings::PowerToyValues values =
+ PowerToysSettings::PowerToyValues::from_json_string(config);
+
+ // If you don't need to do any custom processing of the settings, proceed
+ // to persists the values calling:
+ values.save_to_settings_file();
+ }
+ catch (std::exception&)
+ {
+ // Improper JSON.
+ }
+ }
+
+ // Enable the powertoy
+ virtual void enable()
+ {
+ m_enabled = true;
+ HINSTANCE hInstance = reinterpret_cast(&__ImageBase);
+ std::thread(createMainWindow, hInstance, &uiFlag).detach();
+ start_lowlevel_keyboard_hook();
+ }
+
+ // Disable the powertoy
+ virtual void disable()
+ {
+ m_enabled = false;
+ stop_lowlevel_keyboard_hook();
+ }
+
+ // Returns if the powertoys is enabled
+ virtual bool is_enabled() override
+ {
+ return m_enabled;
+ }
+
+ // Handle incoming event, data is event-specific
+ virtual intptr_t signal_event(const wchar_t* name, intptr_t data) override
+ {
+ return 0;
+ }
+
+ // This methods are part of an experimental features not fully supported yet
+ virtual void register_system_menu_helper(PowertoySystemMenuIface* helper) override {}
+
+ virtual void signal_system_menu_action(const wchar_t* name) override {}
+
+ // Hook procedure definition
+ static LRESULT CALLBACK hook_proc(int nCode, WPARAM wParam, LPARAM lParam)
+ {
+ LowlevelKeyboardEvent event;
+ if (nCode == HC_ACTION)
+ {
+ event.lParam = reinterpret_cast(lParam);
+ event.wParam = wParam;
+ if (powerkeys_object_ptr->HandleKeyboardHookEvent(&event) == 1)
+ {
+ return 1;
+ }
+ }
+ return CallNextHookEx(hook_handle_copy, nCode, wParam, lParam);
+ }
+
+ // Prevent system-wide input lagging while paused in the debugger
+ //#define DISABLE_LOWLEVEL_KBHOOK_WHEN_DEBUGGED
+ void start_lowlevel_keyboard_hook()
+ {
+#if defined(_DEBUG) && defined(DISABLE_LOWLEVEL_KBHOOK_WHEN_DEBUGGED)
+ if (IsDebuggerPresent())
+ {
+ return;
+ }
+#endif
+
+ if (!hook_handle)
+ {
+ hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, hook_proc, GetModuleHandle(NULL), NULL);
+ hook_handle_copy = hook_handle;
+ if (!hook_handle)
+ {
+ throw std::runtime_error("Cannot install keyboard listener");
+ }
+ }
+ }
+
+ void stop_lowlevel_keyboard_hook()
+ {
+ if (hook_handle)
+ {
+ UnhookWindowsHookEx(hook_handle);
+ hook_handle = nullptr;
+ }
+ }
+
+ // This function can be used in HandleKeyboardHookEvent before the single key remap event to use the UI and suppress events while the remap window is active.
+ void DetectKeyUIBackend()
+ {
+ if (uiFlag)
+ {
+ // Disables the [ key when the UI window is up
+ singleKeyReMap[VK_OEM_4] = 0x0;
+ }
+ else if (singleKeyReMap.find(VK_OEM_4) == singleKeyReMap.end())
+ {
+ }
+ else
+ {
+ // Removes the remapping if the UI window is not up and the remapping doesn't already exist
+ singleKeyReMap.erase(VK_OEM_4);
+ }
+ }
+
+ intptr_t HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept
+ {
+ DetectKeyUIBackend();
+ intptr_t SingleKeyRemapResult = HandleSingleKeyRemapEvent(data);
+ // Single key remaps have priority. If a key is remapped, only the remapped version should be visible to the shortcuts
+ if (SingleKeyRemapResult == 1)
+ {
+ return 1;
+ }
+
+ intptr_t SingleKeyToggleToModResult = HandleSingleKeyToggleToModEvent(data);
+ intptr_t AppSpecificShortcutRemapResult = HandleAppSpecificShortcutRemapEvent(data);
+ if (AppSpecificShortcutRemapResult == 1)
+ {
+ return 1;
+ }
+ intptr_t OSLevelShortcutRemapResult = HandleOSLevelShortcutRemapEvent(data);
+ if ((SingleKeyRemapResult + SingleKeyToggleToModResult + OSLevelShortcutRemapResult + AppSpecificShortcutRemapResult) > 0)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ intptr_t HandleSingleKeyRemapEvent(LowlevelKeyboardEvent* data) noexcept
+ {
+ if (!(data->lParam->dwExtraInfo & POWERKEYS_INJECTED_FLAG))
+ {
+ auto it = singleKeyReMap.find(data->lParam->vkCode);
+ if (it != singleKeyReMap.end())
+ {
+ // If mapped to 0x0 then the key is disabled
+ if (it->second == 0x0)
+ {
+ return 1;
+ }
+
+ int key_count = 1;
+ LPINPUT keyEventList = new INPUT[size_t(key_count)]();
+ memset(keyEventList, 0, sizeof(keyEventList));
+ keyEventList[0].type = INPUT_KEYBOARD;
+ keyEventList[0].ki.wVk = it->second;
+ keyEventList[0].ki.dwFlags = 0;
+ keyEventList[0].ki.dwExtraInfo = POWERKEYS_SINGLEKEY_FLAG;
+ if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
+ {
+ keyEventList[0].ki.dwFlags = KEYEVENTF_KEYUP;
+ }
+
+ UINT res = SendInput(key_count, keyEventList, sizeof(INPUT));
+ delete[] keyEventList;
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+ intptr_t HandleSingleKeyToggleToModEvent(LowlevelKeyboardEvent* data) noexcept
+ {
+ if (!(data->lParam->dwExtraInfo & POWERKEYS_INJECTED_FLAG))
+ {
+ auto it = singleKeyToggleToMod.find(data->lParam->vkCode);
+ if (it != singleKeyToggleToMod.end())
+ {
+ // To avoid long presses (which leads to continuous keydown messages) from toggling the key on and off
+ if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
+ {
+ if (it->second == false)
+ {
+ singleKeyToggleToMod[data->lParam->vkCode] = true;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ int key_count = 2;
+ LPINPUT keyEventList = new INPUT[size_t(key_count)]();
+ memset(keyEventList, 0, sizeof(keyEventList));
+ keyEventList[0].type = INPUT_KEYBOARD;
+ keyEventList[0].ki.wVk = (WORD)data->lParam->vkCode;
+ keyEventList[0].ki.dwFlags = 0;
+ keyEventList[0].ki.dwExtraInfo = POWERKEYS_SINGLEKEY_FLAG;
+ keyEventList[1].type = INPUT_KEYBOARD;
+ keyEventList[1].ki.wVk = (WORD)data->lParam->vkCode;
+ keyEventList[1].ki.dwFlags = KEYEVENTF_KEYUP;
+ keyEventList[1].ki.dwExtraInfo = POWERKEYS_SINGLEKEY_FLAG;
+
+ UINT res = SendInput(key_count, keyEventList, sizeof(INPUT));
+ delete[] keyEventList;
+
+ // Reset the long press flag when the key has been lifted.
+ if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
+ {
+ singleKeyToggleToMod[data->lParam->vkCode] = false;
+ }
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+ intptr_t HandleShortcutRemapEvent(LowlevelKeyboardEvent* data, std::map, std::pair, bool>>& reMap) noexcept
+ {
+ for (auto& it : reMap)
+ {
+ DWORD src_1 = it.first[0];
+ DWORD src_2 = it.first[1];
+ WORD dest_1 = it.second.first[0];
+ WORD dest_2 = it.second.first[1];
+ // If the shortcut has been pressed down
+ if ((GetAsyncKeyState(src_1) & 0x8000) && !it.second.second)
+ {
+ if (data->lParam->vkCode == src_2 && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN))
+ {
+ int key_count = 4;
+ LPINPUT keyEventList = new INPUT[size_t(key_count)]();
+ memset(keyEventList, 0, sizeof(keyEventList));
+ if (src_1 == dest_1)
+ {
+ key_count = 1;
+ keyEventList[0].type = INPUT_KEYBOARD;
+ keyEventList[0].ki.wVk = dest_2;
+ keyEventList[0].ki.dwFlags = 0;
+ keyEventList[0].ki.dwExtraInfo = POWERKEYS_SHORTCUT_FLAG;
+ }
+ else
+ {
+ keyEventList[0].type = INPUT_KEYBOARD;
+ keyEventList[0].ki.wVk = (WORD)DUMMY_KEY;
+ keyEventList[0].ki.dwFlags = KEYEVENTF_KEYUP;
+ keyEventList[0].ki.dwExtraInfo = POWERKEYS_SHORTCUT_FLAG;
+ keyEventList[1].type = INPUT_KEYBOARD;
+ keyEventList[1].ki.wVk = (WORD)src_1;
+ keyEventList[1].ki.dwFlags = KEYEVENTF_KEYUP;
+ keyEventList[1].ki.dwExtraInfo = POWERKEYS_SHORTCUT_FLAG;
+ keyEventList[2].type = INPUT_KEYBOARD;
+ keyEventList[2].ki.wVk = dest_1;
+ keyEventList[2].ki.dwFlags = 0;
+ keyEventList[2].ki.dwExtraInfo = POWERKEYS_SHORTCUT_FLAG;
+ keyEventList[3].type = INPUT_KEYBOARD;
+ keyEventList[3].ki.wVk = dest_2;
+ keyEventList[3].ki.dwFlags = 0;
+ keyEventList[3].ki.dwExtraInfo = POWERKEYS_SHORTCUT_FLAG;
+ }
+
+ it.second.second = true;
+ UINT res = SendInput(key_count, keyEventList, sizeof(INPUT));
+ delete[] keyEventList;
+ return 1;
+ }
+ }
+ else if (it.second.second)
+ {
+ // If the modifier key of the original shortcut is released before the normal key
+ if (data->lParam->vkCode == src_1 && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP))
+ {
+ int key_count = 2;
+ LPINPUT keyEventList = new INPUT[size_t(key_count)]();
+ memset(keyEventList, 0, sizeof(keyEventList));
+ keyEventList[0].type = INPUT_KEYBOARD;
+ keyEventList[0].ki.wVk = dest_2;
+ keyEventList[0].ki.dwFlags = KEYEVENTF_KEYUP;
+ keyEventList[0].ki.dwExtraInfo = POWERKEYS_SHORTCUT_FLAG;
+ keyEventList[1].type = INPUT_KEYBOARD;
+ keyEventList[1].ki.wVk = dest_1;
+ keyEventList[1].ki.dwFlags = KEYEVENTF_KEYUP;
+ keyEventList[1].ki.dwExtraInfo = POWERKEYS_SHORTCUT_FLAG;
+ it.second.second = false;
+ UINT res = SendInput(key_count, keyEventList, sizeof(INPUT));
+
+ delete[] keyEventList;
+ return 1;
+ }
+
+ if (GetAsyncKeyState(dest_1) & 0x8000)
+ {
+ // If the original shortcut is still held down the keyboard will see the original normal key along with the new modifier (keys held down send repeated keydown messages)
+ if (data->lParam->vkCode == src_2 && (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN))
+ {
+ int key_count = 1;
+ LPINPUT keyEventList = new INPUT[size_t(key_count)]();
+ memset(keyEventList, 0, sizeof(keyEventList));
+ keyEventList[0].type = INPUT_KEYBOARD;
+ keyEventList[0].ki.wVk = dest_2;
+ keyEventList[0].ki.dwFlags = 0;
+ keyEventList[0].ki.dwExtraInfo = POWERKEYS_SHORTCUT_FLAG;
+
+ it.second.second = true;
+ UINT res = SendInput(key_count, keyEventList, sizeof(INPUT));
+ delete[] keyEventList;
+ return 1;
+ }
+ // If the normal key is released from the original shortcut then revert the keyboard state to just the original modifier being held down
+ if (data->lParam->vkCode == src_2 && (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP))
+ {
+ int key_count = 4;
+ LPINPUT keyEventList = new INPUT[size_t(key_count)]();
+ memset(keyEventList, 0, sizeof(keyEventList));
+ if (src_1 == dest_1)
+ {
+ key_count = 1;
+ keyEventList[0].type = INPUT_KEYBOARD;
+ keyEventList[0].ki.wVk = dest_2;
+ keyEventList[0].ki.dwFlags = KEYEVENTF_KEYUP;
+ keyEventList[0].ki.dwExtraInfo = POWERKEYS_SHORTCUT_FLAG;
+ }
+ else
+ {
+ keyEventList[0].type = INPUT_KEYBOARD;
+ keyEventList[0].ki.wVk = dest_2;
+ keyEventList[0].ki.dwFlags = KEYEVENTF_KEYUP;
+ keyEventList[0].ki.dwExtraInfo = POWERKEYS_SHORTCUT_FLAG;
+ keyEventList[1].type = INPUT_KEYBOARD;
+ keyEventList[1].ki.wVk = dest_1;
+ keyEventList[1].ki.dwFlags = KEYEVENTF_KEYUP;
+ keyEventList[1].ki.dwExtraInfo = POWERKEYS_SHORTCUT_FLAG;
+ keyEventList[2].type = INPUT_KEYBOARD;
+ keyEventList[2].ki.wVk = (WORD)src_1;
+ keyEventList[2].ki.dwFlags = 0;
+ keyEventList[2].ki.dwExtraInfo = POWERKEYS_SHORTCUT_FLAG;
+ keyEventList[3].type = INPUT_KEYBOARD;
+ keyEventList[3].ki.wVk = (WORD)DUMMY_KEY;
+ keyEventList[3].ki.dwFlags = KEYEVENTF_KEYUP;
+ keyEventList[3].ki.dwExtraInfo = POWERKEYS_SHORTCUT_FLAG;
+ }
+ it.second.second = false;
+ UINT res = SendInput(key_count, keyEventList, sizeof(INPUT));
+ delete[] keyEventList;
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ intptr_t HandleOSLevelShortcutRemapEvent(LowlevelKeyboardEvent* data) noexcept
+ {
+ if (data->lParam->dwExtraInfo != POWERKEYS_SHORTCUT_FLAG)
+ {
+ return HandleShortcutRemapEvent(data, osLevelShortcutReMap);
+ }
+
+ return 0;
+ }
+
+ std::wstring GetCurrentApplication(bool keepPath)
+ {
+ // Using GetGUIThreadInfo for getting the process of the window in focus. GetForegroundWindow has issues with UWP apps as it returns the Application Frame Host as its linked process
+ GUITHREADINFO guiThreadInfo;
+ guiThreadInfo.cbSize = sizeof(GUITHREADINFO);
+ GetGUIThreadInfo(0, &guiThreadInfo);
+ HWND current_window_handle = guiThreadInfo.hwndFocus;
+
+ // If no window in focus, use the active window
+ if (current_window_handle == nullptr)
+ {
+ current_window_handle = guiThreadInfo.hwndActive;
+ }
+ DWORD process_id;
+ DWORD nSize = MAX_PATH;
+ WCHAR buffer[MAX_PATH] = { 0 };
+
+ // Get process ID of the focus window
+ DWORD thread_id = GetWindowThreadProcessId(current_window_handle, &process_id);
+ HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_id);
+
+ // Get full path of the executable
+ bool res = QueryFullProcessImageName(hProc, 0, buffer, &nSize);
+ std::wstring process_name;
+ CloseHandle(hProc);
+
+ process_name = buffer;
+ if (res)
+ {
+ PathStripPath(buffer);
+
+ if (!keepPath)
+ {
+ process_name = buffer;
+ }
+ }
+ return process_name;
+ }
+
+ intptr_t HandleAppSpecificShortcutRemapEvent(LowlevelKeyboardEvent* data) noexcept
+ {
+ if (data->lParam->dwExtraInfo != POWERKEYS_SHORTCUT_FLAG)
+ {
+ std::wstring process_name = GetCurrentApplication(false);
+ if (process_name.empty())
+ {
+ return 0;
+ }
+
+ auto it = appSpecificShortcutReMap.find(process_name);
+ if (it != appSpecificShortcutReMap.end())
+ {
+ return HandleShortcutRemapEvent(data, appSpecificShortcutReMap[process_name]);
+ }
+ }
+
+ return 0;
+ }
+};
+
+HHOOK PowerKeys::hook_handle = nullptr;
+HHOOK PowerKeys::hook_handle_copy = nullptr;
+PowerKeys* PowerKeys::powerkeys_object_ptr = nullptr;
+
+extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
+{
+ return new PowerKeys();
+}
\ No newline at end of file
diff --git a/src/modules/PowerKeys/pch.cpp b/src/modules/PowerKeys/pch.cpp
new file mode 100644
index 0000000000..6db1b5fa29
--- /dev/null
+++ b/src/modules/PowerKeys/pch.cpp
@@ -0,0 +1,2 @@
+#include "pch.h"
+#pragma comment(lib, "windowsapp")
\ No newline at end of file
diff --git a/src/modules/PowerKeys/pch.h b/src/modules/PowerKeys/pch.h
new file mode 100644
index 0000000000..616ba8b3ce
--- /dev/null
+++ b/src/modules/PowerKeys/pch.h
@@ -0,0 +1,8 @@
+#pragma once
+#define WIN32_LEAN_AND_MEAN
+#include
+#include
+#include
+#include
+#include
+#include
\ No newline at end of file
diff --git a/src/modules/PowerKeys/resource.h b/src/modules/PowerKeys/resource.h
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/modules/PowerKeys/trace.cpp b/src/modules/PowerKeys/trace.cpp
new file mode 100644
index 0000000000..1ba411198c
--- /dev/null
+++ b/src/modules/PowerKeys/trace.cpp
@@ -0,0 +1,29 @@
+#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()
+{
+ TraceLoggingRegister(g_hProvider);
+}
+
+void Trace::UnregisterProvider()
+{
+ TraceLoggingUnregister(g_hProvider);
+}
+
+void Trace::MyEvent()
+{
+ TraceLoggingWrite(
+ g_hProvider,
+ "PowerToyName_MyEvent",
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
+}
diff --git a/src/modules/PowerKeys/trace.h b/src/modules/PowerKeys/trace.h
new file mode 100644
index 0000000000..acd37a7a20
--- /dev/null
+++ b/src/modules/PowerKeys/trace.h
@@ -0,0 +1,9 @@
+#pragma once
+
+class Trace
+{
+public:
+ static void RegisterProvider();
+ static void UnregisterProvider();
+ static void MyEvent();
+};
diff --git a/src/modules/PowerKeysUI/EditKeyboardWindow.cpp b/src/modules/PowerKeysUI/EditKeyboardWindow.cpp
new file mode 100644
index 0000000000..d8b5f2c31f
--- /dev/null
+++ b/src/modules/PowerKeysUI/EditKeyboardWindow.cpp
@@ -0,0 +1,106 @@
+#include "EditKeyboardWindow.h"
+
+LRESULT CALLBACK EditKeyboardWindowProc(HWND, UINT, WPARAM, LPARAM);
+
+HWND hWndXamlIslandEditKeyboardWindow = nullptr;
+
+void registerWinClass(HINSTANCE& hInst)
+{
+ // The main window class name.
+ const wchar_t szWindowClass[] = L"EditKeyboardWindow";
+ WNDCLASSEX windowClass = {};
+
+ windowClass.cbSize = sizeof(WNDCLASSEX);
+ windowClass.lpfnWndProc = EditKeyboardWindowProc;
+ windowClass.hInstance = hInst;
+ windowClass.lpszClassName = szWindowClass;
+ windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW);
+
+ windowClass.hIconSm = LoadIcon(windowClass.hInstance, IDI_APPLICATION);
+
+ if (RegisterClassEx(&windowClass) == NULL)
+ {
+ MessageBox(NULL, L"Windows registration failed!", L"Error", NULL);
+ return;
+ }
+}
+
+void createEditKeyboardWindow(HINSTANCE hInst)
+{
+ // The main window class name.
+ const wchar_t szWindowClass[] = L"EditKeyboardWindow";
+ HWND _hWndEditKeyboardWindow = CreateWindow(
+ szWindowClass,
+ L"PowerKeys Remap Keyboard",
+ WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ 400,
+ 400,
+ NULL,
+ NULL,
+ hInst,
+ NULL);
+ if (_hWndEditKeyboardWindow == NULL)
+ {
+ MessageBox(NULL, L"Call to CreateWindow failed!", L"Error", NULL);
+ return;
+ }
+
+ // This DesktopWindowXamlSource is the object that enables a non-UWP desktop application
+ // to host UWP controls in any UI element that is associated with a window handle (HWND).
+ DesktopWindowXamlSource desktopSource;
+ // Get handle to corewindow
+ auto interop = desktopSource.as();
+ // Parent the DesktopWindowXamlSource object to current window
+ check_hresult(interop->AttachToWindow(_hWndEditKeyboardWindow));
+
+ // Get the new child window's hwnd
+ interop->get_WindowHandle(&hWndXamlIslandEditKeyboardWindow);
+ // Update the xaml island window size becuase initially is 0,0
+ SetWindowPos(hWndXamlIslandEditKeyboardWindow, 0, 0, 0, 400, 400, SWP_SHOWWINDOW);
+
+ //Creating the Xaml content
+ Windows::UI::Xaml::Controls::StackPanel xamlContainer;
+ xamlContainer.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
+ Windows::UI::Xaml::Controls::Button bt;
+ bt.Content(winrt::box_value(winrt::to_hstring("Don't Type key")));
+ xamlContainer.Children().Append(bt);
+ xamlContainer.UpdateLayout();
+ desktopSource.Content(xamlContainer);
+ ////End XAML Island section
+ if (_hWndEditKeyboardWindow)
+ {
+ ShowWindow(_hWndEditKeyboardWindow, SW_SHOW);
+ UpdateWindow(_hWndEditKeyboardWindow);
+ }
+
+ // Message loop:
+ MSG msg = {};
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ desktopSource.Close();
+}
+
+LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
+{
+ RECT rcClient;
+ switch (messageCode)
+ {
+ case WM_PAINT:
+ GetClientRect(hWnd, &rcClient);
+ SetWindowPos(hWndXamlIslandEditKeyboardWindow, 0, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom, SWP_SHOWWINDOW);
+ break;
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+ default:
+ return DefWindowProc(hWnd, messageCode, wParam, lParam);
+ break;
+ }
+
+ return 0;
+}
diff --git a/src/modules/PowerKeysUI/EditKeyboardWindow.h b/src/modules/PowerKeysUI/EditKeyboardWindow.h
new file mode 100644
index 0000000000..357f0e2d20
--- /dev/null
+++ b/src/modules/PowerKeysUI/EditKeyboardWindow.h
@@ -0,0 +1,26 @@
+#pragma once
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "winrt/Windows.Foundation.h"
+#include "winrt/Windows.Foundation.Numerics.h"
+#include "winrt/Windows.UI.Xaml.Controls.Primitives.h"
+
+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::Foundation;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+
+__declspec(dllexport) void createEditKeyboardWindow(HINSTANCE hInst);
+__declspec(dllexport) void registerWinClass(HINSTANCE& hInst);
\ No newline at end of file
diff --git a/src/modules/PowerKeysUI/MainWindow.cpp b/src/modules/PowerKeysUI/MainWindow.cpp
new file mode 100644
index 0000000000..8b3f6a9eb4
--- /dev/null
+++ b/src/modules/PowerKeysUI/MainWindow.cpp
@@ -0,0 +1,165 @@
+#include "MainWindow.h"
+#include "EditKeyboardWindow.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::Foundation;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+
+HWND _hWndMain;
+HINSTANCE _hInstance;
+// This Hwnd will be the window handler for the Xaml Island: A child window that contains Xaml.
+HWND hWndXamlIslandMain = nullptr;
+bool isRegistrationCompleted = false;
+
+void createMainWindow(HINSTANCE hInstance, bool* ptr)
+{
+ _hInstance = hInstance;
+
+ // The main window class name.
+ const wchar_t szWindowClass[] = L"MainWindowClass";
+ if (!isRegistrationCompleted)
+ {
+ registerWinClass(_hInstance);
+ WNDCLASSEX windowClass = {};
+
+ windowClass.cbSize = sizeof(WNDCLASSEX);
+ windowClass.lpfnWndProc = MainWindowProc;
+ windowClass.hInstance = hInstance;
+ windowClass.lpszClassName = szWindowClass;
+ windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+
+ windowClass.hIconSm = LoadIcon(windowClass.hInstance, IDI_APPLICATION);
+
+ if (RegisterClassEx(&windowClass) == NULL)
+ {
+ MessageBox(NULL, L"Windows registration failed!", L"Error", NULL);
+ return;
+ }
+ isRegistrationCompleted = true;
+ }
+
+ _hWndMain = CreateWindow(
+ szWindowClass,
+ L"PowerKeys Settings",
+ WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ NULL,
+ NULL,
+ hInstance,
+ NULL);
+ if (_hWndMain == NULL)
+ {
+ MessageBox(NULL, L"Call to CreateWindow failed!", L"Error", NULL);
+ return;
+ }
+
+ //XAML Island section
+
+ // This DesktopWindowXamlSource is the object that enables a non-UWP desktop application
+ // to host UWP controls in any UI element that is associated with a window handle (HWND).
+ DesktopWindowXamlSource desktopSource;
+ // Get handle to corewindow
+ auto interop = desktopSource.as();
+ // Parent the DesktopWindowXamlSource object to current window
+ check_hresult(interop->AttachToWindow(_hWndMain));
+
+ // Get the new child window's hwnd
+ interop->get_WindowHandle(&hWndXamlIslandMain);
+ // Update the xaml island window size becuase initially is 0,0
+ SetWindowPos(hWndXamlIslandMain, 0, 0, 0, 800, 800, SWP_SHOWWINDOW);
+
+ //Creating the Xaml content
+ Windows::UI::Xaml::Controls::StackPanel xamlContainer;
+ xamlContainer.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() });
+
+ Windows::UI::Xaml::Controls::StackPanel keyRow;
+ keyRow.Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
+ keyRow.Spacing(10);
+ keyRow.Margin({ 10 });
+
+ Windows::Foundation::Collections::IVector keyNames{ single_threaded_vector(
+ { winrt::box_value(L"Alt"),
+ winrt::box_value(L"Delete"),
+ winrt::box_value(L"LAlt"),
+ winrt::box_value(L"LWin"),
+ winrt::box_value(L"Shift"),
+ winrt::box_value(L"NumLock"),
+ winrt::box_value(L"LCtrl") }) };
+ Windows::UI::Xaml::Controls::ComboBox cb;
+ cb.IsEditable(true);
+ cb.Width(200);
+ cb.ItemsSource(keyNames);
+
+ Windows::UI::Xaml::Controls::Button bt;
+ bt.Content(winrt::box_value(winrt::to_hstring("Edit Keyboard")));
+ bt.Click([&](IInspectable const& sender, RoutedEventArgs const&) {
+ if (ptr != nullptr)
+ {
+ *ptr = true;
+ }
+ std::thread th(createEditKeyboardWindow, _hInstance);
+ th.join();
+ if (ptr != nullptr)
+ {
+ *ptr = false;
+ }
+ });
+
+ Windows::UI::Xaml::Controls::Button bt2;
+ bt2.Content(winrt::box_value(winrt::to_hstring("Edit Shortcuts")));
+ bt2.Click([&](IInspectable const& sender, RoutedEventArgs const&) {
+ });
+
+ keyRow.Children().Append(cb);
+ keyRow.Children().Append(bt);
+ keyRow.Children().Append(bt2);
+
+
+ xamlContainer.Children().Append(keyRow);
+ xamlContainer.UpdateLayout();
+ desktopSource.Content(xamlContainer);
+ //End XAML Island section
+ if (_hWndMain)
+ {
+ ShowWindow(_hWndMain, SW_SHOW);
+ UpdateWindow(_hWndMain);
+ }
+
+ //Message loop:
+ MSG msg = {};
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ desktopSource.Close();
+}
+
+LRESULT CALLBACK MainWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
+{
+ RECT rcClient;
+
+ switch (messageCode)
+ {
+ case WM_PAINT:
+ GetClientRect(hWnd, &rcClient);
+ SetWindowPos(hWndXamlIslandMain, 0, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom, SWP_SHOWWINDOW);
+ break;
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+ default:
+ return DefWindowProc(hWnd, messageCode, wParam, lParam);
+ break;
+ }
+
+ return 0;
+}
diff --git a/src/modules/PowerKeysUI/MainWindow.h b/src/modules/PowerKeysUI/MainWindow.h
new file mode 100644
index 0000000000..3827e636ab
--- /dev/null
+++ b/src/modules/PowerKeysUI/MainWindow.h
@@ -0,0 +1,17 @@
+#pragma once
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "winrt/Windows.Foundation.h"
+#include "winrt/Windows.Foundation.Numerics.h"
+#include "winrt/Windows.UI.Xaml.Controls.Primitives.h"
+
+__declspec(dllexport) void createMainWindow(HINSTANCE hInstance, bool* ptr);
+LRESULT CALLBACK MainWindowProc(HWND, UINT, WPARAM, LPARAM);
\ No newline at end of file
diff --git a/src/modules/PowerKeysUI/PowerKeysUI.vcxproj b/src/modules/PowerKeysUI/PowerKeysUI.vcxproj
new file mode 100644
index 0000000000..4442e8d43f
--- /dev/null
+++ b/src/modules/PowerKeysUI/PowerKeysUI.vcxproj
@@ -0,0 +1,173 @@
+
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 15.0
+ {EAF23649-EF6E-478B-980E-81FAD96CCA2A}
+ Win32Proj
+ PowerKeysUI
+ 10.0.18362.0
+ true
+ PowerKeysUI
+
+
+
+ Application
+ true
+ v142
+ Unicode
+
+
+ Application
+ false
+ v142
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v142
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v142
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+ false
+
+
+
+ Level3
+ Disabled
+ true
+ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+
+
+ true
+ Windows
+
+
+
+
+ Level3
+ Disabled
+ true
+ UNICODE;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ stdcpp17
+
+
+ true
+ Windows
+ 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)
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+
+
+ true
+ true
+ true
+ Windows
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+
+
+ true
+ true
+ true
+ Windows
+ 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)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/PowerKeysUI/PowerKeysUI.vcxproj.filters b/src/modules/PowerKeysUI/PowerKeysUI.vcxproj.filters
new file mode 100644
index 0000000000..31dfe7a343
--- /dev/null
+++ b/src/modules/PowerKeysUI/PowerKeysUI.vcxproj.filters
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/PowerKeysUI/packages.config b/src/modules/PowerKeysUI/packages.config
new file mode 100644
index 0000000000..29fa9c47fc
--- /dev/null
+++ b/src/modules/PowerKeysUI/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/runner/PowerToys.exe.manifest b/src/runner/PowerToys.exe.manifest
new file mode 100644
index 0000000000..f75f762c33
--- /dev/null
+++ b/src/runner/PowerToys.exe.manifest
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/runner/main.cpp b/src/runner/main.cpp
index 0c197f103d..471d97a62d 100644
--- a/src/runner/main.cpp
+++ b/src/runner/main.cpp
@@ -203,7 +203,8 @@ int runner(bool isProcessElevated)
L"PowerRenameExt.dll",
L"ImageResizerExt.dll",
L"powerpreview.dll",
- L"WindowWalker.dll"
+ L"WindowWalker.dll",
+ L"PowerKeys.dll"
};
for (auto& file : std::filesystem::directory_iterator(L"modules/"))
{
diff --git a/src/runner/runner.vcxproj b/src/runner/runner.vcxproj
index 8e1a891e96..69818f10f5 100644
--- a/src/runner/runner.vcxproj
+++ b/src/runner/runner.vcxproj
@@ -14,7 +14,7 @@
15.0
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}
powertoys
- 10.0
+ 10.0.18362.0
runner
@@ -240,6 +240,9 @@
+
+
+