mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 02:36:19 +02:00
FancyZones and Shortcut Guide initial commit
Co-authored-by: Alexis Campailla <alexis@janeasystems.com> Co-authored-by: Bret Anderson <bretan@microsoft.com> Co-authored-by: Enrico Giordani <enrico.giordani@gmail.com> Co-authored-by: Jaime Bernardo <jaime@janeasystems.com> Co-authored-by: Jeff Bogdan <jeffbog@microsoft.com> Co-authored-by: March Rogers <marchr@microsoft.com> Co-authored-by: Mike Harsh <mharsh@microsoft.com> Co-authored-by: Nachum Bundak <Nachum.Bundak@microsoft.com> Co-authored-by: Oliver Jones <ojones@microsoft.com> Co-authored-by: Patrick Little <plittle@microsoft.com>
This commit is contained in:
committed by
Bartosz Sosnowski
parent
10c5396099
commit
8431b80e48
48
src/common/README.md
Normal file
48
src/common/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Introduction
|
||||
The common lib, as the name suggests, contains code shared by multiple PowerToys components and modules.
|
||||
|
||||
# Classes and structures
|
||||
|
||||
#### class Animation: [header](./animation.h) [source](./animation.cpp)
|
||||
Animation helper class with two easing-in animations: linear and exponential.
|
||||
|
||||
#### class AsyncMessageQueue: [header](./async_message_queue.h)
|
||||
Header-only asynchronous message queue. Used by `TwoWayPipeMessageIPC`.
|
||||
|
||||
#### class TwoWayPipeMessageIPC: [header](./two_way_pipe_message_ipc.h)
|
||||
Header-only asynchronous IPC messaging class. Used by the runner to communicate with the settings window.
|
||||
|
||||
#### class D2DSVG: [header](./d2d_svg.h) [source](./d2d_svg.cpp)
|
||||
Class for loading, rendering and for some basic modifications of SVG graphics.
|
||||
|
||||
#### class D2DText: [header](./d2d_text.h) [source](./d2d_text.cpp)
|
||||
Class for rendering text using DirectX.
|
||||
|
||||
#### class D2DWindow: [header](./d2d_window.h) [source](./d2d_window.cpp)
|
||||
Base class for creating borderless windows, with DirectX enabled rendering pipeline.
|
||||
|
||||
#### class DPIAware: [header](./dpi_aware.h) [source](./dpi_aware.cpp)
|
||||
Helper class for creating DPI-aware applications.
|
||||
|
||||
#### struct MonitorInfo: [header](./monitors.h) [source](./monitors.cpp)
|
||||
Class for obtaining information about physical displays connected to the machine.
|
||||
|
||||
#### class Settings, class PowerToyValues, class CustomActionObject: [header](./settings_objects.h) [source](./settings_objects.cpp)
|
||||
Classes used to define settings screens for the PowerToys modules.
|
||||
|
||||
#### class Tasklist: [header](./tasklist_positions.h) [source](./tasklist_positions.cpp)
|
||||
Class that can detect the position of the windows buttons on the taskbar. It also detects which window will react to pressing `WinKey + number`.
|
||||
|
||||
#### struct WindowsColors: [header](./windows_colors.h) [source](./windows_colors.cpp)
|
||||
Class for detecting the current Windows color scheme.
|
||||
|
||||
# Helpers
|
||||
|
||||
#### Common helpers: [header](./common.h) [source](./common.cpp)
|
||||
Various helper functions.
|
||||
|
||||
#### Settings helpers: [header](./settings_helpers.h)
|
||||
Helper methods for the settings.
|
||||
|
||||
#### Start visible helper: [header](./start_visible.h) [source](./start_visible.cpp)
|
||||
Contains function to test if the Start menu is visible.
|
||||
22
src/common/Telemetry/PowerToys.wprp
Normal file
22
src/common/Telemetry/PowerToys.wprp
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<WindowsPerformanceRecorder Version="1.0" Author="Microsoft Corporation" Copyright="Microsoft Corporation" Company="Microsoft Corporation">
|
||||
<Profiles>
|
||||
<EventCollector Id="EventCollector_Microsoft.PowerToys" Name="Microsoft.PowerToys">
|
||||
<BufferSize Value="64" />
|
||||
<Buffers Value="4" />
|
||||
</EventCollector>
|
||||
<EventProvider Id="EventProvider_Microsoft.PowerToys" Name="38e8889b-9731-53f5-e901-e8a7c1753074" />
|
||||
<Profile Id="Microsoft.PowerToys.Verbose.File" Name="Microsoft.PowerToys" Description="Microsoft.PowerToys" LoggingMode="File" DetailLevel="Verbose">
|
||||
<Collectors>
|
||||
<EventCollectorId Value="EventCollector_Microsoft.PowerToys">
|
||||
<EventProviders>
|
||||
<EventProviderId Value="EventProvider_Microsoft.PowerToys" />
|
||||
</EventProviders>
|
||||
</EventCollectorId>
|
||||
</Collectors>
|
||||
</Profile>
|
||||
<Profile Id="Microsoft.PowerToys.Light.File" Name="Microsoft.PowerToys" Description="Microsoft.PowerToys" Base="Microsoft.PowerToys.Verbose.File" LoggingMode="File" DetailLevel="Light" />
|
||||
<Profile Id="Microsoft.PowerToys.Verbose.Memory" Name="Microsoft.PowerToys" Description="Microsoft.PowerToys" Base="Microsoft.PowerToys.Verbose.File" LoggingMode="Memory" DetailLevel="Verbose" />
|
||||
<Profile Id="Microsoft.PowerToys.Light.Memory" Name="Microsoft.PowerToys" Description="Microsoft.PowerToys" Base="Microsoft.PowerToys.Verbose.File" LoggingMode="Memory" DetailLevel="Light" />
|
||||
</Profiles>
|
||||
</WindowsPerformanceRecorder>
|
||||
6
src/common/Telemetry/ProjectTelemetry.h
Normal file
6
src/common/Telemetry/ProjectTelemetry.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include <TraceLoggingDefines.h>
|
||||
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_hProvider);
|
||||
6
src/common/Telemetry/TraceLoggingDefines.h
Normal file
6
src/common/Telemetry/TraceLoggingDefines.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#define TraceLoggingOptionProjectTelemetry() TraceLoggingOptionGroup(0x42749043, 0x438c, 0x46a2, 0x82, 0xbe, 0xc6, 0xcb, 0xeb, 0x19, 0x2f, 0xf2)
|
||||
#define ProjectTelemetryPrivacyDataTag(tag) TraceLoggingUInt64((tag), "Ignore")
|
||||
#define ProjectTelemetryTag_ProductAndServicePerformance 0x0u
|
||||
#define PROJECT_KEYWORD_MEASURE 0x0
|
||||
25
src/common/Telemetry/readme.md
Normal file
25
src/common/Telemetry/readme.md
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
# Overview
|
||||
|
||||
Telemetry from the PowerToys provider can be captured using the PowerToys.wprp file and WPR.
|
||||
|
||||
## Starting trace capture
|
||||
|
||||
To capture a trace for the PowerToys provider, run the following:
|
||||
|
||||
`wpr.exe -start "PowerToys.wprp"`
|
||||
|
||||
## Stopping trace capture
|
||||
|
||||
To capture a trace for the PowerToys provider, run the following:
|
||||
|
||||
`wpr.exe -Stop "Trace.etl"`
|
||||
|
||||
## Viewing Events
|
||||
|
||||
Open the trace.etl file in WPA.
|
||||
|
||||
## Additional Resources
|
||||
[Tracelogging on MSDN](https://docs.microsoft.com/en-us/windows/win32/tracelogging/trace-logging-portal)
|
||||
|
||||
[Recording and Viewing Events](https://docs.microsoft.com/en-us/windows/win32/tracelogging/tracelogging-record-and-display-tracelogging-events)
|
||||
62
src/common/UnitTests-CommonLib/Settings.Tests.cpp
Normal file
62
src/common/UnitTests-CommonLib/Settings.Tests.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "pch.h"
|
||||
#include <settings_objects.h>
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
using namespace PowerToysSettings;
|
||||
|
||||
namespace UnitTestsCommonLib
|
||||
{
|
||||
TEST_CLASS(SettingsUnitTests)
|
||||
{
|
||||
private:
|
||||
const std::wstring m_json = L"{\"name\":\"Module Name\",\"properties\" : {\"bool_toggle_true\":{\"value\":true},\"bool_toggle_false\":{\"value\":false},\"color_picker\" : {\"value\":\"#ff8d12\"},\"int_spinner\" : {\"value\":10},\"string_text\" : {\"value\":\"a quick fox\"}},\"version\" : \"1.0\" }";
|
||||
|
||||
public:
|
||||
TEST_METHOD(LoadFromJsonBoolTrue)
|
||||
{
|
||||
PowerToyValues values = PowerToyValues::from_json_string(m_json);
|
||||
Assert::IsTrue(values.is_bool_value(L"bool_toggle_true"));
|
||||
|
||||
bool value = values.get_bool_value(L"bool_toggle_true");
|
||||
Assert::AreEqual(true, value);
|
||||
}
|
||||
|
||||
TEST_METHOD(LoadFromJsonBoolFalse)
|
||||
{
|
||||
PowerToyValues values = PowerToyValues::from_json_string(m_json);
|
||||
Assert::IsTrue(values.is_bool_value(L"bool_toggle_false"));
|
||||
|
||||
bool value = values.get_bool_value(L"bool_toggle_false");
|
||||
Assert::AreEqual(false, value);
|
||||
}
|
||||
|
||||
TEST_METHOD(LoadFromJsonInt)
|
||||
{
|
||||
PowerToyValues values = PowerToyValues::from_json_string(m_json);
|
||||
Assert::IsTrue(values.is_int_value(L"int_spinner"));
|
||||
|
||||
int value = values.get_int_value(L"int_spinner");
|
||||
Assert::AreEqual(10, value);
|
||||
}
|
||||
|
||||
TEST_METHOD(LoadFromJsonString)
|
||||
{
|
||||
PowerToyValues values = PowerToyValues::from_json_string(m_json);
|
||||
Assert::IsTrue(values.is_string_value(L"string_text"));
|
||||
|
||||
std::wstring value = values.get_string_value(L"string_text");
|
||||
std::wstring expected = L"a quick fox";
|
||||
Assert::AreEqual(expected, value);
|
||||
}
|
||||
|
||||
TEST_METHOD(LoadFromJsonColorPicker)
|
||||
{
|
||||
PowerToyValues values = PowerToyValues::from_json_string(m_json);
|
||||
Assert::IsTrue(values.is_string_value(L"color_picker"));
|
||||
|
||||
std::wstring value = values.get_string_value(L"color_picker");
|
||||
std::wstring expected = L"#ff8d12";
|
||||
Assert::AreEqual(expected, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
115
src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj
Normal file
115
src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj
Normal file
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<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>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{1A066C63-64B3-45F8-92FE-664E1CCE8077}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>UnitTestsCommonLib</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectSubType>NativeUnitTestProject</ProjectSubType>
|
||||
</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>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
</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 Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\;..\Telemetry;..\..\..\deps\cpprestsdk\include;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<UseFullPaths>true</UseFullPaths>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>RuntimeObject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(VCInstallDir)UnitTest\include;..\;..\..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<UseFullPaths>true</UseFullPaths>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>RuntimeObject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Settings.Tests.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\common.vcxproj">
|
||||
<Project>{74485049-c722-400f-abe5-86ac52d929b3}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?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;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Settings.Tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
5
src/common/UnitTests-CommonLib/pch.cpp
Normal file
5
src/common/UnitTests-CommonLib/pch.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
// pch.cpp: source file corresponding to the pre-compiled header
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
|
||||
16
src/common/UnitTests-CommonLib/pch.h
Normal file
16
src/common/UnitTests-CommonLib/pch.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// pch.h: This is a precompiled header file.
|
||||
// Files listed below are compiled only once, improving build performance for future builds.
|
||||
// This also affects IntelliSense performance, including code completion and many code browsing features.
|
||||
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
|
||||
// Do not add files here that you will be updating frequently as this negates the performance advantage.
|
||||
|
||||
#ifndef PCH_H
|
||||
#define PCH_H
|
||||
|
||||
// add headers that you want to pre-compile here
|
||||
#include <Windows.h>
|
||||
#include <winrt\base.h>
|
||||
|
||||
#include "CppUnitTest.h"
|
||||
|
||||
#endif //PCH_H
|
||||
43
src/common/animation.cpp
Normal file
43
src/common/animation.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "pch.h"
|
||||
#include "animation.h"
|
||||
|
||||
Animation::Animation(double duration, double start, double stop) :
|
||||
start_value(start), end_value(stop), duration(duration), start(std::chrono::high_resolution_clock::now()) { }
|
||||
|
||||
void Animation::reset() {
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
void Animation::reset(double duration) {
|
||||
this->duration = duration;
|
||||
reset();
|
||||
}
|
||||
void Animation::reset(double duration, double start, double stop) {
|
||||
start_value = start;
|
||||
end_value = stop;
|
||||
reset(duration);
|
||||
}
|
||||
|
||||
static double ease_out_expo(double t) {
|
||||
return 1 - pow(2, -8 * t);
|
||||
}
|
||||
|
||||
double Animation::apply_animation_function(double t, AnimFunctions apply_function) const {
|
||||
switch (apply_function) {
|
||||
case EASE_OUT_EXPO:
|
||||
return ease_out_expo(t);
|
||||
case LINEAR:
|
||||
default:
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
double Animation::value(AnimFunctions apply_function) const {
|
||||
auto anim_duration = std::chrono::high_resolution_clock::now() - start;
|
||||
double t = std::chrono::duration<double>(anim_duration).count() / duration;
|
||||
if (t >= 1)
|
||||
return end_value;
|
||||
return start_value + (end_value - start_value) * apply_animation_function(t, apply_function);
|
||||
}
|
||||
bool Animation::done() const {
|
||||
return std::chrono::high_resolution_clock::now() - start >= std::chrono::duration<double>(duration);
|
||||
}
|
||||
31
src/common/animation.h
Normal file
31
src/common/animation.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
|
||||
/*
|
||||
Usage:
|
||||
When creating animation contstructor takes one parameter - how long
|
||||
should the animation take in seconds.
|
||||
|
||||
Call reset() when starting animation.
|
||||
|
||||
When redering, call value() to get value from 0 to 1 - depending on animation
|
||||
progress.
|
||||
*/
|
||||
class Animation {
|
||||
public:
|
||||
enum AnimFunctions {
|
||||
LINEAR = 0,
|
||||
EASE_OUT_EXPO
|
||||
};
|
||||
|
||||
Animation(double duration = 1, double start = 0, double stop = 1);
|
||||
void reset();
|
||||
void reset(double duration);
|
||||
void reset(double duration, double start, double stop);
|
||||
double value(AnimFunctions apply_function) const;
|
||||
bool done() const;
|
||||
private:
|
||||
double apply_animation_function(double t, AnimFunctions apply_function) const;
|
||||
std::chrono::high_resolution_clock::time_point start;
|
||||
double start_value, end_value, duration;
|
||||
};
|
||||
47
src/common/async_message_queue.h
Normal file
47
src/common/async_message_queue.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <string>
|
||||
|
||||
class AsyncMessageQueue {
|
||||
private:
|
||||
std::mutex queue_mutex;
|
||||
std::queue<std::wstring> message_queue;
|
||||
std::condition_variable message_ready;
|
||||
bool interrupted = false;
|
||||
|
||||
//Disable copy
|
||||
AsyncMessageQueue(const AsyncMessageQueue&);
|
||||
AsyncMessageQueue& operator=(const AsyncMessageQueue&);
|
||||
|
||||
public:
|
||||
AsyncMessageQueue() {
|
||||
}
|
||||
void queue_message(std::wstring message) {
|
||||
this->queue_mutex.lock();
|
||||
this->message_queue.push(message);
|
||||
this->queue_mutex.unlock();
|
||||
this->message_ready.notify_one();
|
||||
}
|
||||
std::wstring pop_message() {
|
||||
std::unique_lock<std::mutex> lock(this->queue_mutex);
|
||||
while (message_queue.empty() && !this->interrupted) {
|
||||
this->message_ready.wait(lock);
|
||||
}
|
||||
if (this->interrupted) {
|
||||
//Just returns a empty string if the queue was interrupted.
|
||||
return std::wstring(L"");
|
||||
}
|
||||
std::wstring message = this->message_queue.front();
|
||||
this->message_queue.pop();
|
||||
return message;
|
||||
}
|
||||
void interrupt() {
|
||||
this->queue_mutex.lock();
|
||||
this->interrupted = true;
|
||||
this->queue_mutex.unlock();
|
||||
this->message_ready.notify_all();
|
||||
}
|
||||
};
|
||||
152
src/common/common.cpp
Normal file
152
src/common/common.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include "pch.h"
|
||||
#include "common.h"
|
||||
#include <dwmapi.h>
|
||||
#pragma comment(lib, "dwmapi.lib")
|
||||
#include <strsafe.h>
|
||||
|
||||
|
||||
std::optional<RECT> get_button_pos(HWND hwnd) {
|
||||
RECT button;
|
||||
if (DwmGetWindowAttribute(hwnd, DWMWA_CAPTION_BUTTON_BOUNDS, &button, sizeof(RECT)) == S_OK) {
|
||||
return button;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<RECT> get_window_pos(HWND hwnd) {
|
||||
RECT window;
|
||||
if (DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &window, sizeof(window)) == S_OK) {
|
||||
return window;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<POINT> get_mouse_pos() {
|
||||
POINT point;
|
||||
if (GetCursorPos(&point) == 0) {
|
||||
return {};
|
||||
} else {
|
||||
return point;
|
||||
}
|
||||
}
|
||||
|
||||
int width(const RECT& rect) {
|
||||
return rect.right - rect.left;
|
||||
}
|
||||
|
||||
int height(const RECT& rect) {
|
||||
return rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
bool operator<(const RECT& lhs, const RECT& rhs) {
|
||||
auto lhs_tuple = std::make_tuple(lhs.left, lhs.right, lhs.top, lhs.bottom);
|
||||
auto rhs_tuple = std::make_tuple(rhs.left, rhs.right, rhs.top, rhs.bottom);
|
||||
return lhs_tuple < rhs_tuple;
|
||||
}
|
||||
|
||||
RECT keep_rect_inside_rect(const RECT& small_rect, const RECT& big_rect) {
|
||||
RECT result = small_rect;
|
||||
if ((result.right - result.left) > (big_rect.right - big_rect.left)) {
|
||||
// small_rect is too big horizontally. resize it.
|
||||
result.right = big_rect.right;
|
||||
result.left = big_rect.left;
|
||||
} else {
|
||||
if (result.right > big_rect.right) {
|
||||
// move the rect left.
|
||||
result.left -= result.right-big_rect.right;
|
||||
result.right -= result.right-big_rect.right;
|
||||
}
|
||||
if (result.left < big_rect.left) {
|
||||
// move the rect right.
|
||||
result.right += big_rect.left-result.left;
|
||||
result.left += big_rect.left-result.left;
|
||||
}
|
||||
}
|
||||
if ((result.bottom - result.top) > (big_rect.bottom - big_rect.top)) {
|
||||
// small_rect is too big vertically. resize it.
|
||||
result.bottom = big_rect.bottom;
|
||||
result.top = big_rect.top;
|
||||
} else {
|
||||
if (result.bottom > big_rect.bottom) {
|
||||
// move the rect up.
|
||||
result.top -= result.bottom-big_rect.bottom;
|
||||
result.bottom -= result.bottom-big_rect.bottom;
|
||||
}
|
||||
if (result.top < big_rect.top) {
|
||||
// move the rect down.
|
||||
result.bottom += big_rect.top-result.top;
|
||||
result.top += big_rect.top-result.top;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int run_message_loop() {
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, NULL, 0, 0)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
return static_cast<int>(msg.wParam);
|
||||
}
|
||||
|
||||
void show_last_error_message(LPCWSTR lpszFunction, DWORD dw) {
|
||||
// Retrieve the system error message for the error code
|
||||
LPWSTR lpMsgBuf = NULL;
|
||||
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
dw,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
lpMsgBuf,
|
||||
0, NULL) > 0) {
|
||||
// Display the error message and exit the process
|
||||
LPWSTR lpDisplayBuf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, (lstrlenW(lpMsgBuf) + lstrlenW(lpszFunction) + 40) * sizeof(WCHAR));
|
||||
if (lpDisplayBuf != NULL) {
|
||||
StringCchPrintfW(lpDisplayBuf,
|
||||
LocalSize(lpDisplayBuf) / sizeof(WCHAR),
|
||||
L"%s failed with error %d: %s",
|
||||
lpszFunction, dw, lpMsgBuf);
|
||||
MessageBoxW(NULL, (LPCTSTR)lpDisplayBuf, L"Error", MB_OK);
|
||||
LocalFree(lpDisplayBuf);
|
||||
}
|
||||
LocalFree(lpMsgBuf);
|
||||
}
|
||||
}
|
||||
|
||||
WindowState get_window_state(HWND hwnd) {
|
||||
WINDOWPLACEMENT placement;
|
||||
placement.length = sizeof(WINDOWPLACEMENT);
|
||||
if (GetWindowPlacement(hwnd, &placement) == 0) {
|
||||
return UNKNONW;
|
||||
}
|
||||
if (placement.showCmd == SW_MINIMIZE || placement.showCmd == SW_SHOWMINIMIZED || IsIconic(hwnd)) {
|
||||
return MINIMIZED;
|
||||
}
|
||||
if (placement.showCmd == SW_MAXIMIZE || placement.showCmd == SW_SHOWMAXIMIZED) {
|
||||
return MAXIMIZED;
|
||||
}
|
||||
auto rectp = get_window_pos(hwnd);
|
||||
if (!rectp) {
|
||||
return UNKNONW;
|
||||
}
|
||||
auto rect = *rectp;
|
||||
MONITORINFO monitor;
|
||||
monitor.cbSize = sizeof(MONITORINFO);
|
||||
auto h_monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
GetMonitorInfo(h_monitor, &monitor);
|
||||
bool top_left = monitor.rcWork.top == rect.top && monitor.rcWork.left == rect.left;
|
||||
bool bottom_left = monitor.rcWork.bottom == rect.bottom && monitor.rcWork.left == rect.left;
|
||||
bool top_right = monitor.rcWork.top == rect.top && monitor.rcWork.right == rect.right;
|
||||
bool bottom_right = monitor.rcWork.bottom == rect.bottom && monitor.rcWork.right == rect.right;
|
||||
if (top_left && bottom_left) return SNAPED_LEFT;
|
||||
if (top_left) return SNAPED_TOP_LEFT;
|
||||
if (bottom_left) return SNAPED_BOTTOM_LEFT;
|
||||
if (top_right && bottom_right) return SNAPED_RIGHT;
|
||||
if (top_right) return SNAPED_TOP_RIGHT;
|
||||
if (bottom_right) return SNAPED_BOTTOM_RIGHT;
|
||||
return RESTORED;
|
||||
}
|
||||
36
src/common/common.h
Normal file
36
src/common/common.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <Windows.h>
|
||||
|
||||
// Returns RECT with positions of the minmize/maximize buttons of the given window.
|
||||
// Does not always work, since some apps draw custom toolbars.
|
||||
std::optional<RECT> get_button_pos(HWND hwnd);
|
||||
// Gets position of given window.
|
||||
std::optional<RECT> get_window_pos(HWND hwnd);
|
||||
// Gets mouse postion.
|
||||
std::optional<POINT> get_mouse_pos();
|
||||
// Calculate sizes
|
||||
int width(const RECT& rect);
|
||||
int height(const RECT& rect);
|
||||
// Compare rects
|
||||
bool operator<(const RECT& lhs, const RECT& rhs);
|
||||
// Moves and/or resizes small_rect to fit inside big_rect.
|
||||
RECT keep_rect_inside_rect(const RECT& small_rect, const RECT& big_rect);
|
||||
// Initializes and runs windows message loop
|
||||
int run_message_loop();
|
||||
|
||||
void show_last_error_message(LPCWSTR lpszFunction, DWORD dw);
|
||||
|
||||
enum WindowState {
|
||||
UNKNONW,
|
||||
MINIMIZED,
|
||||
MAXIMIZED,
|
||||
SNAPED_TOP_LEFT,
|
||||
SNAPED_LEFT,
|
||||
SNAPED_BOTTOM_LEFT,
|
||||
SNAPED_TOP_RIGHT,
|
||||
SNAPED_RIGHT,
|
||||
SNAPED_BOTTOM_RIGHT,
|
||||
RESTORED
|
||||
};
|
||||
WindowState get_window_state(HWND hwnd);
|
||||
138
src/common/common.vcxproj
Normal file
138
src/common/common.vcxproj
Normal file
@@ -0,0 +1,138 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<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>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{74485049-C722-400F-ABE5-86AC52D929B3}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>common</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>common</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</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 Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<AdditionalIncludeDirectories>inc;telemetry;..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<AdditionalIncludeDirectories>inc;telemetry;..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="animation.h" />
|
||||
<ClInclude Include="async_message_queue.h" />
|
||||
<ClInclude Include="d2d_svg.h" />
|
||||
<ClInclude Include="d2d_text.h" />
|
||||
<ClInclude Include="d2d_window.h" />
|
||||
<ClInclude Include="dpi_aware.h" />
|
||||
<ClInclude Include="monitors.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="settings_helpers.h" />
|
||||
<ClInclude Include="settings_objects.h" />
|
||||
<ClInclude Include="start_visible.h" />
|
||||
<ClInclude Include="tasklist_positions.h" />
|
||||
<ClInclude Include="common.h" />
|
||||
<ClInclude Include="Telemetry\ProjectTelemetry.h" />
|
||||
<ClInclude Include="Telemetry\TraceLoggingDefines.h" />
|
||||
<ClInclude Include="two_way_pipe_message_ipc.h" />
|
||||
<ClInclude Include="windows_colors.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="animation.cpp" />
|
||||
<ClCompile Include="d2d_svg.cpp" />
|
||||
<ClCompile Include="d2d_text.cpp" />
|
||||
<ClCompile Include="d2d_window.cpp" />
|
||||
<ClCompile Include="dpi_aware.cpp" />
|
||||
<ClCompile Include="monitors.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="settings_helpers.cpp" />
|
||||
<ClCompile Include="settings_objects.cpp" />
|
||||
<ClCompile Include="start_visible.cpp" />
|
||||
<ClCompile Include="tasklist_positions.cpp" />
|
||||
<ClCompile Include="common.cpp" />
|
||||
<ClCompile Include="windows_colors.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\deps\cpprestsdk\cpprestsdk.vcxproj">
|
||||
<Project>{4e577735-dfab-41af-8a6e-b6e8872a2928}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
109
src/common/common.vcxproj.filters
Normal file
109
src/common/common.vcxproj.filters
Normal file
@@ -0,0 +1,109 @@
|
||||
<?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;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\Direct2D">
|
||||
<UniqueIdentifier>{ed0f9961-6b12-408b-8dbc-fed779a557ac}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\Telemetry">
|
||||
<UniqueIdentifier>{3e9f944e-5d97-4a28-8865-2eff3a3568e7}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="d2d_svg.h">
|
||||
<Filter>Header Files\Direct2D</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="d2d_text.h">
|
||||
<Filter>Header Files\Direct2D</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="d2d_window.h">
|
||||
<Filter>Header Files\Direct2D</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="animation.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="monitors.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="tasklist_positions.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="windows_colors.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="start_visible.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="common.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Telemetry\ProjectTelemetry.h">
|
||||
<Filter>Header Files\Telemetry</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Telemetry\TraceLoggingDefines.h">
|
||||
<Filter>Header Files\Telemetry</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="two_way_pipe_message_ipc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="async_message_queue.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="settings_helpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="settings_objects.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dpi_aware.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<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="pch.cpp" />
|
||||
<ClCompile Include="animation.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="monitors.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="tasklist_positions.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="windows_colors.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="start_visible.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="common.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="settings_helpers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="settings_objects.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dpi_aware.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
105
src/common/d2d_svg.cpp
Normal file
105
src/common/d2d_svg.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "pch.h"
|
||||
#include "d2d_svg.h"
|
||||
|
||||
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()));
|
||||
|
||||
winrt::check_hresult(d2d_dc->CreateSvgDocument(
|
||||
svg_stream.get(),
|
||||
D2D1::SizeF(1, 1),
|
||||
svg.put()));
|
||||
|
||||
winrt::com_ptr<ID2D1SvgElement> root;
|
||||
svg->GetRoot(root.put());
|
||||
float tmp;
|
||||
winrt::check_hresult(root->GetAttributeValue(L"width", &tmp));
|
||||
svg_width = (int)tmp;
|
||||
winrt::check_hresult(root->GetAttributeValue(L"height", &tmp));
|
||||
svg_height = (int)tmp;
|
||||
return *this;
|
||||
}
|
||||
|
||||
D2DSVG& D2DSVG::resize(int x, int y, int width, int height, float fill, float max_scale) {
|
||||
// Center
|
||||
transform = D2D1::Matrix3x2F::Identity();
|
||||
transform = transform * D2D1::Matrix3x2F::Translation((width - svg_width) / 2.0f, (height - svg_height) / 2.0f);
|
||||
float h_scale = fill * height / svg_height;
|
||||
float v_scale = fill * width / svg_width;
|
||||
used_scale = min(h_scale, v_scale);
|
||||
if (max_scale > 0) {
|
||||
used_scale = min(used_scale, max_scale);
|
||||
}
|
||||
transform = transform * D2D1::Matrix3x2F::Scale(used_scale, used_scale, D2D1::Point2F(width / 2.0f, height / 2.0f));
|
||||
transform = transform * D2D1::Matrix3x2F::Translation((float)x, (float)y);
|
||||
return *this;
|
||||
}
|
||||
|
||||
D2DSVG& D2DSVG::recolor(uint32_t oldcolor, uint32_t newcolor) {
|
||||
auto new_color = D2D1::ColorF(newcolor & 0xFFFFFF, 1);
|
||||
auto old_color = D2D1::ColorF(oldcolor & 0xFFFFFF, 1);
|
||||
std::function<void(ID2D1SvgElement* element)> recurse = [&](ID2D1SvgElement* element) {
|
||||
if (!element)
|
||||
return;
|
||||
if (element->IsAttributeSpecified(L"fill")) {
|
||||
D2D1_COLOR_F elem_fill;
|
||||
winrt::com_ptr<ID2D1SvgPaint> paint;
|
||||
element->GetAttributeValue(L"fill", paint.put());
|
||||
paint->GetColor(&elem_fill);
|
||||
if (elem_fill.r == old_color.r && elem_fill.g == old_color.g && elem_fill.b == old_color.b) {
|
||||
winrt::check_hresult(element->SetAttributeValue(L"fill", new_color));
|
||||
}
|
||||
}
|
||||
winrt::com_ptr<ID2D1SvgElement> sub;
|
||||
element->GetFirstChild(sub.put());
|
||||
while (sub) {
|
||||
recurse(sub.get());
|
||||
winrt::com_ptr<ID2D1SvgElement> next;
|
||||
element->GetNextChild(sub.get(), next.put());
|
||||
sub = next;
|
||||
}
|
||||
};
|
||||
winrt::com_ptr<ID2D1SvgElement> root;
|
||||
svg->GetRoot(root.put());
|
||||
recurse(root.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
D2DSVG& D2DSVG::render(ID2D1DeviceContext5* d2d_dc) {
|
||||
D2D1_MATRIX_3X2_F current;
|
||||
d2d_dc->GetTransform(¤t);
|
||||
d2d_dc->SetTransform(transform * current);
|
||||
d2d_dc->DrawSvgDocument(svg.get());
|
||||
d2d_dc->SetTransform(current);
|
||||
return *this;
|
||||
}
|
||||
|
||||
D2DSVG& D2DSVG::toggle_element(const wchar_t* id, bool visible) {
|
||||
winrt::com_ptr<ID2D1SvgElement> element;
|
||||
if (svg->FindElementById(id, element.put()) != S_OK)
|
||||
return *this;
|
||||
if (!element)
|
||||
return *this;
|
||||
element->SetAttributeValue(L"display", visible ? D2D1_SVG_DISPLAY::D2D1_SVG_DISPLAY_INLINE : D2D1_SVG_DISPLAY::D2D1_SVG_DISPLAY_NONE);
|
||||
return *this;
|
||||
}
|
||||
|
||||
winrt::com_ptr<ID2D1SvgElement> D2DSVG::find_element(const std::wstring& id) {
|
||||
winrt::com_ptr< ID2D1SvgElement> element;
|
||||
winrt::check_hresult(svg->FindElementById(id.c_str(), element.put()));
|
||||
return element;
|
||||
}
|
||||
|
||||
D2D1_RECT_F D2DSVG::rescale(D2D1_RECT_F rect) {
|
||||
D2D1_RECT_F result;
|
||||
auto src = reinterpret_cast<D2D1_POINT_2F*>(&rect);
|
||||
auto dst = reinterpret_cast<D2D1_POINT_2F*>(&result);
|
||||
dst[0] = src[0] * transform;
|
||||
dst[1] = src[1] * transform;
|
||||
return result;
|
||||
}
|
||||
24
src/common/d2d_svg.h
Normal file
24
src/common/d2d_svg.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <d2d1_3.h>
|
||||
#include <d2d1_3helper.h>
|
||||
#include <winrt/base.h>
|
||||
#include <string>
|
||||
|
||||
class D2DSVG {
|
||||
public:
|
||||
D2DSVG& load(const std::wstring& filename, ID2D1DeviceContext5* d2d_dc);
|
||||
D2DSVG& resize(int x, int y, int width, int height, float fill, float max_scale = -1.0f);
|
||||
D2DSVG& render(ID2D1DeviceContext5* d2d_dc);
|
||||
D2DSVG& recolor(uint32_t oldcolor, uint32_t newcolor);
|
||||
float get_scale() const { return used_scale; }
|
||||
int width() const { return svg_width; }
|
||||
int height() const { return svg_height; }
|
||||
D2DSVG& toggle_element(const wchar_t* id, bool visible);
|
||||
winrt::com_ptr<ID2D1SvgElement> find_element(const std::wstring& id);
|
||||
D2D1_RECT_F rescale(D2D1_RECT_F rect);
|
||||
protected:
|
||||
float used_scale = 1.0f;
|
||||
winrt::com_ptr<ID2D1SvgDocument> svg;
|
||||
int svg_width = -1, svg_height = -1;
|
||||
D2D1::Matrix3x2F transform;
|
||||
};
|
||||
48
src/common/d2d_text.cpp
Normal file
48
src/common/d2d_text.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "pch.h"
|
||||
#include "d2d_text.h"
|
||||
|
||||
D2DText::D2DText(float text_size, float scale) {
|
||||
winrt::check_hresult(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(factory), reinterpret_cast<IUnknown**>(factory.put_void())));
|
||||
resize(text_size, scale);
|
||||
winrt::check_hresult(format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER));
|
||||
winrt::check_hresult(format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER));
|
||||
}
|
||||
|
||||
D2DText & D2DText::resize(float text_size, float scale) {
|
||||
format = nullptr;
|
||||
winrt::check_hresult(factory->CreateTextFormat(L"Segoe UI",
|
||||
nullptr,
|
||||
DWRITE_FONT_WEIGHT_NORMAL,
|
||||
DWRITE_FONT_STYLE_NORMAL,
|
||||
DWRITE_FONT_STRETCH_NORMAL,
|
||||
text_size * scale,
|
||||
L"en-us",
|
||||
format.put()));
|
||||
winrt::check_hresult(format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER));
|
||||
return *this;
|
||||
}
|
||||
|
||||
D2DText & D2DText::set_aligment_left() {
|
||||
winrt::check_hresult(format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING));
|
||||
return *this;
|
||||
}
|
||||
|
||||
D2DText & D2DText::set_aligment_center() {
|
||||
winrt::check_hresult(format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER));
|
||||
return *this;
|
||||
}
|
||||
|
||||
D2DText & D2DText::set_aligment_right() {
|
||||
winrt::check_hresult(format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING));
|
||||
return *this;
|
||||
}
|
||||
|
||||
void D2DText::write(ID2D1DeviceContext5 * d2d_dc, D2D1_COLOR_F color, D2D1_RECT_F rect, std::wstring text) {
|
||||
winrt::com_ptr<ID2D1SolidColorBrush> brush;
|
||||
d2d_dc->CreateSolidColorBrush(color, brush.put());
|
||||
d2d_dc->DrawText(text.c_str(),
|
||||
(UINT32)text.length(),
|
||||
format.get(),
|
||||
rect,
|
||||
brush.get());
|
||||
}
|
||||
16
src/common/d2d_text.h
Normal file
16
src/common/d2d_text.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <winrt/base.h>
|
||||
#include <dwrite.h>
|
||||
|
||||
class D2DText {
|
||||
public:
|
||||
D2DText(float text_size = 15.0f, float scale = 1.0f);
|
||||
D2DText& resize(float text_size, float scale);
|
||||
D2DText& set_aligment_left();
|
||||
D2DText& set_aligment_center();
|
||||
D2DText& set_aligment_right();
|
||||
void write(ID2D1DeviceContext5* d2d_dc, D2D1_COLOR_F color, D2D1_RECT_F rect, std::wstring text);
|
||||
private:
|
||||
winrt::com_ptr<IDWriteFactory> factory;
|
||||
winrt::com_ptr<IDWriteTextFormat> format;
|
||||
};
|
||||
184
src/common/d2d_window.cpp
Normal file
184
src/common/d2d_window.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
#include "pch.h"
|
||||
#include "d2d_window.h"
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
D2DWindow::D2DWindow() {
|
||||
static const WCHAR* class_name = L"PToyD2DPopup";
|
||||
WNDCLASS wc = {};
|
||||
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wc.hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
|
||||
wc.lpszClassName = class_name;
|
||||
wc.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wc.lpfnWndProc = d2d_window_proc;
|
||||
RegisterClass(&wc);
|
||||
hwnd = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOREDIRECTIONBITMAP | WS_EX_LAYERED,
|
||||
wc.lpszClassName,
|
||||
L"PToyD2DPopup",
|
||||
WS_POPUP| WS_VISIBLE,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
nullptr, nullptr, wc.hInstance, this);
|
||||
WINRT_VERIFY(hwnd);
|
||||
}
|
||||
|
||||
void D2DWindow::show(UINT x, UINT y, UINT width, UINT height) {
|
||||
if (!initialized) {
|
||||
base_init();
|
||||
}
|
||||
base_resize(width, height);
|
||||
render_empty();
|
||||
on_show();
|
||||
SetWindowPos(hwnd, HWND_TOPMOST, x, y, width, height, 0);
|
||||
ShowWindow(hwnd, SW_SHOWNORMAL);
|
||||
UpdateWindow(hwnd);
|
||||
}
|
||||
|
||||
void D2DWindow::hide() {
|
||||
ShowWindow(hwnd, SW_HIDE);
|
||||
on_hide();
|
||||
}
|
||||
|
||||
void D2DWindow::initialize() {
|
||||
base_init();
|
||||
}
|
||||
|
||||
void D2DWindow::base_init() {
|
||||
std::unique_lock lock(mutex);
|
||||
// D2D1Factory is independent from the device, no need to recreate it if we need to recreate the device.
|
||||
if (!d2d_factory) {
|
||||
#ifdef _DEBUG
|
||||
D2D1_FACTORY_OPTIONS options = { D2D1_DEBUG_LEVEL_INFORMATION };
|
||||
#else
|
||||
D2D1_FACTORY_OPTIONS options = {};
|
||||
#endif
|
||||
winrt::check_hresult(D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
|
||||
__uuidof(d2d_factory),
|
||||
&options,
|
||||
d2d_factory.put_void()));
|
||||
}
|
||||
// For all other stuff - assing nullptr first to release the object, to reset the com_ptr.
|
||||
d2d_dc = nullptr;
|
||||
d2d_device = nullptr;
|
||||
dxgi_factory = nullptr;
|
||||
dxgi_device = nullptr;
|
||||
d3d_device = nullptr;
|
||||
winrt::check_hresult(D3D11CreateDevice(nullptr,
|
||||
D3D_DRIVER_TYPE_HARDWARE,
|
||||
nullptr,
|
||||
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
|
||||
nullptr,
|
||||
0,
|
||||
D3D11_SDK_VERSION,
|
||||
d3d_device.put(),
|
||||
nullptr,
|
||||
nullptr));
|
||||
winrt::check_hresult(d3d_device->QueryInterface(__uuidof(dxgi_device), dxgi_device.put_void()));
|
||||
winrt::check_hresult(CreateDXGIFactory2(0, __uuidof(dxgi_factory), dxgi_factory.put_void()));
|
||||
winrt::check_hresult(d2d_factory->CreateDevice(dxgi_device.get(), d2d_device.put()));
|
||||
winrt::check_hresult(d2d_device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, d2d_dc.put()));
|
||||
init();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void D2DWindow::base_resize(UINT width, UINT height) {
|
||||
std::unique_lock lock(mutex);
|
||||
if (!initialized) {
|
||||
return;
|
||||
}
|
||||
window_width = width;
|
||||
window_height = height;
|
||||
if (window_width == 0 || window_height == 0) {
|
||||
return;
|
||||
}
|
||||
DXGI_SWAP_CHAIN_DESC1 sc_description = {};
|
||||
sc_description.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
sc_description.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
sc_description.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
|
||||
sc_description.BufferCount = 2;
|
||||
sc_description.SampleDesc.Count = 1;
|
||||
sc_description.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
|
||||
sc_description.Width = window_width;
|
||||
sc_description.Height = window_height;
|
||||
dxgi_swap_chain = nullptr;
|
||||
winrt::check_hresult(dxgi_factory->CreateSwapChainForComposition(dxgi_device.get(),
|
||||
&sc_description,
|
||||
nullptr,
|
||||
dxgi_swap_chain.put()));
|
||||
composition_device = nullptr;
|
||||
winrt::check_hresult(DCompositionCreateDevice(dxgi_device.get(),
|
||||
__uuidof(composition_device),
|
||||
composition_device.put_void()));
|
||||
|
||||
composition_target = nullptr;
|
||||
winrt::check_hresult(composition_device->CreateTargetForHwnd(hwnd, true, composition_target.put()));
|
||||
|
||||
composition_visual = nullptr;
|
||||
winrt::check_hresult(composition_device->CreateVisual(composition_visual.put()));
|
||||
winrt::check_hresult(composition_visual->SetContent(dxgi_swap_chain.get()));
|
||||
winrt::check_hresult(composition_target->SetRoot(composition_visual.get()));
|
||||
|
||||
dxgi_surface = nullptr;
|
||||
winrt::check_hresult(dxgi_swap_chain->GetBuffer(0, __uuidof(dxgi_surface), dxgi_surface.put_void()));
|
||||
D2D1_BITMAP_PROPERTIES1 properties = {};
|
||||
properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
|
||||
properties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
properties.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
|
||||
|
||||
d2d_bitmap = nullptr;
|
||||
winrt::check_hresult(d2d_dc->CreateBitmapFromDxgiSurface(dxgi_surface.get(),
|
||||
properties,
|
||||
d2d_bitmap.put()));
|
||||
d2d_dc->SetTarget(d2d_bitmap.get());
|
||||
resize();
|
||||
}
|
||||
|
||||
void D2DWindow::base_render() {
|
||||
std::unique_lock lock(mutex);
|
||||
if (!initialized || !d2d_dc || !d2d_bitmap)
|
||||
return;
|
||||
d2d_dc->BeginDraw();
|
||||
render(d2d_dc.get());
|
||||
winrt::check_hresult(d2d_dc->EndDraw());
|
||||
winrt::check_hresult(dxgi_swap_chain->Present(1, 0));
|
||||
winrt::check_hresult(composition_device->Commit());
|
||||
}
|
||||
|
||||
void D2DWindow::render_empty() {
|
||||
std::unique_lock lock(mutex);
|
||||
if (!initialized || !d2d_dc || !d2d_bitmap)
|
||||
return;
|
||||
d2d_dc->BeginDraw();
|
||||
d2d_dc->Clear();
|
||||
winrt::check_hresult(d2d_dc->EndDraw());
|
||||
winrt::check_hresult(dxgi_swap_chain->Present(1, 0));
|
||||
winrt::check_hresult(composition_device->Commit());
|
||||
}
|
||||
|
||||
D2DWindow::~D2DWindow() {
|
||||
ShowWindow(hwnd, SW_HIDE);
|
||||
DestroyWindow(hwnd);
|
||||
}
|
||||
|
||||
D2DWindow* D2DWindow::this_from_hwnd(HWND window) {
|
||||
return reinterpret_cast<D2DWindow*>(GetWindowLongPtr(window, GWLP_USERDATA));
|
||||
}
|
||||
|
||||
LRESULT __stdcall D2DWindow::d2d_window_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) {
|
||||
switch (message) {
|
||||
case WM_NCCREATE: {
|
||||
auto create_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
|
||||
SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(create_struct->lpCreateParams));
|
||||
return TRUE;
|
||||
}
|
||||
case WM_MOVE:
|
||||
case WM_SIZE:
|
||||
this_from_hwnd(window)->base_resize((unsigned)lparam & 0xFFFF, (unsigned)lparam >> 16);
|
||||
// Fall through to call 'base_render()'
|
||||
case WM_PAINT:
|
||||
this_from_hwnd(window)->base_render();
|
||||
return 0;
|
||||
default:
|
||||
return DefWindowProc(window, message, wparam, lparam);
|
||||
}
|
||||
}
|
||||
60
src/common/d2d_window.h
Normal file
60
src/common/d2d_window.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include <winrt/base.h>
|
||||
#include <Windows.h>
|
||||
#include <dxgi1_3.h>
|
||||
#include <d3d11_2.h>
|
||||
#include <d2d1_3.h>
|
||||
#include <d2d1_3helper.h>
|
||||
#include <d2d1helper.h>
|
||||
#include <dcomp.h>
|
||||
#include <dwmapi.h>
|
||||
#include <string>
|
||||
#include "d2d_svg.h"
|
||||
|
||||
class D2DWindow
|
||||
{
|
||||
public:
|
||||
D2DWindow();
|
||||
void show(UINT x, UINT y, UINT width, UINT height);
|
||||
void hide();
|
||||
void initialize();
|
||||
virtual ~D2DWindow();
|
||||
protected:
|
||||
// Implement this:
|
||||
|
||||
// Initialization - called when D2D device needs to be created.
|
||||
// When called all D2DWindow members will be initialized, including d2d_dc
|
||||
virtual void init() = 0;
|
||||
// resize - when called, window_width and window_height will have current window size
|
||||
virtual void resize() = 0;
|
||||
// render - called on WM_PAIT, BeginPaint/EndPaint is handled by D2DWindow
|
||||
virtual void render(ID2D1DeviceContext5* d2d_dc) = 0;
|
||||
// on_show, on_hide - called when the window is about to be shown or about to be hidden
|
||||
virtual void on_show() = 0;
|
||||
virtual void on_hide() = 0;
|
||||
|
||||
static LRESULT __stdcall d2d_window_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam);
|
||||
static D2DWindow* this_from_hwnd(HWND window);
|
||||
|
||||
void base_init();
|
||||
void base_resize(UINT width, UINT height);
|
||||
void base_render();
|
||||
void render_empty();
|
||||
|
||||
std::recursive_mutex mutex;
|
||||
bool initialized = false;
|
||||
HWND hwnd;
|
||||
UINT window_width, window_height;
|
||||
winrt::com_ptr<ID3D11Device> d3d_device;
|
||||
winrt::com_ptr<IDXGIDevice> dxgi_device;
|
||||
winrt::com_ptr<IDXGIFactory2> dxgi_factory;
|
||||
winrt::com_ptr<IDXGISwapChain1> dxgi_swap_chain;
|
||||
winrt::com_ptr<IDCompositionDevice> composition_device;
|
||||
winrt::com_ptr<IDCompositionTarget> composition_target;
|
||||
winrt::com_ptr<IDCompositionVisual> composition_visual;
|
||||
winrt::com_ptr<IDXGISurface2> dxgi_surface;
|
||||
winrt::com_ptr<ID2D1Bitmap1> d2d_bitmap;
|
||||
winrt::com_ptr<ID2D1Factory6> d2d_factory;
|
||||
winrt::com_ptr<ID2D1Device5> d2d_device;
|
||||
winrt::com_ptr<ID2D1DeviceContext5> d2d_dc;
|
||||
};
|
||||
28
src/common/dpi_aware.cpp
Normal file
28
src/common/dpi_aware.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "pch.h"
|
||||
#include "dpi_aware.h"
|
||||
#include "monitors.h"
|
||||
#include <ShellScalingApi.h>
|
||||
|
||||
HRESULT DPIAware::GetScreenDPIForWindow(HWND hwnd, UINT &dpi_x, UINT &dpi_y) {
|
||||
auto monitor_handle = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
dpi_x = 0;
|
||||
dpi_y = 0;
|
||||
if (monitor_handle != nullptr) {
|
||||
return GetDpiForMonitor(monitor_handle, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y);
|
||||
} else {
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
void DPIAware::Convert(HMONITOR monitor_handle, int &width, int &height) {
|
||||
if (monitor_handle == NULL) {
|
||||
const POINT ptZero = { 0, 0 };
|
||||
monitor_handle = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
|
||||
}
|
||||
|
||||
UINT dpi_x, dpi_y;
|
||||
if (GetDpiForMonitor(monitor_handle, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y) == S_OK) {
|
||||
width = width * dpi_x / DEFAULT_DPI;
|
||||
height = height * dpi_y / DEFAULT_DPI;
|
||||
}
|
||||
}
|
||||
11
src/common/dpi_aware.h
Normal file
11
src/common/dpi_aware.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "windef.h"
|
||||
|
||||
class DPIAware {
|
||||
private:
|
||||
static const int DEFAULT_DPI = 96;
|
||||
|
||||
public:
|
||||
static HRESULT GetScreenDPIForWindow(HWND hwnd, UINT & dpi_x, UINT & dpi_y);
|
||||
static void Convert(HMONITOR monitor_handle, int &width, int &high);
|
||||
};
|
||||
67
src/common/monitors.cpp
Normal file
67
src/common/monitors.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "pch.h"
|
||||
#include "monitors.h"
|
||||
|
||||
bool operator==(const ScreenSize& lhs, const ScreenSize& rhs) {
|
||||
auto lhs_tuple = std::make_tuple(lhs.rect.left, lhs.rect.right, lhs.rect.top, lhs.rect.bottom);
|
||||
auto rhs_tuple = std::make_tuple(rhs.rect.left, rhs.rect.right, rhs.rect.top, rhs.rect.bottom);
|
||||
return lhs_tuple == rhs_tuple;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK get_displays_enum_cb(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data) {
|
||||
MONITORINFOEX monitor_info;
|
||||
monitor_info.cbSize = sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(monitor, &monitor_info);
|
||||
reinterpret_cast<std::vector<MonitorInfo>*>(data)->emplace_back(monitor, monitor_info.rcWork);
|
||||
return true;
|
||||
};
|
||||
|
||||
static BOOL CALLBACK get_displays_enum_cb_with_toolbar(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data) {
|
||||
MONITORINFOEX monitor_info;
|
||||
monitor_info.cbSize = sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(monitor, &monitor_info);
|
||||
reinterpret_cast<std::vector<MonitorInfo>*>(data)->emplace_back(monitor, monitor_info.rcMonitor);
|
||||
return true;
|
||||
};
|
||||
|
||||
std::vector<MonitorInfo> MonitorInfo::GetMonitors(bool include_toolbars) {
|
||||
std::vector<MonitorInfo> monitors;
|
||||
EnumDisplayMonitors(NULL, NULL, include_toolbars ? get_displays_enum_cb_with_toolbar : get_displays_enum_cb, reinterpret_cast<LPARAM>(&monitors));
|
||||
std::sort(begin(monitors), end(monitors), [](const MonitorInfo& lhs, const MonitorInfo& rhs) {
|
||||
return lhs.rect < rhs.rect;
|
||||
});
|
||||
return monitors;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK get_primary_display_enum_cb(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data) {
|
||||
MONITORINFOEX monitor_info;
|
||||
monitor_info.cbSize = sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(monitor, &monitor_info);
|
||||
if (monitor_info.dwFlags & MONITORINFOF_PRIMARY) {
|
||||
reinterpret_cast<MonitorInfo*>(data)->handle = monitor;
|
||||
reinterpret_cast<MonitorInfo*>(data)->rect = monitor_info.rcWork;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
MonitorInfo MonitorInfo::GetPrimaryMonitor() {
|
||||
MonitorInfo primary({}, {});
|
||||
EnumDisplayMonitors(NULL, NULL, get_primary_display_enum_cb, reinterpret_cast<LPARAM>(&primary));
|
||||
return primary;
|
||||
}
|
||||
|
||||
MonitorInfo MonitorInfo::GetFromWindow(HWND hwnd) {
|
||||
auto monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
return GetFromHandle(monitor);
|
||||
}
|
||||
|
||||
MonitorInfo MonitorInfo::GetFromPoint(POINT p) {
|
||||
auto monitor = MonitorFromPoint(p, MONITOR_DEFAULTTONEAREST);
|
||||
return GetFromHandle(monitor);
|
||||
}
|
||||
|
||||
MonitorInfo MonitorInfo::GetFromHandle(HMONITOR monitor) {
|
||||
MONITORINFOEX monitor_info;
|
||||
monitor_info.cbSize = sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(monitor, &monitor_info);
|
||||
return MonitorInfo(monitor, monitor_info.rcWork);
|
||||
}
|
||||
41
src/common/monitors.h
Normal file
41
src/common/monitors.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <vector>
|
||||
|
||||
struct ScreenSize {
|
||||
explicit ScreenSize(RECT rect) : rect(rect) {}
|
||||
RECT rect;
|
||||
int left() const { return rect.left; }
|
||||
int right() const { return rect.right; }
|
||||
int top() const { return rect.top; }
|
||||
int bottom() const { return rect.bottom; }
|
||||
int height() const { return rect.bottom - rect.top; };
|
||||
int width() const { return rect.right - rect.left; };
|
||||
POINT top_left() const { return { rect.left, rect.top }; };
|
||||
POINT top_middle() const { return { rect.left + width() / 2, rect.top }; };
|
||||
POINT top_right() const { return { rect.right, rect.top }; };
|
||||
POINT middle_left() const { return { rect.left, rect.top + height() / 2 }; };
|
||||
POINT middle() const { return { rect.left + width() / 2, rect.top + height() / 2 }; };
|
||||
POINT middle_right() const { return { rect.right, rect.top + height() / 2 }; };
|
||||
POINT bottom_left() const { return { rect.left, rect.bottom }; };
|
||||
POINT bottm_midle() const { return { rect.left + width() / 2, rect.bottom }; };
|
||||
POINT bottom_right() const { return { rect.right, rect.bottom }; };
|
||||
};
|
||||
|
||||
struct MonitorInfo : ScreenSize {
|
||||
explicit MonitorInfo(HMONITOR monitor, RECT rect) : handle(monitor), ScreenSize(rect) {}
|
||||
HMONITOR handle;
|
||||
|
||||
// Returns monitor rects ordered from left to right
|
||||
static std::vector<MonitorInfo> GetMonitors(bool include_toolbar);
|
||||
// Return primary display
|
||||
static MonitorInfo GetPrimaryMonitor();
|
||||
// Return monitor on which hwnd window is displayed
|
||||
static MonitorInfo GetFromWindow(HWND hwnd);
|
||||
// Return monitor nearest to a point
|
||||
static MonitorInfo GetFromPoint(POINT p);
|
||||
// Return monitor info given a HMONITOR
|
||||
static MonitorInfo GetFromHandle(HMONITOR monitor);
|
||||
};
|
||||
|
||||
bool operator==(const ScreenSize& lhs, const ScreenSize& rhs);
|
||||
1
src/common/pch.cpp
Normal file
1
src/common/pch.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
||||
26
src/common/pch.h
Normal file
26
src/common/pch.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#include <winrt/base.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 <vector>
|
||||
|
||||
#include "common.h"
|
||||
75
src/common/settings_helpers.cpp
Normal file
75
src/common/settings_helpers.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "pch.h"
|
||||
#include "settings_helpers.h"
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
namespace PTSettingsHelper {
|
||||
std::wstring get_root_save_folder_location() {
|
||||
PWSTR local_app_path;
|
||||
std::wstring result(L"");
|
||||
|
||||
winrt::check_hresult(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &local_app_path));
|
||||
result = std::wstring(local_app_path);
|
||||
CoTaskMemFree(local_app_path);
|
||||
|
||||
result += L"\\Microsoft\\PowerToys";
|
||||
std::filesystem::path save_path(result);
|
||||
if (!std::filesystem::exists(save_path)) {
|
||||
std::filesystem::create_directories(save_path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring get_module_save_folder_location(const std::wstring& powertoy_name) {
|
||||
std::wstring result = get_root_save_folder_location();
|
||||
result += L"\\";
|
||||
result += powertoy_name;
|
||||
std::filesystem::path save_path(result);
|
||||
if (!std::filesystem::exists(save_path)) {
|
||||
std::filesystem::create_directories(save_path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring get_module_save_file_location(const std::wstring& powertoy_name) {
|
||||
std::wstring result = get_module_save_folder_location(powertoy_name);
|
||||
result += L"\\settings.json";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring get_powertoys_general_save_file_location() {
|
||||
std::wstring result = get_root_save_folder_location();
|
||||
result += L"\\settings.json";
|
||||
return result;
|
||||
}
|
||||
|
||||
void save_module_settings(const std::wstring& powertoy_name, web::json::value& settings) {
|
||||
std::wstring save_file_location = get_module_save_file_location(powertoy_name);
|
||||
std::ofstream save_file(save_file_location, std::ios::binary);
|
||||
settings.serialize(save_file);
|
||||
save_file.close();
|
||||
}
|
||||
|
||||
web::json::value load_module_settings(const std::wstring& powertoy_name) {
|
||||
std::wstring save_file_location = get_module_save_file_location(powertoy_name);
|
||||
std::ifstream save_file(save_file_location, std::ios::binary);
|
||||
web::json::value result = web::json::value::parse(save_file);
|
||||
save_file.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
void save_general_settings(web::json::value& settings) {
|
||||
std::wstring save_file_location = get_powertoys_general_save_file_location();
|
||||
std::ofstream save_file(save_file_location, std::ios::binary);
|
||||
settings.serialize(save_file);
|
||||
save_file.close();
|
||||
}
|
||||
|
||||
web::json::value load_general_settings() {
|
||||
std::wstring save_file_location = get_powertoys_general_save_file_location();
|
||||
std::ifstream save_file(save_file_location, std::ios::binary);
|
||||
web::json::value result = web::json::value::parse(save_file);
|
||||
save_file.close();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
13
src/common/settings_helpers.h
Normal file
13
src/common/settings_helpers.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <Shlobj.h>
|
||||
#include <cpprest/json.h>
|
||||
|
||||
namespace PTSettingsHelper {
|
||||
|
||||
void save_module_settings(const std::wstring& powertoy_name, web::json::value& settings);
|
||||
web::json::value load_module_settings(const std::wstring& powertoy_name);
|
||||
void save_general_settings(web::json::value& settings);
|
||||
web::json::value load_general_settings();
|
||||
|
||||
}
|
||||
237
src/common/settings_objects.cpp
Normal file
237
src/common/settings_objects.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
#include "pch.h"
|
||||
#include "settings_objects.h"
|
||||
#include "settings_helpers.h"
|
||||
|
||||
namespace PowerToysSettings {
|
||||
|
||||
Settings::Settings(const HINSTANCE hinstance, const std::wstring& powertoy_name) {
|
||||
m_instance = hinstance;
|
||||
m_json = web::json::value::object();
|
||||
m_json.as_object()[L"version"] = web::json::value::string(L"1.0");
|
||||
m_json.as_object()[L"name"] = web::json::value::string(powertoy_name);
|
||||
m_json.as_object()[L"properties"] = web::json::value::object();
|
||||
}
|
||||
|
||||
void Settings::set_description(UINT resource_id) {
|
||||
m_json.as_object()[L"description"] = web::json::value::string(get_resource(resource_id));
|
||||
}
|
||||
|
||||
void Settings::set_description(const std::wstring& description) {
|
||||
m_json.as_object()[L"description"] = web::json::value::string(description);
|
||||
}
|
||||
|
||||
void Settings::set_icon_key(const std::wstring& icon_key) {
|
||||
m_json.as_object()[L"icon_key"] = web::json::value::string(icon_key);
|
||||
}
|
||||
|
||||
void Settings::set_overview_link(const std::wstring& overview_link) {
|
||||
m_json.as_object()[L"overview_link"] = web::json::value::string(overview_link);
|
||||
}
|
||||
|
||||
void Settings::set_video_link(const std::wstring& video_link) {
|
||||
m_json.as_object()[L"video_link"] = web::json::value::string(video_link);
|
||||
}
|
||||
|
||||
// add_bool_toogle overloads.
|
||||
void Settings::add_bool_toogle(const std::wstring& name, UINT description_resource_id, bool value) {
|
||||
add_bool_toogle(name, get_resource(description_resource_id), value);
|
||||
}
|
||||
|
||||
void Settings::add_bool_toogle(const std::wstring& name, const std::wstring& description, bool value) {
|
||||
web::json::value item = web::json::value::object();
|
||||
item.as_object()[L"display_name"] = web::json::value::string(description);
|
||||
item.as_object()[L"editor_type"] = web::json::value::string(L"bool_toggle");
|
||||
item.as_object()[L"value"] = web::json::value::boolean(value);
|
||||
item.as_object()[L"order"] = web::json::value::number(++m_curr_priority);
|
||||
|
||||
m_json.as_object()[L"properties"].as_object()[name] = item;
|
||||
}
|
||||
|
||||
// add_int_spinner overloads.
|
||||
void Settings::add_int_spinner(const std::wstring& name, UINT description_resource_id, int value, int min, int max, int step) {
|
||||
add_int_spinner(name, get_resource(description_resource_id), value, min, max, step);
|
||||
}
|
||||
|
||||
void Settings::add_int_spinner(const std::wstring& name, const std::wstring& description, int value, int min, int max, int step) {
|
||||
web::json::value item = web::json::value::object();
|
||||
item.as_object()[L"display_name"] = web::json::value::string(description);
|
||||
item.as_object()[L"editor_type"] = web::json::value::string(L"int_spinner");
|
||||
item.as_object()[L"value"] = web::json::value::number(value);
|
||||
item.as_object()[L"min"] = web::json::value::number(min);
|
||||
item.as_object()[L"max"] = web::json::value::number(max);
|
||||
item.as_object()[L"step"] = web::json::value::number(step);
|
||||
item.as_object()[L"order"] = web::json::value::number(++m_curr_priority);
|
||||
|
||||
m_json.as_object()[L"properties"].as_object()[name] = item;
|
||||
}
|
||||
|
||||
// add_string overloads.
|
||||
void Settings::add_string(const std::wstring& name, UINT description_resource_id, const std::wstring& value) {
|
||||
add_string(name, get_resource(description_resource_id), value);
|
||||
}
|
||||
|
||||
void Settings::add_string(const std::wstring& name, const std::wstring& description, const std::wstring& value) {
|
||||
web::json::value item = web::json::value::object();
|
||||
item.as_object()[L"display_name"] = web::json::value::string(description);
|
||||
item.as_object()[L"editor_type"] = web::json::value::string(L"string_text");
|
||||
item.as_object()[L"value"] = web::json::value::string(value);
|
||||
item.as_object()[L"order"] = web::json::value::number(++m_curr_priority);
|
||||
|
||||
m_json.as_object()[L"properties"].as_object()[name] = item;
|
||||
}
|
||||
|
||||
// add_color_picker overloads.
|
||||
void Settings::add_color_picker(const std::wstring& name, UINT description_resource_id, const std::wstring& value) {
|
||||
add_color_picker(name, get_resource(description_resource_id), value);
|
||||
}
|
||||
|
||||
void Settings::add_color_picker(const std::wstring& name, const std::wstring& description, const std::wstring& value) {
|
||||
web::json::value item = web::json::value::object();
|
||||
item.as_object()[L"display_name"] = web::json::value::string(description);
|
||||
item.as_object()[L"editor_type"] = web::json::value::string(L"color_picker");
|
||||
item.as_object()[L"value"] = web::json::value::string(value);
|
||||
item.as_object()[L"order"] = web::json::value::number(++m_curr_priority);
|
||||
|
||||
m_json.as_object()[L"properties"].as_object()[name] = item;
|
||||
}
|
||||
|
||||
// add_custom_action overloads.
|
||||
void Settings::add_custom_action(const std::wstring& name, UINT description_resource_id, UINT button_text_resource_id, UINT ext_description_resource_id) {
|
||||
add_custom_action(name, get_resource(description_resource_id), get_resource(button_text_resource_id), get_resource(ext_description_resource_id));
|
||||
}
|
||||
|
||||
void Settings::add_custom_action(const std::wstring& name, UINT description_resource_id, UINT button_text_resource_id, const std::wstring& value) {
|
||||
add_custom_action(name, get_resource(description_resource_id), get_resource(button_text_resource_id), value);
|
||||
}
|
||||
|
||||
void Settings::add_custom_action(const std::wstring& name, const std::wstring& description, const std::wstring& button_text, const std::wstring& value) {
|
||||
web::json::value item = web::json::value::object();
|
||||
item.as_object()[L"display_name"] = web::json::value::string(description);
|
||||
item.as_object()[L"button_text"] = web::json::value::string(button_text);
|
||||
item.as_object()[L"editor_type"] = web::json::value::string(L"custom_action");
|
||||
item.as_object()[L"value"] = web::json::value::string(value);
|
||||
item.as_object()[L"order"] = web::json::value::number(++m_curr_priority);
|
||||
|
||||
m_json.as_object()[L"properties"].as_object()[name] = item;
|
||||
}
|
||||
|
||||
// Serialization methods.
|
||||
std::wstring Settings::serialize() {
|
||||
return m_json.serialize();
|
||||
}
|
||||
|
||||
bool Settings::serialize_to_buffer(wchar_t* buffer, int *buffer_size) {
|
||||
std::wstring result = m_json.serialize();
|
||||
int result_len = (int)result.length();
|
||||
|
||||
if (buffer == nullptr || *buffer_size < result_len) {
|
||||
*buffer_size = result_len + 1;
|
||||
return false;
|
||||
} else {
|
||||
wcscpy_s(buffer, *buffer_size, result.c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Resource helper.
|
||||
std::wstring Settings::get_resource(UINT resource_id) {
|
||||
if (resource_id != 0) {
|
||||
wchar_t buffer[512];
|
||||
if (LoadString(m_instance, resource_id, buffer, ARRAYSIZE(buffer)) > 0) {
|
||||
return std::wstring(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return L"RESOURCE ID NOT FOUND: " + std::to_wstring(resource_id);
|
||||
}
|
||||
|
||||
PowerToyValues::PowerToyValues(const std::wstring& powertoy_name) {
|
||||
_name = powertoy_name;
|
||||
m_json = web::json::value::object();
|
||||
set_version();
|
||||
m_json.as_object()[L"name"] = web::json::value::string(powertoy_name);
|
||||
m_json.as_object()[L"properties"] = web::json::value::object();
|
||||
}
|
||||
|
||||
PowerToyValues PowerToyValues::from_json_string(const std::wstring& json) {
|
||||
PowerToyValues result = PowerToyValues();
|
||||
result.m_json = web::json::value::parse(json);
|
||||
result._name = result.m_json.as_object()[L"name"].as_string();
|
||||
return result;
|
||||
}
|
||||
|
||||
PowerToyValues PowerToyValues::load_from_settings_file(const std::wstring & powertoy_name) {
|
||||
PowerToyValues result = PowerToyValues();
|
||||
result.m_json = PTSettingsHelper::load_module_settings(powertoy_name);
|
||||
result._name = powertoy_name;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
web::json::value add_property_generic(const std::wstring& name, T value) {
|
||||
std::vector<std::pair<std::wstring, web::json::value>> vector = { std::make_pair(L"value", web::json::value(value)) };
|
||||
return web::json::value::object(vector);
|
||||
}
|
||||
|
||||
template <>
|
||||
void PowerToyValues::add_property(const std::wstring& name, bool value) {
|
||||
m_json.as_object()[L"properties"].as_object()[name] = add_property_generic(name, value);
|
||||
};
|
||||
|
||||
template <>
|
||||
void PowerToyValues::add_property(const std::wstring& name, int value) {
|
||||
m_json.as_object()[L"properties"].as_object()[name] = add_property_generic(name, value);
|
||||
};
|
||||
|
||||
template <>
|
||||
void PowerToyValues::add_property(const std::wstring& name, std::wstring value) {
|
||||
m_json.as_object()[L"properties"].as_object()[name] = add_property_generic(name, value);
|
||||
};
|
||||
|
||||
bool PowerToyValues::is_bool_value(const std::wstring& property_name) {
|
||||
return m_json.is_object() &&
|
||||
m_json.has_object_field(L"properties") &&
|
||||
m_json[L"properties"].has_object_field(property_name) &&
|
||||
m_json[L"properties"][property_name].has_boolean_field(L"value");
|
||||
}
|
||||
|
||||
bool PowerToyValues::is_int_value(const std::wstring& property_name) {
|
||||
return m_json.is_object() &&
|
||||
m_json.has_object_field(L"properties") &&
|
||||
m_json[L"properties"].has_object_field(property_name) &&
|
||||
m_json[L"properties"][property_name].has_integer_field(L"value");
|
||||
}
|
||||
|
||||
bool PowerToyValues::is_string_value(const std::wstring& property_name) {
|
||||
return m_json.is_object() &&
|
||||
m_json.has_object_field(L"properties") &&
|
||||
m_json[L"properties"].has_object_field(property_name) &&
|
||||
m_json[L"properties"][property_name].has_string_field(L"value");
|
||||
}
|
||||
|
||||
bool PowerToyValues::get_bool_value(const std::wstring& property_name) {
|
||||
return m_json[L"properties"][property_name][L"value"].as_bool();
|
||||
}
|
||||
|
||||
int PowerToyValues::get_int_value(const std::wstring& property_name) {
|
||||
return m_json[L"properties"][property_name][L"value"].as_integer();
|
||||
}
|
||||
|
||||
std::wstring PowerToyValues::get_string_value(const std::wstring& property_name) {
|
||||
return m_json[L"properties"][property_name][L"value"].as_string();
|
||||
}
|
||||
|
||||
std::wstring PowerToyValues::serialize() {
|
||||
set_version();
|
||||
return m_json.serialize();
|
||||
}
|
||||
|
||||
void PowerToyValues::save_to_settings_file() {
|
||||
set_version();
|
||||
PTSettingsHelper::save_module_settings(_name, m_json);
|
||||
}
|
||||
|
||||
void PowerToyValues::set_version() {
|
||||
m_json.as_object()[L"version"] = web::json::value::string(m_version);
|
||||
}
|
||||
}
|
||||
96
src/common/settings_objects.h
Normal file
96
src/common/settings_objects.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <cpprest/json.h>
|
||||
|
||||
namespace PowerToysSettings {
|
||||
|
||||
class Settings {
|
||||
public:
|
||||
Settings(
|
||||
const HINSTANCE hinstance, // Module handle of the PowerToy DLL 'IMAGE_DOS_HEADER __ImageBase'
|
||||
const std::wstring& powertoy_name
|
||||
);
|
||||
|
||||
// Add additional general information to the PowerToy settings.
|
||||
void set_description(UINT resource_id);
|
||||
void set_description(const std::wstring& description);
|
||||
|
||||
void set_icon_key(const std::wstring& icon_key);
|
||||
void set_overview_link(const std::wstring& overview_link);
|
||||
void set_video_link(const std::wstring& video_link);
|
||||
|
||||
// Add properties to the PowerToy settings.
|
||||
void add_bool_toogle(const std::wstring& name, UINT description_resource_id, bool value);
|
||||
void add_bool_toogle(const std::wstring& name, const std::wstring& description, bool value);
|
||||
|
||||
void add_int_spinner(const std::wstring& name, UINT description_resource_id, int value, int min, int max, int step);
|
||||
void add_int_spinner(const std::wstring& name, const std::wstring& description, int value, int min, int max, int step);
|
||||
|
||||
void add_string(const std::wstring& name, UINT description_resource_id, const std::wstring& value);
|
||||
void add_string(const std::wstring& name, const std::wstring& description, const std::wstring& value);
|
||||
|
||||
void add_color_picker(const std::wstring& name, UINT description_resource_id, const std::wstring& value);
|
||||
void add_color_picker(const std::wstring& name, const std::wstring& description, const std::wstring& value);
|
||||
|
||||
void add_custom_action(const std::wstring& name, UINT description_resource_id, UINT button_text_resource_id, UINT ext_description_resource_id);
|
||||
void add_custom_action(const std::wstring& name, UINT description_resource_id, UINT button_text_resource_id, const std::wstring& value);
|
||||
void add_custom_action(const std::wstring& name, const std::wstring& description, const std::wstring& button_text, const std::wstring& value);
|
||||
|
||||
// Serialize the internal json to a string.
|
||||
std::wstring serialize();
|
||||
// Serialize the internal json to the input buffer.
|
||||
bool serialize_to_buffer(wchar_t* buffer, int *buffer_size);
|
||||
|
||||
private:
|
||||
web::json::value m_json;
|
||||
int m_curr_priority = 0; // For keeping order when adding elements.
|
||||
HINSTANCE m_instance;
|
||||
|
||||
std::wstring get_resource(UINT resource_id);
|
||||
};
|
||||
|
||||
class PowerToyValues {
|
||||
public:
|
||||
PowerToyValues(const std::wstring& powertoy_name);
|
||||
static PowerToyValues from_json_string(const std::wstring& json);
|
||||
static PowerToyValues load_from_settings_file(const std::wstring& powertoy_name);
|
||||
|
||||
template <typename T>
|
||||
void add_property(const std::wstring& name, T value);
|
||||
|
||||
// Check property value type
|
||||
bool is_bool_value(const std::wstring& property_name);
|
||||
bool is_int_value(const std::wstring& property_name);
|
||||
bool is_string_value(const std::wstring& property_name);
|
||||
|
||||
// Get property value
|
||||
bool get_bool_value(const std::wstring& property_name);
|
||||
int get_int_value(const std::wstring& property_name);
|
||||
std::wstring get_string_value(const std::wstring& property_name);
|
||||
|
||||
std::wstring serialize();
|
||||
void save_to_settings_file();
|
||||
|
||||
private:
|
||||
const std::wstring m_version = L"1.0";
|
||||
void set_version();
|
||||
web::json::value m_json;
|
||||
std::wstring _name;
|
||||
PowerToyValues() {}
|
||||
};
|
||||
|
||||
class CustomActionObject {
|
||||
public:
|
||||
static CustomActionObject from_json_string(const std::wstring& json) {
|
||||
web::json::value parsed_json = web::json::value::parse(json);
|
||||
return CustomActionObject(parsed_json);
|
||||
}
|
||||
|
||||
std::wstring get_name() { return m_json[L"action_name"].as_string(); }
|
||||
std::wstring get_value() { return m_json[L"value"].as_string(); }
|
||||
|
||||
protected:
|
||||
CustomActionObject(web::json::value action_json) : m_json(action_json) {};
|
||||
web::json::value m_json;
|
||||
};
|
||||
}
|
||||
16
src/common/start_visible.cpp
Normal file
16
src/common/start_visible.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "pch.h"
|
||||
#include "start_visible.h"
|
||||
|
||||
bool is_start_visible() {
|
||||
static winrt::com_ptr<IAppVisibility> app_visibility;
|
||||
if (!app_visibility) {
|
||||
winrt::check_hresult(CoCreateInstance(CLSID_AppVisibility,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
__uuidof(app_visibility),
|
||||
app_visibility.put_void()));
|
||||
}
|
||||
BOOL visible;
|
||||
auto result = app_visibility->IsLauncherVisible(&visible);
|
||||
return SUCCEEDED(result) && visible;
|
||||
}
|
||||
4
src/common/start_visible.h
Normal file
4
src/common/start_visible.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
bool is_start_visible();
|
||||
|
||||
93
src/common/tasklist_positions.cpp
Normal file
93
src/common/tasklist_positions.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "pch.h"
|
||||
#include "tasklist_positions.h"
|
||||
|
||||
void Tasklist::update() {
|
||||
// Get HWND of the tasklist
|
||||
auto tasklist_hwnd = FindWindowA("Shell_TrayWnd", nullptr);
|
||||
if (!tasklist_hwnd) return;
|
||||
tasklist_hwnd = FindWindowExA(tasklist_hwnd, 0, "ReBarWindow32", nullptr);
|
||||
if (!tasklist_hwnd) return;
|
||||
tasklist_hwnd = FindWindowExA(tasklist_hwnd, 0, "MSTaskSwWClass", nullptr);
|
||||
if (!tasklist_hwnd) return;
|
||||
tasklist_hwnd = FindWindowExA(tasklist_hwnd, 0, "MSTaskListWClass", nullptr);
|
||||
if (!tasklist_hwnd) return;
|
||||
if (!automation) {
|
||||
winrt::check_hresult(CoCreateInstance(CLSID_CUIAutomation,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IUIAutomation,
|
||||
automation.put_void()));
|
||||
winrt::check_hresult(automation->CreateTrueCondition(true_condition.put()));
|
||||
}
|
||||
element = nullptr;
|
||||
winrt::check_hresult(automation->ElementFromHandle(tasklist_hwnd, element.put()));
|
||||
}
|
||||
|
||||
bool Tasklist::update_buttons(std::vector<TasklistButton>& buttons) {
|
||||
if (!automation || !element) {
|
||||
return false;
|
||||
}
|
||||
winrt::com_ptr<IUIAutomationElementArray> elements;
|
||||
if (element->FindAll(TreeScope_Children, true_condition.get(), elements.put()) < 0)
|
||||
return false;
|
||||
if (!elements)
|
||||
return false;
|
||||
int count;
|
||||
if (elements->get_Length(&count) < 0)
|
||||
return false;
|
||||
winrt::com_ptr<IUIAutomationElement> child;
|
||||
std::vector<TasklistButton> found_butttons;
|
||||
found_butttons.reserve(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
child = nullptr;
|
||||
if (elements->GetElement(i, child.put()) < 0)
|
||||
return false;
|
||||
TasklistButton button;
|
||||
if (VARIANT var_rect; child->GetCurrentPropertyValue(UIA_BoundingRectanglePropertyId, &var_rect) >= 0) {
|
||||
if (var_rect.vt == (VT_R8 | VT_ARRAY)) {
|
||||
LONG pos;
|
||||
double value;
|
||||
pos = 0; SafeArrayGetElement(var_rect.parray, &pos, &value);
|
||||
button.x = (long)value;
|
||||
pos = 1; SafeArrayGetElement(var_rect.parray, &pos, &value);
|
||||
button.y = (long)value;
|
||||
pos = 2; SafeArrayGetElement(var_rect.parray, &pos, &value);
|
||||
button.width = (long)value;
|
||||
pos = 3; SafeArrayGetElement(var_rect.parray, &pos, &value);
|
||||
button.height = (long)value;
|
||||
}
|
||||
VariantClear(&var_rect);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (BSTR automation_id; child->get_CurrentAutomationId(&automation_id) >= 0) {
|
||||
button.name = automation_id;
|
||||
SysFreeString(automation_id);
|
||||
}
|
||||
found_butttons.push_back(button);
|
||||
}
|
||||
// assign keynums
|
||||
buttons.clear();
|
||||
for (auto& button : found_butttons) {
|
||||
if (buttons.empty()) {
|
||||
button.keynum = 1;
|
||||
buttons.push_back(std::move(button));
|
||||
} else {
|
||||
if (button.x < buttons.back().x || button.y < buttons.back().y) // skip 2nd row
|
||||
break;
|
||||
if (button.name == buttons.back().name)
|
||||
continue; // skip buttons from the same app
|
||||
button.keynum = buttons.back().keynum + 1;
|
||||
buttons.push_back(std::move(button));
|
||||
if (button.keynum == 10)
|
||||
break; // no more than 10 buttons
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<TasklistButton> Tasklist::get_buttons() {
|
||||
std::vector<TasklistButton> buttons;
|
||||
update_buttons(buttons);
|
||||
return buttons;
|
||||
}
|
||||
22
src/common/tasklist_positions.h
Normal file
22
src/common/tasklist_positions.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
#include <Windows.h>
|
||||
#include <UIAutomationClient.h>
|
||||
|
||||
struct TasklistButton {
|
||||
std::wstring name;
|
||||
long x, y, width, height, keynum;
|
||||
};
|
||||
|
||||
class Tasklist {
|
||||
public:
|
||||
void update();
|
||||
std::vector<TasklistButton> get_buttons();
|
||||
bool update_buttons(std::vector<TasklistButton>& buttons);
|
||||
private:
|
||||
winrt::com_ptr<IUIAutomation> automation;
|
||||
winrt::com_ptr<IUIAutomationElement> element;
|
||||
winrt::com_ptr<IUIAutomationCondition> true_condition;
|
||||
};
|
||||
429
src/common/two_way_pipe_message_ipc.h
Normal file
429
src/common/two_way_pipe_message_ipc.h
Normal file
@@ -0,0 +1,429 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include "async_message_queue.h"
|
||||
#include <WinSafer.h>
|
||||
#include <Sddl.h>
|
||||
#include <accctrl.h>
|
||||
#include <aclapi.h>
|
||||
|
||||
class TwoWayPipeMessageIPC {
|
||||
public:
|
||||
typedef void(*callback_function)(const std::wstring&);
|
||||
void send(std::wstring msg) {
|
||||
output_queue.queue_message(msg);
|
||||
}
|
||||
TwoWayPipeMessageIPC(std::wstring _input_pipe_name, std::wstring _output_pipe_name, callback_function p_func) {
|
||||
input_pipe_name = _input_pipe_name;
|
||||
output_pipe_name = _output_pipe_name;
|
||||
dispatch_inc_message_function = p_func;
|
||||
}
|
||||
void start(HANDLE _restricted_pipe_token) {
|
||||
output_queue_thread = std::thread(&TwoWayPipeMessageIPC::consume_output_queue_thread, this);
|
||||
input_queue_thread = std::thread(&TwoWayPipeMessageIPC::consume_input_queue_thread, this);
|
||||
input_pipe_thread = std::thread(&TwoWayPipeMessageIPC::start_named_pipe_server, this, _restricted_pipe_token);
|
||||
}
|
||||
|
||||
void end() {
|
||||
closed = true;
|
||||
input_queue.interrupt();
|
||||
input_queue_thread.join();
|
||||
output_queue.interrupt();
|
||||
output_queue_thread.join();
|
||||
pipe_connect_handle_mutex.lock();
|
||||
if (current_connect_pipe_handle != NULL) {
|
||||
//Cancels the Pipe currently waiting for a connection.
|
||||
CancelIoEx(current_connect_pipe_handle,NULL);
|
||||
}
|
||||
pipe_connect_handle_mutex.unlock();
|
||||
input_pipe_thread.join();
|
||||
}
|
||||
|
||||
private:
|
||||
AsyncMessageQueue input_queue;
|
||||
AsyncMessageQueue output_queue;
|
||||
std::wstring output_pipe_name;
|
||||
std::wstring input_pipe_name;
|
||||
std::thread input_queue_thread;
|
||||
std::thread output_queue_thread;
|
||||
std::thread input_pipe_thread;
|
||||
std::mutex pipe_connect_handle_mutex; // For manipulating the current_connect_pipe
|
||||
|
||||
HANDLE current_connect_pipe_handle = NULL;
|
||||
bool closed = false;
|
||||
TwoWayPipeMessageIPC::callback_function dispatch_inc_message_function;
|
||||
const DWORD BUFSIZE = 1024;
|
||||
|
||||
void send_pipe_message(std::wstring message) {
|
||||
// Adapted from https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipe-client
|
||||
HANDLE output_pipe_handle;
|
||||
const wchar_t* message_send = message.c_str();
|
||||
BOOL fSuccess = FALSE;
|
||||
DWORD cbToWrite, cbWritten, dwMode;
|
||||
const wchar_t* lpszPipename = output_pipe_name.c_str();
|
||||
|
||||
// Try to open a named pipe; wait for it, if necessary.
|
||||
|
||||
while (1) {
|
||||
output_pipe_handle = CreateFile(
|
||||
lpszPipename, // pipe name
|
||||
GENERIC_READ | // read and write access
|
||||
GENERIC_WRITE,
|
||||
0, // no sharing
|
||||
NULL, // default security attributes
|
||||
OPEN_EXISTING, // opens existing pipe
|
||||
0, // default attributes
|
||||
NULL); // no template file
|
||||
|
||||
// Break if the pipe handle is valid.
|
||||
|
||||
if (output_pipe_handle != INVALID_HANDLE_VALUE)
|
||||
break;
|
||||
|
||||
// Exit if an error other than ERROR_PIPE_BUSY occurs.
|
||||
DWORD curr_error = 0;
|
||||
if ((curr_error = GetLastError()) != ERROR_PIPE_BUSY) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// All pipe instances are busy, so wait for 20 seconds.
|
||||
|
||||
if (!WaitNamedPipe(lpszPipename, 20000)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
dwMode = PIPE_READMODE_MESSAGE;
|
||||
fSuccess = SetNamedPipeHandleState(
|
||||
output_pipe_handle, // pipe handle
|
||||
&dwMode, // new pipe mode
|
||||
NULL, // don't set maximum bytes
|
||||
NULL); // don't set maximum time
|
||||
if (!fSuccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send a message to the pipe server.
|
||||
|
||||
cbToWrite = (lstrlen(message_send)) * sizeof(WCHAR); // no need to send final '\0'. Pipe is in message mode.
|
||||
|
||||
fSuccess = WriteFile(
|
||||
output_pipe_handle, // pipe handle
|
||||
message_send, // message
|
||||
cbToWrite, // message length
|
||||
&cbWritten, // bytes written
|
||||
NULL); // not overlapped
|
||||
if (!fSuccess) {
|
||||
return;
|
||||
}
|
||||
CloseHandle(output_pipe_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
void consume_output_queue_thread() {
|
||||
while (!closed) {
|
||||
std::wstring message = output_queue.pop_message();
|
||||
if (message.length() == 0) {
|
||||
break;
|
||||
}
|
||||
send_pipe_message(message);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL GetLogonSID(HANDLE hToken, PSID *ppsid) {
|
||||
// From https://docs.microsoft.com/en-us/previous-versions/aa446670(v=vs.85)
|
||||
BOOL bSuccess = FALSE;
|
||||
DWORD dwIndex;
|
||||
DWORD dwLength = 0;
|
||||
PTOKEN_GROUPS ptg = NULL;
|
||||
|
||||
// Verify the parameter passed in is not NULL.
|
||||
if (NULL == ppsid)
|
||||
goto Cleanup;
|
||||
|
||||
// Get required buffer size and allocate the TOKEN_GROUPS buffer.
|
||||
|
||||
if (!GetTokenInformation(
|
||||
hToken, // handle to the access token
|
||||
TokenGroups, // get information about the token's groups
|
||||
(LPVOID)ptg, // pointer to TOKEN_GROUPS buffer
|
||||
0, // size of buffer
|
||||
&dwLength // receives required buffer size
|
||||
)) {
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
goto Cleanup;
|
||||
|
||||
ptg = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(),
|
||||
HEAP_ZERO_MEMORY, dwLength);
|
||||
|
||||
if (ptg == NULL)
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
// Get the token group information from the access token.
|
||||
|
||||
if (!GetTokenInformation(
|
||||
hToken, // handle to the access token
|
||||
TokenGroups, // get information about the token's groups
|
||||
(LPVOID)ptg, // pointer to TOKEN_GROUPS buffer
|
||||
dwLength, // size of buffer
|
||||
&dwLength // receives required buffer size
|
||||
)) {
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
// Loop through the groups to find the logon SID.
|
||||
|
||||
for (dwIndex = 0; dwIndex < ptg->GroupCount; dwIndex++)
|
||||
if ((ptg->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID)
|
||||
== SE_GROUP_LOGON_ID) {
|
||||
// Found the logon SID; make a copy of it.
|
||||
|
||||
dwLength = GetLengthSid(ptg->Groups[dwIndex].Sid);
|
||||
*ppsid = (PSID)HeapAlloc(GetProcessHeap(),
|
||||
HEAP_ZERO_MEMORY, dwLength);
|
||||
if (*ppsid == NULL)
|
||||
goto Cleanup;
|
||||
if (!CopySid(dwLength, *ppsid, ptg->Groups[dwIndex].Sid)) {
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid);
|
||||
goto Cleanup;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
bSuccess = TRUE;
|
||||
|
||||
Cleanup:
|
||||
|
||||
// Free the buffer for the token groups.
|
||||
|
||||
if (ptg != NULL)
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)ptg);
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
VOID FreeLogonSID(PSID *ppsid) {
|
||||
// From https://docs.microsoft.com/en-us/previous-versions/aa446670(v=vs.85)
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid);
|
||||
}
|
||||
|
||||
|
||||
int change_pipe_security_allow_restricted_token(HANDLE handle, HANDLE token) {
|
||||
PACL old_dacl, new_dacl;
|
||||
PSECURITY_DESCRIPTOR sd;
|
||||
EXPLICIT_ACCESS ea;
|
||||
PSID user_restricted;
|
||||
int error;
|
||||
|
||||
if (!GetLogonSID(token, &user_restricted)) {
|
||||
error = 5; // No access error.
|
||||
goto Ldone;
|
||||
}
|
||||
|
||||
if (GetSecurityInfo(handle,
|
||||
SE_KERNEL_OBJECT,
|
||||
DACL_SECURITY_INFORMATION,
|
||||
NULL,
|
||||
NULL,
|
||||
&old_dacl,
|
||||
NULL,
|
||||
&sd)) {
|
||||
error = GetLastError();
|
||||
goto Lclean_sid;
|
||||
}
|
||||
|
||||
memset(&ea, 0, sizeof(EXPLICIT_ACCESS));
|
||||
ea.grfAccessPermissions |= GENERIC_READ | FILE_WRITE_ATTRIBUTES;
|
||||
ea.grfAccessPermissions |= GENERIC_WRITE | FILE_READ_ATTRIBUTES;
|
||||
ea.grfAccessPermissions |= SYNCHRONIZE;
|
||||
ea.grfAccessMode = SET_ACCESS;
|
||||
ea.grfInheritance = NO_INHERITANCE;
|
||||
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||||
ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
|
||||
ea.Trustee.ptstrName = (LPTSTR)user_restricted;
|
||||
|
||||
if (SetEntriesInAcl(1, &ea, old_dacl, &new_dacl)) {
|
||||
error = GetLastError();
|
||||
goto Lclean_sd;
|
||||
}
|
||||
|
||||
if (SetSecurityInfo(handle,
|
||||
SE_KERNEL_OBJECT,
|
||||
DACL_SECURITY_INFORMATION,
|
||||
NULL,
|
||||
NULL,
|
||||
new_dacl,
|
||||
NULL)) {
|
||||
error = GetLastError();
|
||||
goto Lclean_dacl;
|
||||
}
|
||||
|
||||
error = 0;
|
||||
|
||||
Lclean_dacl:
|
||||
LocalFree((HLOCAL)new_dacl);
|
||||
Lclean_sd:
|
||||
LocalFree((HLOCAL)sd);
|
||||
Lclean_sid:
|
||||
FreeLogonSID(&user_restricted);
|
||||
Ldone:
|
||||
return error;
|
||||
}
|
||||
|
||||
HANDLE create_medium_integrity_token() {
|
||||
HANDLE restricted_token_handle;
|
||||
SAFER_LEVEL_HANDLE level_handle = NULL;
|
||||
DWORD sid_size = SECURITY_MAX_SID_SIZE;
|
||||
BYTE medium_sid[SECURITY_MAX_SID_SIZE];
|
||||
if (!SaferCreateLevel(SAFER_SCOPEID_USER, SAFER_LEVELID_NORMALUSER, SAFER_LEVEL_OPEN, &level_handle, NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
if (!SaferComputeTokenFromLevel(level_handle, NULL, &restricted_token_handle, 0, NULL)) {
|
||||
SaferCloseLevel(level_handle);
|
||||
return NULL;
|
||||
}
|
||||
SaferCloseLevel(level_handle);
|
||||
|
||||
if (!CreateWellKnownSid(WinMediumLabelSid, nullptr, medium_sid, &sid_size)) {
|
||||
CloseHandle(restricted_token_handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TOKEN_MANDATORY_LABEL integrity_level = { 0 };
|
||||
integrity_level.Label.Attributes = SE_GROUP_INTEGRITY;
|
||||
integrity_level.Label.Sid = reinterpret_cast<PSID>(medium_sid);
|
||||
|
||||
if (!SetTokenInformation(restricted_token_handle, TokenIntegrityLevel, &integrity_level, sizeof(integrity_level))) {
|
||||
CloseHandle(restricted_token_handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return restricted_token_handle;
|
||||
}
|
||||
|
||||
void handle_pipe_connection(HANDLE input_pipe_handle) {
|
||||
//Adapted from https://docs.microsoft.com/en-us/windows/win32/ipc/multithreaded-pipe-server
|
||||
HANDLE hHeap = GetProcessHeap();
|
||||
uint8_t* pchRequest = (uint8_t*)HeapAlloc(hHeap, 0, BUFSIZE * sizeof(uint8_t));
|
||||
|
||||
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
|
||||
BOOL fSuccess = FALSE;
|
||||
|
||||
// Do some extra error checking since the app will keep running even if this thread fails.
|
||||
std::list<std::vector<uint8_t>> message_parts;
|
||||
|
||||
if (input_pipe_handle == NULL) {
|
||||
if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pchRequest == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop until done reading
|
||||
do {
|
||||
// Read client requests from the pipe. This simplistic code only allows messages
|
||||
// up to BUFSIZE characters in length.
|
||||
ZeroMemory(pchRequest, BUFSIZE * sizeof(uint8_t));
|
||||
fSuccess = ReadFile(
|
||||
input_pipe_handle, // handle to pipe
|
||||
pchRequest, // buffer to receive data
|
||||
BUFSIZE * sizeof(uint8_t), // size of buffer
|
||||
&cbBytesRead, // number of bytes read
|
||||
NULL); // not overlapped I/O
|
||||
|
||||
if (!fSuccess && GetLastError() != ERROR_MORE_DATA) {
|
||||
break;
|
||||
}
|
||||
std::vector<uint8_t> part_vector;
|
||||
part_vector.reserve(cbBytesRead);
|
||||
std::copy(pchRequest, pchRequest + cbBytesRead, std::back_inserter(part_vector));
|
||||
message_parts.push_back(part_vector);
|
||||
} while (!fSuccess);
|
||||
|
||||
if (fSuccess) {
|
||||
// Reconstruct the total_message.
|
||||
std::vector<uint8_t> reconstructed_message;
|
||||
size_t total_size = 0;
|
||||
for (auto& part_vector : message_parts) {
|
||||
total_size += part_vector.size();
|
||||
}
|
||||
reconstructed_message.reserve(total_size);
|
||||
for (auto& part_vector : message_parts) {
|
||||
std::move(part_vector.begin(), part_vector.end(), std::back_inserter(reconstructed_message));
|
||||
}
|
||||
std::wstring unicode_msg;
|
||||
unicode_msg.assign(reinterpret_cast<std::wstring::const_pointer>(reconstructed_message.data()), reconstructed_message.size() / sizeof(std::wstring::value_type));
|
||||
input_queue.queue_message(unicode_msg);
|
||||
}
|
||||
|
||||
// Flush the pipe to allow the client to read the pipe's contents
|
||||
// before disconnecting. Then disconnect the pipe, and close the
|
||||
// handle to this pipe instance.
|
||||
|
||||
FlushFileBuffers(input_pipe_handle);
|
||||
DisconnectNamedPipe(input_pipe_handle);
|
||||
CloseHandle(input_pipe_handle);
|
||||
|
||||
HeapFree(hHeap, 0, pchRequest);
|
||||
|
||||
printf("InstanceThread exitting.\n");
|
||||
}
|
||||
|
||||
void start_named_pipe_server(HANDLE token) {
|
||||
// Adapted from https://docs.microsoft.com/en-us/windows/win32/ipc/multithreaded-pipe-server
|
||||
const wchar_t* pipe_name = input_pipe_name.c_str();
|
||||
BOOL connected = FALSE;
|
||||
HANDLE connect_pipe_handle = INVALID_HANDLE_VALUE;
|
||||
while(!closed) {
|
||||
{
|
||||
std::unique_lock lock(pipe_connect_handle_mutex);
|
||||
connect_pipe_handle = CreateNamedPipe(
|
||||
pipe_name,
|
||||
PIPE_ACCESS_DUPLEX |
|
||||
WRITE_DAC,
|
||||
PIPE_TYPE_MESSAGE |
|
||||
PIPE_READMODE_MESSAGE |
|
||||
PIPE_WAIT,
|
||||
PIPE_UNLIMITED_INSTANCES,
|
||||
BUFSIZE,
|
||||
BUFSIZE,
|
||||
0,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (connect_pipe_handle == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (token != NULL) {
|
||||
int err = change_pipe_security_allow_restricted_token(connect_pipe_handle, token);
|
||||
}
|
||||
current_connect_pipe_handle = connect_pipe_handle;
|
||||
}
|
||||
connected = ConnectNamedPipe(connect_pipe_handle, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
|
||||
{
|
||||
std::unique_lock lock(pipe_connect_handle_mutex);
|
||||
current_connect_pipe_handle = NULL;
|
||||
}
|
||||
if (connected) {
|
||||
std::thread(&TwoWayPipeMessageIPC::handle_pipe_connection, this, connect_pipe_handle).detach();
|
||||
} else {
|
||||
// Client could not connect.
|
||||
CloseHandle(connect_pipe_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void consume_input_queue_thread() {
|
||||
while (!closed) {
|
||||
std::wstring message = input_queue.pop_message();
|
||||
if (message.length() == 0) {
|
||||
break;
|
||||
}
|
||||
dispatch_inc_message_function(message);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
71
src/common/windows_colors.cpp
Normal file
71
src/common/windows_colors.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "pch.h"
|
||||
#include "windows_colors.h"
|
||||
|
||||
DWORD WindowsColors::rgb_color(DWORD abgr_color) {
|
||||
// registry keeps the colors in ABGR format, we want RGB
|
||||
auto r = (abgr_color & 0xFF);
|
||||
auto g = (abgr_color & 0xFF00) >> 8;
|
||||
auto b = (abgr_color & 0xFF0000) >> 16;
|
||||
return (r << 16) | (g << 8) | b;
|
||||
}
|
||||
DWORD WindowsColors::rgb_color(winrt::Windows::UI::Color color) {
|
||||
return ((DWORD)color.R << 16) | ((DWORD)color.G << 8) | ((DWORD)color.B);
|
||||
}
|
||||
WindowsColors::Color WindowsColors::get_button_face_color() {
|
||||
winrt::Windows::UI::ViewManagement::UISettings uiSettings;
|
||||
return uiSettings.UIElementColor(winrt::Windows::UI::ViewManagement::UIElementType::ButtonFace);
|
||||
}
|
||||
WindowsColors::Color WindowsColors::get_button_text_color() {
|
||||
winrt::Windows::UI::ViewManagement::UISettings uiSettings;
|
||||
return uiSettings.UIElementColor(winrt::Windows::UI::ViewManagement::UIElementType::ButtonText);
|
||||
}
|
||||
WindowsColors::Color WindowsColors::get_highlight_color() {
|
||||
winrt::Windows::UI::ViewManagement::UISettings uiSettings;
|
||||
return uiSettings.UIElementColor(winrt::Windows::UI::ViewManagement::UIElementType::Highlight);
|
||||
}
|
||||
WindowsColors::Color WindowsColors::get_hotlight_color() {
|
||||
winrt::Windows::UI::ViewManagement::UISettings uiSettings;
|
||||
return uiSettings.UIElementColor(winrt::Windows::UI::ViewManagement::UIElementType::Hotlight);
|
||||
}
|
||||
WindowsColors::Color WindowsColors::get_highlight_text_color() {
|
||||
winrt::Windows::UI::ViewManagement::UISettings uiSettings;
|
||||
return uiSettings.UIElementColor(winrt::Windows::UI::ViewManagement::UIElementType::HighlightText);
|
||||
}
|
||||
WindowsColors::Color WindowsColors::get_accent_light_1_color() {
|
||||
winrt::Windows::UI::ViewManagement::UISettings uiSettings;
|
||||
return uiSettings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::AccentLight1);
|
||||
}
|
||||
WindowsColors::Color WindowsColors::get_accent_light_2_color() {
|
||||
winrt::Windows::UI::ViewManagement::UISettings uiSettings;
|
||||
return uiSettings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::AccentLight2);
|
||||
}
|
||||
WindowsColors::Color WindowsColors::get_accent_dark_1_color() {
|
||||
winrt::Windows::UI::ViewManagement::UISettings uiSettings;
|
||||
return uiSettings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::AccentDark1);
|
||||
}
|
||||
WindowsColors::Color WindowsColors::get_accent_color() {
|
||||
winrt::Windows::UI::ViewManagement::UISettings uiSettings;
|
||||
return uiSettings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Accent);
|
||||
}
|
||||
WindowsColors::Color WindowsColors::get_background_color() {
|
||||
winrt::Windows::UI::ViewManagement::UISettings uiSettings;
|
||||
return uiSettings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Background);
|
||||
}
|
||||
|
||||
bool WindowsColors::update() {
|
||||
auto new_accent_color_menu = rgb_color(get_accent_color());
|
||||
auto new_start_color_menu = new_accent_color_menu;
|
||||
auto new_desktop_fill_color = rgb_color(GetSysColor(COLOR_DESKTOP));
|
||||
auto new_light_mode = rgb_color(get_background_color()) != 0; //Dark mode will have black as the background color.
|
||||
|
||||
bool changed = new_accent_color_menu != accent_color_menu ||
|
||||
new_start_color_menu != start_color_menu ||
|
||||
new_light_mode != light_mode ||
|
||||
new_desktop_fill_color != desktop_fill_color;
|
||||
accent_color_menu = new_accent_color_menu;
|
||||
start_color_menu = new_start_color_menu;
|
||||
light_mode = new_light_mode;
|
||||
desktop_fill_color = new_desktop_fill_color;
|
||||
|
||||
return changed;
|
||||
}
|
||||
27
src/common/windows_colors.h
Normal file
27
src/common/windows_colors.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <winrt/Windows.UI.ViewManagement.h>
|
||||
|
||||
struct WindowsColors {
|
||||
using Color = winrt::Windows::UI::Color;
|
||||
|
||||
static DWORD rgb_color(DWORD abgr_color);
|
||||
static DWORD rgb_color(Color color);
|
||||
static Color get_button_face_color();
|
||||
static Color get_button_text_color();
|
||||
static Color get_highlight_color();
|
||||
static Color get_hotlight_color();
|
||||
static Color get_highlight_text_color();
|
||||
static Color get_accent_light_1_color();
|
||||
static Color get_accent_light_2_color();
|
||||
static Color get_accent_dark_1_color();
|
||||
static Color get_accent_color();
|
||||
static Color get_background_color();
|
||||
|
||||
// Update colors - returns true if the values where changed
|
||||
bool update();
|
||||
|
||||
DWORD accent_color_menu = 0,
|
||||
start_color_menu = 0,
|
||||
desktop_fill_color = 0;
|
||||
bool light_mode = true;
|
||||
};
|
||||
Reference in New Issue
Block a user