[Shortcut Guide] Move into separate process (#11359)
1
.github/actions/spell-check/expect.txt
vendored
@ -112,6 +112,7 @@ atlstr
|
||||
attr
|
||||
Attribs
|
||||
aumid
|
||||
Aut
|
||||
AUTHN
|
||||
AUTOAPPEND
|
||||
autocomplete
|
||||
|
@ -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'
|
||||
|
@ -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}
|
||||
|
@ -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" />
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
16
src/modules/ShortcutGuide/ShortcutGuide/PropertySheet.props
Normal 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>
|
@ -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>
|
BIN
src/modules/ShortcutGuide/ShortcutGuide/Shortcut-Guide.ico
Normal file
After Width: | Height: | Size: 17 KiB |
@ -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
|
@ -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>
|
189
src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj
Normal 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>
|
@ -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>
|
@ -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"";
|
||||
};
|
@ -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());
|
@ -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:
|
@ -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;
|
||||
};
|
129
src/modules/ShortcutGuide/ShortcutGuide/main.cpp
Normal 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;
|
||||
}
|
@ -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))
|
@ -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;
|
@ -1 +1 @@
|
||||
#include "pch.h"
|
||||
#include "pch.h"
|
@ -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>
|
13
src/modules/ShortcutGuide/ShortcutGuide/resource.base.h
Normal 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
|
||||
//////////////////////////////
|
419
src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp
Normal 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;
|
||||
}
|
@ -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;
|
||||
};
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 362 KiB After Width: | Height: | Size: 362 KiB |
Before Width: | Height: | Size: 368 KiB After Width: | Height: | Size: 368 KiB |
47
src/modules/ShortcutGuide/ShortcutGuide/target_state.cpp
Normal 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;
|
||||
}
|
33
src/modules/ShortcutGuide/ShortcutGuide/target_state.h
Normal 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;
|
||||
};
|
45
src/modules/ShortcutGuide/ShortcutGuide/trace.cpp
Normal 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));
|
||||
}
|
11
src/modules/ShortcutGuide/ShortcutGuide/trace.h
Normal 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;
|
||||
};
|
@ -0,0 +1,14 @@
|
||||
{
|
||||
"Projects": [
|
||||
{
|
||||
"LanguageSet": "Azure_Languages",
|
||||
"LocItems": [
|
||||
{
|
||||
"SourceFile": "src\\modules\\ShortcutGuide\\ShortcutGuideModuleInterface\\Resources.resx",
|
||||
"CopyOption": "LangIDOnName",
|
||||
"OutputPath": "src\\modules\\ShortcutGuide\\ShortcutGuideModuleInterface"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -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>
|
@ -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
|
@ -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>
|
@ -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>
|
@ -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();
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
||||
</packages>
|
@ -0,0 +1 @@
|
||||
#include "pch.h"
|
@ -0,0 +1,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>
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
bool winkey_held();
|
||||
bool only_winkey_key_held();
|
@ -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>
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
@ -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;
|
||||
}
|
@ -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;
|
||||
};
|
@ -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"));
|
||||
}
|
@ -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;
|
||||
};
|
121
src/runner/CentralizedHotkeys.cpp
Normal 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;
|
||||
}
|
||||
}
|
45
src/runner/CentralizedHotkeys.h
Normal 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);
|
||||
}
|
@ -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",
|
||||
};
|
||||
|
||||
|
@ -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 });
|
||||
}
|
||||
}
|
||||
|