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

This commit is contained in:
Mykhailo Pylyp
2021-05-20 15:07:34 +03:00
committed by GitHub
parent c948c1fca8
commit 601da71f15
114 changed files with 2346 additions and 1771 deletions

View File

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

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<!--
To customize common C++/WinRT project properties:
* right-click the project node
* expand the Common Properties item
* select the C++/WinRT property page
For more advanced scenarios, and complete documentation, please see:
https://github.com/Microsoft/cppwinrt/tree/master/nuget
-->
<PropertyGroup />
<ItemDefinitionGroup />
</Project>

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,54 @@
#include <windows.h>
#include "Generated Files/resource.h"
#include "../../../../common/version/version.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON1 ICON "Shortcut-Guide.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
</assembly>

View File

@@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h ShortcutGuide.base.rc ShortcutGuide.rc" />
</Target>
<PropertyGroup Label="Globals">
<CppWinRTOptimized>true</CppWinRTOptimized>
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
<CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata>
<MinimalCoreWin>true</MinimalCoreWin>
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{2edb3eb4-fa92-4bff-b2d8-566584837231}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>ShortcutGuide</RootNamespace>
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0'">v141</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0'">v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="PropertySheet.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<TargetName>PowerToys.$(MSBuildProjectName)</TargetName>
</PropertyGroup>
<PropertyGroup>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\ShortcutGuide\$(ProjectName)\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\ShortcutGuide\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>;..\..\..\common\inc;..\..\..\common\Telemetry;..\..\..\;..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>ole32.lib;Shell32.lib;OleAut32.lib;Dbghelp.lib;Dwmapi.lib;Dcomp.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="animation.h" />
<ClInclude Include="d2d_svg.h" />
<ClInclude Include="d2d_text.h" />
<ClInclude Include="d2d_window.h" />
<ClInclude Include="Generated Files\resource.h" />
<ClInclude Include="native_event_waiter.h" />
<ClInclude Include="overlay_window.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.base.h" />
<ClInclude Include="ShortcutGuideSettings.h" />
<ClInclude Include="ShortcutGuideConstants.h" />
<ClInclude Include="shortcut_guide.h" />
<ClInclude Include="start_visible.h" />
<ClInclude Include="target_state.h" />
<ClInclude Include="tasklist_positions.h" />
<ClInclude Include="trace.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="animation.cpp" />
<ClCompile Include="d2d_svg.cpp" />
<ClCompile Include="d2d_text.cpp" />
<ClCompile Include="d2d_window.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="native_event_waiter.cpp" />
<ClCompile Include="overlay_window.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="shortcut_guide.cpp" />
<ClCompile Include="start_visible.cpp" />
<ClCompile Include="target_state.cpp" />
<ClCompile Include="tasklist_positions.cpp" />
<ClCompile Include="trace.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="PropertySheet.props" />
<CopyFileToFolders Include="svgs\0.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\1.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\2.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\3.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\4.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\5.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\6.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\7.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\8.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\9.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\no_active_window.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\overlay.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\overlay_portrait.svg">
<FileType>Document</FileType>
<DestinationFolders>$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\Display\Display.vcxproj">
<Project>{caba8dfb-823b-4bf2-93ac-3f31984150d9}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\ShortcutGuide.rc" />
<None Include="ShortcutGuide.base.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="Shortcut-Guide.ico" />
</ItemGroup>
<ItemGroup>
<Manifest Include="ShortcutGuide.exe.manifest" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View File

@@ -0,0 +1,180 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="svgs">
<UniqueIdentifier>{800507f8-718d-48d1-aa56-96c040795b8a}</UniqueIdentifier>
</Filter>
<Filter Include="Generated Files">
<UniqueIdentifier>{cb917ac7-30da-494b-81f1-cbe4415e91f4}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="animation.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d2d_svg.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d2d_text.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d2d_window.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="native_event_waiter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="overlay_window.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="shortcut_guide.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ShortcutGuideConstants.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="start_visible.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="target_state.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="tasklist_positions.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="trace.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.base.h">
<Filter>Resource Files</Filter>
</ClInclude>
<ClInclude Include="Generated Files\resource.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="ShortcutGuideSettings.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="animation.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d2d_svg.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d2d_text.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d2d_window.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="native_event_waiter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="overlay_window.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="shortcut_guide.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="start_visible.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="target_state.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="tasklist_positions.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="trace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
<None Include="packages.config" />
<None Include="ShortcutGuide.base.rc">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<CopyFileToFolders Include="svgs\0.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\1.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\2.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\3.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\4.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\5.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\6.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\7.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\8.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\9.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\no_active_window.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\overlay.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\overlay_portrait.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
<Filter>Resource Files</Filter>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\ShortcutGuide.rc">
<Filter>Generated Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Image Include="Shortcut-Guide.ico">
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>
<ItemGroup>
<Manifest Include="ShortcutGuide.exe.manifest" />
</ItemGroup>
</Project>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>0</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-7.000000, -159.000000)">
<g id="0" transform="translate(7.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M26.5,31.1376953 C24.166655,31.1376953 23,29.4993653 23,26.2226562 C23,24.522778 23.3155893,23.2273808 23.9467773,22.3364258 C24.5779654,21.4454708 25.4928325,21 26.6914062,21 C28.9700635,21 30.109375,22.6656734 30.109375,25.9970703 C30.109375,27.6559328 29.798343,28.9274044 29.1762695,29.8115234 C28.5541961,30.6956424 27.6621152,31.1376953 26.5,31.1376953 Z M26.5957031,22.6474609 C25.6614537,22.6474609 25.1943359,23.8209518 25.1943359,26.1679688 C25.1943359,28.3782663 25.6523392,29.4833984 26.5683594,29.4833984 C27.461593,29.4833984 27.9082031,28.3440869 27.9082031,26.0654297 C27.9082031,23.7867725 27.4707075,22.6474609 26.5957031,22.6474609 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>1</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-67.000000, -159.000000)">
<g id="1" transform="translate(67.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<polygon fill="#FFFFFF" fill-rule="nonzero" points="30.2548828 30.9667969 24.0546875 30.9667969 24.0546875 29.2578125 26.0849609 29.2578125 26.0849609 23.1533203 24 23.6044922 24 21.8544922 28.2382812 21 28.2382812 29.2578125 30.2548828 29.2578125"></polygon>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>2</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-127.000000, -159.000000)">
<g id="2" transform="translate(127.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M26.4746094,29.1347656 L26.4746094,29.1757812 L30.453125,29.1757812 L30.453125,30.9667969 L24,30.9667969 L24,29.2851562 L26.8095703,26.5986328 C27.3746773,26.0563124 27.772297,25.6017271 28.0024414,25.2348633 C28.2325858,24.8679995 28.3476562,24.4817729 28.3476562,24.0761719 C28.3476562,23.1738236 27.8623095,22.7226562 26.8916016,22.7226562 C26.0484984,22.7226562 25.2418658,23.0576138 24.4716797,23.7275391 L24.4716797,21.8271484 C25.3238975,21.2757134 26.2854764,21 27.3564453,21 C28.3590545,21 29.1417615,21.2517878 29.7045898,21.7553711 C30.2674182,22.2589543 30.5488281,22.9391233 30.5488281,23.7958984 C30.5488281,24.9397844 29.8629626,26.1223897 28.4912109,27.34375 L26.4746094,29.1347656 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>3</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-187.000000, -159.000000)">
<g id="3" transform="translate(187.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M24,30.6455078 L24,28.765625 C24.6562533,29.244143 25.4218706,29.4833984 26.296875,29.4833984 C26.84831,29.4833984 27.2778305,29.36491 27.5854492,29.1279297 C27.8930679,28.8909493 28.046875,28.560549 28.046875,28.1367188 C28.046875,27.6992166 27.85661,27.3619804 27.4760742,27.125 C27.0955385,26.8880196 26.5725945,26.7695312 25.9072266,26.7695312 L25.0253906,26.7695312 L25.0253906,25.1152344 L25.8388672,25.1152344 C27.1149152,25.1152344 27.7529297,24.6914105 27.7529297,23.84375 C27.7529297,23.04622 27.2630257,22.6474609 26.2832031,22.6474609 C25.6269498,22.6474609 24.9889354,22.8593729 24.3691406,23.2832031 L24.3691406,21.5195312 C25.0572951,21.1731754 25.8593704,21 26.7753906,21 C27.7779998,21 28.5584282,21.2255837 29.1166992,21.6767578 C29.6749702,22.1279319 29.9541016,22.7135381 29.9541016,23.4335938 C29.9541016,24.7141991 29.304694,25.5162744 28.0058594,25.8398438 L28.0058594,25.8740234 C28.6985712,25.9606124 29.2454407,26.2124003 29.6464844,26.6293945 C30.047528,27.0463888 30.2480469,27.5579397 30.2480469,28.1640625 C30.2480469,29.0800827 29.9130893,29.8046848 29.2431641,30.3378906 C28.5732388,30.8710964 27.6481179,31.1376953 26.4677734,31.1376953 C25.4560496,31.1376953 24.6334667,30.9736345 24,30.6455078 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>4</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-247.000000, -159.000000)">
<g id="4" transform="translate(247.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M30.4169922,28.7177734 L29.2412109,28.7177734 L29.2412109,30.8027344 L27.2724609,30.8027344 L27.2724609,28.7177734 L23,28.7177734 L23,27.34375 L27.1152344,21 L29.2412109,21 L29.2412109,27.1796875 L30.4169922,27.1796875 L30.4169922,28.7177734 Z M27.2998047,23.1601562 L27.2587891,23.1601562 C27.1949867,23.3242196 27.071941,23.5794254 26.8896484,23.9257812 L24.7910156,27.1796875 L27.2724609,27.1796875 L27.2724609,24.0966797 C27.2724609,23.8232408 27.2815754,23.5110695 27.2998047,23.1601562 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>5</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-307.000000, -159.000000)">
<g id="5" transform="translate(307.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M24,30.5498047 L24,28.7041016 C24.6699252,29.1142599 25.3831342,29.3193359 26.1396484,29.3193359 C26.7093127,29.3193359 27.1547835,29.1837579 27.4760742,28.9125977 C27.7973649,28.6414374 27.9580078,28.2734398 27.9580078,27.8085938 C27.9580078,26.8378858 27.2721423,26.3525391 25.9003906,26.3525391 C25.4537738,26.3525391 24.9137401,26.3935543 24.2802734,26.4755859 L24.2802734,21 L29.7080078,21 L29.7080078,22.7636719 L26.1259766,22.7636719 L26.1259766,24.7255859 C26.4130874,24.6982421 26.6842435,24.6845703 26.9394531,24.6845703 C27.9466196,24.6845703 28.7350232,24.9488906 29.3046875,25.4775391 C29.8743518,26.0061875 30.1591797,26.7171179 30.1591797,27.6103516 C30.1591797,28.5992888 29.8196649,29.4070607 29.140625,30.0336914 C28.4615851,30.6603221 27.5410214,30.9736328 26.3789062,30.9736328 C25.4355422,30.9736328 24.6425813,30.8323582 24,30.5498047 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>6</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-367.000000, -159.000000)">
<g id="6" transform="translate(367.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M30.2070312,21.2666016 L30.2070312,23.0712891 C29.710284,22.7887356 29.1679717,22.6474609 28.5800781,22.6474609 C27.8554651,22.6474609 27.2709983,22.9243136 26.8266602,23.4780273 C26.382322,24.031741 26.1510418,24.7734329 26.1328125,25.703125 L26.1738281,25.703125 C26.6432315,25.0104132 27.3290971,24.6640625 28.2314453,24.6640625 C29.0517619,24.6640625 29.7023088,24.9420545 30.1831055,25.4980469 C30.6639021,26.0540392 30.9042969,26.7900345 30.9042969,27.7060547 C30.9042969,28.6904346 30.5852896,29.5084603 29.9472656,30.1601562 C29.3092416,30.8118522 28.4980518,31.1376953 27.5136719,31.1376953 C26.4108018,31.1376953 25.5494823,30.7389363 24.9296875,29.9414062 C24.3098927,29.1438762 24,28.0250723 24,26.5849609 C24,24.88964 24.398759,23.5349986 25.1962891,22.5209961 C25.9938191,21.5069936 27.076165,21 28.4433594,21 C29.1998736,21 29.7877583,21.0888663 30.2070312,21.2666016 Z M27.5,26.2636719 C27.1035136,26.2636719 26.7879244,26.4026679 26.5532227,26.6806641 C26.318521,26.9586602 26.2011719,27.3141254 26.2011719,27.7470703 C26.2011719,28.2255883 26.3207996,28.6346012 26.5600586,28.9741211 C26.7993176,29.313641 27.1171855,29.4833984 27.5136719,29.4833984 C27.9010436,29.4833984 28.2109363,29.33187 28.4433594,29.0288086 C28.6757824,28.7257472 28.7919922,28.3304061 28.7919922,27.8427734 C28.7919922,26.7900338 28.3613324,26.2636719 27.5,26.2636719 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>7</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-427.000000, -159.000000)">
<g id="7" transform="translate(427.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<polygon fill="#FFFFFF" fill-rule="nonzero" points="30.8222656 21.9980469 27.3632812 30.8027344 25.0800781 30.8027344 28.5664062 22.7636719 24 22.7636719 24 21 30.8222656 21"></polygon>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>8</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-487.000000, -159.000000)">
<g id="8" transform="translate(487.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M25.9755859,25.8740234 L25.9755859,25.8398438 C24.8544866,25.379555 24.2939453,24.6344453 24.2939453,23.6044922 C24.2939453,22.847978 24.5947236,22.2247746 25.1962891,21.7348633 C25.7978546,21.244952 26.574865,21 27.5273438,21 C28.4980517,21 29.2727836,21.2290016 29.8515625,21.6870117 C30.4303414,22.1450218 30.7197266,22.7363245 30.7197266,23.4609375 C30.7197266,24.5319064 30.0999411,25.32031 28.8603516,25.8261719 L28.8603516,25.8535156 C29.5257195,26.0494801 30.0418276,26.3719053 30.4086914,26.8208008 C30.7755552,27.2696963 30.9589844,27.7744113 30.9589844,28.3349609 C30.9589844,29.2008507 30.6388378,29.8844376 29.9985352,30.3857422 C29.3582325,30.8870468 28.4638729,31.1376953 27.3154297,31.1376953 C26.3219351,31.1376953 25.5209991,30.8927433 24.9125977,30.402832 C24.3041962,29.9129207 24,29.2646525 24,28.4580078 C24,27.286778 24.6585221,26.4254585 25.9755859,25.8740234 Z M28.6894531,23.6455078 C28.6894531,23.3128239 28.5834972,23.0473643 28.371582,22.8491211 C28.1596669,22.6508779 27.878257,22.5517578 27.5273438,22.5517578 C27.1855452,22.5517578 26.9018566,22.6554352 26.6762695,22.862793 C26.4506825,23.0701508 26.3378906,23.3356104 26.3378906,23.6591797 C26.3378906,24.2698598 26.7298138,24.7324203 27.5136719,25.046875 C28.29753,24.7233057 28.6894531,24.2561879 28.6894531,23.6455078 Z M27.4179688,26.6943359 C26.5019485,27.0543638 26.0439453,27.6057906 26.0439453,28.3486328 C26.0439453,28.7041033 26.1840806,28.9980457 26.4643555,29.2304688 C26.7446303,29.4628918 27.0852845,29.5791016 27.4863281,29.5791016 C27.919273,29.5791016 28.2679023,29.4651704 28.5322266,29.2373047 C28.7965508,29.009439 28.9287109,28.7086607 28.9287109,28.3349609 C28.9287109,27.5875614 28.4251352,27.0406918 27.4179688,26.6943359 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>9</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-547.000000, -159.000000)">
<g id="9" transform="translate(547.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M24.5605469,30.7548828 L24.5605469,28.9775391 C25.0755234,29.3147803 25.6953089,29.4833984 26.4199219,29.4833984 C27.1764361,29.4833984 27.7609029,29.2270533 28.1733398,28.7143555 C28.5857768,28.2016576 28.7942708,27.4736375 28.7988281,26.5302734 L28.7578125,26.5166016 C28.3248676,27.1591829 27.6663456,27.4804688 26.7822266,27.4804688 C25.9801392,27.4804688 25.3159206,27.2001981 24.7895508,26.6396484 C24.263181,26.0790988 24,25.352218 24,24.4589844 C24,23.4153594 24.3212858,22.5779654 24.9638672,21.9467773 C25.6064485,21.3155893 26.4381459,21 27.4589844,21 C28.5071667,21 29.342282,21.3964804 29.9643555,22.1894531 C30.5864289,22.9824258 30.8974609,24.1171801 30.8974609,25.59375 C30.8974609,27.3437587 30.5226274,28.705236 29.7729492,29.6782227 C29.023271,30.6512093 27.9762437,31.1376953 26.6318359,31.1376953 C25.8343059,31.1376953 25.1438831,31.0100924 24.5605469,30.7548828 Z M27.3974609,22.6474609 C27.0237612,22.6474609 26.716147,22.7989894 26.4746094,23.1020508 C26.2330717,23.4051122 26.1123047,23.795896 26.1123047,24.2744141 C26.1123047,24.7711613 26.2319324,25.1619452 26.4711914,25.4467773 C26.7104504,25.7316095 27.0351542,25.8740234 27.4453125,25.8740234 C27.8190123,25.8740234 28.1243478,25.7384454 28.3613281,25.4672852 C28.5983085,25.1961249 28.7167969,24.8554708 28.7167969,24.4453125 C28.7167969,23.930336 28.5926119,23.5019548 28.3442383,23.1601562 C28.0958646,22.8183577 27.7802754,22.6474609 27.3974609,22.6474609 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 362 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 368 KiB

View File

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

View File

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

View File

@@ -0,0 +1,45 @@
#include "pch.h"
#include "trace.h"
TRACELOGGING_DEFINE_PROVIDER(
g_hProvider,
"Microsoft.PowerToys",
// {38e8889b-9731-53f5-e901-e8a7c1753074}
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
TraceLoggingOptionProjectTelemetry());
void Trace::RegisterProvider() noexcept
{
TraceLoggingRegister(g_hProvider);
}
void Trace::UnregisterProvider() noexcept
{
TraceLoggingUnregister(g_hProvider);
}
void Trace::SendGuideSession(const __int64 duration_ms, const wchar_t* close_type) noexcept
{
TraceLoggingWrite(
g_hProvider,
"ShortcutGuide_GuideSession",
TraceLoggingInt64(duration_ms, "DurationInMs"),
TraceLoggingWideString(close_type, "CloseType"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::SendSettings(ShortcutGuideSettings settings) noexcept
{
TraceLoggingWrite(
g_hProvider,
"ShortcutGuide_Settings",
TraceLoggingWideString(settings.hotkey.c_str(), "Hotkey"),
TraceLoggingInt32(settings.overlayOpacity, "OverlayOpacity"),
TraceLoggingWideString(settings.theme.c_str(), "Theme"),
TraceLoggingWideString(settings.disabledApps.c_str(), "DisabledApps"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

View File

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

View File

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

View File

@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Shortcut_Guide" xml:space="preserve">
<value>Shortcut Guide</value>
</data>
</root>

View File

@@ -0,0 +1,45 @@
#include <windows.h>
#include "Generated Files/resource.h"
#include "../../../../common/version/version.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@@ -1,133 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h shortcut_guide.base.rc shortcut_guide.rc" />
</Target>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>overlaywindow</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
<ProjectName>ShortcutGuide</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\$(ProjectName)\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..\..\common\inc;..\..\common\Telemetry;..\..\;..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>Dwmapi.lib;Dcomp.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="animation.h" />
<ClInclude Include="d2d_svg.h" />
<ClInclude Include="d2d_text.h" />
<ClInclude Include="d2d_window.h" />
<ClInclude Include="native_event_waiter.h" />
<ClInclude Include="overlay_window.h" />
<ClInclude Include="keyboard_state.h" />
<ClInclude Include="Generated Files/resource.h" />
<None Include="resource.base.h" />
<ClInclude Include="ShortcutGuideConstants.h" />
<ClInclude Include="shortcut_guide.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="start_visible.h" />
<ClInclude Include="target_state.h" />
<ClInclude Include="trace.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="animation.cpp" />
<ClCompile Include="d2d_svg.cpp" />
<ClCompile Include="d2d_text.cpp" />
<ClCompile Include="d2d_window.cpp" />
<ClCompile Include="native_event_waiter.cpp" />
<ClCompile Include="overlay_window.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="keyboard_state.cpp" />
<ClCompile Include="shortcut_guide.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="start_visible.cpp" />
<ClCompile Include="target_state.cpp" />
<ClCompile Include="tasklist_positions.cpp" />
<ClCompile Include="trace.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\common\Display\Display.vcxproj">
<Project>{caba8dfb-823b-4bf2-93ac-3f31984150d9}</Project>
</ProjectReference>
<ProjectReference Include="..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\common\Themes\Themes.vcxproj">
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files/shortcut_guide.rc" />
<None Include="shortcut_guide.base.rc" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<None Include="Resources.resx" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h ShortcutGuideModuleInterface.base.rc ShortcutGuideModuleInterface.rc" />
</Target>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{2d604c07-51fc-46bb-9eb7-75aecc7f5e81}</ProjectGuid>
<RootNamespace>ShortcutGuideModuleInterface</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
<ProjectName>ShortcutGuideModuleInterface</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\ShortcutGuide\$(ProjectName)\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\ShortcutGuide\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>;..\..\..\common\inc;..\..\..\common\Telemetry;..\..\..\;..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>Dwmapi.lib;Dcomp.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="Generated Files\resource.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.base.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\ShortcutGuideModuleInterface.rc" />
<None Include="ShortcutGuideModuleInterface.base.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Generated Files">
<UniqueIdentifier>{7008d2a1-37df-4ef1-8d2c-e27de2a1a181}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.base.h">
<Filter>Resource Files</Filter>
</ClInclude>
<ClInclude Include="Generated Files\resource.h">
<Filter>Generated Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="ShortcutGuideModuleInterface.base.rc">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
<Filter>Resource Files</Filter>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\ShortcutGuideModuleInterface.rc">
<Filter>Generated Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

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

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
</packages>

View File

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

View File

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

View File

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

View File

@@ -1,36 +0,0 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <mutex>
#include "shortcut_guide.h"
#include "overlay_window.h"
#include "trace.h"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Trace::RegisterProvider();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
Trace::UnregisterProvider();
break;
}
return TRUE;
}
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
if (!instance)
{
instance = new OverlayWindow();
return instance;
}
else
{
return nullptr;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,80 +0,0 @@
#include "pch.h"
#include "trace.h"
TRACELOGGING_DEFINE_PROVIDER(
g_hProvider,
"Microsoft.PowerToys",
// {38e8889b-9731-53f5-e901-e8a7c1753074}
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
TraceLoggingOptionProjectTelemetry());
void Trace::RegisterProvider() noexcept
{
TraceLoggingRegister(g_hProvider);
}
void Trace::UnregisterProvider() noexcept
{
TraceLoggingUnregister(g_hProvider);
}
void Trace::HideGuide(const __int64 duration_ms, std::vector<int>& key_pressed) noexcept
{
std::string vk_codes;
std::vector<int>::iterator it;
for (it = key_pressed.begin(); it != key_pressed.end();)
{
vk_codes += std::to_string(*it);
if (++it != key_pressed.end())
{
vk_codes += " ";
}
}
TraceLoggingWrite(
g_hProvider,
"ShortcutGuide_HideGuide",
TraceLoggingInt64(duration_ms, "DurationInMs"),
TraceLoggingInt64(key_pressed.size(), "NumberOfKeysPressed"),
TraceLoggingString(vk_codes.c_str(), "ListOfKeysPressed"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::EnableShortcutGuide(const bool enabled) noexcept
{
TraceLoggingWrite(
g_hProvider,
"ShortcutGuide_EnableGuide",
TraceLoggingBoolean(enabled, "Enabled"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::SettingsChanged(const int press_delay_time, const int overlay_opacity, const std::wstring& theme) noexcept
{
TraceLoggingWrite(
g_hProvider,
"ShortcutGuide_SettingsChanged",
TraceLoggingInt32(press_delay_time, "PressDelayTime"),
TraceLoggingInt32(overlay_opacity, "OverlayOpacity"),
TraceLoggingWideString(theme.c_str(), "Theme"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
// Log if an error occurs in Shortcut Guide
void Trace::Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept
{
TraceLoggingWrite(
g_hProvider,
"ShortcutGuide_Error",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
TraceLoggingValue(methodName.c_str(), "MethodName"),
TraceLoggingValue(errorCode, "ErrorCode"),
TraceLoggingValue(errorMessage.c_str(), "ErrorMessage"));
}

View File

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