[Shortcut Guide] Move into separate process (#11359)

This commit is contained in:
Mykhailo Pylyp 2021-05-20 15:07:34 +03:00 committed by GitHub
parent c948c1fca8
commit 601da71f15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
114 changed files with 2346 additions and 1812 deletions

View File

@ -112,6 +112,7 @@ atlstr
attr
Attribs
aumid
Aut
AUTHN
AUTOAPPEND
autocomplete

View File

@ -147,7 +147,8 @@ build:
- 'modules\launcher\Wox.Plugin.dll'
- 'modules\Microsoft.Launcher.dll'
- 'modules\PowerRename\PowerRenameExt.dll'
- 'modules\ShortcutGuide\ShortcutGuide.dll'
- 'modules\ShortcutGuide\ShortcutGuide\PowerToys.ShortcutGuide.exe'
- 'modules\ShortcutGuide\ShortcutGuideModuleInterface\ShortcutGuideModuleInterface.dll'
- 'Notifications.dll'
- 'os-detection.dll'
- 'PowerToys.exe'

View File

@ -22,15 +22,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runner", "src\runner\runner
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B} = {0B43679E-EDFA-4DA0-AD30-F4628B308B1B}
{B25AC7A5-FB9F-4789-B392-D5C85E948670} = {B25AC7A5-FB9F-4789-B392-D5C85E948670}
{AF2349B8-E5B6-4004-9502-687C1C7730B1} = {AF2349B8-E5B6-4004-9502-687C1C7730B1}
{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA} = {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {17DA04DF-E393-4397-9CF0-84DABE11032E}
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99} = {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}
{655C9AF2-18D3-4DA6-80E4-85504A7722BA} = {655C9AF2-18D3-4DA6-80E4-85504A7722BA}
{89F34AF7-1C34-4A72-AA6E-534BCF972BD9} = {89F34AF7-1C34-4A72-AA6E-534BCF972BD9}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShortcutGuide", "src\modules\shortcut_guide\shortcut_guide.vcxproj", "{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{4574FDD0-F61D-4376-98BF-E5A1262C11EC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "interface", "interface", "{3BB8493E-D18E-4485-A320-CB40F90F55AE}"
@ -324,6 +321,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEditorLibrar
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEditorTest", "src\modules\keyboardmanager\KeyboardManagerEditorTest\KeyboardManagerEditorTest.vcxproj", "{62173D9A-6724-4C00-A1C8-FB646480A9EC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shortcutguide", "shortcutguide", "{106CBECA-0701-4FC3-838C-9DF816A19AE2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShortcutGuideModuleInterface", "src\modules\ShortcutGuide\ShortcutGuideModuleInterface\ShortcutGuideModuleInterface.vcxproj", "{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShortcutGuide", "src\modules\ShortcutGuide\ShortcutGuide\ShortcutGuide.vcxproj", "{2EDB3EB4-FA92-4BFF-B2D8-566584837231}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -334,10 +337,6 @@ Global
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Debug|x64.Build.0 = Debug|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Release|x64.ActiveCfg = Release|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Release|x64.Build.0 = Release|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}.Release|x64.ActiveCfg = Release|x64
{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}.Release|x64.Build.0 = Release|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}.Release|x64.ActiveCfg = Release|x64
@ -660,12 +659,19 @@ Global
{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
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}.Debug|x64.ActiveCfg = Debug|x64
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}.Debug|x64.Build.0 = Debug|x64
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}.Release|x64.ActiveCfg = Release|x64
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}.Release|x64.Build.0 = Release|x64
{2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Debug|x64.ActiveCfg = Debug|x64
{2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Debug|x64.Build.0 = Debug|x64
{2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Release|x64.ActiveCfg = Release|x64
{2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{3BB8493E-D18E-4485-A320-CB40F90F55AE} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{D1D6BC88-09AE-4FB4-AD24-5DED46A791DD} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
@ -759,6 +765,9 @@ Global
{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}
{106CBECA-0701-4FC3-838C-9DF816A19AE2} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81} = {106CBECA-0701-4FC3-838C-9DF816A19AE2}
{2EDB3EB4-FA92-4BFF-B2D8-566584837231} = {106CBECA-0701-4FC3-838C-9DF816A19AE2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

View File

@ -6,13 +6,14 @@
<?define ImageResizerProjectName="ImageResizer"?>
<?define KeyboardManagerProjectName="KeyboardManager"?>
<?define PowerRenameProjectName="PowerRename"?>
<?define ShortcutGuideProjectName="ShortcutGuide"?>
<?define ColorPickerProjectName="ColorPicker"?>
<?define RepoDir="$(var.ProjectDir)..\..\" ?>
<?define BinX64Dir="$(var.RepoDir)x64\$(var.Configuration)\" ?>
<Product Id="*"
<?define ShortcutGuideExecutable=$(var.BinX64Dir)\modules\ShortcutGuide\ShortcutGuide?>
<?define ShortcutGuideModuleInterface=$(var.BinX64Dir)\modules\ShortcutGuide\ShortcutGuideModuleInterface?>
<Product Id="*"
Name="PowerToys (Preview)"
Language="1033"
Version="$(var.Version)"
@ -205,11 +206,16 @@
<Directory Id="ProgramFiles64Folder">
<Directory Id="INSTALLFOLDER" Name="PowerToys">
<Directory Id="SvgsInstallFolder" Name="svgs"/>
<Directory Id="ToolsFolder" Name="Tools"/>
<Directory Id="ToolsFolder" Name="Tools"/>
<Directory Id="ModulesInstallFolder" Name="modules">
<Directory Id="ImageResizerInstallFolder" Name="$(var.ImageResizerProjectName)" />
<Directory Id="PowerRenameInstallFolder" Name="$(var.PowerRenameProjectName)"/>
<Directory Id="ShortcutGuideInstallFolder" Name="$(var.ShortcutGuideProjectName)"/>
<Directory Id="ShortcutGuideInstallFolder" Name="ShortcutGuide">
<Directory Id="ShortcutGuideExecutableInstallFolder" Name="ShortcutGuide">
<Directory Id="ShortcutGuideSvgsInstallFolder" Name="svgs"/>
</Directory>
<Directory Id="ShortcutGuideModuleInterfaceInstallFolder" Name="ShortcutGuideModuleInterface"/>
</Directory>
<Directory Id="FileExplorerPreviewInstallFolder" Name="FileExplorerPreview" />
<Directory Id="FancyZonesInstallFolder" Name="$(var.FancyZonesProjectName)" />
<Directory Id="KeyboardManagerInstallFolder" Name="$(var.KeyboardManagerProjectName)">
@ -355,21 +361,21 @@
</Component>
</DirectoryRef>
<DirectoryRef Id="SvgsInstallFolder" FileSource="$(var.BinX64Dir)svgs\">
<Component Id="PowerToysSvgs" Guid="7C4D4EED-9338-423D-992C-DCE02F3E2D35" Win64="yes">
<File Source="$(var.BinX64Dir)svgs\0.svg" />
<File Source="$(var.BinX64Dir)svgs\1.svg" />
<File Source="$(var.BinX64Dir)svgs\2.svg" />
<File Source="$(var.BinX64Dir)svgs\3.svg" />
<File Source="$(var.BinX64Dir)svgs\4.svg" />
<File Source="$(var.BinX64Dir)svgs\5.svg" />
<File Source="$(var.BinX64Dir)svgs\6.svg" />
<File Source="$(var.BinX64Dir)svgs\7.svg" />
<File Source="$(var.BinX64Dir)svgs\8.svg" />
<File Source="$(var.BinX64Dir)svgs\9.svg" />
<File Source="$(var.BinX64Dir)svgs\no_active_window.svg" />
<File Source="$(var.BinX64Dir)svgs\overlay.svg" />
<File Source="$(var.BinX64Dir)svgs\overlay_portrait.svg" />
<DirectoryRef Id="ShortcutGuideSvgsInstallFolder" FileSource="$(var.ShortcutGuideExecutable)\svgs\">
<Component Id="ShortcutGuideSvgs" Guid="7C4D4EED-9338-423D-992C-DCE02F3E2D35" Win64="yes">
<File Source="$(var.ShortcutGuideExecutable)\svgs\0.svg" />
<File Source="$(var.ShortcutGuideExecutable)\svgs\1.svg" />
<File Source="$(var.ShortcutGuideExecutable)\svgs\2.svg" />
<File Source="$(var.ShortcutGuideExecutable)\svgs\3.svg" />
<File Source="$(var.ShortcutGuideExecutable)\svgs\4.svg" />
<File Source="$(var.ShortcutGuideExecutable)\svgs\5.svg" />
<File Source="$(var.ShortcutGuideExecutable)\svgs\6.svg" />
<File Source="$(var.ShortcutGuideExecutable)\svgs\7.svg" />
<File Source="$(var.ShortcutGuideExecutable)\svgs\8.svg" />
<File Source="$(var.ShortcutGuideExecutable)\svgs\9.svg" />
<File Source="$(var.ShortcutGuideExecutable)\svgs\no_active_window.svg" />
<File Source="$(var.ShortcutGuideExecutable)\svgs\overlay.svg" />
<File Source="$(var.ShortcutGuideExecutable)\svgs\overlay_portrait.svg" />
</Component>
</DirectoryRef>
<DirectoryRef Id="FancyZonesInstallFolder" FileSource="$(var.BinX64Dir)modules\">
@ -582,9 +588,15 @@
</Component>
</DirectoryRef>
<DirectoryRef Id="ShortcutGuideInstallFolder" FileSource="$(var.BinX64Dir)modules\$(var.ShortcutGuideProjectName)\">
<Component Id="Module_ShortcutGuide" Guid="CBD0AC09-91D3-428E-B2B3-05745ADF3473" Win64="yes">
<File Source="$(var.BinX64Dir)modules\$(var.ShortcutGuideProjectName)\$(var.ShortcutGuideProjectName).dll" KeyPath="yes" />
<DirectoryRef Id="ShortcutGuideModuleInterfaceInstallFolder" FileSource="$(var.ShortcutGuideModuleInterface)">
<Component Id="Module_ShortcutGuideModuleInterface" Guid="CBD0AC09-91D3-428E-B2B3-05745ADF3473" Win64="yes">
<File Source="$(var.ShortcutGuideModuleInterface)\ShortcutGuideModuleInterface.dll" KeyPath="yes" />
</Component>
</DirectoryRef>
<DirectoryRef Id="ShortcutGuideExecutableInstallFolder" FileSource="$(var.ShortcutGuideExecutable)">
<Component Id="Module_ShortcutGuideExecutable" Guid="DA6E5710-F1DF-44EB-A316-300FA39544E9" Win64="yes">
<File Source="$(var.ShortcutGuideExecutable)\PowerToys.ShortcutGuide.exe" KeyPath="yes" />
</Component>
</DirectoryRef>
@ -786,8 +798,9 @@
<ComponentRef Id="Notice_md" />
<ComponentRef Id="powertoysinterop_dll" />
<ComponentRef Id="vcredist_dlls" />
<ComponentRef Id="PowerToysSvgs" />
<ComponentRef Id="Module_ShortcutGuide" />
<ComponentRef Id="ShortcutGuideSvgs" />
<ComponentRef Id="Module_ShortcutGuideModuleInterface" />
<ComponentRef Id="Module_ShortcutGuideExecutable" />
<ComponentRef Id="Module_FancyZones" />
<ComponentRef Id="DesktopShortcut" />
<ComponentRef Id="Module_PowerRename" />

View File

@ -155,7 +155,33 @@ namespace PowerToysSettings
return get_modifiers_repeat() | MOD_NOREPEAT;
}
protected:
std::wstring to_string()
{
std::wstring result = L"";
if (shift_pressed())
{
result += L"shift+";
}
if (ctrl_pressed())
{
result += L"ctrl+";
}
if (win_pressed())
{
result += L"win+";
}
if (alt_pressed())
{
result += L"alt+";
}
result += key_from_code(get_code());
return result;
}
static std::wstring key_from_code(UINT key_code)
{
auto layout = GetKeyboardLayout(0);
@ -209,6 +235,8 @@ namespace PowerToysSettings
}
return L"(Key " + std::to_wstring(key_code) + L")";
}
protected:
HotkeyObject(json::JsonObject hotkey_json) :
m_json(std::move(hotkey_json))
{

View File

@ -150,10 +150,6 @@ public
static String ^ ShowColorPickerSharedEvent() {
return gcnew String(CommonSharedConstants::SHOW_COLOR_PICKER_SHARED_EVENT);
}
static String ^ ShowShortcutGuideSharedEvent() {
return gcnew String(CommonSharedConstants::SHOW_SHORTCUT_GUIDE_SHARED_EVENT);
}
};
}

View File

@ -22,8 +22,7 @@ namespace CommonSharedConstants
// Path to the event used to show Color Picker
const wchar_t SHOW_COLOR_PICKER_SHARED_EVENT[] = L"Local\\ShowColorPickerEvent-8c46be2a-3e05-4186-b56b-4ae986ef2525";
// Path to the event used to show Shortcut Guide
const wchar_t SHOW_SHORTCUT_GUIDE_SHARED_EVENT[] = L"Local\\ShowShortcutGuideEvent-6982d682-7462-404f-95af-86ae3f089c4f";
const wchar_t SHORTCUT_GUIDE_EXIT_EVENT[] = L"Local\\ShortcutGuide-ExitEvent-35697cdd-a3d2-47d6-a246-34efcc73eac0";
// Max DWORD for key code to disable keys.
const int VK_DISABLED = 0x100;

View File

@ -4,9 +4,9 @@
"LanguageSet": "Azure_Languages",
"LocItems": [
{
"SourceFile": "src\\modules\\shortcut_guide\\Resources.resx",
"SourceFile": "src\\modules\\ShortcutGuide\\ShortcutGuide\\Resources.resx",
"CopyOption": "LangIDOnName",
"OutputPath": "src\\modules\\shortcut_guide"
"OutputPath": "src\\modules\\ShortcutGuide\\ShortcutGuide"
}
]
}

View File

@ -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>

View File

@ -117,9 +117,6 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Setting_Description_Press_Time" xml:space="preserve">
<value>How long to press the Windows key before showing the Shortcut Guide (ms)</value>
</data>
<data name="Setting_Description_Overlay_Opacity" xml:space="preserve">
<value>Opacity of the Shortcut Guide's overlay background (%)</value>
</data>

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,54 @@
#include <windows.h>
#include "Generated Files/resource.h"
#include "../../../../common/version/version.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON1 ICON "Shortcut-Guide.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", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@ -0,0 +1,8 @@
<?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>
</assembly>

View File

@ -0,0 +1,189 @@
<?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')" />
<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 ShortcutGuide.base.rc ShortcutGuide.rc" />
</Target>
<PropertyGroup Label="Globals">
<CppWinRTOptimized>true</CppWinRTOptimized>
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
<CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata>
<MinimalCoreWin>true</MinimalCoreWin>
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{2edb3eb4-fa92-4bff-b2d8-566584837231}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>ShortcutGuide</RootNamespace>
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0'">v141</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0'">v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LinkIncremental>false</LinkIncremental>
</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\ShortcutGuide\$(ProjectName)\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\ShortcutGuide\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>;..\..\..\common\inc;..\..\..\common\Telemetry;..\..\..\;..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>ole32.lib;Shell32.lib;OleAut32.lib;Dbghelp.lib;Dwmapi.lib;Dcomp.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="animation.h" />
<ClInclude Include="d2d_svg.h" />
<ClInclude Include="d2d_text.h" />
<ClInclude Include="d2d_window.h" />
<ClInclude Include="Generated Files\resource.h" />
<ClInclude Include="native_event_waiter.h" />
<ClInclude Include="overlay_window.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.base.h" />
<ClInclude Include="ShortcutGuideSettings.h" />
<ClInclude Include="ShortcutGuideConstants.h" />
<ClInclude Include="shortcut_guide.h" />
<ClInclude Include="start_visible.h" />
<ClInclude Include="target_state.h" />
<ClInclude Include="tasklist_positions.h" />
<ClInclude Include="trace.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="animation.cpp" />
<ClCompile Include="d2d_svg.cpp" />
<ClCompile Include="d2d_text.cpp" />
<ClCompile Include="d2d_window.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="native_event_waiter.cpp" />
<ClCompile Include="overlay_window.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="shortcut_guide.cpp" />
<ClCompile Include="start_visible.cpp" />
<ClCompile Include="target_state.cpp" />
<ClCompile Include="tasklist_positions.cpp" />
<ClCompile Include="trace.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="PropertySheet.props" />
<CopyFileToFolders Include="svgs\0.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\1.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\2.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\3.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\4.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\5.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\6.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\7.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\8.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\9.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\no_active_window.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\overlay.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\overlay_portrait.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
</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>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\ShortcutGuide.rc" />
<None Include="ShortcutGuide.base.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="Shortcut-Guide.ico" />
</ItemGroup>
<ItemGroup>
<Manifest Include="ShortcutGuide.exe.manifest" />
</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>

View File

@ -0,0 +1,180 @@
<?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>
<Filter Include="svgs">
<UniqueIdentifier>{800507f8-718d-48d1-aa56-96c040795b8a}</UniqueIdentifier>
</Filter>
<Filter Include="Generated Files">
<UniqueIdentifier>{cb917ac7-30da-494b-81f1-cbe4415e91f4}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="animation.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d2d_svg.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d2d_text.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d2d_window.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="native_event_waiter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="overlay_window.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="shortcut_guide.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ShortcutGuideConstants.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="start_visible.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="target_state.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="tasklist_positions.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="trace.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.base.h">
<Filter>Resource Files</Filter>
</ClInclude>
<ClInclude Include="Generated Files\resource.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="ShortcutGuideSettings.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>
<ClCompile Include="animation.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d2d_svg.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d2d_text.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d2d_window.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="native_event_waiter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="overlay_window.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="shortcut_guide.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="start_visible.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="target_state.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="tasklist_positions.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="trace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
<None Include="packages.config" />
<None Include="ShortcutGuide.base.rc">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<CopyFileToFolders Include="svgs\0.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\1.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\2.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\3.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\4.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\5.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\6.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\7.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\8.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\9.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\no_active_window.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\overlay.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\overlay_portrait.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
<Filter>Resource Files</Filter>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\ShortcutGuide.rc">
<Filter>Generated Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Image Include="Shortcut-Guide.ico">
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>
<ItemGroup>
<Manifest Include="ShortcutGuide.exe.manifest" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,10 @@
#pragma once
#include <string>
struct ShortcutGuideSettings
{
std::wstring hotkey = L"shift+win+/";
int overlayOpacity = 90;
std::wstring theme = L"system";
std::wstring disabledApps = L"";
};

View File

@ -5,17 +5,20 @@ D2DSVG& D2DSVG::load(const std::wstring& filename, ID2D1DeviceContext5* d2d_dc)
{
svg = nullptr;
winrt::com_ptr<IStream> svg_stream;
winrt::check_hresult(SHCreateStreamOnFileEx(filename.c_str(),
STGM_READ,
FILE_ATTRIBUTE_NORMAL,
FALSE,
nullptr,
svg_stream.put()));
auto h = SHCreateStreamOnFileEx(filename.c_str(),
STGM_READ,
FILE_ATTRIBUTE_NORMAL,
FALSE,
nullptr,
svg_stream.put());
winrt::check_hresult(h);
winrt::check_hresult(d2d_dc->CreateSvgDocument(
auto h1 = d2d_dc->CreateSvgDocument(
svg_stream.get(),
D2D1::SizeF(1, 1),
svg.put()));
svg.put());
winrt::check_hresult(h1);
winrt::com_ptr<ID2D1SvgElement> root;
svg->GetRoot(root.put());

View File

@ -3,8 +3,7 @@
#include <common/utils/resources.h>
D2DWindow::D2DWindow(std::optional<std::function<std::remove_pointer_t<WNDPROC>>> _pre_wnd_proc) :
pre_wnd_proc(std::move(_pre_wnd_proc))
D2DWindow::D2DWindow()
{
static const WCHAR* class_name = L"PToyD2DPopup";
WNDCLASS wc = {};
@ -190,10 +189,6 @@ D2DWindow* D2DWindow::this_from_hwnd(HWND window)
LRESULT __stdcall D2DWindow::d2d_window_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam)
{
auto self = this_from_hwnd(window);
if (self && self->pre_wnd_proc.has_value())
{
(*self->pre_wnd_proc)(window, message, wparam, lparam);
}
switch (message)
{
case WM_NCCREATE:

View File

@ -17,7 +17,7 @@
class D2DWindow
{
public:
D2DWindow(std::optional<std::function<std::remove_pointer_t<WNDPROC>>> pre_wnd_proc = std::nullopt);
D2DWindow();
void show(UINT x, UINT y, UINT width, UINT height);
void hide();
void initialize();
@ -62,6 +62,4 @@ protected:
winrt::com_ptr<ID2D1Factory6> d2d_factory;
winrt::com_ptr<ID2D1Device5> d2d_device;
winrt::com_ptr<ID2D1DeviceContext5> d2d_dc;
std::optional<std::function<std::remove_pointer_t<WNDPROC>>> pre_wnd_proc;
};

View File

@ -0,0 +1,129 @@
#include "pch.h"
#include <Windows.h>
#include <common/utils/window.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/utils/ProcessWaiter.h>
#include <common/utils/winapi_error.h>
#include <common/utils/UnhandledExceptionHandler_x64.h>
#include <common/utils/logger_helper.h>
#include <common/utils/EventWaiter.h>
#include "shortcut_guide.h"
#include "target_state.h"
#include "ShortcutGuideConstants.h"
#include "trace.h"
const std::wstring instanceMutexName = L"Local\\PowerToys_ShortcutGuide_InstanceMutex";
// set current path to the executable path
bool SetCurrentPath()
{
TCHAR buffer[MAX_PATH] = { 0 };
if (!GetModuleFileName(NULL, buffer, MAX_PATH))
{
Logger::error(L"Failed to get module path. {}", get_last_error_or_default(GetLastError()));
return false;
}
if (!PathRemoveFileSpec(buffer))
{
Logger::error(L"Failed to remove file from module path. {}", get_last_error_or_default(GetLastError()));
return false;
}
std::error_code err;
std::filesystem::current_path(buffer, err);
if (err.value())
{
Logger::error("Failed to set current path. {}", err.message());
return false;
}
return true;
}
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ PWSTR lpCmdLine, _In_ int nCmdShow)
{
winrt::init_apartment();
LoggerHelpers::init_logger(ShortcutGuideConstants::ModuleKey, L"ShortcutGuide", LogSettings::shortcutGuideLoggerName);
InitUnhandledExceptionHandler_x64();
Logger::trace("Starting Shortcut Guide");
if (!SetCurrentPath())
{
return false;
}
Trace::RegisterProvider();
if (std::wstring(lpCmdLine).find(L' ') != std::wstring::npos)
{
Logger::trace("Sending settings telemetry");
auto settings = OverlayWindow::GetSettings();
Trace::SendSettings(settings);
Trace::UnregisterProvider();
return 0;
}
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"Shortcut Guide instance is already running");
Trace::UnregisterProvider();
return 0;
}
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 Shortcut Guide");
PostThreadMessage(mainThreadId, WM_QUIT, 0, 0);
});
}
auto hwnd = GetForegroundWindow();
auto window = OverlayWindow(hwnd);
EventWaiter exitEventWaiter;
if (window.IsDisabled())
{
Logger::trace("SG is disabled for the current foreground app. Exiting SG");
Trace::UnregisterProvider();
return 0;
}
else
{
auto mainThreadId = GetCurrentThreadId();
exitEventWaiter = EventWaiter(CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT, [mainThreadId, &window](int err) {
if (err != ERROR_SUCCESS)
{
Logger::error(L"Failed to wait for {} event. {}", CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT, get_last_error_or_default(err));
}
else
{
Logger::trace(L"{} event was signaled", CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT);
}
window.CloseWindow(HideWindowType::THE_SHORTCUT_PRESSED, mainThreadId);
});
}
window.ShowWindow();
run_message_loop();
Trace::UnregisterProvider();
return 0;
}

View File

@ -6,7 +6,6 @@
#include <common/utils/resources.h>
#include <common/utils/window.h>
#include "keyboard_state.h"
#include "shortcut_guide.h"
#include "trace.h"
#include "Generated Files/resource.h"
@ -268,8 +267,8 @@ D2D1_RECT_F D2DOverlaySVG::get_snap_right() const
return result;
}
D2DOverlayWindow::D2DOverlayWindow(std::optional<std::function<std::remove_pointer_t<WNDPROC>>> pre_wnd_proc) :
total_screen({}), animation(0.3), D2DWindow(std::move(pre_wnd_proc))
D2DOverlayWindow::D2DOverlayWindow() :
total_screen({}), animation(0.3), D2DWindow()
{
tasklist_thread = std::thread([&] {
while (running)
@ -361,7 +360,6 @@ void D2DOverlayWindow::show(HWND active_window, bool snappable)
shown_start_time = std::chrono::steady_clock::now();
lock.unlock();
D2DWindow::show(primary_screen.left(), primary_screen.top(), primary_screen.width(), primary_screen.height());
key_pressed.clear();
// Check if taskbar is auto-hidden. If so, don't display the number arrows
APPBARDATA param = {};
param.cbSize = sizeof(APPBARDATA);
@ -374,115 +372,6 @@ void D2DOverlayWindow::show(HWND active_window, bool snappable)
}
}
void D2DOverlayWindow::animate(int vk_code)
{
animate(vk_code, 0);
}
void D2DOverlayWindow::animate(int vk_code, int offset)
{
if (!initialized || !use_overlay)
{
return;
}
bool done = false;
for (auto& animation : key_animations)
{
if (animation.vk_code == vk_code)
{
animation.animation.reset(0.1, 0, 1);
done = true;
}
}
if (done)
{
return;
}
AnimateKeys animation;
std::wstring id;
animation.vk_code = vk_code;
winrt::com_ptr<ID2D1SvgElement> button_letter, parent;
if (vk_code >= 0x41 && vk_code <= 0x5A)
{
id.push_back('A' + (vk_code - 0x41));
}
else
{
switch (vk_code)
{
case VK_SNAPSHOT:
case VK_PRINT:
id = L"PrnScr";
break;
case VK_CONTROL:
case VK_LCONTROL:
case VK_RCONTROL:
id = L"Ctrl";
break;
case VK_UP:
id = L"KeyUp";
break;
case VK_LEFT:
id = L"KeyLeft";
break;
case VK_DOWN:
id = L"KeyDown";
break;
case VK_RIGHT:
id = L"KeyRight";
break;
case VK_OEM_PLUS:
case VK_ADD:
id = L"KeyPlus";
break;
case VK_OEM_MINUS:
case VK_SUBTRACT:
id = L"KeyMinus";
break;
case VK_TAB:
id = L"Tab";
break;
case VK_RETURN:
id = L"Enter";
break;
default:
return;
}
}
if (offset > 0)
{
id += L"_" + std::to_wstring(offset);
}
button_letter = use_overlay->find_element(id);
if (!button_letter)
{
return;
}
button_letter->GetParent(parent.put());
if (!parent)
{
return;
}
parent->GetPreviousChild(button_letter.get(), animation.button.put());
if (!animation.button || !animation.button->IsAttributeSpecified(L"fill"))
{
animation.button = nullptr;
parent->GetNextChild(button_letter.get(), animation.button.put());
}
if (!animation.button || !animation.button->IsAttributeSpecified(L"fill"))
{
return;
}
winrt::com_ptr<ID2D1SvgPaint> paint;
animation.button->GetAttributeValue(L"fill", paint.put());
paint->GetColor(&animation.original);
animate(vk_code, offset + 1);
std::unique_lock lock(mutex);
animation.animation.reset(0.1, 0, 1);
key_animations.push_back(animation);
key_pressed.push_back(vk_code);
}
void D2DOverlayWindow::on_show()
{
// show override does everything
@ -490,6 +379,7 @@ void D2DOverlayWindow::on_show()
void D2DOverlayWindow::on_hide()
{
Logger::trace("D2DOverlayWindow::on_hide()");
tasklist_cv_mutex.lock();
tasklist_update = false;
tasklist_cv_mutex.unlock();
@ -502,10 +392,11 @@ void D2DOverlayWindow::on_hide()
// Trace the event only if the overlay window was visible.
if (shown_start_time.time_since_epoch().count() > 0)
{
Trace::HideGuide(std::chrono::duration_cast<std::chrono::milliseconds>(shown_end_time - shown_start_time).count(), key_pressed);
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(shown_end_time - shown_start_time).count();
Logger::trace(L"Duration: {}. Close Type: {}", duration, windowCloseType);
Trace::SendGuideSession(duration, windowCloseType.c_str());
shown_start_time = {};
}
key_pressed.clear();
}
D2DOverlayWindow::~D2DOverlayWindow()
@ -758,6 +649,11 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_dc)
// Thumbnail logic:
auto window_state = get_window_state(active_window);
auto thumb_window = get_window_pos(active_window);
if (!thumb_window.has_value())
{
thumb_window = RECT();
}
bool miniature_shown = active_window != nullptr && thumbnail != nullptr && thumb_window && window_state != MINIMIZED;
RECT client_rect;
if (thumb_window && GetClientRect(active_window, &client_rect))

View File

@ -47,18 +47,21 @@ struct AnimateKeys
class D2DOverlayWindow : public D2DWindow
{
public:
D2DOverlayWindow(std::optional<std::function<std::remove_pointer_t<WNDPROC>>> pre_wnd_proc = std::nullopt);
D2DOverlayWindow();
void show(HWND active_window, bool snappable);
void animate(int vk_code);
~D2DOverlayWindow();
void apply_overlay_opacity(float opacity);
void set_theme(const std::wstring& theme);
void quick_hide();
HWND get_window_handle();
void SetWindowCloseType(std::wstring windowCloseType)
{
this->windowCloseType = windowCloseType;
}
private:
void animate(int vk_code, int offset);
std::wstring windowCloseType;
bool show_thumbnail(const RECT& rect, double alpha);
void hide_thumbnail();
virtual void init() override;
@ -70,7 +73,6 @@ private:
bool running = true;
std::vector<AnimateKeys> key_animations;
std::vector<int> key_pressed;
std::vector<MonitorInfo> monitors;
ScreenSize total_screen;
int monitor_dx = 0, monitor_dy = 0;

View File

@ -1 +1 @@
#include "pch.h"
#include "pch.h"

View File

@ -1,29 +1,30 @@
#pragma once
#define NOMINMAX
#include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <Windows.h>
#include <dxgi1_3.h>
#include <d3d11_2.h>
#include <d2d1_3.h>
#include <d2d1_3helper.h>
#include <d2d1helper.h>
#include <dwrite.h>
#include <dcomp.h>
#include <dwmapi.h>
#include <Shobjidl.h>
#include <Shlwapi.h>
#include <string>
#include <algorithm>
#include <chrono>
#include <mutex>
#include <thread>
#include <functional>
#include <condition_variable>
#include <stdexcept>
#include <tuple>
#include <unordered_set>
#include <string>
#include <ProjectTelemetry.h>
#include <filesystem>
#pragma once
#define NOMINMAX
#include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <Windows.h>
#include <dxgi1_3.h>
#include <d3d11_2.h>
#include <d2d1_3.h>
#include <d2d1_3helper.h>
#include <d2d1helper.h>
#include <dwrite.h>
#include <dcomp.h>
#include <dwmapi.h>
#include <Shobjidl.h>
#include <Shlwapi.h>
#include <string>
#include <algorithm>
#include <chrono>
#include <mutex>
#include <thread>
#include <functional>
#include <condition_variable>
#include <stdexcept>
#include <tuple>
#include <unordered_set>
#include <string>
#include <ProjectTelemetry.h>
#include <filesystem>
#include <common/logger/logger.h>

View File

@ -0,0 +1,13 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by ShortcutGuide.rc
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys ShortcutGuide"
#define INTERNAL_NAME "ShortcutGuide"
#define ORIGINAL_FILENAME "PowerToys.ShortcutGuide.exe"
// Non-localizable
//////////////////////////////

View File

@ -0,0 +1,419 @@
#include "pch.h"
#include "shortcut_guide.h"
#include "target_state.h"
#include "trace.h"
#include <common/SettingsAPI/settings_objects.h>
#include <common/debug_control.h>
#include <common/interop/shared_constants.h>
#include <sstream>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/SettingsAPI/settings_objects.h>
#include <common/logger/logger.h>
#include <common/utils/process_path.h>
#include <common/utils/resources.h>
#include <common/utils/string_utils.h>
#include <common/utils/winapi_error.h>
#include <common/utils/window.h>
#include <Psapi.h>
#include <common/hooks/LowlevelKeyboardEvent.h>
// TODO: refactor singleton
OverlayWindow* instance = nullptr;
namespace
{
// Window properties relevant to ShortcutGuide
struct ShortcutGuideWindowInfo
{
HWND hwnd = nullptr; // Handle to the top-level foreground window or nullptr if there is no such window
bool snappable = false; // True, if the window can react to Windows Snap keys
bool disabled = false;
};
ShortcutGuideWindowInfo GetShortcutGuideWindowInfo(HWND active_window)
{
ShortcutGuideWindowInfo result;
active_window = GetAncestor(active_window, GA_ROOT);
if (!IsWindowVisible(active_window))
{
return result;
}
auto style = GetWindowLong(active_window, GWL_STYLE);
auto exStyle = GetWindowLong(active_window, GWL_EXSTYLE);
if ((style & WS_CHILD) == WS_CHILD ||
(style & WS_DISABLED) == WS_DISABLED ||
(exStyle & WS_EX_TOOLWINDOW) == WS_EX_TOOLWINDOW ||
(exStyle & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE)
{
return result;
}
std::array<char, 256> class_name;
GetClassNameA(active_window, class_name.data(), static_cast<int>(class_name.size()));
if (is_system_window(active_window, class_name.data()))
{
return result;
}
static HWND cortana_hwnd = nullptr;
if (cortana_hwnd == nullptr)
{
if (strcmp(class_name.data(), "Windows.UI.Core.CoreWindow") == 0 &&
get_process_path(active_window).ends_with(L"SearchUI.exe"))
{
cortana_hwnd = active_window;
return result;
}
}
else if (cortana_hwnd == active_window)
{
return result;
}
result.hwnd = active_window;
// In reality, Windows Snap works if even one of those styles is set
// for a window, it is just limited. If there is no WS_MAXIMIZEBOX using
// WinKey + Up just won't maximize the window. Similary, without
// WS_MINIMIZEBOX the window will not get minimized. A "Save As..." dialog
// is a example of such window - it can be snapped to both sides and to
// all screen corners, but will not get maximized nor minimized.
// For now, since ShortcutGuide can only disable entire "Windows Controls"
// group, we require that the window supports all the options.
result.snappable = ((style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX) &&
((style & WS_MINIMIZEBOX) == WS_MINIMIZEBOX) &&
((style & WS_THICKFRAME) == WS_THICKFRAME);
return result;
}
const LPARAM eventActivateWindow = 1;
bool wasWinPressed = false;
bool isWinPressed()
{
return (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000);
}
// all modifiers without win key
std::vector<int> modifierKeys = { VK_SHIFT, VK_LSHIFT, VK_RSHIFT, VK_CONTROL, VK_LCONTROL, VK_RCONTROL, VK_MENU, VK_LMENU, VK_RMENU };
// returns false if there are other modifiers pressed or win key isn' pressed
bool onlyWinPressed()
{
if (!isWinPressed())
{
return false;
}
for (auto key : modifierKeys)
{
if (GetAsyncKeyState(key) & 0x8000)
{
return false;
}
}
return true;
}
bool isWin(int key)
{
return key == VK_LWIN || key == VK_RWIN;
}
bool isKeyDown(LowlevelKeyboardEvent event)
{
return event.wParam == WM_KEYDOWN || event.wParam == WM_SYSKEYDOWN;
}
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
LowlevelKeyboardEvent event;
if (nCode == HC_ACTION)
{
event.lParam = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
event.wParam = wParam;
if (event.lParam->vkCode == VK_ESCAPE)
{
Logger::trace(L"ESC key was pressed");
instance->CloseWindow(HideWindowType::ESC_PRESSED);
}
if (wasWinPressed && !isKeyDown(event) && isWin(event.lParam->vkCode))
{
Logger::trace(L"Win key was released");
instance->CloseWindow(HideWindowType::WIN_RELEASED);
}
if (isKeyDown(event) && isWin(event.lParam->vkCode))
{
wasWinPressed = true;
}
if (onlyWinPressed() && isKeyDown(event) && !isWin(event.lParam->vkCode))
{
Logger::trace(L"Shortcut with win key was pressed");
instance->CloseWindow(HideWindowType::WIN_SHORTCUT_PRESSED);
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
std::wstring ToWstring(HideWindowType type)
{
switch (type)
{
case HideWindowType::ESC_PRESSED:
return L"ESC_PRESSED";
case HideWindowType::WIN_RELEASED:
return L"WIN_RELEASED";
case HideWindowType::WIN_SHORTCUT_PRESSED:
return L"WIN_SHORTCUT_PRESSED";
case HideWindowType::THE_SHORTCUT_PRESSED:
return L"THE_SHORTCUT_PRESSED";
}
return L"";
}
}
OverlayWindow::OverlayWindow(HWND activeWindow)
{
instance = this;
this -> activeWindow = activeWindow;
app_name = GET_RESOURCE_STRING(IDS_SHORTCUT_GUIDE);
Logger::info("Overlay Window is creating");
init_settings();
keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), NULL);
if (!keyboardHook)
{
Logger::warn(L"Failed to create low level keyboard hook. {}", get_last_error_or_default(GetLastError()));
}
}
void OverlayWindow::ShowWindow()
{
winkey_popup = std::make_unique<D2DOverlayWindow>();
winkey_popup->apply_overlay_opacity(((float)overlayOpacity.value) / 100.0f);
winkey_popup->set_theme(theme.value);
target_state = std::make_unique<TargetState>();
try
{
winkey_popup->initialize();
}
catch (...)
{
Logger::critical("Winkey popup failed to initialize");
return;
}
target_state->toggle_force_shown();
}
void OverlayWindow::CloseWindow(HideWindowType type, int mainThreadId)
{
if (mainThreadId == 0)
{
mainThreadId = GetCurrentThreadId();
}
if (this->winkey_popup)
{
this->winkey_popup->SetWindowCloseType(ToWstring(type));
Logger::trace(L"Terminating process");
PostThreadMessage(mainThreadId, WM_QUIT, 0, 0);
}
}
bool OverlayWindow::IsDisabled()
{
WCHAR exePath[MAX_PATH] = L"";
instance->get_exe_path(activeWindow, exePath);
if (wcslen(exePath) > 0)
{
return is_disabled_app(exePath);
}
return false;
}
OverlayWindow::~OverlayWindow()
{
if (event_waiter)
{
event_waiter.reset();
}
if (winkey_popup)
{
winkey_popup->hide();
}
if (target_state)
{
target_state->exit();
target_state.reset();
}
if (winkey_popup)
{
winkey_popup.reset();
}
if (keyboardHook)
{
UnhookWindowsHookEx(keyboardHook);
}
}
void OverlayWindow::on_held()
{
auto windowInfo = GetShortcutGuideWindowInfo(activeWindow);
if (windowInfo.disabled)
{
target_state->was_hidden();
return;
}
winkey_popup->show(windowInfo.hwnd, windowInfo.snappable);
}
void OverlayWindow::quick_hide()
{
winkey_popup->quick_hide();
}
void OverlayWindow::was_hidden()
{
target_state->was_hidden();
}
bool OverlayWindow::overlay_visible() const
{
return target_state->active();
}
void OverlayWindow::init_settings()
{
auto settings = GetSettings();
overlayOpacity.value = settings.overlayOpacity;
theme.value = settings.theme;
disabledApps.value = settings.disabledApps;
update_disabled_apps();
}
bool OverlayWindow::is_disabled_app(wchar_t* exePath)
{
if (exePath == nullptr)
{
return false;
}
auto exePathUpper = std::wstring(exePath);
CharUpperBuffW(exePathUpper.data(), (DWORD)exePathUpper.length());
for (const auto& row : disabled_apps_array)
{
const auto pos = exePathUpper.rfind(row);
const auto last_slash = exePathUpper.rfind('\\');
// Check that row occurs in disabled_apps_array, and its last occurrence contains in itself the first character after the last backslash.
if (pos != std::wstring::npos && pos <= last_slash + 1 && pos + row.length() > last_slash)
{
return true;
}
}
return false;
}
void OverlayWindow::update_disabled_apps()
{
disabled_apps_array.clear();
auto disabledUppercase = disabledApps.value;
CharUpperBuffW(disabledUppercase.data(), (DWORD)disabledUppercase.length());
std::wstring_view view(disabledUppercase);
view = trim(view);
while (!view.empty())
{
auto pos = (std::min)(view.find_first_of(L"\r\n"), view.length());
disabled_apps_array.emplace_back(view.substr(0, pos));
view.remove_prefix(pos);
view = trim(view);
}
}
void OverlayWindow::get_exe_path(HWND window, wchar_t* path)
{
if (disabled_apps_array.empty())
{
return;
}
DWORD pid = 0;
GetWindowThreadProcessId(window, &pid);
if (pid != 0)
{
HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (processHandle && GetProcessImageFileName(processHandle, path, MAX_PATH) > 0)
{
CloseHandle(processHandle);
}
}
}
ShortcutGuideSettings OverlayWindow::GetSettings() noexcept
{
ShortcutGuideSettings settings;
json::JsonObject properties;
try
{
PowerToysSettings::PowerToyValues settingsValues =
PowerToysSettings::PowerToyValues::load_from_settings_file(app_key);
auto settingsObject = settingsValues.get_raw_json();
if (!settingsObject.GetView().Size())
{
return settings;
}
properties = settingsObject.GetNamedObject(L"properties");
}
catch (...)
{
Logger::warn("Failed to read settings. Use default settings");
return settings;
}
try
{
settings.hotkey = PowerToysSettings::HotkeyObject::from_json(properties.GetNamedObject(OpenShortcut::name)).to_string();
}
catch (...)
{
}
try
{
settings.overlayOpacity = (int)properties.GetNamedObject(OverlayOpacity::name).GetNamedNumber(L"value");
}
catch (...)
{
}
try
{
settings.theme = (std::wstring)properties.GetNamedObject(Theme::name).GetNamedString(L"value");
}
catch (...)
{
}
try
{
settings.disabledApps = (std::wstring)properties.GetNamedObject(DisabledApps::name).GetNamedString(L"value");
}
catch (...)
{
}
return settings;
}

View File

@ -1,90 +1,85 @@
#pragma once
#include <interface/powertoy_module_interface.h>
#include "overlay_window.h"
#include "native_event_waiter.h"
#include "Generated Files/resource.h"
#include <common/hooks/LowlevelKeyboardEvent.h>
// We support only one instance of the overlay
extern class OverlayWindow* instance;
class TargetState;
class OverlayWindow : public PowertoyModuleIface
{
public:
OverlayWindow();
virtual const wchar_t* get_name() override;
virtual const wchar_t* get_key() override;
virtual bool get_config(wchar_t* buffer, int* buffer_size) override;
virtual void set_config(const wchar_t* config) override;
virtual void enable() override;
virtual void disable() override;
virtual bool is_enabled() override;
void on_held();
void on_held_press(DWORD vkCode);
void quick_hide();
void was_hidden();
intptr_t signal_event(LowlevelKeyboardEvent* event);
virtual void destroy() override;
bool overlay_visible() const;
bool is_disabled_app(wchar_t* exePath);
void get_exe_path(HWND window, wchar_t* exePath);
private:
std::wstring app_name;
//contains the non localized key of the powertoy
std::wstring app_key;
std::unique_ptr<TargetState> target_state;
std::unique_ptr<D2DOverlayWindow> winkey_popup;
bool _enabled = false;
HHOOK hook_handle;
std::unique_ptr<NativeEventWaiter> event_waiter;
std::vector<std::wstring> disabled_apps_array;
void init_settings();
void disable(bool trace_event);
void update_disabled_apps();
struct PressTime
{
PCWSTR name = L"press_time";
int value = 900; // ms
int resourceId = IDS_SETTING_DESCRIPTION_PRESS_TIME;
} pressTime;
struct OverlayOpacity
{
PCWSTR name = L"overlay_opacity";
int value = 90; // percent
int resourceId = IDS_SETTING_DESCRIPTION_OVERLAY_OPACITY;
} overlayOpacity;
struct Theme
{
PCWSTR name = L"theme";
std::wstring value = L"system";
int resourceId = IDS_SETTING_DESCRIPTION_THEME;
std::vector<std::pair<std::wstring, UINT>> keys_and_texts = {
{ L"system", IDS_SETTING_DESCRIPTION_THEME_SYSTEM },
{ L"light", IDS_SETTING_DESCRIPTION_THEME_LIGHT },
{ L"dark", IDS_SETTING_DESCRIPTION_THEME_DARK }
};
} theme;
struct DisabledApps
{
PCWSTR name = L"disabled_apps";
std::wstring value = L"";
} disabledApps;
};
#pragma once
#include "../interface/powertoy_module_interface.h"
//#include <interface/powertoy_module_interface.h>
#include "overlay_window.h"
#include "native_event_waiter.h"
#include "ShortcutGuideSettings.h"
#include "ShortcutGuideConstants.h"
#include "Generated Files/resource.h"
// We support only one instance of the overlay
extern class OverlayWindow* instance;
class TargetState;
enum class HideWindowType
{
ESC_PRESSED,
WIN_RELEASED,
WIN_SHORTCUT_PRESSED,
THE_SHORTCUT_PRESSED
};
class OverlayWindow
{
public:
OverlayWindow(HWND activeWindow);
void ShowWindow();
void CloseWindow(HideWindowType type, int mainThreadId = 0);
bool IsDisabled();
void on_held();
void quick_hide();
void was_hidden();
bool overlay_visible() const;
bool is_disabled_app(wchar_t* exePath);
void get_exe_path(HWND window, wchar_t* exePath);
~OverlayWindow();
static ShortcutGuideSettings GetSettings() noexcept;
private:
std::wstring app_name;
//contains the non localized key of the powertoy
static inline std::wstring app_key = ShortcutGuideConstants::ModuleKey;
std::unique_ptr<TargetState> target_state;
std::unique_ptr<D2DOverlayWindow> winkey_popup;
std::unique_ptr<NativeEventWaiter> event_waiter;
std::vector<std::wstring> disabled_apps_array;
void init_settings();
void update_disabled_apps();
HWND activeWindow;
HHOOK keyboardHook;
struct OverlayOpacity
{
static inline PCWSTR name = L"overlay_opacity";
int value;
int resourceId = IDS_SETTING_DESCRIPTION_OVERLAY_OPACITY;
} overlayOpacity;
struct Theme
{
static inline PCWSTR name = L"theme";
std::wstring value;
int resourceId = IDS_SETTING_DESCRIPTION_THEME;
std::vector<std::pair<std::wstring, UINT>> keys_and_texts = {
{ L"system", IDS_SETTING_DESCRIPTION_THEME_SYSTEM },
{ L"light", IDS_SETTING_DESCRIPTION_THEME_LIGHT },
{ L"dark", IDS_SETTING_DESCRIPTION_THEME_DARK }
};
} theme;
struct DisabledApps
{
static inline PCWSTR name = L"disabled_apps";
std::wstring value;
} disabledApps;
struct OpenShortcut
{
static inline PCWSTR name = L"open_shortcutguide";
} openShortcut;
};

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 362 KiB

After

Width:  |  Height:  |  Size: 362 KiB

View File

Before

Width:  |  Height:  |  Size: 368 KiB

After

Width:  |  Height:  |  Size: 368 KiB

View File

@ -0,0 +1,47 @@
#include "pch.h"
#include "target_state.h"
#include "start_visible.h"
#include <common/interop/shared_constants.h>
#include <common/logger/logger.h>
constexpr unsigned VK_S = 0x53;
void TargetState::was_hidden()
{
std::unique_lock<std::recursive_mutex> lock(mutex);
// Ignore callbacks from the D2DOverlayWindow
if (state == ForceShown)
{
return;
}
state = Hidden;
lock.unlock();
cv.notify_one();
}
void TargetState::exit()
{
std::unique_lock lock(mutex);
state = Exiting;
lock.unlock();
cv.notify_one();
}
void TargetState::toggle_force_shown()
{
std::unique_lock lock(mutex);
if (state != ForceShown)
{
state = ForceShown;
instance->on_held();
}
else
{
state = Hidden;
}
}
bool TargetState::active() const
{
return state == ForceShown || state == Shown;
}

View File

@ -0,0 +1,33 @@
#pragma once
#include <mutex>
#include <condition_variable>
#include "shortcut_guide.h"
struct KeyEvent
{
bool key_down;
unsigned vk_code;
};
class TargetState
{
public:
TargetState() = default;
void was_hidden();
void exit();
void toggle_force_shown();
bool active() const;
private:
std::recursive_mutex mutex;
std::condition_variable_any cv;
enum State
{
Hidden,
Shown,
ForceShown,
Exiting
};
std::atomic<State> state = Hidden;
};

View File

@ -0,0 +1,45 @@
#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);
}
void Trace::SendGuideSession(const __int64 duration_ms, const wchar_t* close_type) noexcept
{
TraceLoggingWrite(
g_hProvider,
"ShortcutGuide_GuideSession",
TraceLoggingInt64(duration_ms, "DurationInMs"),
TraceLoggingWideString(close_type, "CloseType"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::SendSettings(ShortcutGuideSettings settings) noexcept
{
TraceLoggingWrite(
g_hProvider,
"ShortcutGuide_Settings",
TraceLoggingWideString(settings.hotkey.c_str(), "Hotkey"),
TraceLoggingInt32(settings.overlayOpacity, "OverlayOpacity"),
TraceLoggingWideString(settings.theme.c_str(), "Theme"),
TraceLoggingWideString(settings.disabledApps.c_str(), "DisabledApps"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "ShortcutGuideSettings.h"
class Trace
{
public:
static void RegisterProvider() noexcept;
static void UnregisterProvider() noexcept;
static void SendGuideSession(const __int64 duration_ms, const wchar_t* close_type) noexcept;
static void SendSettings(ShortcutGuideSettings settings) noexcept;
};

View File

@ -0,0 +1,14 @@
{
"Projects": [
{
"LanguageSet": "Azure_Languages",
"LocItems": [
{
"SourceFile": "src\\modules\\ShortcutGuide\\ShortcutGuideModuleInterface\\Resources.resx",
"CopyOption": "LangIDOnName",
"OutputPath": "src\\modules\\ShortcutGuide\\ShortcutGuideModuleInterface"
}
]
}
]
}

View File

@ -0,0 +1,123 @@
<?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="Shortcut_Guide" xml:space="preserve">
<value>Shortcut Guide</value>
</data>
</root>

View File

@ -0,0 +1,45 @@
#include <windows.h>
#include "Generated Files/resource.h"
#include "../../../../common/version/version.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#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", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@ -1,133 +1,103 @@
<?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')" />
<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>
<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 shortcut_guide.base.rc shortcut_guide.rc" />
</Target>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>overlaywindow</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
<ProjectName>ShortcutGuide</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</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\$(ProjectName)\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..\..\common\inc;..\..\common\Telemetry;..\..\;..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>Dwmapi.lib;Dcomp.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="animation.h" />
<ClInclude Include="d2d_svg.h" />
<ClInclude Include="d2d_text.h" />
<ClInclude Include="d2d_window.h" />
<ClInclude Include="native_event_waiter.h" />
<ClInclude Include="overlay_window.h" />
<ClInclude Include="keyboard_state.h" />
<ClInclude Include="Generated Files/resource.h" />
<None Include="resource.base.h" />
<ClInclude Include="ShortcutGuideConstants.h" />
<ClInclude Include="shortcut_guide.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="start_visible.h" />
<ClInclude Include="target_state.h" />
<ClInclude Include="trace.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="animation.cpp" />
<ClCompile Include="d2d_svg.cpp" />
<ClCompile Include="d2d_text.cpp" />
<ClCompile Include="d2d_window.cpp" />
<ClCompile Include="native_event_waiter.cpp" />
<ClCompile Include="overlay_window.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="keyboard_state.cpp" />
<ClCompile Include="shortcut_guide.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="start_visible.cpp" />
<ClCompile Include="target_state.cpp" />
<ClCompile Include="tasklist_positions.cpp" />
<ClCompile Include="trace.cpp" />
</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>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files/shortcut_guide.rc" />
<None Include="shortcut_guide.base.rc" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<None Include="Resources.resx" />
</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')" />
<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>
<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 ShortcutGuideModuleInterface.base.rc ShortcutGuideModuleInterface.rc" />
</Target>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{2d604c07-51fc-46bb-9eb7-75aecc7f5e81}</ProjectGuid>
<RootNamespace>ShortcutGuideModuleInterface</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
<ProjectName>ShortcutGuideModuleInterface</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</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\ShortcutGuide\$(ProjectName)\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\ShortcutGuide\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>;..\..\..\common\inc;..\..\..\common\Telemetry;..\..\..\;..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>Dwmapi.lib;Dcomp.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="Generated Files\resource.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.base.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\ShortcutGuideModuleInterface.rc" />
<None Include="ShortcutGuideModuleInterface.base.rc" />
</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>

View File

@ -0,0 +1,55 @@
<?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="Generated Files">
<UniqueIdentifier>{7008d2a1-37df-4ef1-8d2c-e27de2a1a181}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.base.h">
<Filter>Resource Files</Filter>
</ClInclude>
<ClInclude Include="Generated Files\resource.h">
<Filter>Generated Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="ShortcutGuideModuleInterface.base.rc">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
<Filter>Resource Files</Filter>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\ShortcutGuideModuleInterface.rc">
<Filter>Generated Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,308 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <mutex>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/utils/winapi_error.h>
#include <common/utils/logger_helper.h>
#include <common/interop/shared_constants.h>
#include "../interface/powertoy_module_interface.h"
#include "Generated Files/resource.h"
#include <common/SettingsAPI/settings_objects.h>
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
return TRUE;
}
class ShortcutGuideModule : public PowertoyModuleIface
{
public:
ShortcutGuideModule()
{
app_name = GET_RESOURCE_STRING(IDS_SHORTCUT_GUIDE);
app_key = L"Shortcut Guide";
LoggerHelpers::init_logger(app_key, L"ModuleInterface", LogSettings::shortcutGuideLoggerName);
std::filesystem::path oldLogPath(PTSettingsHelper::get_module_save_folder_location(app_key));
oldLogPath.append("ShortcutGuideLogs");
LoggerHelpers::delete_old_log_folder(oldLogPath);
exitEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT);
if (!exitEvent)
{
Logger::warn(L"Failed to create {} event. {}", CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT, get_last_error_or_default(GetLastError()));
}
InitSettings();
}
virtual const wchar_t* get_name() override
{
return app_name.c_str();
}
virtual const wchar_t* get_key() override
{
return app_key.c_str();
}
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
{
HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
PowerToysSettings::Settings settings(hinstance, get_name());
return settings.serialize_to_buffer(buffer, buffer_size);
}
virtual void set_config(const wchar_t* config) override
{
Logger::trace("set_config()");
try
{
// Parse the input JSON string.
PowerToysSettings::PowerToyValues values =
PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
ParseHotkey(values);
}
catch (std::exception ex)
{
Logger::error("Failed to parse settings. {}", ex.what());
}
}
virtual void enable() override
{
Logger::info("Shortcut Guide is enabling");
if (!_enabled)
{
_enabled = true;
}
else
{
Logger::warn("Shortcut guide is already enabled");
}
}
virtual void disable() override
{
Logger::info("ShortcutGuideModule::disable()");
if (_enabled)
{
_enabled = false;
TerminateProcess();
}
else
{
Logger::warn("Shortcut Guide is already disabled");
}
}
virtual bool is_enabled() override
{
return _enabled;
}
virtual void destroy() override
{
this->disable();
if (exitEvent)
{
CloseHandle(exitEvent);
}
delete this;
}
virtual std::optional<HotkeyEx> GetHotkeyEx() override
{
Logger::trace("GetHotkeyEx()");
return m_hotkey;
}
virtual void OnHotkeyEx() override
{
Logger::trace("OnHotkeyEx()");
if (!_enabled)
{
return;
}
if (IsProcessActive())
{
TerminateProcess();
return;
}
if (m_hProcess)
{
CloseHandle(m_hProcess);
m_hProcess = nullptr;
}
StartProcess();
}
virtual void send_settings_telemetry() override
{
Logger::trace("Send settings telemetry");
if (!StartProcess(L"telemetry"))
{
Logger::error("Failed to create a process to send settings telemetry");
}
}
private:
std::wstring app_name;
//contains the non localized key of the powertoy
std::wstring app_key;
bool _enabled = false;
HANDLE m_hProcess = nullptr;
// Hotkey to invoke the module
HotkeyEx m_hotkey;
HANDLE exitEvent;
bool StartProcess(std::wstring args = L"")
{
if (exitEvent)
{
ResetEvent(exitEvent);
}
unsigned long powertoys_pid = GetCurrentProcessId();
std::wstring executable_args = L"";
executable_args.append(std::to_wstring(powertoys_pid));
if (!args.empty())
{
executable_args.append(L" ");
executable_args.append(args);
}
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = L"modules\\ShortcutGuide\\ShortcutGuide\\PowerToys.ShortcutGuide.exe";
sei.nShow = SW_SHOWNORMAL;
sei.lpParameters = executable_args.data();
if (ShellExecuteExW(&sei) == false)
{
Logger::error(L"Failed to start SG process. {}", get_last_error_or_default(GetLastError()));
auto message = get_last_error_message(GetLastError());
if (message.has_value())
{
Logger::error(message.value());
}
return false;
}
Logger::trace(L"Started SG process with pid={}", GetProcessId(sei.hProcess));
m_hProcess = sei.hProcess;
return true;
}
void TerminateProcess()
{
if (m_hProcess)
{
if (WaitForSingleObject(m_hProcess, 0) != WAIT_OBJECT_0)
{
if (exitEvent && SetEvent(exitEvent))
{
Logger::trace(L"Signaled {}", CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT);
}
else
{
Logger::warn(L"Failed to signal {}", CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT);
}
}
else
{
CloseHandle(m_hProcess);
m_hProcess = nullptr;
Logger::trace("SG process was already terminated");
}
}
}
bool IsProcessActive()
{
return m_hProcess && WaitForSingleObject(m_hProcess, 0) != WAIT_OBJECT_0;
}
void InitSettings()
{
try
{
PowerToysSettings::PowerToyValues settings =
PowerToysSettings::PowerToyValues::load_from_settings_file(app_key);
ParseHotkey(settings);
}
catch (std::exception ex)
{
Logger::error("Failed to init settings. {}", ex.what());
}
catch(...)
{
Logger::error("Failed to init settings");
}
}
void ParseHotkey(PowerToysSettings::PowerToyValues& settings)
{
auto settingsObject = settings.get_raw_json();
if (settingsObject.GetView().Size())
{
try
{
auto jsonHotkeyObject = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"open_shortcutguide");
auto hotkey = PowerToysSettings::HotkeyObject::from_json(jsonHotkeyObject);
m_hotkey = HotkeyEx();
if (hotkey.win_pressed())
{
m_hotkey.modifiersMask |= MOD_WIN;
}
if (hotkey.ctrl_pressed())
{
m_hotkey.modifiersMask |= MOD_CONTROL;
}
if (hotkey.shift_pressed())
{
m_hotkey.modifiersMask |= MOD_SHIFT;
}
if (hotkey.alt_pressed())
{
m_hotkey.modifiersMask |= MOD_ALT;
}
m_hotkey.vkCode = hotkey.get_code();
}
catch (...)
{
Logger::warn("Failed to initialize Shortcut Guide start shortcut");
}
}
else
{
Logger::info("Shortcut Guide settings are empty");
}
if (!m_hotkey.modifiersMask)
{
Logger::info("Shortcut Guide is going to use default shortcut");
m_hotkey.modifiersMask = MOD_SHIFT | MOD_WIN;
m_hotkey.vkCode = VK_OEM_2;
}
}
};
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
return new ShortcutGuideModule();
}

View File

@ -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>

View File

@ -0,0 +1 @@
#include "pch.h"

View File

@ -0,0 +1,8 @@
#pragma once
#define NOMINMAX
#include <winrt/base.h>
#include <Windows.h>
#include <ProjectTelemetry.h>
#include <filesystem>
#include <common/logger/logger.h>
#include <common/utils/resources.h>

View File

@ -47,6 +47,12 @@ public:
std::strong_ordering operator<=>(const Hotkey&) const = default;
};
struct HotkeyEx
{
WORD modifiersMask = 0;
WORD vkCode = 0;
};
/* Returns the localized name of the PowerToy*/
virtual const wchar_t* get_name() = 0;
@ -78,6 +84,15 @@ public:
*/
virtual size_t get_hotkeys(Hotkey* buffer, size_t buffer_size) { return 0; }
virtual std::optional<HotkeyEx> GetHotkeyEx()
{
return std::nullopt;
}
virtual void OnHotkeyEx()
{
}
/* Called when one of the registered hotkeys is pressed. Should return true
* if the key press is to be swallowed.
*/

View File

@ -1,36 +0,0 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <mutex>
#include "shortcut_guide.h"
#include "overlay_window.h"
#include "trace.h"
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;
}
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
if (!instance)
{
instance = new OverlayWindow();
return instance;
}
else
{
return nullptr;
}
}

View File

@ -1,94 +0,0 @@
#include "pch.h"
#include "keyboard_state.h"
bool winkey_held()
{
auto left = GetAsyncKeyState(VK_LWIN);
auto right = GetAsyncKeyState(VK_RWIN);
return (left & 0x8000) || (right & 0x8000);
}
// Returns true if the VK code is in the range of valid keys.
// Some VK codes should not be checked because they would return
// false positives when checking if only the "Win" key is pressed.
constexpr bool should_check(int vk)
{
switch (vk)
{
case VK_CANCEL:
case VK_BACK:
case VK_TAB:
case VK_CLEAR:
case VK_ESCAPE:
case VK_APPS:
case VK_SLEEP:
case VK_NUMLOCK:
case VK_SCROLL:
case VK_OEM_102:
return true;
}
if (vk >= VK_SHIFT && vk <= VK_CAPITAL)
{
return true;
}
if (vk >= VK_SPACE && vk <= VK_HELP)
{
return true;
}
// Digits
if (vk >= 0x30 && vk <= 0x39)
{
return true;
}
// Letters
if (vk >= 0x41 && vk <= 0x5A)
{
return true;
}
if (vk >= VK_NUMPAD0 && vk <= VK_F24)
{
return true;
}
if (vk >= VK_LSHIFT && vk <= VK_LAUNCH_APP2)
{
return true;
}
if (vk >= VK_OEM_1 && vk <= VK_OEM_3)
{
return true;
}
if (vk >= VK_OEM_4 && vk <= VK_OEM_8)
{
return true;
}
if (vk >= VK_ATTN && vk <= VK_OEM_CLEAR)
{
return true;
}
return false;
}
bool only_winkey_key_held()
{
for (int vk = VK_CANCEL; vk <= VK_OEM_CLEAR; vk++)
{
if (should_check(vk))
{
if (GetAsyncKeyState(vk) & 0x8000)
{
return false;
}
}
}
return true;
}

View File

@ -1,3 +0,0 @@
#pragma once
bool winkey_held();
bool only_winkey_key_held();

View File

@ -1,41 +0,0 @@
#include <windows.h>
⌀椀渀挀氀甀搀攀 ∀爀攀猀漀甀爀挀攀⸀栀∀ഀ<EFBFBD>
⌀椀渀挀氀甀搀攀 ∀⸀⸀⼀⸀⸀⼀⸀⸀⼀挀漀洀洀漀渀⼀瘀攀爀猀椀漀渀⼀瘀攀爀猀椀漀渀⸀栀∀ഀ<EFBFBD>
<EFBFBD>
⌀搀攀昀椀渀攀 䄀倀匀吀唀䐀䤀伀开刀䔀䄀䐀伀一䰀夀开匀夀䴀䈀伀䰀匀ഀ<EFBFBD>
⌀椀渀挀氀甀搀攀 ∀眀椀渀爀攀猀⸀栀∀ഀ<EFBFBD>
⌀甀渀搀攀昀 䄀倀匀吀唀䐀䤀伀开刀䔀䄀䐀伀一䰀夀开匀夀䴀䈀伀䰀匀ഀ<EFBFBD>
<EFBFBD>
 嘀䔀刀匀䤀伀一䤀一䘀伀ഀ<EFBFBD>
䘀䤀䰀䔀嘀䔀刀匀䤀伀一 䘀䤀䰀䔀开嘀䔀刀匀䤀伀一ഀ<EFBFBD>
倀刀伀䐀唀䌀吀嘀䔀刀匀䤀伀一 倀刀伀䐀唀䌀吀开嘀䔀刀匀䤀伀一ഀ<EFBFBD>
䘀䤀䰀䔀䘀䰀䄀䜀匀䴀䄀匀䬀 嘀匀开䘀䘀䤀开䘀䤀䰀䔀䘀䰀䄀䜀匀䴀䄀匀䬀ഀ<EFBFBD>
⌀椀昀搀攀昀 开䐀䔀䈀唀䜀ഀ<EFBFBD>
䘀䤀䰀䔀䘀䰀䄀䜀匀 嘀匀开䘀䘀开䐀䔀䈀唀䜀ഀ<EFBFBD>
⌀攀氀猀攀ഀ<EFBFBD>
䘀䤀䰀䔀䘀䰀䄀䜀匀   䰀ഀ<EFBFBD>
⌀攀渀搀椀昀ഀ<EFBFBD>
䘀䤀䰀䔀伀匀 嘀伀匀开一吀开圀䤀一䐀伀圀匀㌀㈀ഀ<EFBFBD>
䘀䤀䰀䔀吀夀倀䔀 嘀䘀吀开䐀䰀䰀ഀ<EFBFBD>
䘀䤀䰀䔀匀唀䈀吀夀倀䔀 嘀䘀吀㈀开唀一䬀一伀圀一 <EFBFBD>
䈀䔀䜀䤀一ഀ<EFBFBD>
    䈀䰀伀䌀䬀 ∀匀琀爀椀渀最䘀椀氀攀䤀渀昀漀∀ഀ<EFBFBD>
    䈀䔀䜀䤀一ഀ<EFBFBD>
        䈀䰀伀䌀䬀    㐀戀  ⼀⼀ 唀匀 䔀渀最氀椀猀栀    㤀⤀Ⰰ 唀渀椀挀漀搀攀   㐀䈀  挀栀愀爀猀攀琀ഀ<EFBFBD>
        䈀䔀䜀䤀一ഀ<EFBFBD>
            嘀䄀䰀唀䔀 ∀䌀漀洀瀀愀渀礀一愀洀攀∀Ⰰ 䌀伀䴀倀䄀一夀开一䄀䴀䔀ഀ<EFBFBD>
            嘀䄀䰀唀䔀 ∀䘀椀氀攀䐀攀猀挀爀椀瀀琀椀漀渀∀Ⰰ 䘀䤀䰀䔀开䐀䔀匀䌀刀䤀倀吀䤀伀一ഀ<EFBFBD>
            嘀䄀䰀唀䔀 ∀䘀椀氀攀嘀攀爀猀椀漀渀∀Ⰰ 䘀䤀䰀䔀开嘀䔀刀匀䤀伀一开匀吀刀䤀一䜀ഀ<EFBFBD>
            嘀䄀䰀唀䔀 ∀䤀渀琀攀爀渀愀氀一愀洀攀∀Ⰰ 䤀一吀䔀刀一䄀䰀开一䄀䴀䔀ഀ<EFBFBD>
            嘀䄀䰀唀䔀 ∀䰀攀最愀氀䌀漀瀀礀爀椀最栀琀∀Ⰰ 䌀伀倀夀刀䤀䜀䠀吀开一伀吀䔀ഀ<EFBFBD>
            嘀䄀䰀唀䔀 ∀伀爀椀最椀渀愀氀䘀椀氀攀渀愀洀攀∀Ⰰ 伀刀䤀䜀䤀一䄀䰀开䘀䤀䰀䔀一䄀䴀䔀ഀ<EFBFBD>
            嘀䄀䰀唀䔀 ∀倀爀漀搀甀挀琀一愀洀攀∀Ⰰ 倀刀伀䐀唀䌀吀开一䄀䴀䔀ഀ<EFBFBD>
            嘀䄀䰀唀䔀 ∀倀爀漀搀甀挀琀嘀攀爀猀椀漀渀∀Ⰰ 倀刀伀䐀唀䌀吀开嘀䔀刀匀䤀伀一开匀吀刀䤀一䜀ഀ<EFBFBD>
        䔀一䐀ഀ<EFBFBD>
    䔀一䐀ഀ<EFBFBD>
    䈀䰀伀䌀䬀 ∀嘀愀爀䘀椀氀攀䤀渀昀漀∀ഀ<EFBFBD>
    䈀䔀䜀䤀一ഀ<EFBFBD>
        嘀䄀䰀唀䔀 ∀吀爀愀渀猀氀愀琀椀漀渀∀Ⰰ  砀㐀 㤀Ⰰ ㄀㈀   ⼀⼀ 唀匀 䔀渀最氀椀猀栀    㤀⤀Ⰰ 唀渀椀挀漀搀攀 ㄀㈀   挀栀愀爀猀攀琀ഀ<EFBFBD>
    䔀一䐀ഀ<EFBFBD>
䔀一䐀ഀ<EFBFBD>
<EFBFBD>

View File

@ -1,487 +0,0 @@
#include "pch.h"
#include "shortcut_guide.h"
#include "target_state.h"
#include "trace.h"
#include <common/SettingsAPI/settings_objects.h>
#include <common/debug_control.h>
#include <common/interop/shared_constants.h>
#include <sstream>
#include <modules/shortcut_guide/ShortcutGuideConstants.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/SettingsAPI/settings_objects.h>
#include <common/logger/logger.h>
#include <common/utils/process_path.h>
#include <common/utils/resources.h>
#include <common/utils/string_utils.h>
#include <common/utils/winapi_error.h>
#include <common/utils/window.h>
#include <Psapi.h>
// TODO: refactor singleton
OverlayWindow* instance = nullptr;
namespace
{
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
LowlevelKeyboardEvent event;
if (nCode == HC_ACTION)
{
event.lParam = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
event.wParam = wParam;
if (instance->signal_event(&event) != 0)
{
return 1;
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
// Window properties relevant to ShortcutGuide
struct ShortcutGuideWindowInfo
{
HWND hwnd = nullptr; // Handle to the top-level foreground window or nullptr if there is no such window
bool snappable = false; // True, if the window can react to Windows Snap keys
bool disabled = false;
};
ShortcutGuideWindowInfo GetShortcutGuideWindowInfo()
{
ShortcutGuideWindowInfo result;
auto active_window = GetForegroundWindow();
active_window = GetAncestor(active_window, GA_ROOT);
if (!IsWindowVisible(active_window))
{
return result;
}
WCHAR exePath[MAX_PATH] = L"";
instance->get_exe_path(active_window, exePath);
if (wcslen(exePath) > 0)
{
result.disabled = instance->is_disabled_app(exePath);
if (result.disabled)
{
return result;
}
}
auto style = GetWindowLong(active_window, GWL_STYLE);
auto exStyle = GetWindowLong(active_window, GWL_EXSTYLE);
if ((style & WS_CHILD) == WS_CHILD ||
(style & WS_DISABLED) == WS_DISABLED ||
(exStyle & WS_EX_TOOLWINDOW) == WS_EX_TOOLWINDOW ||
(exStyle & WS_EX_NOACTIVATE) == WS_EX_NOACTIVATE)
{
return result;
}
std::array<char, 256> class_name;
GetClassNameA(active_window, class_name.data(), static_cast<int>(class_name.size()));
if (is_system_window(active_window, class_name.data()))
{
return result;
}
static HWND cortana_hwnd = nullptr;
if (cortana_hwnd == nullptr)
{
if (strcmp(class_name.data(), "Windows.UI.Core.CoreWindow") == 0 &&
get_process_path(active_window).ends_with(L"SearchUI.exe"))
{
cortana_hwnd = active_window;
return result;
}
}
else if (cortana_hwnd == active_window)
{
return result;
}
result.hwnd = active_window;
// In reality, Windows Snap works if even one of those styles is set
// for a window, it is just limited. If there is no WS_MAXIMIZEBOX using
// WinKey + Up just won't maximize the window. Similary, without
// WS_MINIMIZEBOX the window will not get minimized. A "Save As..." dialog
// is a example of such window - it can be snapped to both sides and to
// all screen corners, but will not get maximized nor minimized.
// For now, since ShortcutGuide can only disable entire "Windows Controls"
// group, we require that the window supports all the options.
result.snappable = ((style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX) &&
((style & WS_MINIMIZEBOX) == WS_MINIMIZEBOX) &&
((style & WS_THICKFRAME) == WS_THICKFRAME);
return result;
}
const LPARAM eventActivateWindow = 1;
}
OverlayWindow::OverlayWindow()
{
app_name = GET_RESOURCE_STRING(IDS_SHORTCUT_GUIDE);
app_key = ShortcutGuideConstants::ModuleKey;
std::filesystem::path logFilePath(PTSettingsHelper::get_module_save_folder_location(app_key));
logFilePath.append(LogSettings::shortcutGuideLogPath);
Logger::init(LogSettings::shortcutGuideLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
Logger::info("Overlay Window is creating");
init_settings();
}
// Return the localized display name of the powertoy
const wchar_t* OverlayWindow::get_name()
{
return app_name.c_str();
}
// Return the non localized key of the powertoy, this will be cached by the runner
const wchar_t* OverlayWindow::get_key()
{
return app_key.c_str();
}
bool OverlayWindow::get_config(wchar_t* buffer, int* buffer_size)
{
HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
PowerToysSettings::Settings settings(hinstance, get_name());
settings.set_description(GET_RESOURCE_STRING(IDS_SETTINGS_DESCRIPTION));
settings.set_overview_link(L"https://aka.ms/PowerToysOverview_ShortcutGuide");
settings.set_icon_key(L"pt-shortcut-guide");
settings.add_int_spinner(
pressTime.name,
pressTime.resourceId,
pressTime.value,
100,
10000,
100);
settings.add_int_spinner(
overlayOpacity.name,
overlayOpacity.resourceId,
overlayOpacity.value,
0,
100,
1);
settings.add_choice_group(
theme.name,
theme.resourceId,
theme.value,
theme.keys_and_texts);
return settings.serialize_to_buffer(buffer, buffer_size);
}
void OverlayWindow::set_config(const wchar_t* config)
{
try
{
// save configuration
PowerToysSettings::PowerToyValues _values =
PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
_values.save_to_settings_file();
Trace::SettingsChanged(pressTime.value, overlayOpacity.value, theme.value);
// apply new settings if powertoy is enabled
if (_enabled)
{
if (const auto press_delay_time = _values.get_int_value(pressTime.name))
{
pressTime.value = *press_delay_time;
if (target_state)
{
target_state->set_delay(*press_delay_time);
}
}
if (const auto overlay_opacity = _values.get_int_value(overlayOpacity.name))
{
overlayOpacity.value = *overlay_opacity;
if (winkey_popup)
{
winkey_popup->apply_overlay_opacity(((float)overlayOpacity.value) / 100.0f);
}
}
if (auto val = _values.get_string_value(theme.name))
{
theme.value = std::move(*val);
if (winkey_popup)
{
winkey_popup->set_theme(theme.value);
}
}
if (auto val = _values.get_string_value(disabledApps.name))
{
disabledApps.value = std::move(*val);
update_disabled_apps();
}
}
}
catch (...)
{
// Improper JSON. TODO: handle the error.
}
}
constexpr int alternative_switch_hotkey_id = 0x2;
constexpr UINT alternative_switch_modifier_mask = MOD_WIN | MOD_SHIFT;
constexpr UINT alternative_switch_vk_code = VK_OEM_2;
void OverlayWindow::enable()
{
Logger::info("Shortcut Guide is enabling");
auto switcher = [&](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
if (msg == WM_KEYDOWN && wparam == VK_ESCAPE && instance->target_state->active())
{
instance->target_state->toggle_force_shown();
return 0;
}
if (msg == WM_APP && lparam == eventActivateWindow)
{
instance->target_state->toggle_force_shown();
return 0;
}
if (msg != WM_HOTKEY)
{
return 0;
}
const auto vk_code = HIWORD(lparam);
const auto modifiers_mask = LOWORD(lparam);
if (alternative_switch_vk_code != vk_code || alternative_switch_modifier_mask != modifiers_mask)
{
return 0;
}
instance->target_state->toggle_force_shown();
return 0;
};
if (!_enabled)
{
Trace::EnableShortcutGuide(true);
winkey_popup = std::make_unique<D2DOverlayWindow>(std::move(switcher));
winkey_popup->apply_overlay_opacity(((float)overlayOpacity.value) / 100.0f);
winkey_popup->set_theme(theme.value);
target_state = std::make_unique<TargetState>(pressTime.value);
try
{
winkey_popup->initialize();
}
catch (...)
{
Logger::critical("Winkey popup failed to initialize");
return;
}
#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED)
const bool hook_disabled = IsDebuggerPresent();
#else
const bool hook_disabled = false;
#endif
if (!hook_disabled)
{
hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), NULL);
if (!hook_handle)
{
DWORD errorCode = GetLastError();
show_last_error_message(L"SetWindowsHookEx", errorCode, L"PowerToys - Shortcut Guide");
auto errorMessage = get_last_error_message(errorCode);
Trace::Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"OverlayWindow.enable.SetWindowsHookEx");
}
}
RegisterHotKey(winkey_popup->get_window_handle(), alternative_switch_hotkey_id, alternative_switch_modifier_mask, alternative_switch_vk_code);
auto show_action = [&]() {
PostMessageW(winkey_popup->get_window_handle(), WM_APP, 0, eventActivateWindow);
};
event_waiter = std::make_unique<NativeEventWaiter>(CommonSharedConstants::SHOW_SHORTCUT_GUIDE_SHARED_EVENT, show_action);
}
_enabled = true;
}
void OverlayWindow::disable(bool trace_event)
{
Logger::info("Shortcut Guide is disabling");
if (_enabled)
{
_enabled = false;
if (trace_event)
{
Trace::EnableShortcutGuide(false);
}
UnregisterHotKey(winkey_popup->get_window_handle(), alternative_switch_hotkey_id);
event_waiter.reset();
winkey_popup->hide();
target_state->exit();
target_state.reset();
winkey_popup.reset();
if (hook_handle)
{
bool success = UnhookWindowsHookEx(hook_handle);
if (success)
{
hook_handle = nullptr;
}
}
}
}
void OverlayWindow::disable()
{
this->disable(true);
}
bool OverlayWindow::is_enabled()
{
return _enabled;
}
intptr_t OverlayWindow::signal_event(LowlevelKeyboardEvent* event)
{
if (!_enabled)
{
return 0;
}
if (event->wParam == WM_KEYDOWN ||
event->wParam == WM_SYSKEYDOWN ||
event->wParam == WM_KEYUP ||
event->wParam == WM_SYSKEYUP)
{
bool suppress = target_state->signal_event(event->lParam->vkCode,
event->wParam == WM_KEYDOWN || event->wParam == WM_SYSKEYDOWN);
return suppress ? 1 : 0;
}
else
{
return 0;
}
}
void OverlayWindow::on_held()
{
auto windowInfo = GetShortcutGuideWindowInfo();
if (windowInfo.disabled)
{
target_state->was_hidden();
return;
}
winkey_popup->show(windowInfo.hwnd, windowInfo.snappable);
}
void OverlayWindow::on_held_press(DWORD vkCode)
{
winkey_popup->animate(vkCode);
}
void OverlayWindow::quick_hide()
{
winkey_popup->quick_hide();
}
void OverlayWindow::was_hidden()
{
target_state->was_hidden();
}
void OverlayWindow::destroy()
{
this->disable(false);
delete this;
instance = nullptr;
}
bool OverlayWindow::overlay_visible() const
{
return target_state->active();
}
void OverlayWindow::init_settings()
{
try
{
PowerToysSettings::PowerToyValues settings =
PowerToysSettings::PowerToyValues::load_from_settings_file(OverlayWindow::get_key());
if (const auto val = settings.get_int_value(pressTime.name))
{
pressTime.value = *val;
}
if (const auto val = settings.get_int_value(overlayOpacity.name))
{
overlayOpacity.value = *val;
}
if (auto val = settings.get_string_value(theme.name))
{
theme.value = std::move(*val);
}
if (auto val = settings.get_string_value(disabledApps.name))
{
disabledApps.value = std::move(*val);
update_disabled_apps();
}
}
catch (std::exception&)
{
// Error while loading from the settings file. Just let default values stay as they are.
}
}
bool OverlayWindow::is_disabled_app(wchar_t* exePath)
{
if (exePath == nullptr)
{
return false;
}
auto exePathUpper = std::wstring(exePath);
CharUpperBuffW(exePathUpper.data(), (DWORD)exePathUpper.length());
for (const auto& row : disabled_apps_array)
{
const auto pos = exePathUpper.rfind(row);
const auto last_slash = exePathUpper.rfind('\\');
// Check that row occurs in disabled_apps_array, and its last occurrence contains in itself the first character after the last backslash.
if (pos != std::wstring::npos && pos <= last_slash + 1 && pos + row.length() > last_slash)
{
return true;
}
}
return false;
}
void OverlayWindow::update_disabled_apps()
{
disabled_apps_array.clear();
auto disabledUppercase = disabledApps.value;
CharUpperBuffW(disabledUppercase.data(), (DWORD)disabledUppercase.length());
std::wstring_view view(disabledUppercase);
view = trim(view);
while (!view.empty())
{
auto pos = (std::min)(view.find_first_of(L"\r\n"), view.length());
disabled_apps_array.emplace_back(view.substr(0, pos));
view.remove_prefix(pos);
view = trim(view);
}
}
void OverlayWindow::get_exe_path(HWND window, wchar_t* path)
{
if (disabled_apps_array.empty())
{
return;
}
DWORD pid = 0;
GetWindowThreadProcessId(window, &pid);
if (pid != 0)
{
HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (processHandle && GetProcessImageFileName(processHandle, path, MAX_PATH) > 0)
{
CloseHandle(processHandle);
}
}
}

View File

@ -1,114 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="pch.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="keyboard_state.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="shortcut_guide.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="target_state.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="overlay_window.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="trace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d2d_svg.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d2d_text.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d2d_window.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="animation.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="start_visible.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="tasklist_positions.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="native_event_waiter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="keyboard_state.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="shortcut_guide.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="target_state.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="overlay_window.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="trace.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Generated Files/resource.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="ShortcutGuideConstants.h" />
<ClInclude Include="d2d_svg.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d2d_text.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d2d_window.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="animation.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="start_visible.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="native_event_waiter.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Header Files">
<UniqueIdentifier>{2c7c97f7-0d87-4230-a4b2-baf2cfc35d58}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{aa4b6713-589d-42ef-804d-3a045833f83f}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{d7932c11-20ad-4625-adbc-0780ea5e308d}</UniqueIdentifier>
</Filter>
<Filter Include="Generated Files">
<UniqueIdentifier>{41a2f27e-76b5-4799-94c3-90a33a71786b}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="Resources.resx">
<Filter>Resource Files</Filter>
</None>
<None Include="shortcut_guide.base.rc">
<Filter>Resource Files</Filter>
</None>
<None Include="resource.base.h">
<Filter>Header Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files/shortcut_guide.rc">
<Filter>Generated Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@ -1,250 +0,0 @@
#include "pch.h"
#include "target_state.h"
#include "start_visible.h"
#include "keyboard_state.h"
#include <common/interop/shared_constants.h>
#include <common/logger/logger.h>
TargetState::TargetState(int ms_delay) :
// TODO: All this processing should be done w/o a separate thread etc. in pre_wnd_proc of winkey_popup to avoid
// multithreading. Use SetTimer for delayed events
delay(std::chrono::milliseconds(ms_delay)),
thread(&TargetState::thread_proc, this)
{
}
constexpr unsigned VK_S = 0x53;
bool TargetState::signal_event(unsigned vk_code, bool key_down)
{
std::unique_lock lock(mutex);
// Ignore repeated key presses
if (!events.empty() && events.back().key_down == key_down && events.back().vk_code == vk_code)
{
return false;
}
// Hide the overlay when WinKey + Shift + S is pressed
if (key_down && state == Shown && vk_code == VK_S && (GetKeyState(VK_LSHIFT) || GetKeyState(VK_RSHIFT)))
{
// We cannot use normal hide() here, there is stuff that needs deinitialization.
// It can be safely done when the user releases the WinKey.
instance->quick_hide();
}
const bool win_key_released = !key_down && (vk_code == VK_LWIN || vk_code == VK_RWIN);
constexpr auto overlay_fade_in_animation_time = std::chrono::milliseconds(300);
const auto overlay_active = state == Shown && (std::chrono::system_clock::now() - signal_timestamp > overlay_fade_in_animation_time);
const bool suppress_win_release = win_key_released && (state == ForceShown || overlay_active) && !nonwin_key_was_pressed_during_shown;
events.push_back({ key_down, vk_code });
lock.unlock();
cv.notify_one();
if (suppress_win_release)
{
// Send a 0xFF VK code, which is outside of the VK code range, to prevent
// the start menu from appearing.
INPUT input[3] = { {}, {}, {} };
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0xFF;
input[0].ki.dwExtraInfo = CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0xFF;
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
input[1].ki.dwExtraInfo = CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG;
input[2].type = INPUT_KEYBOARD;
input[2].ki.wVk = vk_code;
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
input[2].ki.dwExtraInfo = CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG;
SendInput(3, input, sizeof(INPUT));
}
return suppress_win_release;
}
void TargetState::was_hidden()
{
std::unique_lock<std::recursive_mutex> lock(mutex);
// Ignore callbacks from the D2DOverlayWindow
if (state == ForceShown)
{
return;
}
state = Hidden;
events.clear();
lock.unlock();
cv.notify_one();
}
void TargetState::exit()
{
std::unique_lock lock(mutex);
events.clear();
state = Exiting;
lock.unlock();
cv.notify_one();
thread.join();
}
KeyEvent TargetState::next()
{
auto e = events.front();
events.pop_front();
return e;
}
void TargetState::handle_hidden()
{
std::unique_lock lock(mutex);
if (events.empty())
cv.wait(lock);
if (events.empty() || state == Exiting)
return;
auto event = next();
if (event.key_down && (event.vk_code == VK_LWIN || event.vk_code == VK_RWIN))
{
state = Timeout;
winkey_timestamp = std::chrono::system_clock::now();
}
}
void TargetState::handle_shown(const bool forced)
{
std::unique_lock lock(mutex);
if (events.empty())
{
cv.wait(lock);
}
if (events.empty() || state == Exiting)
{
return;
}
auto event = next();
if (event.vk_code == VK_LWIN || event.vk_code == VK_RWIN)
{
if (!forced && (!event.key_down || !winkey_held()))
{
state = Hidden;
}
return;
}
if (event.key_down)
{
nonwin_key_was_pressed_during_shown = true;
lock.unlock();
instance->on_held_press(event.vk_code);
}
}
void TargetState::thread_proc()
{
while (true)
{
switch (state)
{
case Hidden:
handle_hidden();
break;
case Timeout:
try
{
handle_timeout();
}
catch (...)
{
Logger::critical("Timeout, handle_timeout failed.");
}
break;
case Shown:
try
{
handle_shown(false);
}
catch (...)
{
Logger::critical("Shown, handle_shown failed.");
}
break;
case ForceShown:
try
{
handle_shown(true);
}
catch (...)
{
Logger::critical("ForceShown, handle_shown failed.");
}
break;
case Exiting:
default:
return;
}
}
}
void TargetState::handle_timeout()
{
std::unique_lock lock(mutex);
auto wait_time = delay - (std::chrono::system_clock::now() - winkey_timestamp);
if (events.empty())
{
cv.wait_for(lock, wait_time);
}
if (state == Exiting)
{
return;
}
// Skip all VK_*WIN-down events
while (!events.empty())
{
auto event = events.front();
if (event.key_down && (event.vk_code == VK_LWIN || event.vk_code == VK_RWIN))
events.pop_front();
else
break;
}
// If we've detected that a user is holding anything other than VK_*WIN or start menu is visible, we should hide
if (!events.empty() || !only_winkey_key_held() || is_start_visible())
{
state = Hidden;
return;
}
if (std::chrono::system_clock::now() - winkey_timestamp < delay)
{
return;
}
signal_timestamp = std::chrono::system_clock::now();
nonwin_key_was_pressed_during_shown = false;
state = Shown;
lock.unlock();
instance->on_held();
}
void TargetState::set_delay(int ms_delay)
{
std::unique_lock lock(mutex);
delay = std::chrono::milliseconds(ms_delay);
}
void TargetState::toggle_force_shown()
{
std::unique_lock lock(mutex);
events.clear();
if (state != ForceShown)
{
state = ForceShown;
instance->on_held();
}
else
{
state = Hidden;
}
}
bool TargetState::active() const
{
return state == ForceShown || state == Shown;
}

View File

@ -1,50 +0,0 @@
#pragma once
#include <deque>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include "shortcut_guide.h"
struct KeyEvent
{
bool key_down;
unsigned vk_code;
};
class TargetState
{
public:
TargetState(int ms_delay);
bool signal_event(unsigned vk_code, bool key_down);
void was_hidden();
void exit();
void set_delay(int ms_delay);
void toggle_force_shown();
bool active() const;
private:
KeyEvent next();
void handle_hidden();
void handle_timeout();
void handle_shown(const bool forced);
void thread_proc();
std::recursive_mutex mutex;
std::condition_variable_any cv;
std::chrono::system_clock::time_point winkey_timestamp, signal_timestamp;
std::chrono::milliseconds delay;
std::deque<KeyEvent> events;
enum State
{
Hidden,
Timeout,
Shown,
ForceShown,
Exiting
};
std::atomic<State> state = Hidden;
bool nonwin_key_was_pressed_during_shown = false;
std::thread thread;
};

View File

@ -1,80 +0,0 @@
#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);
}
void Trace::HideGuide(const __int64 duration_ms, std::vector<int>& key_pressed) noexcept
{
std::string vk_codes;
std::vector<int>::iterator it;
for (it = key_pressed.begin(); it != key_pressed.end();)
{
vk_codes += std::to_string(*it);
if (++it != key_pressed.end())
{
vk_codes += " ";
}
}
TraceLoggingWrite(
g_hProvider,
"ShortcutGuide_HideGuide",
TraceLoggingInt64(duration_ms, "DurationInMs"),
TraceLoggingInt64(key_pressed.size(), "NumberOfKeysPressed"),
TraceLoggingString(vk_codes.c_str(), "ListOfKeysPressed"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::EnableShortcutGuide(const bool enabled) noexcept
{
TraceLoggingWrite(
g_hProvider,
"ShortcutGuide_EnableGuide",
TraceLoggingBoolean(enabled, "Enabled"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::SettingsChanged(const int press_delay_time, const int overlay_opacity, const std::wstring& theme) noexcept
{
TraceLoggingWrite(
g_hProvider,
"ShortcutGuide_SettingsChanged",
TraceLoggingInt32(press_delay_time, "PressDelayTime"),
TraceLoggingInt32(overlay_opacity, "OverlayOpacity"),
TraceLoggingWideString(theme.c_str(), "Theme"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
// Log if an error occurs in Shortcut Guide
void Trace::Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept
{
TraceLoggingWrite(
g_hProvider,
"ShortcutGuide_Error",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
TraceLoggingValue(methodName.c_str(), "MethodName"),
TraceLoggingValue(errorCode, "ErrorCode"),
TraceLoggingValue(errorMessage.c_str(), "ErrorMessage"));
}

View File

@ -1,12 +0,0 @@
#pragma once
class Trace
{
public:
static void RegisterProvider() noexcept;
static void UnregisterProvider() noexcept;
static void HideGuide(const __int64 duration_ms, std::vector<int>& key_pressed) noexcept;
static void EnableShortcutGuide(const bool enabled) noexcept;
static void SettingsChanged(const int press_delay_time, const int overlay_opacity, const std::wstring& theme) noexcept;
static void Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept;
};

View File

@ -0,0 +1,121 @@
#include "pch.h"
#include "CentralizedHotkeys.h"
#include <map>
#include <common/logger/logger.h>
#include <common/utils/winapi_error.h>
#include <common/SettingsAPI/settings_objects.h>
namespace CentralizedHotkeys
{
std::map<Shortcut, std::vector<Action>> actions;
std::map<Shortcut, int> ids;
HWND runnerWindow;
std::wstring ToWstring(const Shortcut& shortcut)
{
std::wstring res = L"";
if (shortcut.modifiersMask & MOD_SHIFT)
{
res += L"shift+";
}
if (shortcut.modifiersMask & MOD_CONTROL)
{
res += L"ctrl+";
}
if (shortcut.modifiersMask & MOD_WIN)
{
res += L"win+";
}
if (shortcut.modifiersMask & MOD_ALT)
{
res += L"alt+";
}
res += PowerToysSettings::HotkeyObject::key_from_code(shortcut.vkCode);
return res;
}
bool AddHotkeyAction(Shortcut shortcut, Action action)
{
if (!actions[shortcut].empty())
{
// It will only work if previous one is rewritten
Logger::warn(L"{} shortcut is already registered", ToWstring(shortcut));
}
actions[shortcut].push_back(action);
// Register hotkey if it is the first shortcut
if (actions[shortcut].size() == 1)
{
if (ids.find(shortcut) == ids.end())
{
static int nextId = 0;
ids[shortcut] = nextId++;
}
if (!RegisterHotKey(runnerWindow, ids[shortcut], shortcut.modifiersMask, shortcut.vkCode))
{
Logger::warn(L"Failed to add {} shortcut. {}", ToWstring(shortcut), get_last_error_or_default(GetLastError()));
return false;
}
Logger::trace(L"{} shortcut registered", ToWstring(shortcut));
return true;
}
return true;
}
void UnregisterHotkeysForModule(std::wstring moduleName)
{
for (auto it = actions.begin(); it != actions.end(); it++)
{
auto val = std::find_if(it->second.begin(), it->second.end(), [moduleName](Action a) { return a.moduleName == moduleName; });
if (val != it->second.end())
{
it->second.erase(val);
if (it->second.empty())
{
if (!UnregisterHotKey(runnerWindow, ids[it->first]))
{
Logger::warn(L"Failed to unregister {} shortcut. {}", ToWstring(it->first), get_last_error_or_default(GetLastError()));
}
else
{
Logger::trace(L"{} shortcut unregistered", ToWstring(it->first));
}
}
}
}
}
void PopulateHotkey(Shortcut shortcut)
{
if (!actions.empty())
{
try
{
actions[shortcut].begin()->action(shortcut.modifiersMask, shortcut.vkCode);
}
catch(std::exception ex)
{
Logger::error("Failed to execute hotkey's action. {}", ex.what());
}
catch(...)
{
Logger::error(L"Failed to execute hotkey's action");
}
}
}
void RegisterWindow(HWND hwnd)
{
runnerWindow = hwnd;
}
}

View File

@ -0,0 +1,45 @@
#pragma once
#include <Windows.h>
#include <functional>
namespace CentralizedHotkeys
{
struct Action
{
std::wstring moduleName;
std::function<void(WORD, WORD)> action;
Action(std::wstring moduleName = L"", std::function<void(WORD, WORD)> action = ([](WORD modifiersMask, WORD vkCode) {}))
{
this->moduleName = moduleName;
this->action = action;
}
};
struct Shortcut
{
WORD modifiersMask;
WORD vkCode;
Shortcut(WORD modifiersMask = 0, WORD vkCode = 0)
{
this->modifiersMask = modifiersMask;
this->vkCode = vkCode;
}
bool operator<(const Shortcut& key) const
{
return std::pair<WORD, WORD>{ this->modifiersMask, this->vkCode } < std::pair<WORD, WORD>{ key.modifiersMask, key.vkCode };
}
};
std::wstring ToWstring(const Shortcut& shortcut);
bool AddHotkeyAction(Shortcut shortcut, Action action);
void UnregisterHotkeysForModule(std::wstring moduleName);
void PopulateHotkey(Shortcut shortcut);
void RegisterWindow(HWND hwnd);
}

View File

@ -32,6 +32,7 @@
#include <Psapi.h>
#include <RestartManager.h>
#include "centralized_kb_hook.h"
#include "CentralizedHotkeys.h"
#if _DEBUG && _WIN64
#include "unhandled_exception_handler.h"
@ -155,7 +156,7 @@ int runner(bool isProcessElevated, bool openSettings, bool openOobe)
L"modules/KeyboardManager/KeyboardManager.dll",
L"modules/Launcher/Microsoft.Launcher.dll",
L"modules/PowerRename/PowerRenameExt.dll",
L"modules/ShortcutGuide/ShortcutGuide.dll",
L"modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.dll",
L"modules/ColorPicker/ColorPicker.dll",
};

View File

@ -1,7 +1,9 @@
#include "pch.h"
#include "powertoy_module.h"
#include "centralized_kb_hook.h"
#include "CentralizedHotkeys.h"
#include <common/logger/logger.h>
#include <common/utils/winapi_error.h>
std::map<std::wstring, PowertoyModule>& modules()
{
@ -46,6 +48,7 @@ PowertoyModule::PowertoyModule(PowertoyModuleIface* pt_module, HMODULE handle) :
}
update_hotkeys();
UpdateHotkeyEx();
}
void PowertoyModule::update_hotkeys()
@ -66,3 +69,19 @@ void PowertoyModule::update_hotkeys()
});
}
}
void PowertoyModule::UpdateHotkeyEx()
{
CentralizedHotkeys::UnregisterHotkeysForModule(pt_module->get_key());
auto container = pt_module->GetHotkeyEx();
if (container.has_value())
{
auto hotkey = container.value();
auto modulePtr = pt_module.get();
auto action = [modulePtr](WORD modifiersMask, WORD vkCode) {
modulePtr->OnHotkeyEx();
};
CentralizedHotkeys::AddHotkeyAction({ hotkey.modifiersMask, hotkey.vkCode }, { pt_module->get_key(), action });
}
}

Some files were not shown because too many files have changed in this diff Show More